diff --git a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java index 8f88fd91..7a1deb75 100644 --- a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java +++ b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java @@ -388,7 +388,7 @@ class Camera1 extends CameraImpl { public void onPictureTaken(byte[] data, Camera camera) { mIsCapturingImage = false; camera.startPreview(); // This is needed, read somewhere in the docs. - mCameraListener.processJpegPicture(data, consistentWithView, exifFlip); + mCameraListener.processImage(data, consistentWithView, exifFlip); } }); } @@ -405,10 +405,12 @@ class Camera1 extends CameraImpl { public void onPreviewFrame(final byte[] data, Camera camera) { // Got to rotate the preview frame, since byte[] data here does not include // EXIF tags automatically set by camera. So either we add EXIF, or we rotate. - // TODO: add exif, and also care about flipping. + // Adding EXIF to a byte array, unfortunately, is hard. Camera.Parameters params = mCamera.getParameters(); - final int rotation = computeExifRotation(); - final boolean flip = rotation % 180 != 0; + final int sensorToDevice = computeExifRotation(); + final int sensorToDisplay = computeSensorToDisplayOffset(); + final boolean exifFlip = computeExifFlip(); + final boolean flip = sensorToDevice % 180 != 0; final int preWidth = mPreviewSize.getWidth(); final int preHeight = mPreviewSize.getHeight(); final int postWidth = flip ? preHeight : preWidth; @@ -417,9 +419,11 @@ class Camera1 extends CameraImpl { new Thread(new Runnable() { @Override public void run() { - byte[] rotatedData = RotationHelper.rotate(data, preWidth, preHeight, rotation); + + final boolean consistentWithView = (sensorToDevice + sensorToDisplay + 180) % 180 == 0; + byte[] rotatedData = RotationHelper.rotate(data, preWidth, preHeight, sensorToDevice); YuvImage yuv = new YuvImage(rotatedData, format, postWidth, postHeight, null); - mCameraListener.processYuvPicture(yuv); + mCameraListener.processSnapshot(yuv, consistentWithView, exifFlip); mIsCapturingImage = false; } }).start(); diff --git a/camerakit/src/main/base/com/flurgle/camerakit/PreviewImpl.java b/camerakit/src/main/base/com/flurgle/camerakit/PreviewImpl.java index 926e7c98..60ceb30b 100644 --- a/camerakit/src/main/base/com/flurgle/camerakit/PreviewImpl.java +++ b/camerakit/src/main/base/com/flurgle/camerakit/PreviewImpl.java @@ -49,7 +49,7 @@ abstract class PreviewImpl { void setDesiredSize(int width, int height) { this.mDesiredWidth = width; this.mDesiredHeight = height; - refreshScale(); + crop(); } final Size getSurfaceSize() { @@ -64,7 +64,7 @@ abstract class PreviewImpl { protected final void onSurfaceAvailable(int width, int height) { mSurfaceWidth = width; mSurfaceHeight = height; - refreshScale(); + crop(); mSurfaceCallback.onSurfaceAvailable(); } @@ -75,7 +75,7 @@ abstract class PreviewImpl { if (width != mSurfaceWidth || height != mSurfaceHeight) { mSurfaceWidth = width; mSurfaceHeight = height; - refreshScale(); + crop(); mSurfaceCallback.onSurfaceChanged(); } } @@ -84,7 +84,7 @@ abstract class PreviewImpl { protected final void onSurfaceDestroyed() { mSurfaceWidth = 0; mSurfaceHeight = 0; - refreshScale(); + crop(); } @@ -93,7 +93,7 @@ abstract class PreviewImpl { * to match the desired aspect ratio. * This means that the external part of the surface will be cropped by the outer view. */ - private final void refreshScale() { + private final void crop() { getView().post(new Runnable() { @Override public void run() { @@ -116,4 +116,14 @@ abstract class PreviewImpl { } }); } + + + /** + * Whether we are cropping the output. + * If false, this means that the output image will match the visible bounds. + * @return true if cropping + */ + final boolean isCropping() { + return getView().getScaleX() > 1 || getView().getScaleY() > 1; + } } diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java index bbc29a1b..7679a61e 100644 --- a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java +++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java @@ -983,23 +983,20 @@ public class CameraView extends FrameLayout implements LifecycleObserver { * @param consistentWithView is the final image (decoded respecting EXIF data) consistent with * the view width and height? Or should we flip dimensions to have a * consistent measure? - * @param flipPicture whether this picture should be flipped horizontally after decoding, - * because it was taken with the front camera. + * @param flipHorizontally whether this picture should be flipped horizontally after decoding, + * because it was taken with the front camera. */ - public void processJpegPicture(final byte[] jpeg, final boolean consistentWithView, final boolean flipPicture) { + public void processImage(final byte[] jpeg, final boolean consistentWithView, final boolean flipHorizontally) { getWorkerHandler().post(new Runnable() { @Override public void run() { byte[] jpeg2 = jpeg; - if (mCropOutput) { + if (mCropOutput && mPreviewImpl.isCropping()) { // If consistent, dimensions of the jpeg Bitmap and dimensions of getWidth(), getHeight() // Live in the same reference system. - AspectRatio targetRatio; - if (consistentWithView) { - targetRatio = AspectRatio.of(getWidth(), getHeight()); - } else { - targetRatio = AspectRatio.of(getHeight(), getWidth()); - } + int w = consistentWithView ? getWidth() : getHeight(); + int h = consistentWithView ? getHeight() : getWidth(); + AspectRatio targetRatio = AspectRatio.of(w, h); Log.e(TAG, "is Consistent? " + consistentWithView); Log.e(TAG, "viewWidth? " + getWidth() + ", viewHeight? " + getHeight()); jpeg2 = CropHelper.cropToJpeg(jpeg, targetRatio, mJpegQuality); @@ -1010,11 +1007,13 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } - public void processYuvPicture(YuvImage yuv) { + public void processSnapshot(YuvImage yuv, boolean consistentWithView, boolean flipHorizontally) { byte[] jpeg; - if (mCropOutput) { - AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); - jpeg = CropHelper.cropToJpeg(yuv, outputRatio, mJpegQuality); + if (mCropOutput && mPreviewImpl.isCropping()) { + int w = consistentWithView ? getWidth() : getHeight(); + int h = consistentWithView ? getHeight() : getWidth(); + AspectRatio targetRatio = AspectRatio.of(w, h); + jpeg = CropHelper.cropToJpeg(yuv, targetRatio, mJpegQuality); } else { ByteArrayOutputStream out = new ByteArrayOutputStream(); yuv.compressToJpeg(new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), mJpegQuality, out); diff --git a/camerakit/src/main/utils/com/flurgle/camerakit/CropHelper.java b/camerakit/src/main/utils/com/flurgle/camerakit/CropHelper.java index ccbf9e9c..2b324b5a 100644 --- a/camerakit/src/main/utils/com/flurgle/camerakit/CropHelper.java +++ b/camerakit/src/main/utils/com/flurgle/camerakit/CropHelper.java @@ -24,8 +24,8 @@ public class CropHelper { // This reads a rotated Bitmap thanks to CameraUtils. Then crops and returns a byte array. // In doing so, EXIF data is deleted. public static byte[] cropToJpeg(byte[] jpeg, AspectRatio targetRatio, int jpegCompression) { + Bitmap image = CameraUtils.decodeBitmap(jpeg); - Log.e("CropHelper", "decoded image has width="+image.getWidth()+", height="+image.getHeight()); Rect cropRect = computeCrop(image.getWidth(), image.getHeight(), targetRatio); Bitmap crop = Bitmap.createBitmap(image, cropRect.left, cropRect.top, cropRect.width(), cropRect.height()); image.recycle(); @@ -44,10 +44,10 @@ public class CropHelper { x = (currentWidth - width) / 2; } else { width = currentWidth; - height = (int) (width * targetRatio.inverse().toFloat()); + height = (int) (width / targetRatio.toFloat()); y = (currentHeight - height) / 2; x = 0; } - return new Rect(x, y, x+width, y+height); + return new Rect(x, y, x + width, y + height); } }