Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ios): image + video quality, disable saving to camera roll/photo album, image scaling #46

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ios/CDVCapture.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ typedef NSUInteger CDVCaptureError;
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info;
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo;
- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker;

- (UIImage*)imageByScalingNotCroppingForSize:(UIImage*)sourceImage targetSize:(CGSize)targetSize;
@end

@interface CDVAudioNavigationController : UINavigationController
Expand Down
138 changes: 117 additions & 21 deletions src/ios/CDVCapture.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ Licensed to the Apache Software Foundation (ASF) under one
#define kW3CMediaFormatDuration @"duration"
#define kW3CMediaModeType @"type"

NSNumber *globalQuality;
NSNumber *globalSaveToPhotoAlbum;
NSNumber *globalWidth;
NSNumber *globalHeight;

@implementation NSBundle (PluginExtensions)

+ (NSBundle*) pluginBundle:(CDVPlugin*)plugin {
Expand Down Expand Up @@ -131,6 +136,15 @@ - (void)captureImage:(CDVInvokedUrlCommand*)command
// options could contain limit and mode neither of which are supported at this time
// taking more than one picture (limit) is only supported if provide own controls via cameraOverlayView property
// can support mode in OS

NSNumber* quality = [options objectForKey:@"quality"];
globalQuality = quality;

NSNumber* saveToPhotoAlbum = [options objectForKey:@"saveToPhotoAlbum"];
globalSaveToPhotoAlbum = saveToPhotoAlbum;

globalWidth = [options objectForKey:@"targetWidth"];
globalHeight = [options objectForKey:@"targetHeight"];

if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
NSLog(@"Capture.imageCapture: camera not available.");
Expand Down Expand Up @@ -172,13 +186,24 @@ - (CDVPluginResult*)processImage:(UIImage*)image type:(NSString*)mimeType forCal
CDVPluginResult* result = nil;

// save the image to photo album
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);

if([globalSaveToPhotoAlbum isEqual:@1]) {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}

if(!([globalWidth isEqual:@0] || [globalHeight isEqual:@0])) {
struct CGSize newScale;
newScale.width = [globalWidth floatValue];
newScale.height = [globalHeight floatValue];

image = [self imageByScalingNotCroppingForSize:image targetSize:newScale];
}

NSData* data = nil;
if (mimeType && [mimeType isEqualToString:@"image/png"]) {
data = UIImagePNGRepresentation(image);
} else {
data = UIImageJPEGRepresentation(image, 0.5);
data = UIImageJPEGRepresentation(image, ((int)globalQuality / 100));
}

// write to temp directory and return URI
Expand Down Expand Up @@ -210,6 +235,48 @@ - (CDVPluginResult*)processImage:(UIImage*)image type:(NSString*)mimeType forCal
return result;
}

- (UIImage*)imageByScalingNotCroppingForSize:(UIImage*)sourceImage targetSize:(CGSize)targetSize
{
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;
}

- (void)captureVideo:(CDVInvokedUrlCommand*)command
{
NSString* callbackId = command.callbackId;
Expand All @@ -222,6 +289,7 @@ - (void)captureVideo:(CDVInvokedUrlCommand*)command
// options could contain limit, duration and mode
// taking more than one video (limit) is only supported if provide own controls via cameraOverlayView property
NSNumber* duration = [options objectForKey:@"duration"];
NSNumber* quality = [options objectForKey:@"quality"];
NSString* mediaType = nil;

if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
Expand Down Expand Up @@ -250,6 +318,18 @@ - (void)captureVideo:(CDVInvokedUrlCommand*)command
pickerController.delegate = self;
pickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
pickerController.allowsEditing = NO;

// Set quality of captured video
if (quality) {
if ([quality intValue] == 1) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it should be zero here.

pickerController.videoQuality = UIImagePickerControllerQualityTypeLow;
} else if ([quality intValue] == 1) {
pickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;
} else {
pickerController.videoQuality = UIImagePickerControllerQualityTypeMedium;
}
}

// iOS 3.0
pickerController.mediaTypes = [NSArray arrayWithObjects:mediaType, nil];

