From f92c5b1e37f3e5d441c988b7487b7d4af43042d3 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Wed, 10 Jul 2019 15:00:55 -0300 Subject: [PATCH] New angle classes, fix snapshot rotation --- .../cameraview/engine/MockCameraEngine.java | 19 --- .../otaliastudios/cameraview/CameraView.java | 11 +- .../cameraview/engine/Camera1Engine.java | 85 ++++-------- .../cameraview/engine/Camera2Engine.java | 58 ++++---- .../cameraview/engine/CameraEngine.java | 123 +++++------------ .../cameraview/engine/offset/Angles.java | 125 ++++++++++++++++++ .../cameraview/engine/offset/Axis.java | 28 ++++ .../cameraview/engine/offset/Reference.java | 28 ++++ .../picture/Snapshot1PictureRecorder.java | 3 +- .../picture/SnapshotGlPictureRecorder.java | 6 +- .../cameraview/preview/GlCameraPreview.java | 2 +- .../preview/TextureCameraPreview.java | 2 +- .../video/SnapshotVideoRecorder.java | 9 +- demo/src/main/res/layout/activity_camera.xml | 2 +- 14 files changed, 285 insertions(+), 216 deletions(-) create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Angles.java create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Axis.java create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Reference.java diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java index 3d3d0ad9..3c2be982 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/engine/MockCameraEngine.java @@ -177,23 +177,4 @@ public class MockCameraEngine extends CameraEngine { protected boolean collectCameraInfo(@NonNull Facing facing) { return true; } - - /* - - @Override - public void setFacing(@NonNull Facing facing) { - mFacing = facing; - } - - @Override - public void setMode(@NonNull Mode mode) { - mMode = mode; - } - - @Override - public void setAudio(@NonNull Audio audio) { - mAudio = audio; - } - - */ } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 025a0288..284a1ecc 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -37,6 +37,7 @@ import com.otaliastudios.cameraview.controls.Engine; import com.otaliastudios.cameraview.controls.Facing; import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.engine.Camera2Engine; +import com.otaliastudios.cameraview.engine.offset.Reference; import com.otaliastudios.cameraview.markers.MarkerLayout; import com.otaliastudios.cameraview.engine.Camera1Engine; import com.otaliastudios.cameraview.engine.CameraEngine; @@ -342,7 +343,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - Size previewSize = mCameraEngine.getPreviewStreamSize(CameraEngine.REF_VIEW); + Size previewSize = mCameraEngine.getPreviewStreamSize(Reference.VIEW); if (previewSize == null) { LOG.w("onMeasure:", "surface is not ready. Calling default behavior."); super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -1549,12 +1550,12 @@ public class CameraView extends FrameLayout implements LifecycleObserver { // Get the preview size and crop according to the current view size. // It's better to do calculations in the REF_VIEW reference, and then flip if needed. - Size preview = mCameraEngine.getUncroppedSnapshotSize(CameraEngine.REF_VIEW); + Size preview = mCameraEngine.getUncroppedSnapshotSize(Reference.VIEW); if (preview == null) return null; // Should never happen. AspectRatio viewRatio = AspectRatio.of(getWidth(), getHeight()); Rect crop = CropHelper.computeCrop(preview, viewRatio); Size cropSize = new Size(crop.width(), crop.height()); - if (mCameraEngine.flip(CameraEngine.REF_VIEW, CameraEngine.REF_OUTPUT)) { + if (mCameraEngine.getAngles().flip(Reference.VIEW, Reference.OUTPUT)) { return cropSize.flip(); } else { return cropSize; @@ -1573,7 +1574,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { */ @Nullable public Size getPictureSize() { - return mCameraEngine.getPictureSize(CameraEngine.REF_OUTPUT); + return mCameraEngine.getPictureSize(Reference.OUTPUT); } @@ -1588,7 +1589,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { */ @Nullable public Size getVideoSize() { - return mCameraEngine.getVideoSize(CameraEngine.REF_OUTPUT); + return mCameraEngine.getVideoSize(Reference.OUTPUT); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java index f51394b5..983acc82 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java @@ -3,7 +3,6 @@ package com.otaliastudios.cameraview.engine; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.graphics.ImageFormat; -import android.graphics.PixelFormat; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.SurfaceTexture; @@ -15,7 +14,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; -import android.util.Log; import android.view.SurfaceHolder; import com.google.android.gms.tasks.Task; @@ -24,6 +22,8 @@ import com.otaliastudios.cameraview.CameraException; import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraOptions; import com.otaliastudios.cameraview.controls.Engine; +import com.otaliastudios.cameraview.engine.offset.Axis; +import com.otaliastudios.cameraview.engine.offset.Reference; import com.otaliastudios.cameraview.frame.Frame; import com.otaliastudios.cameraview.PictureResult; import com.otaliastudios.cameraview.VideoResult; @@ -35,7 +35,6 @@ import com.otaliastudios.cameraview.controls.Hdr; import com.otaliastudios.cameraview.controls.Mode; import com.otaliastudios.cameraview.controls.WhiteBalance; import com.otaliastudios.cameraview.internal.utils.CropHelper; -import com.otaliastudios.cameraview.internal.utils.Op; import com.otaliastudios.cameraview.picture.Full1PictureRecorder; import com.otaliastudios.cameraview.picture.Snapshot1PictureRecorder; import com.otaliastudios.cameraview.picture.SnapshotGlPictureRecorder; @@ -124,7 +123,7 @@ public class Camera1Engine extends CameraEngine implements for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) { Camera.getCameraInfo(i, cameraInfo); if (cameraInfo.facing == internalFacing) { - mSensorOffset = cameraInfo.orientation; + setSensorOffset(facing, cameraInfo.orientation); mCameraId = i; return true; } @@ -151,10 +150,10 @@ public class Camera1Engine extends CameraEngine implements // Set parameters that might have been set before the camera was opened. LOG.i("onStartEngine:", "Applying default parameters."); Camera.Parameters params = mCamera.getParameters(); - mCameraOptions = new CameraOptions(params, flip(REF_SENSOR, REF_VIEW)); + mCameraOptions = new CameraOptions(params, getAngles().flip(Reference.SENSOR, Reference.VIEW)); applyAllParameters(params); mCamera.setParameters(params); - mCamera.setDisplayOrientation(offset(REF_SENSOR, REF_VIEW)); // <- not allowed during preview + mCamera.setDisplayOrientation(getAngles().offset(Reference.SENSOR, Reference.VIEW, Axis.ABSOLUTE)); // <- not allowed during preview LOG.i("onStartEngine:", "Ended"); return Tasks.forResult(null); } @@ -188,7 +187,7 @@ public class Camera1Engine extends CameraEngine implements LOG.i("onStartPreview", "Dispatching onCameraPreviewStreamSizeChanged."); mCallback.onCameraPreviewStreamSizeChanged(); - Size previewSize = getPreviewStreamSize(REF_VIEW); + Size previewSize = getPreviewStreamSize(Reference.VIEW); if (previewSize == null) { throw new IllegalStateException("previewStreamSize should not be null at this point."); } @@ -299,8 +298,8 @@ public class Camera1Engine extends CameraEngine implements @WorkerThread @Override protected void onTakePicture(@NonNull PictureResult.Stub stub) { - stub.rotation = offset(REF_SENSOR, REF_OUTPUT); - stub.size = getPictureSize(REF_OUTPUT); + stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR); + stub.size = getPictureSize(Reference.OUTPUT); mPictureRecorder = new Full1PictureRecorder(stub, Camera1Engine.this, mCamera); mPictureRecorder.take(); } @@ -308,9 +307,9 @@ public class Camera1Engine extends CameraEngine implements @WorkerThread @Override protected void onTakePictureSnapshot(@NonNull PictureResult.Stub stub, @NonNull AspectRatio viewAspectRatio) { - stub.size = getUncroppedSnapshotSize(REF_OUTPUT); // Not the real size: it will be cropped to match the view ratio - stub.rotation = offset(REF_SENSOR, REF_OUTPUT); // Actually it will be rotated and set to 0. - AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; + stub.size = getUncroppedSnapshotSize(Reference.OUTPUT); // Not the real size: it will be cropped to match the view ratio + stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR); // Actually it will be rotated and set to 0. + AspectRatio outputRatio = getAngles().flip(Reference.OUTPUT, Reference.VIEW) ? viewAspectRatio.flip() : viewAspectRatio; if (mPreview instanceof GlCameraPreview && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { mPictureRecorder = new SnapshotGlPictureRecorder(stub, this, (GlCameraPreview) mPreview, outputRatio); @@ -326,8 +325,8 @@ public class Camera1Engine extends CameraEngine implements @Override protected void onTakeVideo(@NonNull VideoResult.Stub stub) { - stub.rotation = offset(REF_SENSOR, REF_OUTPUT); - stub.size = flip(REF_SENSOR, REF_OUTPUT) ? mCaptureSize.flip() : mCaptureSize; + stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR); + stub.size = getAngles().flip(Reference.SENSOR, Reference.OUTPUT) ? mCaptureSize.flip() : mCaptureSize; // Unlock the camera and start recording. try { mCamera.unlock(); @@ -337,9 +336,7 @@ public class Camera1Engine extends CameraEngine implements onVideoResult(null, e); return; } - if (!(mVideoRecorder instanceof Full1VideoRecorder)) { - mVideoRecorder = new Full1VideoRecorder(Camera1Engine.this, mCamera, mCameraId); - } + mVideoRecorder = new Full1VideoRecorder(Camera1Engine.this, mCamera, mCameraId); mVideoRecorder.start(stub); } @@ -355,56 +352,19 @@ public class Camera1Engine extends CameraEngine implements } GlCameraPreview glPreview = (GlCameraPreview) mPreview; - // Size and rotation turned out to be extremely tricky. In case of Snapshot1PictureRecorder - // we use the preview size in REF_OUTPUT (cropped) and offset(REF_SENSOR, REF_OUTPUT) as rotation. - // These values mean that we expect input to be in the REF_SENSOR system. - - // Here everything seems different. We would expect a difference because the two snapshot - // recorders have different mechanics (the picture one uses a SurfaceTexture with setBufferSize, - // the video one here uses the MediaCodec input surface which we can't control). - - // The strangest thing is the fact that the correct angle seems to be the same for FRONT and - // BACK sensor, which means that our sensor correction actually screws things up. For this reason - // facing value is temporarily set to BACK. - Facing realFacing = mFacing; - mFacing = Facing.BACK; - - // These are the angles that make it work on a Nexus5X, compared to the offset() results. - // For instance, SV means offset(REF_SENSOR, REF_VIEW). The rest should be clear. - // CONFIG | WANTED | SV | VS | VO | OV | SO | OS | - // ------------|--------|--------|--------|--------|--------|--------|--------| - // Vertical | 0 | 270 | 90 | 0 | 0 | 270 | 90 | - // Left | 270 | 270 | 90 | 270 | 90 | 180 | 180 | - // Right | 90 | 270 | 90 | 90 | 270 | 0 | 0 | - // Upside down | 180 | 270 | 90 | 180 | 180 | 90 | 270 | - - // The VO is the only correct value. Things change when using FRONT camera, in which case, - // no value is actually correct, and the needed values are the same of BACK! - // CONFIG | WANTED | SV | VS | VO | OV | SO | OS | - // ------------|--------|--------|--------|--------|--------|--------|--------| - // Vertical | 0 | 90 | 270 | 180 | 180 | 270 | 90 | - // Left | 270 | 90 | 270 | 270 | 90 | 0 | 0 | - // Right | 90 | 90 | 270 | 90 | 270 | 180 | 180 | - // Upside down | 180 | 90 | 270 | 0 | 0 | 90 | 270 | - - // Based on this we will use VO for everything. See if we get issues about distortion - // and maybe we can improve. The reason why this happen is beyond my understanding. - - Size outputSize = getUncroppedSnapshotSize(REF_OUTPUT); + // Output size is easy: + Size outputSize = getUncroppedSnapshotSize(Reference.OUTPUT); if (outputSize == null) { throw new IllegalStateException("outputSize should not be null."); } - AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; + AspectRatio outputRatio = getAngles().flip(Reference.OUTPUT, Reference.VIEW) ? viewAspectRatio.flip() : viewAspectRatio; Rect outputCrop = CropHelper.computeCrop(outputSize, outputRatio); outputSize = new Size(outputCrop.width(), outputCrop.height()); stub.size = outputSize; - stub.rotation = offset(REF_VIEW, REF_OUTPUT); + stub.rotation = getAngles().offset(Reference.VIEW, Reference.OUTPUT, Axis.ABSOLUTE); - // Reset facing and start. - mFacing = realFacing; - if (!(mVideoRecorder instanceof SnapshotVideoRecorder)) { - mVideoRecorder = new SnapshotVideoRecorder(Camera1Engine.this, glPreview); - } + // Start. + mVideoRecorder = new SnapshotVideoRecorder(Camera1Engine.this, glPreview); mVideoRecorder.start(stub); } @@ -691,7 +651,7 @@ public class Camera1Engine extends CameraEngine implements public void onPreviewFrame(@NonNull byte[] data, Camera camera) { Frame frame = getFrameManager().getFrame(data, System.currentTimeMillis(), - offset(REF_SENSOR, REF_OUTPUT), + getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR), mPreviewStreamSize, PREVIEW_FORMAT); mCallback.dispatchFrame(frame); @@ -718,8 +678,9 @@ public class Camera1Engine extends CameraEngine implements if (getEngineState() < STATE_STARTED) return; if (!mCameraOptions.isAutoFocusSupported()) return; final PointF p = new PointF(point.x, point.y); // copy. + int offset = getAngles().offset(Reference.SENSOR, Reference.VIEW, Axis.ABSOLUTE); List meteringAreas2 = computeMeteringAreas(p.x, p.y, - viewWidthF, viewHeightF, offset(REF_SENSOR, REF_VIEW)); + viewWidthF, viewHeightF, offset); List meteringAreas1 = meteringAreas2.subList(0, 1); // At this point we are sure that camera supports auto focus... right? Look at CameraView.onTouchEvent(). diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java index 4c704dee..7c700d68 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -44,6 +44,8 @@ import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.controls.Hdr; import com.otaliastudios.cameraview.controls.Mode; import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.engine.offset.Axis; +import com.otaliastudios.cameraview.engine.offset.Reference; import com.otaliastudios.cameraview.frame.Frame; import com.otaliastudios.cameraview.frame.FrameManager; import com.otaliastudios.cameraview.gesture.Gesture; @@ -299,7 +301,8 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv CameraCharacteristics characteristics = mManager.getCameraCharacteristics(cameraId); if (internalFacing == readCharacteristic(characteristics, CameraCharacteristics.LENS_FACING, -99)) { mCameraId = cameraId; - mSensorOffset = readCharacteristic(characteristics, CameraCharacteristics.SENSOR_ORIENTATION, 0); + int sensorOffset = readCharacteristic(characteristics, CameraCharacteristics.SENSOR_ORIENTATION, 0); + setSensorOffset(facing, sensorOffset); return true; } } catch (CameraAccessException ignore) { @@ -330,7 +333,8 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv try { LOG.i("createCamera:", "Applying default parameters."); mCameraCharacteristics = mManager.getCameraCharacteristics(mCameraId); - mCameraOptions = new CameraOptions(mManager, mCameraId, flip(REF_SENSOR, REF_VIEW)); + boolean flip = getAngles().flip(Reference.SENSOR, Reference.VIEW); + mCameraOptions = new CameraOptions(mManager, mCameraId, flip); createRepeatingRequestBuilder(CameraDevice.TEMPLATE_PREVIEW); } catch (CameraAccessException e) { task.trySetException(createCameraException(e)); @@ -489,12 +493,12 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv LOG.i("onStartPreview", "Dispatching onCameraPreviewStreamSizeChanged."); mCallback.onCameraPreviewStreamSizeChanged(); - Size previewSizeForView = getPreviewStreamSize(REF_VIEW); + Size previewSizeForView = getPreviewStreamSize(Reference.VIEW); if (previewSizeForView == null) { throw new IllegalStateException("previewStreamSize should not be null at this point."); } mPreview.setStreamSize(previewSizeForView.getWidth(), previewSizeForView.getHeight()); - mPreview.setDrawRotation(getDisplayOffset()); + mPreview.setDrawRotation(getAngles().offset(Reference.BASE, Reference.VIEW, Axis.ABSOLUTE)); if (hasFrameProcessors()) { getFrameManager().setUp(ImageFormat.getBitsPerPixel(FRAME_PROCESSING_FORMAT), mFrameProcessingSize); } @@ -613,9 +617,9 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @WorkerThread @Override protected void onTakePictureSnapshot(@NonNull PictureResult.Stub stub, @NonNull AspectRatio viewAspectRatio) { - stub.size = getUncroppedSnapshotSize(REF_OUTPUT); // Not the real size: it will be cropped to match the view ratio - stub.rotation = offset(REF_SENSOR, REF_OUTPUT); // Actually it will be rotated and set to 0. - AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; + stub.size = getUncroppedSnapshotSize(Reference.OUTPUT); // Not the real size: it will be cropped to match the view ratio + stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR); // Actually it will be rotated and set to 0. + AspectRatio outputRatio = getAngles().flip(Reference.OUTPUT, Reference.VIEW) ? viewAspectRatio.flip() : viewAspectRatio; if (mPreview instanceof GlCameraPreview) { mPictureRecorder = new SnapshotGlPictureRecorder(stub, this, (GlCameraPreview) mPreview, outputRatio); } else { @@ -626,8 +630,8 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @Override protected void onTakePicture(@NonNull PictureResult.Stub stub) { - stub.rotation = offset(REF_SENSOR, REF_OUTPUT); - stub.size = getPictureSize(REF_OUTPUT); + stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR); + stub.size = getPictureSize(Reference.OUTPUT); try { CaptureRequest.Builder builder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); applyAllParameters(builder); @@ -661,8 +665,8 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @Override protected void onTakeVideo(@NonNull VideoResult.Stub stub) { LOG.i("onTakeVideo", "called."); - stub.rotation = offset(REF_SENSOR, REF_OUTPUT); - stub.size = flip(REF_SENSOR, REF_OUTPUT) ? mCaptureSize.flip() : mCaptureSize; + stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR); + stub.size = getAngles().flip(Reference.SENSOR, Reference.OUTPUT) ? mCaptureSize.flip() : mCaptureSize; if (!Full2VideoRecorder.SUPPORTS_PERSISTENT_SURFACE) { // On API 21 and 22, we must restart the session at each time. // Save the pending data and restart the session. @@ -675,9 +679,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv } private void doTakeVideo(@NonNull final VideoResult.Stub stub) { - if (!(mVideoRecorder instanceof Full2VideoRecorder)) { - mVideoRecorder = new Full2VideoRecorder(this, mCameraId, mFullVideoPersistentSurface); - } + mVideoRecorder = new Full2VideoRecorder(this, mCameraId, mFullVideoPersistentSurface); Full2VideoRecorder recorder = (Full2VideoRecorder) mVideoRecorder; try { createRepeatingRequestBuilder(CameraDevice.TEMPLATE_RECORD); @@ -697,10 +699,6 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv } } - /** - * See {@link CameraEngine#onTakeVideoSnapshot(VideoResult.Stub, AspectRatio)} - * to read about the size and rotation computation. - */ @WorkerThread @Override protected void onTakeVideoSnapshot(@NonNull VideoResult.Stub stub, @NonNull AspectRatio viewAspectRatio) { @@ -708,23 +706,20 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv throw new IllegalStateException("Video snapshots are only supported with GlCameraPreview."); } GlCameraPreview glPreview = (GlCameraPreview) mPreview; - Facing realFacing = mFacing; - mFacing = Facing.BACK; - Size outputSize = getUncroppedSnapshotSize(REF_OUTPUT); + + // Output size is easy: + Size outputSize = getUncroppedSnapshotSize(Reference.OUTPUT); if (outputSize == null) { throw new IllegalStateException("outputSize should not be null."); } - AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; + AspectRatio outputRatio = getAngles().flip(Reference.OUTPUT, Reference.VIEW) ? viewAspectRatio.flip() : viewAspectRatio; Rect outputCrop = CropHelper.computeCrop(outputSize, outputRatio); outputSize = new Size(outputCrop.width(), outputCrop.height()); stub.size = outputSize; - stub.rotation = offset(REF_VIEW, REF_OUTPUT); + stub.rotation = getAngles().offset(Reference.VIEW, Reference.OUTPUT, Axis.ABSOLUTE); - // Reset facing and start. - mFacing = realFacing; - if (!(mVideoRecorder instanceof SnapshotVideoRecorder)) { - mVideoRecorder = new SnapshotVideoRecorder(this, glPreview); - } + // Start. + mVideoRecorder = new SnapshotVideoRecorder(this, glPreview); mVideoRecorder.start(stub); } @@ -1052,7 +1047,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv if (getEngineState() == STATE_STARTED) { Frame frame = getFrameManager().getFrame(data, System.currentTimeMillis(), - offset(REF_SENSOR, REF_OUTPUT), + getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR), mFrameProcessingSize, FRAME_PROCESSING_FORMAT); mCallback.dispatchFrame(frame); @@ -1101,12 +1096,11 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv // This is a good Q/A. https://stackoverflow.com/a/33181620/4288782 // At first, the point is relative to the View system and does not account our own cropping. // Will keep updating these two below. - //noinspection UnnecessaryLocalVariable PointF referencePoint = new PointF(point.x, point.y); Size referenceSize /* = previewSurfaceSize */; // 1. Account for cropping. - Size previewStreamSize = getPreviewStreamSize(REF_VIEW); + Size previewStreamSize = getPreviewStreamSize(Reference.VIEW); Size previewSurfaceSize = mPreview.getSurfaceSize(); if (previewStreamSize == null) throw new IllegalStateException("getPreviewStreamSize should not be null at this point."); AspectRatio previewStreamAspectRatio = AspectRatio.of(previewStreamSize); @@ -1133,7 +1127,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv // 3. Rotate to the stream coordinate system. // Not elegant, but the sin/cos way was failing. - int angle = offset(REF_SENSOR, REF_VIEW); + int angle = getAngles().offset(Reference.SENSOR, Reference.VIEW, Axis.ABSOLUTE); boolean flip = angle % 180 != 0; float tempX = referencePoint.x; float tempY = referencePoint.y; if (angle == 0) { diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java index 5c6a95e2..17bbf096 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java @@ -1,15 +1,12 @@ package com.otaliastudios.cameraview.engine; import android.content.Context; -import android.graphics.Camera; import android.graphics.PointF; -import android.hardware.camera2.CameraCharacteristics; import android.location.Location; import android.os.Handler; import android.os.Looper; -import android.util.Log; import com.google.android.gms.tasks.Continuation; import com.google.android.gms.tasks.OnCompleteListener; @@ -22,6 +19,8 @@ import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraOptions; import com.otaliastudios.cameraview.PictureResult; import com.otaliastudios.cameraview.VideoResult; +import com.otaliastudios.cameraview.engine.offset.Angles; +import com.otaliastudios.cameraview.engine.offset.Reference; import com.otaliastudios.cameraview.frame.Frame; import com.otaliastudios.cameraview.frame.FrameManager; import com.otaliastudios.cameraview.internal.utils.Op; @@ -147,10 +146,6 @@ public abstract class CameraEngine implements public static final int STATE_STARTING = CameraEngineStep.STATE_STARTING; public static final int STATE_STARTED = CameraEngineStep.STATE_STARTED; - public static final int REF_SENSOR = 0; - public static final int REF_VIEW = 1; - public static final int REF_OUTPUT = 2; - // Need to be protected @SuppressWarnings("WeakerAccess") protected WorkerHandler mHandler; @SuppressWarnings("WeakerAccess") protected final Callback mCallback; @@ -161,7 +156,6 @@ public abstract class CameraEngine implements @SuppressWarnings("WeakerAccess") protected VideoRecorder mVideoRecorder; @SuppressWarnings("WeakerAccess") protected Size mCaptureSize; @SuppressWarnings("WeakerAccess") protected Size mPreviewStreamSize; - @SuppressWarnings("WeakerAccess") protected Facing mFacing; @SuppressWarnings("WeakerAccess") protected Flash mFlash; @SuppressWarnings("WeakerAccess") protected WhiteBalance mWhiteBalance; @SuppressWarnings("WeakerAccess") protected VideoCodec mVideoCodec; @@ -170,14 +164,15 @@ public abstract class CameraEngine implements @SuppressWarnings("WeakerAccess") protected float mZoomValue; @SuppressWarnings("WeakerAccess") protected float mExposureCorrectionValue; @SuppressWarnings("WeakerAccess") protected boolean mPlaySounds; - @SuppressWarnings("WeakerAccess") protected int mSensorOffset; // Can be private @VisibleForTesting Handler mCrashHandler; private final FrameManager mFrameManager; + private final Angles mAngles; @Nullable private SizeSelector mPreviewStreamSizeSelector; private SizeSelector mPictureSizeSelector; private SizeSelector mVideoSizeSelector; + private Facing mFacing; private Mode mMode; private Audio mAudio; private long mVideoMaxSize; @@ -188,8 +183,6 @@ public abstract class CameraEngine implements private long mAutoFocusResetDelayMillis; private int mSnapshotMaxWidth = Integer.MAX_VALUE; // in REF_VIEW for consistency with SizeSelectors private int mSnapshotMaxHeight = Integer.MAX_VALUE; // in REF_VIEW for consistency with SizeSelectors - private int mDisplayOffset; - private int mDeviceOrientation; // Steps private final CameraEngineStep.Callback mStepCallback = new CameraEngineStep.Callback() { @@ -219,6 +212,7 @@ public abstract class CameraEngine implements mHandler = WorkerHandler.get("CameraViewEngine"); mHandler.getThread().setUncaughtExceptionHandler(new CrashExceptionHandler()); mFrameManager = instantiateFrameManager(); + mAngles = new Angles(); } public void setPreview(@NonNull CameraPreview cameraPreview) { @@ -573,7 +567,7 @@ public abstract class CameraEngine implements */ @Override public final void onSurfaceAvailable() { - LOG.i("onSurfaceAvailable:", "Size is", getPreviewSurfaceSize(REF_VIEW)); + LOG.i("onSurfaceAvailable:", "Size is", getPreviewSurfaceSize(Reference.VIEW)); mHandler.run(new Runnable() { @Override public void run() { @@ -590,7 +584,7 @@ public abstract class CameraEngine implements @Override public final void onSurfaceChanged() { - LOG.i("onSurfaceChanged:", "Size is", getPreviewSurfaceSize(REF_VIEW), "Posting."); + LOG.i("onSurfaceChanged:", "Size is", getPreviewSurfaceSize(Reference.VIEW), "Posting."); mHandler.run(new Runnable() { @Override public void run() { @@ -786,19 +780,24 @@ public abstract class CameraEngine implements //region Final setters and getters - // This is called before start() and never again. - public final void setDisplayOffset(int displayOffset) { - mDisplayOffset = displayOffset; + @SuppressWarnings("WeakerAccess") + public final Angles getAngles() { + return mAngles; } @SuppressWarnings("WeakerAccess") - public final int getDisplayOffset() { - return mDisplayOffset; + protected final void setSensorOffset(@NonNull Facing facing, int sensorOffset) { + mAngles.setSensorOffset(facing, sensorOffset); + } + + // This is called before start() and never again. + public final void setDisplayOffset(int displayOffset) { + mAngles.setDisplayOffset(displayOffset); } // This can be called multiple times. public final void setDeviceOrientation(int deviceOrientation) { - mDeviceOrientation = deviceOrientation; + mAngles.setDeviceOrientation(deviceOrientation); } public final void setPreviewStreamSizeSelector(@Nullable SizeSelector selector) { @@ -1019,8 +1018,8 @@ public abstract class CameraEngine implements * Camera is about to be opened. Implementors should look into available cameras * and see if anyone matches the given {@link Facing value}. * - * If so, implementors should set {@link #mSensorOffset} and any other information - * (like camera ID) needed to start teh engine. + * If so, implementors should set {@link #setSensorOffset(Facing, int)} and any other information + * (like camera ID) needed to start the engine. * * @param facing the facing value * @return true if we have one @@ -1087,7 +1086,7 @@ public abstract class CameraEngine implements } /** - * The snapshot size is the {@link #getPreviewStreamSize(int)}, but cropped based on the + * The snapshot size is the {@link #getPreviewStreamSize(Reference)}, but cropped based on the * view/surface aspect ratio. * @param stub a picture stub * @param viewAspectRatio the view aspect ratio @@ -1104,9 +1103,6 @@ public abstract class CameraEngine implements stub.isSnapshot = true; stub.facing = mFacing; // Leave the other parameters to subclasses. - LOG.v("takePictureSnapshot", "Rotations", "SV", offset(REF_SENSOR, REF_VIEW), "VS", offset(REF_VIEW, REF_SENSOR)); - LOG.v("takePictureSnapshot", "Rotations", "SO", offset(REF_SENSOR, REF_OUTPUT), "OS", offset(REF_OUTPUT, REF_SENSOR)); - LOG.v("takePictureSnapshot", "Rotations", "VO", offset(REF_VIEW, REF_OUTPUT), "OV", offset(REF_OUTPUT, REF_VIEW)); onTakePictureSnapshot(stub, viewAspectRatio); } }); @@ -1228,73 +1224,31 @@ public abstract class CameraEngine implements //endregion - //region Orientation utils - - private int computeSensorToViewOffset() { - if (mFacing == Facing.FRONT) { - return (360 - ((mSensorOffset + mDisplayOffset) % 360)) % 360; - } else { - return (mSensorOffset - mDisplayOffset + 360) % 360; - } - } - - private int computeSensorToOutputOffset() { - if (mFacing == Facing.FRONT) { - return (mSensorOffset - mDeviceOrientation + 360) % 360; - } else { - return (mSensorOffset + mDeviceOrientation) % 360; - } - } - - // o(S, V) - o(S, O) - // displayOffset - deviceOrientation - - // Returns the offset between two reference systems. - @SuppressWarnings("WeakerAccess") - public final int offset(int fromReference, int toReference) { - if (fromReference == toReference) return 0; - // We only know how to compute offsets with respect to REF_SENSOR. - // That's why we separate the two cases. - if (fromReference == REF_SENSOR) { - return toReference == REF_VIEW ? - computeSensorToViewOffset() : - computeSensorToOutputOffset(); - } - // Maybe the sensor is the other. - if (toReference == REF_SENSOR) { - return (-offset(toReference, fromReference) + 360) % 360; - } - // None of them is the sensor. Use a difference. - return (offset(REF_SENSOR, toReference) - offset(REF_SENSOR, fromReference) + 360) % 360; - } - - public final boolean flip(int reference1, int reference2) { - return offset(reference1, reference2) % 180 != 0; - } + //region Size utilities @Nullable - public final Size getPictureSize(@SuppressWarnings("SameParameterValue") int reference) { + public final Size getPictureSize(@SuppressWarnings("SameParameterValue") @NonNull Reference reference) { if (mCaptureSize == null || mMode == Mode.VIDEO) return null; - return flip(REF_SENSOR, reference) ? mCaptureSize.flip() : mCaptureSize; + return getAngles().flip(Reference.SENSOR, reference) ? mCaptureSize.flip() : mCaptureSize; } @Nullable - public final Size getVideoSize(@SuppressWarnings("SameParameterValue") int reference) { + public final Size getVideoSize(@SuppressWarnings("SameParameterValue") @NonNull Reference reference) { if (mCaptureSize == null || mMode == Mode.PICTURE) return null; - return flip(REF_SENSOR, reference) ? mCaptureSize.flip() : mCaptureSize; + return getAngles().flip(Reference.SENSOR, reference) ? mCaptureSize.flip() : mCaptureSize; } @Nullable - public final Size getPreviewStreamSize(int reference) { + public final Size getPreviewStreamSize(@NonNull Reference reference) { if (mPreviewStreamSize == null) return null; - return flip(REF_SENSOR, reference) ? mPreviewStreamSize.flip() : mPreviewStreamSize; + return getAngles().flip(Reference.SENSOR, reference) ? mPreviewStreamSize.flip() : mPreviewStreamSize; } @SuppressWarnings("SameParameterValue") @Nullable - private Size getPreviewSurfaceSize(int reference) { + private Size getPreviewSurfaceSize(@NonNull Reference reference) { if (mPreview == null) return null; - return flip(REF_VIEW, reference) ? mPreview.getSurfaceSize().flip() : mPreview.getSurfaceSize(); + return getAngles().flip(Reference.VIEW, reference) ? mPreview.getSurfaceSize().flip() : mPreview.getSurfaceSize(); } /** @@ -1318,10 +1272,10 @@ public abstract class CameraEngine implements * apply, despite the capturing mechanism being different. */ @Nullable - public final Size getUncroppedSnapshotSize(int reference) { + public final Size getUncroppedSnapshotSize(@NonNull Reference reference) { Size baseSize = getPreviewStreamSize(reference); if (baseSize == null) return null; - boolean flip = flip(reference, REF_VIEW); + boolean flip = getAngles().flip(reference, Reference.VIEW); int maxWidth = flip ? mSnapshotMaxHeight : mSnapshotMaxWidth; int maxHeight = flip ? mSnapshotMaxWidth : mSnapshotMaxHeight; float baseRatio = AspectRatio.of(baseSize).toFloat(); @@ -1339,11 +1293,6 @@ public abstract class CameraEngine implements } } - - //endregion - - //region Size utils - /** * This is called either on cameraView.start(), or when the underlying surface changes. * It is possible that in the first call the preview surface has not already computed its @@ -1358,10 +1307,10 @@ public abstract class CameraEngine implements } @SuppressWarnings("WeakerAccess") - protected final Size computeCaptureSize(Mode mode) { + protected final Size computeCaptureSize(@NonNull Mode mode) { // We want to pass stuff into the REF_VIEW reference, not the sensor one. // This is already managed by CameraOptions, so we just flip again at the end. - boolean flip = flip(REF_SENSOR, REF_VIEW); + boolean flip = getAngles().flip(Reference.SENSOR, Reference.VIEW); SizeSelector selector; Collection sizes; if (mode == Mode.PICTURE) { @@ -1397,7 +1346,7 @@ public abstract class CameraEngine implements @NonNull List previewSizes = getPreviewStreamAvailableSizes(); // These sizes come in REF_SENSOR. Since there is an external selector involved, // we must convert all of them to REF_VIEW, then flip back when returning. - boolean flip = flip(REF_SENSOR, REF_VIEW); + boolean flip = getAngles().flip(Reference.SENSOR, Reference.VIEW); List sizes = new ArrayList<>(previewSizes.size()); for (Size size : previewSizes) { sizes.add(flip ? size.flip() : size); @@ -1405,7 +1354,7 @@ public abstract class CameraEngine implements // Create our own default selector, which will be used if the external mPreviewStreamSizeSelector // is null, or if it fails in finding a size. - Size targetMinSize = getPreviewSurfaceSize(REF_VIEW); + Size targetMinSize = getPreviewSurfaceSize(Reference.VIEW); if (targetMinSize == null) throw new IllegalStateException("targetMinSize should not be null here."); AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight()); if (flip) targetRatio = targetRatio.flip(); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Angles.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Angles.java new file mode 100644 index 00000000..5ed0dd22 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Angles.java @@ -0,0 +1,125 @@ +package com.otaliastudios.cameraview.engine.offset; + +import androidx.annotation.NonNull; + +import com.otaliastudios.cameraview.controls.Facing; + +public class Angles { + + private Facing mSensorFacing; + private int mSensorOffset = 0; + private int mDisplayOffset = 0; + private int mDeviceOrientation = 0; + + /** + * We want to keep everything in the {@link Axis#ABSOLUTE} reference, + * so a front facing sensor offset must be inverted. + * + * @param sensorFacing sensor facing value + * @param sensorOffset sensor offset + */ + public void setSensorOffset(@NonNull Facing sensorFacing, int sensorOffset) { + sanitizeInput(sensorOffset); + mSensorFacing = sensorFacing; + mSensorOffset = sensorOffset; + if (mSensorFacing == Facing.FRONT) { + mSensorOffset = sanitizeOutput(360 - mSensorOffset); + } + } + + /** + * Sets the display offset. + * @param displayOffset the display offset + */ + public void setDisplayOffset(int displayOffset) { + sanitizeInput(displayOffset); + mDisplayOffset = displayOffset; + } + + /** + * Sets the device orientation. + * @param deviceOrientation the device orientation + */ + public void setDeviceOrientation(int deviceOrientation) { + sanitizeInput(deviceOrientation); + mDeviceOrientation = deviceOrientation; + } + + /** + * Returns the offset between two reference systems, computed along the given axis. + * @param from the source reference system + * @param to the destination reference system + * @param axis the axis + * @return the offset + */ + public int offset(@NonNull Reference from, @NonNull Reference to, @NonNull Axis axis) { + int offset = absoluteOffset(from, to); + if (axis == Axis.RELATIVE_TO_SENSOR) { + if (mSensorFacing == Facing.FRONT) { + offset = sanitizeOutput(360 - offset); + } + } + return offset; + } + + /** + * Old results: + * offset(REF_SENSOR, REF_OUTPUT, Facing.BACK) = mSensorOffset + mDeviceOrientation + * offset(REF_SENSOR, REF_OUTPUT, Facing.FRONT) = -mSensorOffset - mDeviceOrientation + * offset(REF_SENSOR, REF_VIEW, Facing.BACK) = mSensorOffset - mDisplayOffset + * offset(REF_SENSOR, REF_VIEW, Facing.FRONT) = mSensorOffset - mDisplayOffset + * offset(REF_OUTPUT, REF_VIEW, Facing.BACK) = (mSensorOffset - mDisplayOffset) - (mSensorOffset + mDeviceOrientation) = -mDisplayOffset - mDeviceOrientation + * offset(REF_OUTPUT, REF_VIEW, Facing.FRONT) = (mSensorOffset - mDisplayOffset) - (-mSensorOffset - mDeviceOrientation) = 2*mSensorOffset + mDisplayOffset + mDeviceOrientation + */ + + /** + * New results: + * offset(REF_SENSOR, REF_OUTPUT) = mDeviceOrientation + mSensorOffset + * -> REF_SENSOR, REF_OUTPUT is typically wanted in the RELATIVE_TO_SENSOR axis. + * + * offset(REF_SENSOR, REF_VIEW) = -mDisplayOffset + mSensorOffset + * -> REF_SENSOR, REF_VIEW is typically wanted in the ABSOLUTE axis. + */ + + private int absoluteOffset(@NonNull Reference from, @NonNull Reference to) { + if (from == to) { + return 0; + } else if (to == Reference.BASE) { + return 360 - absoluteOffset(to, from); + } else if (from == Reference.BASE) { + switch (to) { + case VIEW: return sanitizeOutput(360 - mDisplayOffset); + case OUTPUT: return sanitizeOutput(mDeviceOrientation); + case SENSOR: return sanitizeOutput(360 - mSensorOffset); + default: throw new RuntimeException("Unknown reference: " + to); + } + } else { + return sanitizeOutput( + absoluteOffset(Reference.BASE, to) + - absoluteOffset(Reference.BASE, from)); + } + } + + /** + * Whether the two references systems are flipped. + * @param from source + * @param to destination + * @return true if flipped + */ + public boolean flip(@NonNull Reference from, @NonNull Reference to) { + return offset(from, to, Axis.ABSOLUTE) % 180 != 0; + } + + private void sanitizeInput(int value) { + if (value != 0 + && value != 90 + && value != 180 + && value != 270) { + throw new IllegalStateException("This value is not sanitized: " + value); + } + } + + private int sanitizeOutput(int value) { + return (value + 360) % 360; + } +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Axis.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Axis.java new file mode 100644 index 00000000..d5f3eb67 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Axis.java @@ -0,0 +1,28 @@ +package com.otaliastudios.cameraview.engine.offset; + +/** + * The axis around which offsets are computed. We have two possibilities: + * - an axis going out of the device screen towards the user's face + * - an axis going out of the device screen towards the back + * + * We are mostly interested in the first one, but some APIs will require + * angles in the sensor reference, in which case, for front cameras, we are + * required to use {@link #RELATIVE_TO_SENSOR}. + */ +public enum Axis { + + /** + * This rotation axis is the one going out of the device screen + * towards the user's face. + */ + ABSOLUTE, + + /** + * This rotation axis takes into account the current + * {@link com.otaliastudios.cameraview.controls.Facing} value. + * + * - for {@link com.otaliastudios.cameraview.controls.Facing#BACK}, this equals {@link #ABSOLUTE} + * - for {@link com.otaliastudios.cameraview.controls.Facing#FRONT}, this is inverted + */ + RELATIVE_TO_SENSOR +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Reference.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Reference.java new file mode 100644 index 00000000..640ff19c --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/offset/Reference.java @@ -0,0 +1,28 @@ +package com.otaliastudios.cameraview.engine.offset; + +public enum Reference { + + /** + * The base reference system has its 'north' aligned with the device natural + * orientation. + */ + BASE, + + /** + * This reference system has its 'north' aligned with the camera sensor. + */ + SENSOR, + + /** + * This reference system has its 'north' aligned with the View hierarchy. + * This can be different than {@link #BASE} if the activity is allowed to rotate + * (or forced into a non natural position). + */ + VIEW, + + /** + * This reference system has its 'north' aligned with the output picture/video. + * This means that it takes into account the device orientation. + */ + OUTPUT +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java index a099fa85..c0404821 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot1PictureRecorder.java @@ -9,6 +9,7 @@ import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.PictureResult; import com.otaliastudios.cameraview.engine.Camera1Engine; import com.otaliastudios.cameraview.engine.CameraEngine; +import com.otaliastudios.cameraview.engine.offset.Reference; import com.otaliastudios.cameraview.internal.utils.CropHelper; import com.otaliastudios.cameraview.internal.utils.RotationHelper; import com.otaliastudios.cameraview.internal.utils.WorkerHandler; @@ -57,7 +58,7 @@ public class Snapshot1PictureRecorder extends PictureRecorder { // Adding EXIF to a byte array, unfortunately, is hard. final int sensorToOutput = mResult.rotation; final Size outputSize = mResult.size; - final Size previewStreamSize = mEngine1.getPreviewStreamSize(CameraEngine.REF_SENSOR); + final Size previewStreamSize = mEngine1.getPreviewStreamSize(Reference.SENSOR); if (previewStreamSize == null) { throw new IllegalStateException("Preview stream size should never be null here."); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java index e4c66728..88ea8a65 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java @@ -4,7 +4,6 @@ import android.annotation.TargetApi; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.SurfaceTexture; -import android.hardware.Camera; import android.opengl.EGL14; import android.opengl.EGLContext; import android.opengl.Matrix; @@ -13,14 +12,13 @@ import android.os.Build; import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.PictureResult; import com.otaliastudios.cameraview.controls.Facing; -import com.otaliastudios.cameraview.engine.Camera1Engine; import com.otaliastudios.cameraview.engine.CameraEngine; +import com.otaliastudios.cameraview.engine.offset.Reference; import com.otaliastudios.cameraview.internal.egl.EglCore; import com.otaliastudios.cameraview.internal.egl.EglViewport; import com.otaliastudios.cameraview.internal.egl.EglWindowSurface; import com.otaliastudios.cameraview.internal.utils.CropHelper; import com.otaliastudios.cameraview.internal.utils.WorkerHandler; -import com.otaliastudios.cameraview.preview.CameraPreview; import com.otaliastudios.cameraview.preview.GlCameraPreview; import com.otaliastudios.cameraview.preview.RendererFrameCallback; import com.otaliastudios.cameraview.preview.RendererThread; @@ -107,7 +105,7 @@ public class SnapshotGlPictureRecorder extends PictureRecorder { // Apply scale and crop: // NOTE: scaleX and scaleY are in REF_VIEW, while our input appears to be in REF_SENSOR. - boolean flip = mEngine.flip(CameraEngine.REF_VIEW, CameraEngine.REF_SENSOR); + boolean flip = mEngine.getAngles().flip(Reference.VIEW, Reference.SENSOR); float realScaleX = flip ? scaleY : scaleX; float realScaleY = flip ? scaleX : scaleY; float scaleTranslX = (1F - realScaleX) / 2F; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java b/cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java index 293e11d6..5cad6af9 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/preview/GlCameraPreview.java @@ -190,7 +190,7 @@ public class GlCameraPreview extends CameraPreview