mirror of
https://github.com/status-im/react-native-camera.git
synced 2025-02-24 09:48:17 +00:00
Merge pull request #1283 from Snapp-FidMe/width_control_and_rotation_fix
Width control and rotation fix
This commit is contained in:
commit
c331964c24
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user