diff --git a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java index 6d3e9af4..2b28abd2 100644 --- a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java +++ b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java @@ -48,6 +48,7 @@ class Camera1 extends CameraImpl { private int mDisplayOffset; private int mDeviceOrientation; + private int mSensorOffset; @Facing private int mFacing; @Flash private int mFlash; @@ -130,6 +131,7 @@ class Camera1 extends CameraImpl { for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) { Camera.getCameraInfo(i, mCameraInfo); if (mCameraInfo.facing == internalFacing) { + mSensorOffset = mCameraInfo.orientation; mCameraId = i; mFacing = facing; break; @@ -291,6 +293,11 @@ class Camera1 extends CameraImpl { return mPreviewSize; } + @Override + boolean shouldFlipSizes() { + return mSensorOffset % 180 != 0; + } + @Override boolean isCameraOpened() { return mCamera != null; @@ -337,9 +344,9 @@ class Camera1 extends CameraImpl { private int computeCameraToDisplayOffset() { if (mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { // or: (360 - ((info.orientation + displayOrientation) % 360)) % 360; - return ((mCameraInfo.orientation - mDisplayOffset) + 360 + 180) % 360; + return ((mSensorOffset - mDisplayOffset) + 360 + 180) % 360; } else { - return (mCameraInfo.orientation - mDisplayOffset + 360) % 360; + return (mSensorOffset - mDisplayOffset + 360) % 360; } } @@ -348,7 +355,7 @@ class Camera1 extends CameraImpl { * the camera APIs as long as you call {@link Camera.Parameters#setRotation(int)}. */ private int computeExifOrientation() { - return (mDeviceOrientation + mCameraInfo.orientation) % 360; + return (mDeviceOrientation + mSensorOffset) % 360; } private int calculateCaptureRotation() { @@ -388,10 +395,10 @@ class Camera1 extends CameraImpl { private void adjustCameraParameters() { - boolean invertPreviewSizes = mDisplayOffset % 180 != 0; + boolean invertPreviewSizes = shouldFlipSizes(); // mDisplayOffset % 180 != 0; mPreview.setDesiredSize( - invertPreviewSizes? getPreviewSize().getHeight() : getPreviewSize().getWidth(), - invertPreviewSizes? getPreviewSize().getWidth() : getPreviewSize().getHeight() + invertPreviewSizes ? getPreviewSize().getHeight() : getPreviewSize().getWidth(), + invertPreviewSizes ? getPreviewSize().getWidth() : getPreviewSize().getHeight() ); mCameraParameters.setPreviewSize(getPreviewSize().getWidth(), getPreviewSize().getHeight()); mCameraParameters.setPictureSize(getCaptureSize().getWidth(), getCaptureSize().getHeight()); diff --git a/camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java b/camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java index c4a37257..73bec674 100644 --- a/camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java +++ b/camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java @@ -231,6 +231,11 @@ class Camera2 extends CameraImpl { return mPreviewSize; } + @Override + boolean shouldFlipSizes() { + return false; + } + @Override boolean isCameraOpened() { return mCamera != null; diff --git a/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java b/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java index 72c4e2f7..6bdc0cd3 100644 --- a/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java +++ b/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java @@ -31,6 +31,7 @@ abstract class CameraImpl { abstract Size getCaptureSize(); abstract Size getPreviewSize(); + abstract boolean shouldFlipSizes(); // Wheter the Sizes should be flipped to match the view orientation. abstract boolean isCameraOpened(); @Nullable diff --git a/camerakit/src/main/base/com/flurgle/camerakit/PreviewImpl.java b/camerakit/src/main/base/com/flurgle/camerakit/PreviewImpl.java index 81e50edd..096e864a 100644 --- a/camerakit/src/main/base/com/flurgle/camerakit/PreviewImpl.java +++ b/camerakit/src/main/base/com/flurgle/camerakit/PreviewImpl.java @@ -1,6 +1,7 @@ package com.flurgle.camerakit; import android.graphics.SurfaceTexture; +import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; @@ -14,6 +15,7 @@ abstract class PreviewImpl { private OnSurfaceChangedCallback mOnSurfaceChangedCallback; // As far as I can see, these are the view/surface dimensions. + // This live in the 'View' orientation. private int mSurfaceWidth; private int mSurfaceHeight; @@ -49,7 +51,8 @@ abstract class PreviewImpl { } // As far as I can see, these are the view/surface dimensions. - // This is called by subclasses. + // This is called by subclasses. 1080, 1794 -- 1080, 1440 + protected void setSurfaceSize(int width, int height) { this.mSurfaceWidth = width; this.mSurfaceHeight = height; @@ -57,7 +60,7 @@ abstract class PreviewImpl { } // As far as I can see, these are the actual preview dimensions, as set in CameraParameters. - // This is called by the CameraImpl. + // This is called by the CameraImpl. 1200, 1600 void setDesiredSize(int width, int height) { this.mDesiredWidth = width; this.mDesiredHeight = height; @@ -73,26 +76,24 @@ 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. */ - protected void refreshScale() { + private void refreshScale() { getView().post(new Runnable() { @Override public void run() { if (mDesiredWidth != 0 && mDesiredHeight != 0) { AspectRatio aspectRatio = AspectRatio.of(mDesiredWidth, mDesiredHeight); - int targetHeight = (int) (getView().getWidth() * aspectRatio.toFloat()); - float scaleY; - if (getView().getHeight() > 0) { - scaleY = (float) targetHeight / (float) getView().getHeight(); - } else { - scaleY = 1; + float targetHeight = (float) mSurfaceWidth / aspectRatio.toFloat(); + float scale = 1; + if (mSurfaceHeight > 0) { + scale = targetHeight / (float) mSurfaceHeight; } - if (scaleY > 1) { - getView().setScaleX(1); - getView().setScaleY(scaleY); + if (scale > 1) { + getView().setScaleX(1f); + getView().setScaleY(scale); } else { - getView().setScaleX(1 / scaleY); - getView().setScaleY(1); + getView().setScaleX(1f / scale); + getView().setScaleY(1f); } } } diff --git a/camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java b/camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java index b717c1d1..36d14776 100644 --- a/camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java +++ b/camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java @@ -14,7 +14,7 @@ class TextureViewPreview extends PreviewImpl { private final TextureView mTextureView; TextureViewPreview(Context context, ViewGroup parent) { - final View view = View.inflate(context, R.layout.texture_view, parent); + final View view = View.inflate(context, R.layout.texture_view, parent); // MATCH_PARENT mTextureView = (TextureView) view.findViewById(R.id.texture_view); mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @@ -28,7 +28,7 @@ class TextureViewPreview extends PreviewImpl { public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { setSurfaceSize(width, height); dispatchSurfaceChanged(); - refreshScale(); + // refreshScale(); } @Override diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java index 0bd22928..8699bef1 100644 --- a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java +++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java @@ -11,8 +11,10 @@ import android.content.ContextWrapper; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.TypedArray; +import android.graphics.Color; import android.graphics.Rect; import android.graphics.YuvImage; +import android.graphics.drawable.ColorDrawable; import android.os.Handler; import android.os.HandlerThread; import android.support.annotation.NonNull; @@ -24,6 +26,7 @@ import android.util.Log; import android.view.Display; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; @@ -174,9 +177,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - Log.e(TAG, "onAttachedToWindow"); if (!isInEditMode()) { - Log.e(TAG, "onAttachedToWindow: enabling orientation detector."); WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); mOrientationHelper.enable(display); @@ -185,9 +186,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { @Override protected void onDetachedFromWindow() { - Log.e(TAG, "onDetachedFromWindow"); if (!isInEditMode()) { - Log.e(TAG, "onDetachedFromWindow: disabling orientation detector."); mOrientationHelper.disable(); } super.onDetachedFromWindow(); @@ -215,20 +214,55 @@ public class CameraView extends FrameLayout implements LifecycleObserver { Size previewSize = getPreviewSize(); if (previewSize == null) { + // Early measure. super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } - if (getLayoutParams().width == LayoutParams.WRAP_CONTENT) { - int height = MeasureSpec.getSize(heightMeasureSpec); - float ratio = (float) height / (float) previewSize.getWidth(); - int width = (int) (previewSize.getHeight() * ratio); + boolean wwc = getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT; + boolean hwc = getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT; + boolean flip = mCameraImpl.shouldFlipSizes(); + float previewWidth = flip ? previewSize.getHeight() : previewSize.getWidth(); + float previewHeight = flip ? previewSize.getWidth() : previewSize.getHeight(); + + if (wwc && hwc) { + // If both dimensions are WRAP_CONTENT, let's try to fit the preview size perfectly + // without cropping. + // TODO: This should actually be a flag, like scaleMode, that replaces cropOutput and adjustViewBounds. + // This is like a fitCenter. + + // preview: 1600x1200 + // parent: 1080x1794 + float parentHeight = MeasureSpec.getSize(heightMeasureSpec); // 1794. + float parentWidth = MeasureSpec.getSize(widthMeasureSpec); // 1080. mode = AT_MOST + float targetRatio = previewHeight / previewWidth; + float currentRatio = parentHeight / parentWidth; + Log.e(TAG, "parentHeight="+parentHeight+", parentWidth="+parentWidth); + Log.e(TAG, "previewHeight="+previewHeight+", previewWidth="+previewWidth); + if (currentRatio > targetRatio) { + // View is too tall. Must reduce height. + int newHeight = (int) (parentWidth * targetRatio); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY)); + } else { + // View is too wide. Must reduce width. + int newWidth = (int) (parentHeight / targetRatio); + super.onMeasure(MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY), heightMeasureSpec); + } + + } else if (wwc) { + // Legacy behavior, with just a WC dimension. This is dangerous because the final size + // might be bigger than the available size, resulting in part of the surface getting cropped. + // Think for example of a 4:3 preview in a 16:9 screen, with width=MP and height=WC. + // This is like a cropCenter. + float height = MeasureSpec.getSize(heightMeasureSpec); + float ratio = height / previewWidth; + int width = (int) (previewHeight * ratio); super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightMeasureSpec); - } else if (getLayoutParams().height == LayoutParams.WRAP_CONTENT) { - int width = MeasureSpec.getSize(widthMeasureSpec); - float ratio = (float) width / (float) previewSize.getHeight(); - int height = (int) (previewSize.getWidth() * ratio); + } else if (hwc) { + float width = MeasureSpec.getSize(widthMeasureSpec); + float ratio = width / previewHeight; + int height = (int) (previewWidth * ratio); super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); } else { @@ -446,12 +480,18 @@ public class CameraView extends FrameLayout implements LifecycleObserver { /** * Returns the size used for the preview, * or null if it hasn't been computed (for example if the surface is not ready). + * @return a Size */ @Nullable public Size getPreviewSize() { return mCameraImpl != null ? mCameraImpl.getPreviewSize() : null; } + /** + * Returns the size used for the capture, + * or null if it hasn't been computed yet (for example if the surface is not ready). + * @return a Size + */ @Nullable public Size getCaptureSize() { return mCameraImpl != null ? mCameraImpl.getCaptureSize() : null;