Merge pull request #1283 from Snapp-FidMe/width_control_and_rotation_fix

Width control and rotation fix
This commit is contained in:
Michaël Villeneuve 2018-02-27 15:07:33 +01:00 committed by GitHub
commit c331964c24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 158 additions and 125 deletions

View File

@ -24,18 +24,18 @@ import java.io.FileOutputStream;
import java.io.IOException;
public class ResolveTakenPictureAsyncTask extends AsyncTask<Void, Void, WritableMap> {
private static final String ERROR_TAG = "E_TAKING_PICTURE_FAILED";
private Promise mPromise;
private byte[] mImageData;
private ReadableMap mOptions;
private File mCacheDirectory;
private Bitmap mBitmap;
private static final String ERROR_TAG = "E_TAKING_PICTURE_FAILED";
private Promise mPromise;
private byte[] mImageData;
private ReadableMap mOptions;
private File mCacheDirectory;
private Bitmap mBitmap;
public ResolveTakenPictureAsyncTask(byte[] imageData, Promise promise, ReadableMap options) {
mPromise = promise;
mOptions = options;
mImageData = imageData;
}
public ResolveTakenPictureAsyncTask(byte[] imageData, Promise promise, ReadableMap options) {
mPromise = promise;
mOptions = options;
mImageData = imageData;
}
public ResolveTakenPictureAsyncTask(byte[] imageData, Promise promise, ReadableMap options, File cacheDirectory) {
mPromise = promise;
@ -44,92 +44,94 @@ public class ResolveTakenPictureAsyncTask extends AsyncTask<Void, Void, Writable
mCacheDirectory = cacheDirectory;
}
private int getQuality() {
return (int) (mOptions.getDouble("quality") * 100);
}
private int getQuality() {
return (int) (mOptions.getDouble("quality") * 100);
}
@Override
protected WritableMap doInBackground(Void... voids) {
WritableMap response = Arguments.createMap();
ByteArrayInputStream inputStream = null;
@Override
protected WritableMap doInBackground(Void... voids) {
WritableMap response = Arguments.createMap();
ByteArrayInputStream inputStream = null;
// we need the stream only for photos from a device
if (mBitmap == null) {
mBitmap = BitmapFactory.decodeByteArray(mImageData, 0, mImageData.length);
inputStream = new ByteArrayInputStream(mImageData);
}
// we need the stream only for photos from a device
if (mBitmap == null) {
mBitmap = BitmapFactory.decodeByteArray(mImageData, 0, mImageData.length);
inputStream = new ByteArrayInputStream(mImageData);
}
try {
if (inputStream != null) {
ExifInterface exifInterface = new ExifInterface(inputStream);
// Get orientation of the image from mImageData via inputStream
int orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED
);
try {
if (inputStream != null) {
ExifInterface exifInterface = new ExifInterface(inputStream);
// Get orientation of the image from mImageData via inputStream
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);
// Rotate the bitmap to the proper orientation if needed
if (orientation != ExifInterface.ORIENTATION_UNDEFINED) {
mBitmap = rotateBitmap(mBitmap, getImageRotation(orientation));
}
if (mOptions.hasKey("width")) {
mBitmap = resizeBitmap(mBitmap, mOptions.getInt("width"));
}
if (mOptions.hasKey("mirrorImage") && mOptions.getBoolean("mirrorImage")) {
mBitmap = flipHorizontally(mBitmap);
}
// Rotate the bitmap to the proper orientation if needed
if (mOptions.hasKey("fixOrientation") && mOptions.getBoolean("fixOrientation") && orientation != ExifInterface.ORIENTATION_UNDEFINED) {
mBitmap = rotateBitmap(mBitmap, getImageRotation(orientation));
}
// Write Exif data to the response if requested
if (mOptions.hasKey("exif") && mOptions.getBoolean("exif")) {
WritableMap exifData = RNCameraViewHelper.getExifData(exifInterface);
response.putMap("exif", exifData);
}
}
if (mOptions.hasKey("mirrorImage") && mOptions.getBoolean("mirrorImage")) {
mBitmap = flipHorizontally(mBitmap);
}
// Upon rotating, write the image's dimensions to the response
response.putInt("width", mBitmap.getWidth());
response.putInt("height", mBitmap.getHeight());
// Write Exif data to the response if requested
if (mOptions.hasKey("exif") && mOptions.getBoolean("exif")) {
WritableMap exifData = RNCameraViewHelper.getExifData(exifInterface);
response.putMap("exif", exifData);
}
}
// Cache compressed image in imageStream
ByteArrayOutputStream imageStream = new ByteArrayOutputStream();
mBitmap.compress(Bitmap.CompressFormat.JPEG, getQuality(), imageStream);
// Upon rotating, write the image's dimensions to the response
response.putInt("width", mBitmap.getWidth());
response.putInt("height", mBitmap.getHeight());
// Write compressed image to file in cache directory
String filePath = writeStreamToFile(imageStream);
File imageFile = new File(filePath);
String fileUri = Uri.fromFile(imageFile).toString();
response.putString("uri", fileUri);
// Cache compressed image in imageStream
ByteArrayOutputStream imageStream = new ByteArrayOutputStream();
mBitmap.compress(Bitmap.CompressFormat.JPEG, getQuality(), imageStream);
// Write base64-encoded image to the response if requested
if (mOptions.hasKey("base64") && mOptions.getBoolean("base64")) {
response.putString("base64", Base64.encodeToString(imageStream.toByteArray(), Base64.DEFAULT));
}
// Write compressed image to file in cache directory
String filePath = writeStreamToFile(imageStream);
File imageFile = new File(filePath);
String fileUri = Uri.fromFile(imageFile).toString();
response.putString("uri", fileUri);
// Cleanup
imageStream.close();
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
// Write base64-encoded image to the response if requested
if (mOptions.hasKey("base64") && mOptions.getBoolean("base64")) {
response.putString("base64", Base64.encodeToString(imageStream.toByteArray(), Base64.DEFAULT));
}
return response;
} catch (Resources.NotFoundException e) {
mPromise.reject(ERROR_TAG, "Documents directory of the app could not be found.", e);
e.printStackTrace();
} catch (IOException e) {
mPromise.reject(ERROR_TAG, "An unknown I/O exception has occurred.", e);
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// Cleanup
imageStream.close();
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
// An exception had to occur, promise has already been rejected. Do not try to resolve it again.
return null;
}
return response;
} catch (Resources.NotFoundException e) {
mPromise.reject(ERROR_TAG, "Documents directory of the app could not be found.", e);
e.printStackTrace();
} catch (IOException e) {
mPromise.reject(ERROR_TAG, "An unknown I/O exception has occurred.", e);
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// An exception had to occur, promise has already been rejected. Do not try to resolve it again.
return null;
}
private Bitmap rotateBitmap(Bitmap source, int angle) {
Matrix matrix = new Matrix();
@ -137,6 +139,14 @@ public class ResolveTakenPictureAsyncTask extends AsyncTask<Void, Void, Writable
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}
private Bitmap resizeBitmap(Bitmap bm, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleRatio = (float) newWidth / (float) width;
return Bitmap.createScaledBitmap(bm, newWidth, (int) (height * scaleRatio), true);
}
private Bitmap flipHorizontally(Bitmap source) {
Matrix matrix = new Matrix();
matrix.preScale(-1.0f, 1.0f);
@ -148,56 +158,56 @@ public class ResolveTakenPictureAsyncTask extends AsyncTask<Void, Void, Writable
private int getImageRotation(int orientation) {
int rotationDegrees = 0;
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotationDegrees = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotationDegrees = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotationDegrees = 270;
break;
case ExifInterface.ORIENTATION_ROTATE_90:
rotationDegrees = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotationDegrees = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotationDegrees = 270;
break;
}
return rotationDegrees;
}
private String writeStreamToFile(ByteArrayOutputStream inputStream) throws IOException {
String outputPath = null;
IOException exception = null;
FileOutputStream outputStream = null;
private String writeStreamToFile(ByteArrayOutputStream inputStream) throws IOException {
String outputPath = null;
IOException exception = null;
FileOutputStream outputStream = null;
try {
outputPath = RNFileUtils.getOutputFilePath(mCacheDirectory, ".jpg");
outputStream = new FileOutputStream(outputPath);
inputStream.writeTo(outputStream);
} catch (IOException e) {
e.printStackTrace();
exception = e;
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
outputPath = RNFileUtils.getOutputFilePath(mCacheDirectory, ".jpg");
outputStream = new FileOutputStream(outputPath);
inputStream.writeTo(outputStream);
} catch (IOException e) {
e.printStackTrace();
exception = e;
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
if (exception != null) {
throw exception;
}
return outputPath;
}
@Override
protected void onPostExecute(WritableMap response) {
super.onPostExecute(response);
// If the response is not null everything went well and we can resolve the promise.
if (response != null) {
mPromise.resolve(response);
}
}
if (exception != null) {
throw exception;
}
return outputPath;
}
@Override
protected void onPostExecute(WritableMap response) {
super.onPostExecute(response);
// If the response is not null everything went well and we can resolve the promise.
if (response != null) {
mPromise.resolve(response);
}
}
}

View File

@ -240,11 +240,15 @@ Takes a picture, saves in your app's cache directory and returns a promise.
Supported options:
- `width` (integer). This property allows to specify the width that the returned image should have, image ratio will not be affected. If no value is specified the maximum image size is used (capture may take longer).
- `quality` (float between 0 to 1.0). This property is used to compress the output jpeg file with 1 meaning no jpeg compression will be applied. If no value is specified `quality:1` is used.
- `base64` (boolean true or false) Use this with `true` if you want a base64 representation of the picture taken on the return data of your promise. If no value is specified `base64:false` is used.
- `exif` (boolean true or false) Use this with `true` if you want a exif data map of the picture taken on the return data of your promise. If no value is specified `exif:false` is used.
- `fixOrientation` (android only, boolean true or false) Use this with `true` if you want to fix incorrect image orientation (can take up to 5 seconds on some devices). Do not provide this if you only need EXIF based orientation.
- `forceUpOrientation` (iOS only, boolean true or false). This property allows to force portrait orientation based on actual data instead of exif data.

View File

@ -328,6 +328,10 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
takenImage = [RNImageUtils forceUpOrientation:takenImage];
}
if ([options[@"width"] integerValue]) {
takenImage = [RNImageUtils scaleImage:takenImage toWidth:[options[@"width"] integerValue]];
}
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
float quality = [options[@"quality"] floatValue];
NSData *takenImageData = UIImageJPEGRepresentation(takenImage, quality);