Expand Down Expand Up @@ -516,7 +596,7 @@ - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingM
result = [self processImage:image type:cameraPicker.mimeType forCallbackId:callbackId];
} else if ([mediaType isEqualToString:(NSString*)kUTTypeMovie]) {
// process video
NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] path];
NSString* moviePath = [(NSURL *)[info objectForKey:UIImagePickerControllerMediaURL] path];
if (moviePath) {
result = [self processVideo:moviePath forCallbackId:callbackId];
}
Expand Down Expand Up @@ -544,12 +624,18 @@ - (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker

@implementation CDVAudioNavigationController

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
- (NSUInteger)supportedInterfaceOrientations
{
// delegate to CVDAudioRecorderViewController
return [self.topViewController supportedInterfaceOrientations];
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
// delegate to CVDAudioRecorderViewController
return [self.topViewController supportedInterfaceOrientations];
}
#else
- (NSUInteger)supportedInterfaceOrientations
{
// delegate to CVDAudioRecorderViewController
return [self.topViewController supportedInterfaceOrientations];
}
#endif

@end
Expand Down Expand Up @@ -709,7 +795,8 @@ - (void)viewDidLoad
NSURL* fileURL = [NSURL fileURLWithPath:filePath isDirectory:NO];

// create AVAudioPlayer
self.avRecorder = [[AVAudioRecorder alloc] initWithURL:fileURL settings:nil error:&err];
NSDictionary *recordSetting = [[NSMutableDictionary alloc] init];
self.avRecorder = [[AVAudioRecorder alloc] initWithURL:fileURL settings:recordSetting error:&err];
if (err) {
NSLog(@"Failed to initialize AVAudioRecorder: %@\n", [err localizedDescription]);
self.avRecorder = nil;
Expand All @@ -724,15 +811,24 @@ - (void)viewDidLoad
}
}

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
- (NSUInteger)supportedInterfaceOrientations
{
NSUInteger orientation = UIInterfaceOrientationMaskPortrait; // must support portrait
NSUInteger supported = [captureCommand.viewController supportedInterfaceOrientations];

orientation = orientation | (supported & UIInterfaceOrientationMaskPortraitUpsideDown);
return orientation;
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
UIInterfaceOrientationMask orientation = UIInterfaceOrientationMaskPortrait;
UIInterfaceOrientationMask supported = [captureCommand.viewController supportedInterfaceOrientations];

orientation = orientation | (supported & UIInterfaceOrientationMaskPortraitUpsideDown);
return orientation;
}
#else
- (NSUInteger)supportedInterfaceOrientations
{
NSUInteger orientation = UIInterfaceOrientationMaskPortrait; // must support portrait
NSUInteger supported = [captureCommand.viewController supportedInterfaceOrientations];

orientation = orientation | (supported & UIInterfaceOrientationMaskPortraitUpsideDown);
return orientation;
}
#endif

- (void)viewDidUnload
Expand Down Expand Up @@ -816,8 +912,8 @@ - (void)stopRecordingCleanup
}
if (self.duration && self.isTimed) {
// VoiceOver announcement so user knows timed recording has finished
BOOL isUIAccessibilityAnnouncementNotification = (&UIAccessibilityAnnouncementNotification != NULL);
if (isUIAccessibilityAnnouncementNotification) {
//BOOL isUIAccessibilityAnnouncementNotification = (&UIAccessibilityAnnouncementNotification != NULL);
if (UIAccessibilityAnnouncementNotification) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 500ull * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, PluginLocalizedString(captureCommand, @"timed recording complete", nil));
});
Expand Down
10 changes: 9 additions & 1 deletion www/CaptureImageOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,22 @@
* specific language governing permissions and limitations
* under the License.
*
*/
*/

/**
* Encapsulates all image capture operation configuration options.
*/
var CaptureImageOptions = function(){
// Upper limit of images user can take. Value must be equal or greater than 1.
this.limit = 1;
// Image quality (0-100)
this.quality = 50;
// Save photos to album/camera roll
this.saveToPhotoAlbum = true;
// Width of the saved image, this must be used with height to take effect
this.targetWidth = 0;
// Height of the saved image, this must be used with width to take effect
this.targetHeight = 0;
};

module.exports = CaptureImageOptions;
2 changes: 1 addition & 1 deletion www/CaptureVideoOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var CaptureVideoOptions = function(){
this.limit = 1;
// Maximum duration of a single video clip in seconds.
this.duration = 0;
// Video quality parameter, 0 means low quality, suitable for MMS messages, and value 1 means high quality.
// Video quality parameter, 0 means low quality, suitable for MMS messages, and value 1 means high quality. Defaults to medium quality in iOS, if set to null.
this.quality = 1;
};

Expand Down