Skip to content

Commit

Permalink
Merge pull request #36 from ruslanskorb/develop
Browse files Browse the repository at this point in the history
Version bump (1.1.0)
  • Loading branch information
ruslanskorb committed Mar 11, 2015
2 parents 01fc568 + baea02f commit bc1b146
Show file tree
Hide file tree
Showing 10 changed files with 655 additions and 85 deletions.
18 changes: 12 additions & 6 deletions Example/RSKImageCropperExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
B803F69E1AAB0A1F004141CF /* CGGeometry+RSKImageCropper.m in Sources */ = {isa = PBXBuildFile; fileRef = B803F69D1AAB0A1F004141CF /* CGGeometry+RSKImageCropper.m */; };
B87A99F019A4D2CD00D12CD4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B87A99EF19A4D2CD00D12CD4 /* Foundation.framework */; };
B87A99F219A4D2CD00D12CD4 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B87A99F119A4D2CD00D12CD4 /* CoreGraphics.framework */; };
B87A99F419A4D2CD00D12CD4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B87A99F319A4D2CD00D12CD4 /* UIKit.framework */; };
Expand All @@ -23,7 +24,7 @@
B87A9A2C19A4D39800D12CD4 /* RSKImageScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = B87A9A2419A4D39800D12CD4 /* RSKImageScrollView.m */; };
B87A9A2D19A4D39800D12CD4 /* RSKImageCropViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B87A9A2719A4D39800D12CD4 /* RSKImageCropViewController.m */; };
B87A9A2E19A4D39800D12CD4 /* RSKTouchView.m in Sources */ = {isa = PBXBuildFile; fileRef = B87A9A2919A4D39800D12CD4 /* RSKTouchView.m */; };
B87A9A2F19A4D39800D12CD4 /* UIImage+FixOrientation.m in Sources */ = {isa = PBXBuildFile; fileRef = B87A9A2B19A4D39800D12CD4 /* UIImage+FixOrientation.m */; };
B87A9A2F19A4D39800D12CD4 /* UIImage+RSKImageCropper.m in Sources */ = {isa = PBXBuildFile; fileRef = B87A9A2B19A4D39800D12CD4 /* UIImage+RSKImageCropper.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -37,6 +38,8 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
B803F69C1AAB0A1F004141CF /* CGGeometry+RSKImageCropper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CGGeometry+RSKImageCropper.h"; sourceTree = "<group>"; };
B803F69D1AAB0A1F004141CF /* CGGeometry+RSKImageCropper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CGGeometry+RSKImageCropper.m"; sourceTree = "<group>"; };
B87A99EC19A4D2CD00D12CD4 /* RSKImageCropperExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RSKImageCropperExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
B87A99EF19A4D2CD00D12CD4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
B87A99F119A4D2CD00D12CD4 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
Expand All @@ -62,8 +65,8 @@
B87A9A2719A4D39800D12CD4 /* RSKImageCropViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSKImageCropViewController.m; sourceTree = "<group>"; };
B87A9A2819A4D39800D12CD4 /* RSKTouchView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RSKTouchView.h; sourceTree = "<group>"; };
B87A9A2919A4D39800D12CD4 /* RSKTouchView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RSKTouchView.m; sourceTree = "<group>"; };
B87A9A2A19A4D39800D12CD4 /* UIImage+FixOrientation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+FixOrientation.h"; sourceTree = "<group>"; };
B87A9A2B19A4D39800D12CD4 /* UIImage+FixOrientation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+FixOrientation.m"; sourceTree = "<group>"; };
B87A9A2A19A4D39800D12CD4 /* UIImage+RSKImageCropper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+RSKImageCropper.h"; sourceTree = "<group>"; };
B87A9A2B19A4D39800D12CD4 /* UIImage+RSKImageCropper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+RSKImageCropper.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -183,8 +186,10 @@
B87A9A2419A4D39800D12CD4 /* RSKImageScrollView.m */,
B87A9A2819A4D39800D12CD4 /* RSKTouchView.h */,
B87A9A2919A4D39800D12CD4 /* RSKTouchView.m */,
B87A9A2A19A4D39800D12CD4 /* UIImage+FixOrientation.h */,
B87A9A2B19A4D39800D12CD4 /* UIImage+FixOrientation.m */,
B803F69C1AAB0A1F004141CF /* CGGeometry+RSKImageCropper.h */,
B803F69D1AAB0A1F004141CF /* CGGeometry+RSKImageCropper.m */,
B87A9A2A19A4D39800D12CD4 /* UIImage+RSKImageCropper.h */,
B87A9A2B19A4D39800D12CD4 /* UIImage+RSKImageCropper.m */,
);
name = RSKImageCropper;
path = ../RSKImageCropper;
Expand Down Expand Up @@ -297,9 +302,10 @@
B87A9A2C19A4D39800D12CD4 /* RSKImageScrollView.m in Sources */,
B87A9A2E19A4D39800D12CD4 /* RSKTouchView.m in Sources */,
B87A9A2019A4D31100D12CD4 /* RSKExampleViewController.m in Sources */,
B87A9A2F19A4D39800D12CD4 /* UIImage+FixOrientation.m in Sources */,
B87A9A2F19A4D39800D12CD4 /* UIImage+RSKImageCropper.m in Sources */,
B87A99FC19A4D2CD00D12CD4 /* main.m in Sources */,
B87A9A2D19A4D39800D12CD4 /* RSKImageCropViewController.m in Sources */,
B803F69E1AAB0A1F004141CF /* CGGeometry+RSKImageCropper.m in Sources */,
B87A9A0019A4D2CD00D12CD4 /* RSKAppDelegate.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<string>1.1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
Expand Down
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Just create a view controller for image cropping and set the delegate.

## Delegate

`RSKImageCropViewControllerDelegate` provides three delegate methods. To use them, implement the delegate in your view controller.
`RSKImageCropViewControllerDelegate` provides four delegate methods. To use them, implement the delegate in your view controller.

```objective-c
@interface ViewController () <RSKImageCropViewControllerDelegate>
Expand All @@ -54,14 +54,27 @@ Then implement the delegate functions.
}
// The original image has been cropped.
- (void)imageCropViewController:(RSKImageCropViewController *)controller didCropImage:(UIImage *)croppedImage usingCropRect:(CGRect)cropRect
- (void)imageCropViewController:(RSKImageCropViewController *)controller
didCropImage:(UIImage *)croppedImage
usingCropRect:(CGRect)cropRect
{
self.imageView.image = croppedImage;
[self.navigationController popViewControllerAnimated:YES];
}
// The original image has been cropped. Additionally provides a rotation angle used to produce image.
- (void)imageCropViewController:(RSKImageCropViewController *)controller
didCropImage:(UIImage *)croppedImage
usingCropRect:(CGRect)cropRect
rotationAngle:(CGFloat)rotationAngle
{
self.imageView.image = croppedImage;
[self.navigationController popViewControllerAnimated:YES];
}
// The original image will be cropped.
- (void)imageCropViewController:(RSKImageCropViewController *)controller willCropImage:(UIImage *)originalImage
- (void)imageCropViewController:(RSKImageCropViewController *)controller
willCropImage:(UIImage *)originalImage
{
// Use when `applyMaskToCroppedImage` set to YES.
[SVProgressHUD show];
Expand All @@ -70,7 +83,7 @@ Then implement the delegate functions.

## DataSource

`RSKImageCropViewControllerDataSource` provides two data source methods. The method `imageCropViewControllerCustomMaskRect:` asks the data source a custom rect for the mask. The method `imageCropViewControllerCustomMaskPath:` asks the data source a custom path for the mask. To use them, implement the data source in your view controller.
`RSKImageCropViewControllerDataSource` provides three data source methods. The method `imageCropViewControllerCustomMaskRect:` asks the data source a custom rect for the mask. The method `imageCropViewControllerCustomMaskPath:` asks the data source a custom path for the mask. The method `imageCropViewControllerCustomMovementRect:` asks the data source a custom rect in which the image can be moved. To use them, implement the data source in your view controller.

```objective-c
@interface ViewController () <RSKImageCropViewControllerDataSource>
Expand Down Expand Up @@ -116,6 +129,12 @@ Then implement the data source functions.
return triangle;
}
// Returns a custom rect in which the image can be moved.
- (CGRect)imageCropViewControllerCustomMovementRect:(RSKImageCropViewController *)controller
{
return [self imageCropViewControllerCustomMaskRect:controller];
}
```

## Coming Soon
Expand Down
2 changes: 1 addition & 1 deletion RSKImageCropper.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'RSKImageCropper'
s.version = '1.0.0'
s.version = '1.1.0'
s.summary = 'An image cropper for iOS like in the Contacts app with support for landscape orientation.'
s.homepage = 'https://github.com/ruslanskorb/RSKImageCropper'
s.license = { :type => 'MIT', :file => 'LICENSE' }
Expand Down
92 changes: 92 additions & 0 deletions RSKImageCropper/CGGeometry+RSKImageCropper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// CGGeometry+RSKImageCropper.h
//
// Copyright (c) 2015 Ruslan Skorb, http://ruslanskorb.com/
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#import <CoreGraphics/CoreGraphics.h>
#import <tgmath.h>

// tgmath functions aren't used on iOS when modules are enabled.
// Open Radar - http://www.openradar.me/16744288
// Work around this by redeclaring things here.

#undef cos
#define cos(__x) __tg_cos(__tg_promote1((__x))(__x))

#undef sin
#define sin(__x) __tg_sin(__tg_promote1((__x))(__x))

#undef atan2
#define atan2(__x, __y) __tg_atan2(__tg_promote2((__x), (__y))(__x), \
__tg_promote2((__x), (__y))(__y))

#undef pow
#define pow(__x, __y) __tg_pow(__tg_promote2((__x), (__y))(__x), \
__tg_promote2((__x), (__y))(__y))

#undef sqrt
#define sqrt(__x) __tg_sqrt(__tg_promote1((__x))(__x))

#undef fabs
#define fabs(__x) __tg_fabs(__tg_promote1((__x))(__x))

#ifdef CGFLOAT_IS_DOUBLE
#define RSK_EPSILON DBL_EPSILON
#else
#define RSK_EPSILON FLT_EPSILON
#endif

// Line segments.
struct RSKLineSegment {
CGPoint start;
CGPoint end;
};
typedef struct RSKLineSegment RSKLineSegment;

// The "empty" point. This is the point returned when, for example, we
// intersect two disjoint line segments. Note that the null point is not the
// same as the zero point.
CG_EXTERN const CGPoint RSKPointNull;

// Returns the exact center point of the given rectangle.
CGPoint RSKRectCenterPoint(CGRect rect);

// Returns the `rect` scaled around the `point` by `sx` and `sy`.
CGRect RSKRectScaleAroundPoint(CGRect rect, CGPoint point, CGFloat sx, CGFloat sy);

// Returns true if `point' is the null point, false otherwise.
bool RSKPointIsNull(CGPoint point);

// Returns the `point` rotated around the `pivot` by `angle`.
CGPoint RSKPointRotateAroundPoint(CGPoint point, CGPoint pivot, CGFloat angle);

// Returns the distance between two points.
CGFloat RSKPointDistance(CGPoint p1, CGPoint p2);

// Make a line segment from two points `start` and `end`.
RSKLineSegment RSKLineSegmentMake(CGPoint start, CGPoint end);

// Returns the line segment rotated around the `pivot` by `angle`.
RSKLineSegment RSKLineSegmentRotateAroundPoint(RSKLineSegment lineSegment, CGPoint pivot, CGFloat angle);

// Returns the intersection of `ls1' and `ls2'. This may return a null point.
CGPoint RSKLineSegmentIntersection(RSKLineSegment ls1, RSKLineSegment ls2);
157 changes: 157 additions & 0 deletions RSKImageCropper/CGGeometry+RSKImageCropper.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//
// CGGeometry+RSKImageCropper.m
//
// Copyright (c) 2015 Ruslan Skorb, http://ruslanskorb.com/
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#import "CGGeometry+RSKImageCropper.h"

const CGPoint RSKPointNull = { INFINITY, INFINITY };

CGPoint RSKRectCenterPoint(CGRect rect)
{
return CGPointMake(CGRectGetMinX(rect) + CGRectGetWidth(rect) / 2,
CGRectGetMinY(rect) + CGRectGetHeight(rect) / 2);
}

CGRect RSKRectScaleAroundPoint(CGRect rect, CGPoint point, CGFloat sx, CGFloat sy)
{
CGAffineTransform translationTransform, scaleTransform;
translationTransform = CGAffineTransformMakeTranslation(-point.x, -point.y);
rect = CGRectApplyAffineTransform(rect, translationTransform);
scaleTransform = CGAffineTransformMakeScale(sx, sy);
rect = CGRectApplyAffineTransform(rect, scaleTransform);
translationTransform = CGAffineTransformMakeTranslation(point.x, point.y);
rect = CGRectApplyAffineTransform(rect, translationTransform);
return rect;
}

bool RSKPointIsNull(CGPoint point)
{
return CGPointEqualToPoint(point, RSKPointNull);
}

CGPoint RSKPointRotateAroundPoint(CGPoint point, CGPoint pivot, CGFloat angle)
{
CGAffineTransform translationTransform, rotationTransform;
translationTransform = CGAffineTransformMakeTranslation(-pivot.x, -pivot.y);
point = CGPointApplyAffineTransform(point, translationTransform);
rotationTransform = CGAffineTransformMakeRotation(angle);
point = CGPointApplyAffineTransform(point, rotationTransform);
translationTransform = CGAffineTransformMakeTranslation(pivot.x, pivot.y);
point = CGPointApplyAffineTransform(point, translationTransform);
return point;
}

CGFloat RSKPointDistance(CGPoint p1, CGPoint p2)
{
CGFloat dx = p1.x - p2.x;
CGFloat dy = p1.y - p2.y;
return sqrt(pow(dx, 2) + pow(dy, 2));
}

RSKLineSegment RSKLineSegmentMake(CGPoint start, CGPoint end)
{
return (RSKLineSegment){ start, end };
}

RSKLineSegment RSKLineSegmentRotateAroundPoint(RSKLineSegment line, CGPoint pivot, CGFloat angle)
{
return RSKLineSegmentMake(RSKPointRotateAroundPoint(line.start, pivot, angle),
RSKPointRotateAroundPoint(line.end, pivot, angle));
}

/*
Equations of line segments:
pA = ls1.start + uA * (ls1.end - ls1.start)
pB = ls2.start + uB * (ls2.end - ls2.start)
In the case when `pA` is equal `pB` we have:
x1 + uA * (x2 - x1) = x3 + uB * (x4 - x3)
y1 + uA * (y2 - y1) = y3 + uB * (y4 - y3)
uA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) / (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
uB = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) / (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
numeratorA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)
denominatorA = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
numeratorA = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)
denominatorB = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
[1] Denominators are equal.
[2] If numerators and denominator are zero, then the line segments are coincident. The point of intersection is the midpoint of the line segment.
x = (x1 + x2) * 0.5
y = (y1 + y2) * 0.5
or
x = (x3 + x4) * 0.5
y = (y3 + y4) * 0.5
[3] If denominator is zero, then the line segments are parallel. There is no point of intersection.
[4] If `uA` and `uB` is included into the interval [0, 1], then the line segments intersects in the point (x, y).
x = x1 + uA * (x2 - x1)
y = y1 + uA * (y2 - y1)
or
x = x3 + uB * (x4 - x3)
y = y3 + uB * (y4 - y3)
*/
CGPoint RSKLineSegmentIntersection(RSKLineSegment ls1, RSKLineSegment ls2)
{
CGFloat x1 = ls1.start.x;
CGFloat y1 = ls1.start.y;
CGFloat x2 = ls1.end.x;
CGFloat y2 = ls1.end.y;
CGFloat x3 = ls2.start.x;
CGFloat y3 = ls2.start.y;
CGFloat x4 = ls2.end.x;
CGFloat y4 = ls2.end.y;

CGFloat numeratorA = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3);
CGFloat numeratorB = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3);
CGFloat denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);

// Check the coincidence.
if (fabs(numeratorA) < RSK_EPSILON && fabs(numeratorB) < RSK_EPSILON && fabs(denominator) < RSK_EPSILON) {
return CGPointMake((x1 + x2) * 0.5, (y1 + y2) * 0.5);
}

// Check the parallelism.
if (fabs(denominator) < RSK_EPSILON) {
return RSKPointNull;
}

// Check the intersection.
CGFloat uA = numeratorA / denominator;
CGFloat uB = numeratorB / denominator;
if (uA < 0 || uA > 1 || uB < 0 || uB > 1) {
return RSKPointNull;
}

return CGPointMake(x1 + uA * (x2 - x1), y1 + uA * (y2 - y1));
}
Loading

0 comments on commit bc1b146

Please sign in to comment.