View File

@ -16,6 +16,7 @@
+ (UIImage *)mirrorImage:(UIImage *)image;
+ (UIImage *)forceUpOrientation:(UIImage *)image;
+ (NSString *)writeImage:(NSData *)image toPath:(NSString *)path;
+ (UIImage *) scaleImage:(UIImage*)image toWidth:(NSInteger)width;
+ (void)updatePhotoMetadata:(CMSampleBufferRef)imageSampleBuffer withAdditionalData:(NSDictionary *)additionalData inResponse:(NSMutableDictionary *)response;
@end

View File

@ -68,6 +68,19 @@
return [fileURL absoluteString];
}
+ (UIImage *) scaleImage:(UIImage*)image toWidth:(NSInteger)width
{
width /= [UIScreen mainScreen].scale; // prevents image from being incorrectly resized on retina displays
float scaleRatio = (float) width / (float) image.size.width;
CGSize size = CGSizeMake(width, (int) (image.size.height * scaleRatio));
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return [UIImage imageWithCGImage:[newImage CGImage] scale:1.0 orientation:(newImage.imageOrientation)];
}
+ (UIImage *)forceUpOrientation:(UIImage *)image
{
if (image.imageOrientation != UIImageOrientationUp) {
@ -79,6 +92,7 @@
return image;
}
+ (void)updatePhotoMetadata:(CMSampleBufferRef)imageSampleBuffer withAdditionalData:(NSDictionary *)additionalData inResponse:(NSMutableDictionary *)response
{
CFDictionaryRef exifAttachments = CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);