Skip to content

Commit

Permalink
Mirror image for Android (react-native-camera#399)
Browse files Browse the repository at this point in the history
* Add mirrorImage support for Android

* Release resources after mirroring

* Mirror image only for TYPE_FRONT

* Remove unnecessary annotation

* Document mirrorImage for Android

* Improved compression and error handling

* Add support for mirroring on Android back camera too
  • Loading branch information
gaguirre authored and rt2zz committed Sep 13, 2016
1 parent 2bbdf8c commit b01c64e
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 7 deletions.
1 change: 1 addition & 0 deletions Example/Example.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ export default class Example extends React.Component {
type={this.state.camera.type}
flashMode={this.state.camera.flashMode}
defaultTouchToFocus
mirrorImage={false}
/>
<View style={[styles.overlay, styles.topOverlay]}>
<TouchableOpacity
Expand Down
2 changes: 1 addition & 1 deletion Example/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ rootProject.name = 'Example'

include ':app'
include ':react-native-camera'
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../../android')
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,9 @@ By default, `onZoomChanged` is not defined and pinch-to-zoom is disabled.

If set to `true`, the device will not sleep while the camera preview is visible. This mimics the behavior of the default camera app, which keeps the device awake while open.

#### `iOS` `mirrorImage`
#### `mirrorImage`

If set to `true`, the image returned will be mirrored..
If set to `true`, the image returned will be mirrored.

## Component instance methods

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,48 @@

package com.lwansbrough.RCTCamera;

import android.annotation.TargetApi;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaActionSound;
import android.media.MediaRecorder;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import android.view.Surface;
import com.facebook.react.bridge.*;

import javax.annotation.Nullable;
import java.io.*;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;

public class RCTCameraModule extends ReactContextBaseJavaModule
implements MediaRecorder.OnInfoListener, LifecycleEventListener {
Expand Down Expand Up @@ -393,6 +413,50 @@ public static byte[] convertFileToByteArray(File f)
return byteArray;
}

private byte[] mirrorImage(byte[] data) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
Bitmap photo = BitmapFactory.decodeStream(inputStream);

Matrix m = new Matrix();
m.preScale(-1, 1);
Bitmap mirroredImage = Bitmap.createBitmap(photo, 0, 0, photo.getWidth(), photo.getHeight(), m, false);

byte[] result = null;

try {
result = compress(mirroredImage, 85);
} catch (OutOfMemoryError e) {
try {
result = compress(mirroredImage, 70);
} catch (OutOfMemoryError e2) {
e.printStackTrace();
}
}

try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}

return result;
}

private byte[] compress(Bitmap bitmap, int quality) throws OutOfMemoryError {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);

try {
return outputStream.toByteArray();
} finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

@ReactMethod
public void capture(final ReadableMap options, final Promise promise) {
int orientation = options.hasKey("orientation") ? options.getInt("orientation") : RCTCamera.getInstance().getOrientation();
Expand Down Expand Up @@ -435,10 +499,20 @@ private void captureWithOrientation(final ReadableMap options, final Promise pro
RCTCamera.getInstance().setCaptureQuality(options.getInt("type"), options.getString("quality"));
}

final Boolean shouldMirror = options.hasKey("mirrorImage") && options.getBoolean("mirrorImage");

RCTCamera.getInstance().adjustCameraRotationToDeviceOrientation(options.getInt("type"), deviceOrientation);
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {

if (shouldMirror) {
data = mirrorImage(data);
if (data == null) {
promise.reject("Error mirroring image");
}
}

camera.stopPreview();
camera.startPreview();
WritableMap response = new WritableNativeMap();
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export default class Camera extends Component {
type: props.type,
title: '',
description: '',
mirrorImage: props.mirrorImage,
...options
};

Expand Down

0 comments on commit b01c64e

Please sign in to comment.