From 41c5c5d7b487738f25af6b28d0a32d46dbec9af7 Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Tue, 21 Jul 2020 12:12:00 +0200 Subject: [PATCH 01/11] add preserveAspectRatio to CameraOptions to preserve aspect ratio of image --- .../java/com/getcapacitor/plugin/Camera.java | 9 +++- .../plugin/camera/CameraSettings.java | 9 ++++ .../plugin/camera/ImageUtils.java | 44 ++++++++++++++++++- core/src/core-plugin-definitions.ts | 7 +++ ios/Capacitor/Capacitor/Plugins/Camera.swift | 40 +++++++++++++++-- 5 files changed, 104 insertions(+), 5 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java index 0cb7491ed..30a495ab7 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java @@ -38,6 +38,7 @@ import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.UUID; /** * The Camera plugin makes it easy to take a photo or have the user select a photo @@ -182,6 +183,7 @@ private CameraSettings getSettings(PluginCall call) { settings.setHeight(call.getInt("height", 0)); settings.setShouldResize(settings.getWidth() > 0 || settings.getHeight() > 0); settings.setShouldCorrectOrientation(call.getBoolean("correctOrientation", CameraSettings.DEFAULT_CORRECT_ORIENTATION)); + settings.setPreserveAspectRatio(call.getBoolean("preserveAspectRatio", false)); try { settings.setSource(CameraSource.valueOf(call.getString("source", CameraSource.PROMPT.getSource()))); } catch (IllegalArgumentException ex) { @@ -410,7 +412,12 @@ private Bitmap prepareBitmap(Bitmap bitmap, Uri imageUri) throws IOException { } if (settings.isShouldResize()) { - final Bitmap newBitmap = ImageUtils.resize(bitmap, settings.getWidth(), settings.getHeight()); + final Bitmap newBitmap = ImageUtils.resize( + bitmap, + settings.getWidth(), + settings.getHeight(), + settings.getPreserveAspectRatio() + ); bitmap = replaceBitmap(bitmap, newBitmap); } return bitmap; diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/CameraSettings.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/CameraSettings.java index 5598ff713..e239a454a 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/CameraSettings.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/CameraSettings.java @@ -15,6 +15,7 @@ public class CameraSettings { private int width = 0; private int height = 0; private CameraSource source = CameraSource.PROMPT; + private boolean preserveAspectRatio = false; public CameraResultType getResultType() { return resultType; @@ -83,4 +84,12 @@ public CameraSource getSource() { public void setSource(CameraSource source) { this.source = source; } + + public void setPreserveAspectRatio(boolean preserveAspectRatio) { + this.preserveAspectRatio = preserveAspectRatio; + } + + public boolean getPreserveAspectRatio() { + return this.preserveAspectRatio; + } } diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java index da121340b..48f83df80 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java @@ -17,6 +17,21 @@ public class ImageUtils { + /** + * Resize an image to the given width and height considering the preserveAspectRatio flag. + * @param bitmap + * @param width + * @param height + * @param preserveAspectRatio + * @return a new, scaled Bitmap + */ + public static Bitmap resize(Bitmap bitmap, final int width, final int height, final boolean preserveAspectRatio) { + if (preserveAspectRatio) { + return ImageUtils.resizePreservingAspectRatio(bitmap, width, height); + } + return ImageUtils.resizeImageWithoutPreservingAspectRatio(bitmap, width, height); + } + /** * Resize an image to the given width and height. Leave one dimension 0 to * perform an aspect-ratio scale on the provided dimension. @@ -25,7 +40,7 @@ public class ImageUtils { * @param height * @return a new, scaled Bitmap */ - public static Bitmap resize(Bitmap bitmap, final int width, final int height) { + private static Bitmap resizeImageWithoutPreservingAspectRatio(Bitmap bitmap, final int width, final int height) { float aspect = bitmap.getWidth() / (float) bitmap.getHeight(); if (width > 0 && height > 0) { return Bitmap.createScaledBitmap(bitmap, width, height, false); @@ -38,6 +53,33 @@ public static Bitmap resize(Bitmap bitmap, final int width, final int height) { return bitmap; } + /** + * Resize an image to the given max width and max height. Constraint can be put + * on one dimension, or both. Resize will always preserve aspect ratio. + * @param bitmap + * @param desiredMaxWidth + * @param desiredMaxHeight + * @return a new, scaled Bitmap + */ + private static Bitmap resizePreservingAspectRatio(Bitmap bitmap, final int desiredMaxWidth, final int desiredMaxHeight) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + // 0 is treated as 'no restriction' + int maxHeight = desiredMaxHeight == 0 ? height : desiredMaxHeight; + int maxWidth = desiredMaxWidth == 0 ? width : desiredMaxWidth; + + // resize with preserved aspect ratio + float newWidth = Math.min(width, maxWidth); + float newHeight = (height * newWidth) / width; + + if (newHeight > maxHeight) { + newWidth = (width * maxHeight) / height; + newHeight = maxHeight; + } + return Bitmap.createScaledBitmap(bitmap, Math.round(newWidth), Math.round(newHeight), false); + } + /** * Transform an image with the given matrix * @param bitmap diff --git a/core/src/core-plugin-definitions.ts b/core/src/core-plugin-definitions.ts index de8f92ced..eff5be603 100644 --- a/core/src/core-plugin-definitions.ts +++ b/core/src/core-plugin-definitions.ts @@ -321,6 +321,13 @@ export interface CameraOptions { * The height of the saved image */ height?: number; + /** + * Wheter to preserve the aspect ratio of the image. + * If the this flag is true the width and height will be used as max values + * and the aspect ratio will be preserved + * Default: false + */ + preserveAspectRatio?: boolean; /** * Whether to automatically rotate the image "up" to correct for orientation * in portrait mode diff --git a/ios/Capacitor/Capacitor/Plugins/Camera.swift b/ios/Capacitor/Capacitor/Plugins/Camera.swift index 35a925281..5674f3b0f 100644 --- a/ios/Capacitor/Capacitor/Plugins/Camera.swift +++ b/ios/Capacitor/Capacitor/Plugins/Camera.swift @@ -29,6 +29,7 @@ struct CameraSettings { var height: Float = 0 var resultType = "base64" var saveToGallery = false + var preserveAspectRatio = false } @objc(CAPCameraPlugin) @@ -72,7 +73,8 @@ public class CAPCameraPlugin : CAPPlugin, UIImagePickerControllerDelegate, UINav settings.direction = CameraDirection(rawValue: call.getString("direction") ?? DEFAULT_DIRECTION.rawValue) ?? DEFAULT_DIRECTION settings.resultType = call.get("resultType", String.self, "base64")! settings.saveToGallery = call.get("saveToGallery", Bool.self, false)! - + settings.preserveAspectRatio = call.get("preserveAspectRatio", Bool.self, false)! + // Get the new image dimensions if provided settings.width = Float(call.get("width", Int.self, 0)!) settings.height = Float(call.get("height", Int.self, 0)!) @@ -229,7 +231,7 @@ public class CAPCameraPlugin : CAPPlugin, UIImagePickerControllerDelegate, UINav } if settings.shouldResize { - guard let convertedImage = resizeImage(image!) else { + guard let convertedImage = resizeImage(image!, settings.preserveAspectRatio) else { self.call?.error("Error resizing image") return } @@ -312,7 +314,14 @@ public class CAPCameraPlugin : CAPPlugin, UIImagePickerControllerDelegate, UINav return meta } - func resizeImage(_ image: UIImage) -> UIImage? { + func resizeImage(_ image: UIImage, _ preserveAspectRatio: Bool) -> UIImage? { + if preserveAspectRatio { + return resizeImagePreservingAspectRatio(image) + } + return resizeImageWithoutPreservingAspectRatio(image) + } + + func resizeImageWithoutPreservingAspectRatio(_ image: UIImage) -> UIImage? { let isAspectScale = settings.width > 0 && settings.height == 0 || settings.height > 0 && settings.width == 0 let aspect = Float(image.size.width / image.size.height); @@ -333,6 +342,31 @@ public class CAPCameraPlugin : CAPPlugin, UIImagePickerControllerDelegate, UINav return scaledImage } + func resizeImagePreservingAspectRatio(_ image: UIImage) -> UIImage? { + let imageHeight = Float(image.size.height) + let imageWidth = Float(image.size.width) + + // 0 is treated as 'no restriction' + let maxHeight = settings.height == 0 ? imageHeight : settings.height + let maxWidth = settings.width == 0 ? imageWidth : settings.width + + // resize with preserved aspect ratio + var newWidth = min(imageWidth, maxWidth) + var newHeight = (imageHeight * newWidth) / imageWidth + if newHeight > maxHeight { + newWidth = (imageWidth * maxHeight) / imageHeight + newHeight = maxHeight + } + let size = CGSize.init(width: Int(newWidth), height: Int(newHeight)) + + UIGraphicsBeginImageContextWithOptions(size, false, 1.0) + image.draw(in: CGRect(origin: CGPoint.zero, size: size)) + + let scaledImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return scaledImage + } + func makeExif(_ exif: [AnyHashable:Any]?) -> [AnyHashable:Any]? { return exif?["{Exif}"] as? [AnyHashable:Any] } From 79863659188fcd59bd219a004f37e2226abf0bcb Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Tue, 21 Jul 2020 12:12:24 +0200 Subject: [PATCH 02/11] prevent app crash while saving to gallery on android --- .../src/main/java/com/getcapacitor/plugin/Camera.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java index 30a495ab7..0f9e7fb84 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java @@ -343,7 +343,12 @@ private void returnResult(PluginCall call, Bitmap bitmap, Uri u) { if (saveToGallery && (imageEditedFileSavePath != null || imageFileSavePath != null)) { try { String fileToSave = imageEditedFileSavePath != null ? imageEditedFileSavePath : imageFileSavePath; - MediaStore.Images.Media.insertImage(getActivity().getContentResolver(), fileToSave, "", ""); + MediaStore.Images.Media.insertImage( + getActivity().getContentResolver(), + fileToSave, + "IMG_" + UUID.randomUUID().toString(), + "" + ); } catch (FileNotFoundException e) { Logger.error(getLogTag(), IMAGE_GALLERY_SAVE_ERROR, e); } From beec0bc78c73a08acf092001ea4d34bb49213505 Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Wed, 22 Jul 2020 09:03:49 +0200 Subject: [PATCH 03/11] re-add resize method without preserveAspectRatio flag --- .../com/getcapacitor/plugin/camera/ImageUtils.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java index 48f83df80..f2ad0c6e3 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java @@ -17,6 +17,17 @@ public class ImageUtils { + /** + * Resize an image to the given width and height. + * @param bitmap + * @param width + * @param height + * @return a new, scaled Bitmap + */ + public static Bitmap resize(Bitmap bitmap, final int width, final int height) { + ImageUtils.resize(bitmap, width, height, false) + } + /** * Resize an image to the given width and height considering the preserveAspectRatio flag. * @param bitmap From 2138fc590b09370c38c6d2ad0981d421b20406aa Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Wed, 22 Jul 2020 09:04:41 +0200 Subject: [PATCH 04/11] update doc of preserveAspectRatio flag in CameraOptions --- core/src/core-plugin-definitions.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/core-plugin-definitions.ts b/core/src/core-plugin-definitions.ts index eff5be603..f3fd99acd 100644 --- a/core/src/core-plugin-definitions.ts +++ b/core/src/core-plugin-definitions.ts @@ -322,9 +322,9 @@ export interface CameraOptions { */ height?: number; /** - * Wheter to preserve the aspect ratio of the image. - * If the this flag is true the width and height will be used as max values - * and the aspect ratio will be preserved + * Whether to preserve the aspect ratio of the image. + * If this flag is true, the width and height will be used as max values + * and the aspect ratio will be preserved. * Default: false */ preserveAspectRatio?: boolean; From d9025dd394426566ce822a030bd980895aee9df4 Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Wed, 22 Jul 2020 09:07:49 +0200 Subject: [PATCH 05/11] add missing return statement :grin: --- .../main/java/com/getcapacitor/plugin/camera/ImageUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java index f2ad0c6e3..934901d66 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java @@ -25,7 +25,7 @@ public class ImageUtils { * @return a new, scaled Bitmap */ public static Bitmap resize(Bitmap bitmap, final int width, final int height) { - ImageUtils.resize(bitmap, width, height, false) + return ImageUtils.resize(bitmap, width, height, false) } /** From ec45c8ee33ca30cf1d21e104645bfc41ad97573d Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Wed, 22 Jul 2020 09:14:05 +0200 Subject: [PATCH 06/11] add missing semicolon --- .../main/java/com/getcapacitor/plugin/camera/ImageUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java index 934901d66..a70977b13 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/camera/ImageUtils.java @@ -25,7 +25,7 @@ public class ImageUtils { * @return a new, scaled Bitmap */ public static Bitmap resize(Bitmap bitmap, final int width, final int height) { - return ImageUtils.resize(bitmap, width, height, false) + return ImageUtils.resize(bitmap, width, height, false); } /** From 9bd7192d91f59ca19d26de5af09aff3945ad1aa3 Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Wed, 22 Jul 2020 10:01:52 +0200 Subject: [PATCH 07/11] add camera button to test preserved aspect ratio --- example/src/pages/camera/camera.html | 1 + example/src/pages/camera/camera.ts | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/example/src/pages/camera/camera.html b/example/src/pages/camera/camera.html index d55138ad4..3e2fa7553 100644 --- a/example/src/pages/camera/camera.html +++ b/example/src/pages/camera/camera.html @@ -26,4 +26,5 @@ + diff --git a/example/src/pages/camera/camera.ts b/example/src/pages/camera/camera.ts index cb9b8c99e..052e00eee 100644 --- a/example/src/pages/camera/camera.ts +++ b/example/src/pages/camera/camera.ts @@ -187,4 +187,19 @@ export class CameraPage { console.log('Got image back', image.path, image.webPath, image.format, image.exif); this.image = this.sanitizer.bypassSecurityTrustResourceUrl(image && (image.dataUrl)); } + + async takePicturePreservingAspectRatio() { + const image = await Plugins.Camera.getPhoto({ + quality: 80, + resultType: CameraResultType.Uri, + source: CameraSource.Camera, + saveToGallery: true, + correctOrientation: true, + height: 1920, + width: 1920, + preserveAspectRatio: true, + }); + console.log('Got image back', image.path, image.webPath, image.format, image.exif); + this.image = this.sanitizer.bypassSecurityTrustResourceUrl(image.webPath); + } } From dd850719c2269dc41c928c769cbbedf3d9e0a9b7 Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Fri, 24 Jul 2020 16:56:01 +0200 Subject: [PATCH 08/11] restore save to gallery handling on android it has nothing to do with aspect ratio --- .../src/main/java/com/getcapacitor/plugin/Camera.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java index 0f9e7fb84..30a495ab7 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java @@ -343,12 +343,7 @@ private void returnResult(PluginCall call, Bitmap bitmap, Uri u) { if (saveToGallery && (imageEditedFileSavePath != null || imageFileSavePath != null)) { try { String fileToSave = imageEditedFileSavePath != null ? imageEditedFileSavePath : imageFileSavePath; - MediaStore.Images.Media.insertImage( - getActivity().getContentResolver(), - fileToSave, - "IMG_" + UUID.randomUUID().toString(), - "" - ); + MediaStore.Images.Media.insertImage(getActivity().getContentResolver(), fileToSave, "", ""); } catch (FileNotFoundException e) { Logger.error(getLogTag(), IMAGE_GALLERY_SAVE_ERROR, e); } From 02a4ea9bc8e78cfd883c342ca5d11e07ab99a7e6 Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Fri, 24 Jul 2020 17:05:38 +0200 Subject: [PATCH 09/11] remove unused UUID import --- .../capacitor/src/main/java/com/getcapacitor/plugin/Camera.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java index 30a495ab7..01fa8a98f 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/Camera.java @@ -38,7 +38,6 @@ import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.UUID; /** * The Camera plugin makes it easy to take a photo or have the user select a photo From 4869ef8b9e7a8f67ccfa8350ff3e00e8facf10fc Mon Sep 17 00:00:00 2001 From: Phil Schilter Date: Fri, 24 Jul 2020 19:39:23 +0200 Subject: [PATCH 10/11] update the definition of preserveAspectRatio in CameraOptions --- core/src/core-plugin-definitions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/core-plugin-definitions.ts b/core/src/core-plugin-definitions.ts index f3fd99acd..ddde3a4b6 100644 --- a/core/src/core-plugin-definitions.ts +++ b/core/src/core-plugin-definitions.ts @@ -324,7 +324,12 @@ export interface CameraOptions { /** * Whether to preserve the aspect ratio of the image. * If this flag is true, the width and height will be used as max values - * and the aspect ratio will be preserved. + * and the aspect ratio will be preserved. This is only relevant when + * both a width and height is passed. When only width or height is provided + * the aspect ratio is always preserved (and this option is a no-op). + * + * A future major version will change this behavior to be default, + * and may also remove this option altogether. * Default: false */ preserveAspectRatio?: boolean; From 8f89c07deb1804cdf8771cd4c4e07a401068d5af Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 24 Jul 2020 19:45:00 +0200 Subject: [PATCH 11/11] fix typo --- core/src/core-plugin-definitions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/core-plugin-definitions.ts b/core/src/core-plugin-definitions.ts index ddde3a4b6..f8a061853 100644 --- a/core/src/core-plugin-definitions.ts +++ b/core/src/core-plugin-definitions.ts @@ -325,7 +325,7 @@ export interface CameraOptions { * Whether to preserve the aspect ratio of the image. * If this flag is true, the width and height will be used as max values * and the aspect ratio will be preserved. This is only relevant when - * both a width and height is passed. When only width or height is provided + * both a width and height are passed. When only width or height is provided * the aspect ratio is always preserved (and this option is a no-op). * * A future major version will change this behavior to be default,