Skip to content

Commit

Permalink
feat(camera): add preserveAspectRatio resizing option (#3309)
Browse files Browse the repository at this point in the history
  • Loading branch information
iphilgood authored Jul 24, 2020
1 parent cfbd1e3 commit 27a8bcb
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,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) {
Expand Down Expand Up @@ -410,7 +411,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,32 @@

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) {
return ImageUtils.resize(bitmap, width, height, false);
}

/**
* 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.
Expand All @@ -25,7 +51,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);
Expand All @@ -38,6 +64,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
Expand Down
12 changes: 12 additions & 0 deletions core/src/core-plugin-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,18 @@ export interface CameraOptions {
* The height of the saved image
*/
height?: number;
/**
* 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 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,
* and may also remove this option altogether.
* Default: false
*/
preserveAspectRatio?: boolean;
/**
* Whether to automatically rotate the image "up" to correct for orientation
* in portrait mode
Expand Down
1 change: 1 addition & 0 deletions example/src/pages/camera/camera.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@
<button (click)="takePictureFile()" ion-button color="primary">File URI</button>
<button (click)="testImageSize()" ion-button color="primary">Image Size Test (#307)</button>
<button (click)="testAndroidBreak()" ion-button color="primary">Photos Orientation Test (#319)</button>
<button (click)="takePicturePreservingAspectRatio()" ion-button color="primary">Preserving Aspect Ratio</button>
</ion-content>
15 changes: 15 additions & 0 deletions example/src/pages/camera/camera.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
40 changes: 37 additions & 3 deletions ios/Capacitor/Capacitor/Plugins/Camera.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct CameraSettings {
var height: Float = 0
var resultType = "base64"
var saveToGallery = false
var preserveAspectRatio = false
}

@objc(CAPCameraPlugin)
Expand Down Expand Up @@ -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)!)
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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);

Expand All @@ -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]
}
Expand Down

0 comments on commit 27a8bcb

Please sign in to comment.