CB-7937 - Re-factor iOS Camera plugin so that it is testable (closes #52)

Signed-off-by: Shazron Abdullah <shazron@apache.org>
This commit is contained in:
Shazron Abdullah 2014-11-05 12:19:42 -08:00
parent 3ab650bdc9
commit 35c653d24d
5 changed files with 597 additions and 466 deletions

View File

@ -133,6 +133,8 @@
<clobbers target="CameraPopoverHandle" />
</js-module>
<header-file src="src/ios/UIImage+CropScaleOrientation.h" />
<source-file src="src/ios/UIImage+CropScaleOrientation.m" />
<header-file src="src/ios/CDVCamera.h" />
<source-file src="src/ios/CDVCamera.m" />
<header-file src="src/ios/CDVJpegHeaderWriter.h" />

View File

@ -42,22 +42,39 @@ enum CDVMediaType {
};
typedef NSUInteger CDVMediaType;
@interface CDVCameraPicker : UIImagePickerController
{}
@interface CDVPictureOptions : NSObject
@property (assign) NSInteger quality;
@property (copy) NSString* callbackId;
@property (copy) NSString* postUrl;
@property (nonatomic) enum CDVDestinationType returnType;
@property (nonatomic) enum CDVEncodingType encodingType;
@property (strong) UIPopoverController* popoverController;
@property (strong) NSNumber* quality;
@property (assign) CDVDestinationType destinationType;
@property (assign) UIImagePickerControllerSourceType sourceType;
@property (assign) CGSize targetSize;
@property (assign) bool correctOrientation;
@property (assign) bool saveToPhotoAlbum;
@property (assign) bool cropToSize;
@property (strong) UIView* webView;
@property (assign) CDVEncodingType encodingType;
@property (assign) CDVMediaType mediaType;
@property (assign) BOOL allowsEditing;
@property (assign) BOOL correctOrientation;
@property (assign) BOOL saveToPhotoAlbum;
@property (strong) NSDictionary* popoverOptions;
@property (assign) UIImagePickerControllerCameraDevice cameraDirection;
@property (assign) BOOL popoverSupported;
@property (assign) BOOL usesGeolocation;
@property (assign) BOOL cropToSize;
+ (instancetype) createFromTakePictureArguments:(NSArray*)arguments;
@end
@interface CDVCameraPicker : UIImagePickerController
@property (strong) CDVPictureOptions* pictureOptions;
@property (copy) NSString* callbackId;
@property (copy) NSString* postUrl;
@property (strong) UIPopoverController* pickerPopoverController;
@property (assign) BOOL cropToSize;
@property (strong) UIView* webView;
+ (instancetype) createFromPictureOptions:(CDVPictureOptions*)options;
@end
@ -92,9 +109,6 @@ typedef NSUInteger CDVMediaType;
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo;
- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker;
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
- (UIImage*)imageByScalingAndCroppingForSize:(UIImage*)anImage toSize:(CGSize)targetSize;
- (UIImage*)imageByScalingNotCroppingForSize:(UIImage*)anImage toSize:(CGSize)frameSize;
- (UIImage*)imageCorrectedForCaptureOrientation:(UIImage*)anImage;
- (void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation;
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error;

View File

@ -19,6 +19,7 @@
#import "CDVCamera.h"
#import "CDVJpegHeaderWriter.h"
#import "UIImage+CropScaleOrientation.h"
#import <Cordova/NSArray+Comparisons.h>
#import <Cordova/NSData+Base64.h>
#import <Cordova/NSDictionary+Extensions.h>
@ -34,6 +35,40 @@
static NSSet* org_apache_cordova_validArrowDirections;
@implementation CDVPictureOptions
+ (instancetype) createFromTakePictureArguments:(NSArray*)arguments
{
CDVPictureOptions* pictureOptions = [[CDVPictureOptions alloc] init];
pictureOptions.quality = [arguments objectAtIndex:0 withDefault:@(50)];
pictureOptions.destinationType = [[arguments objectAtIndex:1 withDefault:@(DestinationTypeFileUri)] unsignedIntegerValue];
pictureOptions.sourceType = [[arguments objectAtIndex:2 withDefault:@(UIImagePickerControllerSourceTypeCamera)] unsignedIntegerValue];
NSNumber* targetWidth = [arguments objectAtIndex:3 withDefault:nil];
NSNumber* targetHeight = [arguments objectAtIndex:4 withDefault:nil];
pictureOptions.targetSize = CGSizeMake(0, 0);
if ((targetWidth != nil) && (targetHeight != nil)) {
pictureOptions.targetSize = CGSizeMake([targetWidth floatValue], [targetHeight floatValue]);
}
pictureOptions.encodingType = [[arguments objectAtIndex:5 withDefault:@(EncodingTypeJPEG)] unsignedIntegerValue];
pictureOptions.mediaType = [[arguments objectAtIndex:6 withDefault:@(MediaTypePicture)] unsignedIntegerValue];
pictureOptions.allowsEditing = [[arguments objectAtIndex:7 withDefault:@(NO)] boolValue];
pictureOptions.correctOrientation = [[arguments objectAtIndex:8 withDefault:@(NO)] boolValue];
pictureOptions.saveToPhotoAlbum = [[arguments objectAtIndex:9 withDefault:@(NO)] boolValue];
pictureOptions.popoverOptions = [arguments objectAtIndex:10 withDefault:nil];
pictureOptions.cameraDirection = [[arguments objectAtIndex:11 withDefault:@(UIImagePickerControllerCameraDeviceRear)] unsignedIntegerValue];
pictureOptions.popoverSupported = NO;
pictureOptions.usesGeolocation = NO;
return pictureOptions;
}
@end
@interface CDVCamera ()
@property (readwrite, assign) BOOL hasPendingOperation;
@ -49,7 +84,6 @@ static NSSet* org_apache_cordova_validArrowDirections;
@synthesize hasPendingOperation, pickerController, locationManager;
- (BOOL)usesGeolocation
{
id useGeo = [self.commandDelegate.settings objectForKey:[@"CameraUsesGeolocation" lowercaseString]];
@ -62,106 +96,55 @@ static NSSet* org_apache_cordova_validArrowDirections;
(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
}
/* takePicture arguments:
* INDEX ARGUMENT
* 0 quality
* 1 destination type
* 2 source type
* 3 targetWidth
* 4 targetHeight
* 5 encodingType
* 6 mediaType
* 7 allowsEdit
* 8 correctOrientation
* 9 saveToPhotoAlbum
* 10 popoverOptions
* 11 cameraDirection
*/
- (void)takePicture:(CDVInvokedUrlCommand*)command
{
NSString* callbackId = command.callbackId;
NSArray* arguments = command.arguments;
self.hasPendingOperation = YES;
__weak CDVCamera* weakSelf = self;
NSString* sourceTypeString = [arguments objectAtIndex:2];
UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; // default
if (sourceTypeString != nil) {
sourceType = (UIImagePickerControllerSourceType)[sourceTypeString intValue];
}
bool hasCamera = [UIImagePickerController isSourceTypeAvailable:sourceType];
if (!hasCamera) {
NSLog(@"Camera.getPicture: source type %lu not available.", (unsigned long)sourceType);
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no camera available"];
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
return;
}
bool allowEdit = [[arguments objectAtIndex:7] boolValue];
NSNumber* targetWidth = [arguments objectAtIndex:3];
NSNumber* targetHeight = [arguments objectAtIndex:4];
NSNumber* mediaValue = [arguments objectAtIndex:6];
CDVMediaType mediaType = (mediaValue) ? [mediaValue intValue] : MediaTypePicture;
CGSize targetSize = CGSizeMake(0, 0);
if ((targetWidth != nil) && (targetHeight != nil)) {
targetSize = CGSizeMake([targetWidth floatValue], [targetHeight floatValue]);
}
// If a popover is already open, close it; we only want one at a time.
if (([[self pickerController] popoverController] != nil) && [[[self pickerController] popoverController] isPopoverVisible]) {
[[[self pickerController] popoverController] dismissPopoverAnimated:YES];
[[[self pickerController] popoverController] setDelegate:nil];
[[self pickerController] setPopoverController:nil];
}
CDVCameraPicker* cameraPicker = [[CDVCameraPicker alloc] init];
self.pickerController = cameraPicker;
cameraPicker.delegate = self;
cameraPicker.sourceType = sourceType;
cameraPicker.allowsEditing = allowEdit; // THIS IS ALL IT TAKES FOR CROPPING - jm
cameraPicker.callbackId = callbackId;
cameraPicker.targetSize = targetSize;
cameraPicker.cropToSize = NO;
// we need to capture this state for memory warnings that dealloc this object
cameraPicker.webView = self.webView;
cameraPicker.popoverSupported = [self popoverSupported];
cameraPicker.usesGeolocation = [self usesGeolocation];
cameraPicker.correctOrientation = [[arguments objectAtIndex:8] boolValue];
cameraPicker.saveToPhotoAlbum = [[arguments objectAtIndex:9] boolValue];
cameraPicker.encodingType = ([arguments objectAtIndex:5]) ? [[arguments objectAtIndex:5] intValue] : EncodingTypeJPEG;
cameraPicker.quality = ([arguments objectAtIndex:0]) ? [[arguments objectAtIndex:0] intValue] : 50;
cameraPicker.returnType = ([arguments objectAtIndex:1]) ? [[arguments objectAtIndex:1] intValue] : DestinationTypeFileUri;
if (sourceType == UIImagePickerControllerSourceTypeCamera) {
// We only allow taking pictures (no video) in this API.
cameraPicker.mediaTypes = [NSArray arrayWithObjects:(NSString*)kUTTypeImage, nil];
// We can only set the camera device if we're actually using the camera.
NSNumber* cameraDirection = [command argumentAtIndex:11 withDefault:[NSNumber numberWithInteger:UIImagePickerControllerCameraDeviceRear]];
cameraPicker.cameraDevice = (UIImagePickerControllerCameraDevice)[cameraDirection intValue];
} else if (mediaType == MediaTypeAll) {
cameraPicker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:sourceType];
} else {
NSArray* mediaArray = [NSArray arrayWithObjects:(NSString*)(mediaType == MediaTypeVideo ? kUTTypeMovie : kUTTypeImage), nil];
cameraPicker.mediaTypes = mediaArray;
}
if ([self popoverSupported] && (sourceType != UIImagePickerControllerSourceTypeCamera)) {
if (cameraPicker.popoverController == nil) {
cameraPicker.popoverController = [[NSClassFromString(@"UIPopoverController")alloc] initWithContentViewController:cameraPicker];
[self.commandDelegate runInBackground:^{
CDVPictureOptions* pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command.arguments];
pictureOptions.popoverSupported = [self popoverSupported];
pictureOptions.usesGeolocation = [self usesGeolocation];
pictureOptions.cropToSize = NO;
BOOL hasCamera = [UIImagePickerController isSourceTypeAvailable:pictureOptions.sourceType];
if (!hasCamera) {
NSLog(@"Camera.getPicture: source type %lu not available.", (unsigned long)pictureOptions.sourceType);
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No camera available"];
[weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
return;
}
NSDictionary* options = [command.arguments objectAtIndex:10 withDefault:nil];
[self displayPopover:options];
} else {
[self.viewController presentViewController:cameraPicker animated:YES completion:nil];
}
self.hasPendingOperation = NO;
// If a popover is already open, close it; we only want one at a time.
if (([[weakSelf pickerController] pickerPopoverController] != nil) && [[[weakSelf pickerController] pickerPopoverController] isPopoverVisible]) {
[[[weakSelf pickerController] pickerPopoverController] dismissPopoverAnimated:YES];
[[[weakSelf pickerController] pickerPopoverController] setDelegate:nil];
[[weakSelf pickerController] setPickerPopoverController:nil];
}
CDVCameraPicker* cameraPicker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
weakSelf.pickerController = cameraPicker;
cameraPicker.delegate = weakSelf;
cameraPicker.callbackId = command.callbackId;
// we need to capture this state for memory warnings that dealloc this object
cameraPicker.webView = weakSelf.webView;
if ([weakSelf popoverSupported] && (pictureOptions.sourceType != UIImagePickerControllerSourceTypeCamera)) {
if (cameraPicker.pickerPopoverController == nil) {
cameraPicker.pickerPopoverController = [[NSClassFromString(@"UIPopoverController") alloc] initWithContentViewController:cameraPicker];
}
[weakSelf displayPopover:pictureOptions.popoverOptions];
weakSelf.hasPendingOperation = NO;
} else {
[weakSelf.viewController presentViewController:cameraPicker animated:YES completion:^{
weakSelf.hasPendingOperation = NO;
}];
}
}];
}
- (void)repositionPopover:(CDVInvokedUrlCommand*)command
@ -190,8 +173,8 @@ static NSSet* org_apache_cordova_validArrowDirections;
}
}
[[[self pickerController] popoverController] setDelegate:self];
[[[self pickerController] popoverController] presentPopoverFromRect:CGRectMake(x, y, width, height)
[[[self pickerController] pickerPopoverController] setDelegate:self];
[[[self pickerController] pickerPopoverController] presentPopoverFromRect:CGRectMake(x, y, width, height)
inView:[self.webView superview]
permittedArrowDirections:arrowDirection
animated:YES];
@ -200,9 +183,9 @@ static NSSet* org_apache_cordova_validArrowDirections;
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if([navigationController isKindOfClass:[UIImagePickerController class]]){
UIImagePickerController * cameraPicker = (UIImagePickerController*)navigationController;
UIImagePickerController* cameraPicker = (UIImagePickerController*)navigationController;
if(![cameraPicker.mediaTypes containsObject:(NSString*) kUTTypeImage]){
if(![cameraPicker.mediaTypes containsObject:(NSString*)kUTTypeImage]){
[viewController.navigationItem setTitle:NSLocalizedString(@"Videos title", nil)];
}
}
@ -245,13 +228,12 @@ static NSSet* org_apache_cordova_validArrowDirections;
- (void)popoverControllerDidDismissPopover:(id)popoverController
{
// [ self imagePickerControllerDidCancel:self.pickerController ]; '
UIPopoverController* pc = (UIPopoverController*)popoverController;
[pc dismissPopoverAnimated:YES];
pc.delegate = nil;
if (self.pickerController && self.pickerController.callbackId && self.pickerController.popoverController) {
self.pickerController.popoverController = nil;
if (self.pickerController && self.pickerController.callbackId && self.pickerController.pickerPopoverController) {
self.pickerController.pickerPopoverController = nil;
NSString* callbackId = self.pickerController.callbackId;
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"]; // error callback expects string ATM
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
@ -259,122 +241,184 @@ static NSSet* org_apache_cordova_validArrowDirections;
self.hasPendingOperation = NO;
}
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
- (NSData*)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictureOptions*)options
{
CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
if (cameraPicker.popoverSupported && (cameraPicker.popoverController != nil)) {
[cameraPicker.popoverController dismissPopoverAnimated:YES];
cameraPicker.popoverController.delegate = nil;
cameraPicker.popoverController = nil;
} else {
[[cameraPicker presentingViewController] dismissViewControllerAnimated:YES completion:nil];
}
CDVPluginResult* result = nil;
NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
// IMAGE TYPE
if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
if (cameraPicker.returnType == DestinationTypeNativeUri) {
NSString* nativeUri = [(NSURL*)[info objectForKey:UIImagePickerControllerReferenceURL] absoluteString];
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
} else {
// get the image
UIImage* image = nil;
if (cameraPicker.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) {
image = [info objectForKey:UIImagePickerControllerEditedImage];
} else {
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
if (cameraPicker.correctOrientation) {
image = [self imageCorrectedForCaptureOrientation:image];
}
UIImage* scaledImage = nil;
if ((cameraPicker.targetSize.width > 0) && (cameraPicker.targetSize.height > 0)) {
// if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping
if (cameraPicker.cropToSize) {
scaledImage = [self imageByScalingAndCroppingForSize:image toSize:cameraPicker.targetSize];
} else {
scaledImage = [self imageByScalingNotCroppingForSize:image toSize:cameraPicker.targetSize];
}
}
NSData* data = nil;
// returnedImage is the image that is returned to caller and (optionally) saved to photo album
UIImage* returnedImage = (scaledImage == nil ? image : scaledImage);
if (cameraPicker.encodingType == EncodingTypePNG) {
data = UIImagePNGRepresentation(returnedImage);
} else if ((cameraPicker.allowsEditing==false) && (cameraPicker.targetSize.width <= 0) && (cameraPicker.targetSize.height <= 0) && (cameraPicker.correctOrientation==false)){
NSData* data = nil;
switch (options.encodingType) {
case EncodingTypePNG:
data = UIImagePNGRepresentation(image);
break;
case EncodingTypeJPEG:
{
if ((options.allowsEditing == NO) && (options.targetSize.width <= 0) && (options.targetSize.height <= 0) && (options.correctOrientation == NO)){
// use image unedited as requested , don't resize
data = UIImageJPEGRepresentation(returnedImage, 1.0);
data = UIImageJPEGRepresentation(image, 1.0);
} else {
data = UIImageJPEGRepresentation(returnedImage, cameraPicker.quality / 100.0f);
if (cameraPicker.usesGeolocation) {
NSDictionary *controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
if (options.usesGeolocation) {
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
if (controllerMetadata) {
self.data = data;
self.metadata = [[NSMutableDictionary alloc] init];
NSMutableDictionary *EXIFDictionary = [[controllerMetadata objectForKey:(NSString *)kCGImagePropertyExifDictionary]mutableCopy];
if (EXIFDictionary) [self.metadata setObject:EXIFDictionary forKey:(NSString *)kCGImagePropertyExifDictionary];
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
if (EXIFDictionary) {
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
}
if (IsAtLeastiOSVersion(@"8.0")) {
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
}
[[self locationManager] startUpdatingLocation];
return;
}
} else {
data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f);
}
}
if (cameraPicker.saveToPhotoAlbum) {
ALAssetsLibrary *library = [ALAssetsLibrary new];
[library writeImageToSavedPhotosAlbum:returnedImage.CGImage orientation:(ALAssetOrientation)(returnedImage.imageOrientation) completionBlock:nil];
}
}
break;
default:
break;
};
return data;
}
if (cameraPicker.returnType == DestinationTypeFileUri) {
// write to temp directory and return URI
// get the temp directory path
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
- (NSString*)tempFilePath:(NSString*)extension
{
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by Apple (vs [NSFileManager defaultManager]) to be threadsafe
NSString* filePath;
// generate unique file name
int i = 1;
do {
filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, extension];
} while ([fileMgr fileExistsAtPath:filePath]);
return filePath;
}
- (UIImage*)retrieveImage:(NSDictionary*)info options:(CDVPictureOptions*)options
{
// get the image
UIImage* image = nil;
if (options.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) {
image = [info objectForKey:UIImagePickerControllerEditedImage];
} else {
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
if (options.correctOrientation) {
image = [image imageCorrectedForCaptureOrientation];
}
UIImage* scaledImage = nil;
if ((options.targetSize.width > 0) && (options.targetSize.height > 0)) {
// if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping
if (options.cropToSize) {
scaledImage = [image imageByScalingAndCroppingForSize:options.targetSize];
} else {
scaledImage = [image imageByScalingNotCroppingForSize:options.targetSize];
}
}
return (scaledImage == nil ? image : scaledImage);
}
- (CDVPluginResult*)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info
{
CDVPluginResult* result = nil;
BOOL saveToPhotoAlbum = options.saveToPhotoAlbum;
UIImage* image = nil;
switch (options.destinationType) {
case DestinationTypeNativeUri:
{
NSString* nativeUri = [(NSURL*)[info objectForKey:UIImagePickerControllerReferenceURL] absoluteString];
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
saveToPhotoAlbum = NO;
}
break;
case DestinationTypeFileUri:
{
image = [self retrieveImage:info options:options];
NSData* data = [self processImage:image info:info options:options];
if (data) {
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
NSString* filePath = [self tempFilePath:extension];
NSError* err = nil;
NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by apple (vs [NSFileManager defaultManager]) to be threadsafe
// generate unique file name
NSString* filePath;
int i = 1;
do {
filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, cameraPicker.encodingType == EncodingTypePNG ? @"png":@"jpg"];
} while ([fileMgr fileExistsAtPath:filePath]);
// save file
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
} else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]];
}
} else {
}
}
break;
case DestinationTypeDataUrl:
{
image = [self retrieveImage:info options:options];
NSData* data = [self processImage:image info:info options:options];
if (data) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[data base64EncodedString]];
}
}
break;
default:
break;
};
if (saveToPhotoAlbum && image) {
ALAssetsLibrary* library = [ALAssetsLibrary new];
[library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:nil];
}
// NOT IMAGE TYPE (MOVIE)
else {
NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString];
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:moviePath];
}
return result;
}
if (result) {
[self.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
}
- (CDVPluginResult*)resultForVideo:(NSDictionary*)info
{
NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString];
return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:moviePath];
}
self.hasPendingOperation = NO;
self.pickerController = nil;
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
__weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
__weak CDVCamera* weakSelf = self;
dispatch_block_t invoke = ^(void) {
__block CDVPluginResult* result = nil;
NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
result = [self resultForImage:cameraPicker.pictureOptions info:info];
}
else {
result = [self resultForVideo:info];
}
if (result) {
[weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
weakSelf.hasPendingOperation = NO;
weakSelf.pickerController = nil;
}
};
if (cameraPicker.pictureOptions.popoverSupported && (cameraPicker.pickerPopoverController != nil)) {
[cameraPicker.pickerPopoverController dismissPopoverAnimated:YES];
cameraPicker.pickerPopoverController.delegate = nil;
cameraPicker.pickerPopoverController = nil;
invoke();
} else {
[[cameraPicker presentingViewController] dismissViewControllerAnimated:YES completion:invoke];
}
}
// older api calls newer didFinishPickingMediaWithInfo
@ -387,171 +431,28 @@ static NSSet* org_apache_cordova_validArrowDirections;
- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker
{
CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
[[cameraPicker presentingViewController] dismissViewControllerAnimated:YES completion:nil];
CDVPluginResult* result;
if ([ALAssetsLibrary authorizationStatus] == ALAuthorizationStatusAuthorized) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"]; // error callback expects string ATM
} else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to assets"]; // error callback expects string ATM
}
__weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
__weak CDVCamera* weakSelf = self;
[self.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
self.hasPendingOperation = NO;
self.pickerController = nil;
}
- (UIImage*)imageByScalingAndCroppingForSize:(UIImage*)anImage toSize:(CGSize)targetSize
{
UIImage* sourceImage = anImage;
UIImage* newImage = nil;
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0, 0.0);
if (CGSizeEqualToSize(imageSize, targetSize) == NO) {
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor > heightFactor) {
scaleFactor = widthFactor; // scale to fit height
dispatch_block_t invoke = ^ (void) {
CDVPluginResult* result;
if ([ALAssetsLibrary authorizationStatus] == ALAuthorizationStatusAuthorized) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"];
} else {
scaleFactor = heightFactor; // scale to fit width
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to assets"];
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
[weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
weakSelf.hasPendingOperation = NO;
weakSelf.pickerController = nil;
};
// center the image
if (widthFactor > heightFactor) {
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
} else if (widthFactor < heightFactor) {
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
UIGraphicsBeginImageContext(targetSize); // this will crop
CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = thumbnailPoint;
thumbnailRect.size.width = scaledWidth;
thumbnailRect.size.height = scaledHeight;
[sourceImage drawInRect:thumbnailRect];
newImage = UIGraphicsGetImageFromCurrentImageContext();
if (newImage == nil) {
NSLog(@"could not scale image");
}
// pop the context to get back to the default
UIGraphicsEndImageContext();
return newImage;
[[cameraPicker presentingViewController] dismissViewControllerAnimated:YES completion:invoke];
}
- (UIImage*)imageCorrectedForCaptureOrientation:(UIImage*)anImage
- (CLLocationManager*)locationManager
{
float rotation_radians = 0;
bool perpendicular = false;
switch ([anImage imageOrientation]) {
case UIImageOrientationUp :
rotation_radians = 0.0;
break;
case UIImageOrientationDown:
rotation_radians = M_PI; // don't be scared of radians, if you're reading this, you're good at math
break;
case UIImageOrientationRight:
rotation_radians = M_PI_2;
perpendicular = true;
break;
case UIImageOrientationLeft:
rotation_radians = -M_PI_2;
perpendicular = true;
break;
default:
break;
}
UIGraphicsBeginImageContext(CGSizeMake(anImage.size.width, anImage.size.height));
CGContextRef context = UIGraphicsGetCurrentContext();
// Rotate around the center point
CGContextTranslateCTM(context, anImage.size.width / 2, anImage.size.height / 2);
CGContextRotateCTM(context, rotation_radians);
CGContextScaleCTM(context, 1.0, -1.0);
float width = perpendicular ? anImage.size.height : anImage.size.width;
float height = perpendicular ? anImage.size.width : anImage.size.height;
CGContextDrawImage(context, CGRectMake(-width / 2, -height / 2, width, height), [anImage CGImage]);
// Move the origin back since the rotation might've change it (if its 90 degrees)
if (perpendicular) {
CGContextTranslateCTM(context, -anImage.size.height / 2, -anImage.size.width / 2);
}
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (UIImage*)imageByScalingNotCroppingForSize:(UIImage*)anImage toSize:(CGSize)frameSize
{
UIImage* sourceImage = anImage;
UIImage* newImage = nil;
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = frameSize.width;
CGFloat targetHeight = frameSize.height;
CGFloat scaleFactor = 0.0;
CGSize scaledSize = frameSize;
if (CGSizeEqualToSize(imageSize, frameSize) == NO) {
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
// opposite comparison to imageByScalingAndCroppingForSize in order to contain the image within the given bounds
if (widthFactor > heightFactor) {
scaleFactor = heightFactor; // scale to fit height
} else {
scaleFactor = widthFactor; // scale to fit width
}
scaledSize = CGSizeMake(MIN(width * scaleFactor, targetWidth), MIN(height * scaleFactor, targetHeight));
}
// If the pixels are floats, it causes a white line in iOS8 and probably other versions too
scaledSize.width = (int)scaledSize.width;
scaledSize.height = (int)scaledSize.height;
UIGraphicsBeginImageContext(scaledSize); // this will resize
[sourceImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
newImage = UIGraphicsGetImageFromCurrentImageContext();
if (newImage == nil) {
NSLog(@"could not scale image");
}
// pop the context to get back to the default
UIGraphicsEndImageContext();
return newImage;
}
- (CLLocationManager *)locationManager {
if (locationManager != nil) {
return locationManager;
}
@ -565,70 +466,76 @@ static NSSet* org_apache_cordova_validArrowDirections;
- (void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation
{
if (locationManager != nil) {
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
NSMutableDictionary *GPSDictionary = [[NSMutableDictionary dictionary] init];
CLLocationDegrees latitude = newLocation.coordinate.latitude;
CLLocationDegrees longitude = newLocation.coordinate.longitude;
// latitude
if (latitude < 0.0) {
latitude = latitude * -1.0f;
[GPSDictionary setObject:@"S" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
} else {
[GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
}
[GPSDictionary setObject:[NSNumber numberWithFloat:latitude] forKey:(NSString*)kCGImagePropertyGPSLatitude];
// longitude
if (longitude < 0.0) {
longitude = longitude * -1.0f;
[GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
}
else {
[GPSDictionary setObject:@"E" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
}
[GPSDictionary setObject:[NSNumber numberWithFloat:longitude] forKey:(NSString*)kCGImagePropertyGPSLongitude];
// altitude
CGFloat altitude = newLocation.altitude;
if (!isnan(altitude)){
if (altitude < 0) {
altitude = -altitude;
[GPSDictionary setObject:@"1" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
} else {
[GPSDictionary setObject:@"0" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
}
[GPSDictionary setObject:[NSNumber numberWithFloat:altitude] forKey:(NSString *)kCGImagePropertyGPSAltitude];
if (locationManager == nil) {
return;
}
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
NSMutableDictionary *GPSDictionary = [[NSMutableDictionary dictionary] init];
CLLocationDegrees latitude = newLocation.coordinate.latitude;
CLLocationDegrees longitude = newLocation.coordinate.longitude;
// latitude
if (latitude < 0.0) {
latitude = latitude * -1.0f;
[GPSDictionary setObject:@"S" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
} else {
[GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
}
[GPSDictionary setObject:[NSNumber numberWithFloat:latitude] forKey:(NSString*)kCGImagePropertyGPSLatitude];
// longitude
if (longitude < 0.0) {
longitude = longitude * -1.0f;
[GPSDictionary setObject:@"W" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
}
else {
[GPSDictionary setObject:@"E" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
}
[GPSDictionary setObject:[NSNumber numberWithFloat:longitude] forKey:(NSString*)kCGImagePropertyGPSLongitude];
// altitude
CGFloat altitude = newLocation.altitude;
if (!isnan(altitude)){
if (altitude < 0) {
altitude = -altitude;
[GPSDictionary setObject:@"1" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
} else {
[GPSDictionary setObject:@"0" forKey:(NSString *)kCGImagePropertyGPSAltitudeRef];
}
// Time and date
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"HH:mm:ss.SSSSSS"];
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
[GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp];
[formatter setDateFormat:@"yyyy:MM:dd"];
[GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSDateStamp];
[self.metadata setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];
[self imagePickerControllerReturnImageResult];
}
[GPSDictionary setObject:[NSNumber numberWithFloat:altitude] forKey:(NSString *)kCGImagePropertyGPSAltitude];
}
// Time and date
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"HH:mm:ss.SSSSSS"];
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
[GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp];
[formatter setDateFormat:@"yyyy:MM:dd"];
[GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSDateStamp];
[self.metadata setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];
[self imagePickerControllerReturnImageResult];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
if (locationManager != nil) {
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
[self imagePickerControllerReturnImageResult];
}
- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error
{
if (locationManager == nil) {
return;
}
[self.locationManager stopUpdatingLocation];
self.locationManager = nil;
[self imagePickerControllerReturnImageResult];
}
- (void)imagePickerControllerReturnImageResult
{
CDVPictureOptions* options = self.pickerController.pictureOptions;
CDVPluginResult* result = nil;
if (self.metadata) {
@ -643,39 +550,31 @@ static NSSet* org_apache_cordova_validArrowDirections;
CFRelease(destinationImage);
}
if (self.pickerController.saveToPhotoAlbum) {
ALAssetsLibrary *library = [ALAssetsLibrary new];
[library writeImageDataToSavedPhotosAlbum:self.data metadata:self.metadata completionBlock:nil];
}
if (self.pickerController.returnType == DestinationTypeFileUri) {
// write to temp directory and return URI
// get the temp directory path
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
NSError* err = nil;
NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by apple (vs [NSFileManager defaultManager]) to be threadsafe
// generate unique file name
NSString* filePath;
int i = 1;
do {
filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, self.pickerController.encodingType == EncodingTypePNG ? @"png":@"jpg"];
} while ([fileMgr fileExistsAtPath:filePath]);
// save file
if (![self.data writeToFile:filePath options:NSAtomicWrite error:&err]) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
switch (options.destinationType) {
case DestinationTypeFileUri:
{
NSError* err = nil;
NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
NSString* filePath = [self tempFilePath:extension];
// save file
if (![self.data writeToFile:filePath options:NSAtomicWrite error:&err]) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
}
else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]];
}
}
else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]];
break;
case DestinationTypeDataUrl:
{
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[self.data base64EncodedString]];
}
}
else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[self.data base64EncodedString]];
}
if (result) {
[self.commandDelegate sendPluginResult:result callbackId:self.pickerController.callbackId];
}
break;
case DestinationTypeNativeUri:
default:
break;
};
if (result) {
[self.commandDelegate sendPluginResult:result callbackId:self.pickerController.callbackId];
@ -685,33 +584,29 @@ static NSSet* org_apache_cordova_validArrowDirections;
self.pickerController = nil;
self.data = nil;
self.metadata = nil;
if (options.saveToPhotoAlbum) {
ALAssetsLibrary *library = [ALAssetsLibrary new];
[library writeImageDataToSavedPhotosAlbum:self.data metadata:self.metadata completionBlock:nil];
}
}
@end
@implementation CDVCameraPicker
@synthesize quality, postUrl;
@synthesize returnType;
@synthesize callbackId;
@synthesize popoverController;
@synthesize targetSize;
@synthesize correctOrientation;
@synthesize saveToPhotoAlbum;
@synthesize encodingType;
@synthesize cropToSize;
@synthesize webView;
@synthesize popoverSupported;
- (BOOL)prefersStatusBarHidden {
- (BOOL)prefersStatusBarHidden
{
return YES;
}
- (UIViewController*)childViewControllerForStatusBarHidden {
- (UIViewController*)childViewControllerForStatusBarHidden
{
return nil;
}
- (void)viewWillAppear:(BOOL)animated {
- (void)viewWillAppear:(BOOL)animated
{
SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate");
if ([self respondsToSelector:sel]) {
[self performSelector:sel withObject:nil afterDelay:0];
@ -720,4 +615,26 @@ static NSSet* org_apache_cordova_validArrowDirections;
[super viewWillAppear:animated];
}
+ (instancetype) createFromPictureOptions:(CDVPictureOptions*)pictureOptions;
{
CDVCameraPicker* cameraPicker = [[CDVCameraPicker alloc] init];
cameraPicker.pictureOptions = pictureOptions;
cameraPicker.sourceType = pictureOptions.sourceType;
cameraPicker.allowsEditing = pictureOptions.allowsEditing;
if (cameraPicker.sourceType == UIImagePickerControllerSourceTypeCamera) {
// We only allow taking pictures (no video) in this API.
cameraPicker.mediaTypes = @[(NSString*)kUTTypeImage];
// We can only set the camera device if we're actually using the camera.
cameraPicker.cameraDevice = pictureOptions.cameraDirection;
} else if (pictureOptions.mediaType == MediaTypeAll) {
cameraPicker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:cameraPicker.sourceType];
} else {
NSArray* mediaArray = @[(NSString*)(pictureOptions.mediaType == MediaTypeVideo ? kUTTypeMovie : kUTTypeImage)];
cameraPicker.mediaTypes = mediaArray;
}
return cameraPicker;
}
@end

View File

@ -0,0 +1,28 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import <UIKit/UIKit.h>
@interface UIImage (CropScaleOrientation)
- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize;
- (UIImage*)imageCorrectedForCaptureOrientation;
- (UIImage*)imageByScalingNotCroppingForSize:(CGSize)targetSize;
@end

View File

@ -0,0 +1,170 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "UIImage+CropScaleOrientation.h"
@implementation UIImage (CropScaleOrientation)
- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize
{
UIImage* sourceImage = self;
UIImage* newImage = nil;
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0, 0.0);
if (CGSizeEqualToSize(imageSize, targetSize) == NO) {
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor > heightFactor) {
scaleFactor = widthFactor; // scale to fit height
} else {
scaleFactor = heightFactor; // scale to fit width
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor > heightFactor) {
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
} else if (widthFactor < heightFactor) {
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
UIGraphicsBeginImageContext(targetSize); // this will crop
CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = thumbnailPoint;
thumbnailRect.size.width = scaledWidth;
thumbnailRect.size.height = scaledHeight;
[sourceImage drawInRect:thumbnailRect];
newImage = UIGraphicsGetImageFromCurrentImageContext();
if (newImage == nil) {
NSLog(@"could not scale image");
}
// pop the context to get back to the default
UIGraphicsEndImageContext();
return newImage;
}
- (UIImage*)imageCorrectedForCaptureOrientation
{
float rotation_radians = 0;
bool perpendicular = false;
switch ([self imageOrientation]) {
case UIImageOrientationUp :
rotation_radians = 0.0;
break;
case UIImageOrientationDown:
rotation_radians = M_PI; // don't be scared of radians, if you're reading this, you're good at math
break;
case UIImageOrientationRight:
rotation_radians = M_PI_2;
perpendicular = true;
break;
case UIImageOrientationLeft:
rotation_radians = -M_PI_2;
perpendicular = true;
break;
default:
break;
}
UIGraphicsBeginImageContext(CGSizeMake(self.size.width, self.size.height));
CGContextRef context = UIGraphicsGetCurrentContext();
// Rotate around the center point
CGContextTranslateCTM(context, self.size.width / 2, self.size.height / 2);
CGContextRotateCTM(context, rotation_radians);
CGContextScaleCTM(context, 1.0, -1.0);
float width = perpendicular ? self.size.height : self.size.width;
float height = perpendicular ? self.size.width : self.size.height;
CGContextDrawImage(context, CGRectMake(-width / 2, -height / 2, width, height), [self CGImage]);
// Move the origin back since the rotation might've change it (if its 90 degrees)
if (perpendicular) {
CGContextTranslateCTM(context, -self.size.height / 2, -self.size.width / 2);
}
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
- (UIImage*)imageByScalingNotCroppingForSize:(CGSize)targetSize
{
UIImage* sourceImage = self;
UIImage* newImage = nil;
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGSize scaledSize = targetSize;
if (CGSizeEqualToSize(imageSize, targetSize) == NO) {
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
// opposite comparison to imageByScalingAndCroppingForSize in order to contain the image within the given bounds
if (widthFactor > heightFactor) {
scaleFactor = heightFactor; // scale to fit height
} else {
scaleFactor = widthFactor; // scale to fit width
}
scaledSize = CGSizeMake(MIN(width * scaleFactor, targetWidth), MIN(height * scaleFactor, targetHeight));
}
// If the pixels are floats, it causes a white line in iOS8 and probably other versions too
scaledSize.width = (int)scaledSize.width;
scaledSize.height = (int)scaledSize.height;
UIGraphicsBeginImageContext(scaledSize); // this will resize
[sourceImage drawInRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height)];
newImage = UIGraphicsGetImageFromCurrentImageContext();
if (newImage == nil) {
NSLog(@"could not scale image");
}
// pop the context to get back to the default
UIGraphicsEndImageContext();
return newImage;
}
@end