From 7df47cdf450beb2f6ccf269c45f8709112b34930 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Fri, 14 Sep 2018 23:14:38 -0300 Subject: [PATCH] Add cropping functionality to GLCameraPreview --- MIGRATION.md | 5 +- .../cameraview/CameraPreviewTest.java | 20 +-- .../com/otaliastudios/cameraview/Camera1.java | 8 +- .../com/otaliastudios/cameraview/Camera2.java | 116 ------------------ .../cameraview/CameraController.java | 4 +- .../cameraview/CameraPreview.java | 47 ++++--- .../cameraview/GLCameraPreview.java | 113 ++++++++++++++--- .../cameraview/TextureCameraPreview.java | 13 +- .../cameraview/demo/Control.java | 1 + demo/src/main/res/layout/activity_camera.xml | 4 +- 10 files changed, 144 insertions(+), 187 deletions(-) delete mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java diff --git a/MIGRATION.md b/MIGRATION.md index 6d8aa00a..7d602f08 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -31,4 +31,7 @@ mode == Mode.PICTURE. - VideoSizeSelector: added. It is needed to choose the capture size in VIDEO mode. Defaults to SizeSelectors.biggest(), but you can choose by aspect ratio or whatever. -- isTakingPicture(): added on top of isTakingVideo(). \ No newline at end of file +- isTakingPicture(): added on top of isTakingVideo(). + +TODO: improve gles stuff +TODO: takeVideoSnapshot \ No newline at end of file diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java index 3d35c998..33136d80 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java @@ -86,25 +86,25 @@ public abstract class CameraPreviewTest extends BaseTest { @Test public void testDesiredSize() { - preview.setDesiredSize(160, 90); - assertEquals(160, preview.getDesiredSize().getWidth()); - assertEquals(90, preview.getDesiredSize().getHeight()); + preview.setInputStreamSize(160, 90); + assertEquals(160, preview.getInputStreamSize().getWidth()); + assertEquals(90, preview.getInputStreamSize().getHeight()); } @Test public void testSurfaceAvailable() { ensureAvailable(); verify(callback, times(1)).onSurfaceAvailable(); - assertEquals(surfaceSize.getWidth(), preview.getSurfaceSize().getWidth()); - assertEquals(surfaceSize.getHeight(), preview.getSurfaceSize().getHeight()); + assertEquals(surfaceSize.getWidth(), preview.getOutputSurfaceSize().getWidth()); + assertEquals(surfaceSize.getHeight(), preview.getOutputSurfaceSize().getHeight()); } @Test public void testSurfaceDestroyed() { ensureAvailable(); ensureDestroyed(); - assertEquals(0, preview.getSurfaceSize().getWidth()); - assertEquals(0, preview.getSurfaceSize().getHeight()); + assertEquals(0, preview.getOutputSurfaceSize().getWidth()); + assertEquals(0, preview.getOutputSurfaceSize().getHeight()); } @Test @@ -136,19 +136,19 @@ public abstract class CameraPreviewTest extends BaseTest { private void setDesiredAspectRatio(float desiredAspectRatio) { preview.mCropTask.listen(); - preview.setDesiredSize((int) (10f * desiredAspectRatio), 10); // Wait... + preview.setInputStreamSize((int) (10f * desiredAspectRatio), 10); // Wait... preview.mCropTask.await(); assertEquals(desiredAspectRatio, getViewAspectRatioWithScale(), 0.01f); } private float getViewAspectRatio() { - Size size = preview.getSurfaceSize(); + Size size = preview.getOutputSurfaceSize(); return AspectRatio.of(size.getWidth(), size.getHeight()).toFloat(); } private float getViewAspectRatioWithScale() { - Size size = preview.getSurfaceSize(); + Size size = preview.getOutputSurfaceSize(); int newWidth = (int) (((float) size.getWidth()) * preview.getView().getScaleX()); int newHeight = (int) (((float) size.getHeight()) * preview.getView().getScaleY()); return AspectRatio.of(newWidth, newHeight).toFloat(); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java index 1d7251c6..72182f61 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java @@ -8,8 +8,6 @@ import android.graphics.SurfaceTexture; import android.graphics.YuvImage; import android.hardware.Camera; import android.location.Location; -import android.media.CamcorderProfile; -import android.media.MediaRecorder; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -73,7 +71,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera // Preview surface is now available. If camera is open, set up. @Override public void onSurfaceAvailable() { - LOG.i("onSurfaceAvailable:", "Size is", mPreview.getSurfaceSize()); + LOG.i("onSurfaceAvailable:", "Size is", mPreview.getOutputSurfaceSize()); schedule(null, false, new Runnable() { @Override public void run() { @@ -87,7 +85,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera // This requires stopping and restarting the preview. @Override public void onSurfaceChanged() { - LOG.i("onSurfaceChanged, size is", mPreview.getSurfaceSize()); + LOG.i("onSurfaceChanged, size is", mPreview.getOutputSurfaceSize()); schedule(null, true, new Runnable() { @Override public void run() { @@ -139,7 +137,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera mCameraCallbacks.onCameraPreviewSizeChanged(); Size previewSize = getPreviewSize(REF_VIEW); - mPreview.setDesiredSize(previewSize.getWidth(), previewSize.getHeight()); + mPreview.setInputStreamSize(previewSize.getWidth(), previewSize.getHeight()); Camera.Parameters params = mCamera.getParameters(); mPreviewFormat = params.getPreviewFormat(); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java deleted file mode 100644 index e88650db..00000000 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.otaliastudios.cameraview; - -import android.annotation.TargetApi; -import android.graphics.PointF; -import android.location.Location; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import java.io.File; - -@TargetApi(21) -class Camera2 extends CameraController { - - public Camera2(CameraView.CameraCallbacks callback) { - super(callback); - } - - @Override - public void onSurfaceAvailable() { - - } - - @Override - public void onSurfaceChanged() { - - } - - @Override - void onStart() { - - } - - @Override - void onStop() { - - } - - @Override - void setMode(Mode mode) { - - } - - @Override - void setFacing(Facing facing) { - - } - - @Override - void setZoom(float zoom, PointF[] points, boolean notify) { - - } - - @Override - void setExposureCorrection(float EVvalue, float[] bounds, PointF[] points, boolean notify) { - - } - - @Override - void setFlash(Flash flash) { - - } - - @Override - void setWhiteBalance(WhiteBalance whiteBalance) { - - } - - @Override - void setHdr(Hdr hdr) { - - } - - @Override - void setAudio(Audio audio) { - - } - - @Override - void setLocation(Location location) { - - } - - @Override - void takePicture() { - - } - - @Override - void takePictureSnapshot(AspectRatio viewAspectRatio) { - } - - @Override - void takeVideo(@NonNull File file) { - - } - - @Override - void stopVideo() { - - } - - @Override - void startAutoFocus(@Nullable Gesture gesture, PointF point) { - - } - - @Override - public void onBufferAvailable(byte[] buffer) { - - } - - @Override - void setPlaySounds(boolean playSounds) { - - } -} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java index bfa1f4e4..2b59e1a0 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java @@ -4,8 +4,6 @@ import android.graphics.PointF; import android.location.Location; -import android.media.CamcorderProfile; -import android.media.MediaRecorder; import android.os.Handler; import android.os.Looper; import android.support.annotation.NonNull; @@ -506,7 +504,7 @@ abstract class CameraController implements // instead of flipping everything to REF_VIEW, we can just flip the // surface size from REF_VIEW to REF_SENSOR, and reflip at the end. AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight()); - Size targetMinSize = mPreview.getSurfaceSize(); + Size targetMinSize = mPreview.getOutputSurfaceSize(); boolean flip = flip(REF_VIEW, REF_SENSOR); if (flip) targetMinSize = targetMinSize.flip(); LOG.i("size:", "computePreviewSize:", "targetRatio:", targetRatio, "targetMinSize:", targetMinSize); diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java index 2fcaf630..7827c03e 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java @@ -2,7 +2,6 @@ package com.otaliastudios.cameraview; import android.content.Context; import android.support.annotation.NonNull; -import android.view.Surface; import android.view.View; import android.view.ViewGroup; @@ -25,12 +24,12 @@ abstract class CameraPreview { protected boolean mCropping; // These are the surface dimensions in REF_VIEW. - protected int mSurfaceWidth; - protected int mSurfaceHeight; + protected int mOutputSurfaceWidth; + protected int mOutputSurfaceHeight; // These are the preview stream dimensions, in REF_VIEW. - protected int mDesiredWidth; - protected int mDesiredHeight; + protected int mInputStreamWidth; + protected int mInputStreamHeight; CameraPreview(Context context, ViewGroup parent, SurfaceCallback callback) { mView = onCreateView(context, parent); @@ -52,25 +51,25 @@ abstract class CameraPreview { // As far as I can see, these are the actual preview dimensions, as set in CameraParameters. // This is called by the CameraImpl. // These must be alredy rotated, if needed, to be consistent with surface/view sizes. - void setDesiredSize(int width, int height) { - LOG.i("setDesiredSize:", "desiredW=", width, "desiredH=", height); - mDesiredWidth = width; - mDesiredHeight = height; + void setInputStreamSize(int width, int height) { + LOG.i("setInputStreamSize:", "desiredW=", width, "desiredH=", height); + mInputStreamWidth = width; + mInputStreamHeight = height; crop(); } - final Size getDesiredSize() { - return new Size(mDesiredWidth, mDesiredHeight); + final Size getInputStreamSize() { + return new Size(mInputStreamWidth, mInputStreamHeight); } - final Size getSurfaceSize() { - return new Size(mSurfaceWidth, mSurfaceHeight); + final Size getOutputSurfaceSize() { + return new Size(mOutputSurfaceWidth, mOutputSurfaceHeight); } final void setSurfaceCallback(SurfaceCallback callback) { mSurfaceCallback = callback; // If surface already available, dispatch. - if (mSurfaceWidth != 0 || mSurfaceHeight != 0) { + if (mOutputSurfaceWidth != 0 || mOutputSurfaceHeight != 0) { mSurfaceCallback.onSurfaceAvailable(); } } @@ -78,8 +77,8 @@ abstract class CameraPreview { protected final void onSurfaceAvailable(int width, int height) { LOG.i("onSurfaceAvailable:", "w=", width, "h=", height); - mSurfaceWidth = width; - mSurfaceHeight = height; + mOutputSurfaceWidth = width; + mOutputSurfaceHeight = height; crop(); mSurfaceCallback.onSurfaceAvailable(); } @@ -89,17 +88,17 @@ abstract class CameraPreview { // This is called by subclasses. protected final void onSurfaceSizeChanged(int width, int height) { LOG.i("onSurfaceSizeChanged:", "w=", width, "h=", height); - if (width != mSurfaceWidth || height != mSurfaceHeight) { - mSurfaceWidth = width; - mSurfaceHeight = height; + if (width != mOutputSurfaceWidth || height != mOutputSurfaceHeight) { + mOutputSurfaceWidth = width; + mOutputSurfaceHeight = height; crop(); mSurfaceCallback.onSurfaceChanged(); } } protected final void onSurfaceDestroyed() { - mSurfaceWidth = 0; - mSurfaceHeight = 0; + mOutputSurfaceWidth = 0; + mOutputSurfaceHeight = 0; } void onResume() {} @@ -109,13 +108,13 @@ abstract class CameraPreview { void onDestroy() {} final boolean isReady() { - return mSurfaceWidth > 0 && mSurfaceHeight > 0; + return mOutputSurfaceWidth > 0 && mOutputSurfaceHeight > 0; } /** * Here we must crop the visible part by applying a > 1 scale to one of our - * dimensions. This way our internal aspect ratio (mSurfaceWidth / mSurfaceHeight) - * will match the preview size aspect ratio (mDesiredWidth / mDesiredHeight). + * dimensions. This way our internal aspect ratio (mOutputSurfaceWidth / mOutputSurfaceHeight) + * will match the preview size aspect ratio (mInputStreamWidth / mInputStreamHeight). * * There might still be some absolute difference (e.g. same ratio but bigger / smaller). * However that should be already managed by the framework. diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/GLCameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/GLCameraPreview.java index 30782dab..460bdd4d 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/GLCameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/GLCameraPreview.java @@ -3,7 +3,9 @@ package com.otaliastudios.cameraview; import android.content.Context; import android.graphics.SurfaceTexture; import android.opengl.GLSurfaceView; +import android.opengl.Matrix; import android.support.annotation.NonNull; +import android.util.Log; import android.view.LayoutInflater; import android.view.SurfaceHolder; import android.view.View; @@ -12,13 +14,47 @@ import android.view.ViewGroup; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; +/** + * - The android camera will stream image to the given {@link SurfaceTexture}. + * + * - in the SurfaceTexture constructor we pass the GL texture handle that we have created. + * + * - The SurfaceTexture is linked to the Camera1 object. It will pass down buffers of data with + * a specified size (that is, the Camera1 preview size). + * + * - When SurfaceTexture.updateTexImage() is called, it will take the latest image from the camera stream + * and update it into the GL texture that was passed. + * + * - Now we have a GL texture referencing data. It must be drawn. + * [Note: it must be drawn using a transformation matrix taken from SurfaceTexture] + * + * - The easy way to render an OpenGL texture is using the {@link GLSurfaceView} class. + * It manages the gl context, hosts a surface and runs a separated rendering thread that will perform + * the rendering. + * + * - As per docs, we ask the GLSurfaceView to delegate rendering to us, using + * {@link GLSurfaceView#setRenderer(GLSurfaceView.Renderer)}. We request a render on the SurfaceView + * anytime the SurfaceTexture notifies that it has new data available (see OnFrameAvailableListener below). + * + * - Everything is linked: + * - The SurfaceTexture has buffers of data of mInputStreamSize + * - The SurfaceView hosts a view (and surface) of size mOutputSurfaceSize + * - We have a GL rich texture to be drawn (in the given method & thread). + * + * TODO + * CROPPING: Managed to do this using Matrix transformation. + * UPDATING: Still bugged: if you change the surface size on the go, the stream is not updated. + * I guess we should create a new texture... + * TAKING PICTURES: Sometime the snapshot takes ages... + * TAKING VIDEOS: Still have not tried... + */ class GLCameraPreview extends CameraPreview implements GLSurfaceView.Renderer { private boolean mDispatched; private final float[] mTransformMatrix = new float[16]; - private int mTextureId = -1; - private SurfaceTexture mSurfaceTexture; - private GLViewport mViewport; + private int mOutputTextureId = -1; + private SurfaceTexture mInputSurfaceTexture; + private GLViewport mOutputViewport; GLCameraPreview(Context context, ViewGroup parent, SurfaceCallback callback) { super(context, parent, callback); @@ -33,9 +69,12 @@ class GLCameraPreview extends CameraPreview imple glView.setEGLContextClientVersion(2); glView.setRenderer(this); glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + // glView.getHolder().setFixedSize(600, 300); glView.getHolder().addCallback(new SurfaceHolder.Callback() { public void surfaceCreated(SurfaceHolder holder) {} - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.e("GlCameraPreview", "width: " + width + ", height: " + height); + } @Override public void surfaceDestroyed(SurfaceHolder holder) { @@ -60,22 +99,26 @@ class GLCameraPreview extends CameraPreview imple @Override void onDestroy() { super.onDestroy(); - if (mSurfaceTexture != null) { - mSurfaceTexture.release(); - mSurfaceTexture = null; + if (mInputSurfaceTexture != null) { + mInputSurfaceTexture.release(); + mInputSurfaceTexture = null; } - if (mViewport != null) { - mViewport.release(); - mViewport = null; + if (mOutputViewport != null) { + mOutputViewport.release(); + mOutputViewport = null; } } + // Renderer thread @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { - mViewport = new GLViewport(); - mTextureId = mViewport.createTexture(); - mSurfaceTexture = new SurfaceTexture(mTextureId); - mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { + mOutputViewport = new GLViewport(); + mOutputTextureId = mOutputViewport.createTexture(); + mInputSurfaceTexture = new SurfaceTexture(mOutputTextureId); + + // Since we are using GLSurfaceView.RENDERMODE_WHEN_DIRTY, we must notify the SurfaceView + // of dirtyness, so that it draws again. This is how it's done. + mInputSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { // requestRender is thread-safe. @@ -84,6 +127,7 @@ class GLCameraPreview extends CameraPreview imple }); } + // Renderer thread @Override public void onSurfaceChanged(GL10 gl, int width, int height) { if (!mDispatched) { @@ -99,15 +143,16 @@ class GLCameraPreview extends CameraPreview imple public void onDrawFrame(GL10 gl) { // Latch the latest frame. If there isn't anything new, // we'll just re-use whatever was there before. - mSurfaceTexture.updateTexImage(); - if (mDesiredWidth <= 0 || mDesiredHeight <= 0) { + mInputSurfaceTexture.updateTexImage(); + if (mInputStreamWidth <= 0 || mInputStreamHeight <= 0) { // Skip drawing. Camera was not opened. return; } // Draw the video frame. - mSurfaceTexture.getTransformMatrix(mTransformMatrix); - mViewport.drawFrame(mTextureId, mTransformMatrix); + mInputSurfaceTexture.getTransformMatrix(mTransformMatrix); + Matrix.scaleM(mTransformMatrix, 0, mScaleX, mScaleY, 1); + mOutputViewport.drawFrame(mOutputTextureId, mTransformMatrix); } @Override @@ -117,7 +162,7 @@ class GLCameraPreview extends CameraPreview imple @Override SurfaceTexture getOutput() { - return mSurfaceTexture; + return mInputSurfaceTexture; } @@ -126,9 +171,39 @@ class GLCameraPreview extends CameraPreview imple return true; } + private float mScaleX = 1F; + private float mScaleY = 1F; + + /** + * To crop in GL, we could actually use view.setScaleX and setScaleY, but only from Android N onward. + * See documentation: https://developer.android.com/reference/android/view/SurfaceView + * + * Note: Starting in platform version Build.VERSION_CODES.N, SurfaceView's window position is updated + * synchronously with other View rendering. This means that translating and scaling a SurfaceView on + * screen will not cause rendering artifacts. Such artifacts may occur on previous versions of the + * platform when its window is positioned asynchronously. + * + * But to support older platforms, this seem to work - computing scale values and requesting a new frame, + * then drawing it with a scaled transformation matrix. See {@link #onDrawFrame(GL10)}. + */ @Override protected void crop() { mCropTask.start(); + if (mInputStreamWidth > 0 && mInputStreamHeight > 0 && mOutputSurfaceWidth > 0 && mOutputSurfaceHeight > 0) { + float scaleX = 1f, scaleY = 1f; + AspectRatio current = AspectRatio.of(mOutputSurfaceWidth, mOutputSurfaceHeight); + AspectRatio target = AspectRatio.of(mInputStreamWidth, mInputStreamHeight); + if (current.toFloat() >= target.toFloat()) { + // We are too short. Must increase height. + scaleY = current.toFloat() / target.toFloat(); + } else { + // We must increase width. + scaleX = target.toFloat() / current.toFloat(); + } + mScaleX = 1F / scaleX; + mScaleY = 1F / scaleY; + getView().requestRender(); + } mCropTask.end(null); } } diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java index 10be709f..20ac3906 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java @@ -5,7 +5,6 @@ import android.content.Context; import android.graphics.SurfaceTexture; import android.support.annotation.NonNull; import android.view.LayoutInflater; -import android.view.Surface; import android.view.TextureView; import android.view.View; import android.view.ViewGroup; @@ -59,8 +58,8 @@ class TextureCameraPreview extends CameraPreview { @TargetApi(15) @Override - void setDesiredSize(int width, int height) { - super.setDesiredSize(width, height); + void setInputStreamSize(int width, int height) { + super.setInputStreamSize(width, height); if (getView().getSurfaceTexture() != null) { getView().getSurfaceTexture().setDefaultBufferSize(width, height); } @@ -77,14 +76,14 @@ class TextureCameraPreview extends CameraPreview { getView().post(new Runnable() { @Override public void run() { - if (mDesiredHeight == 0 || mDesiredWidth == 0 || - mSurfaceHeight == 0 || mSurfaceWidth == 0) { + if (mInputStreamHeight == 0 || mInputStreamWidth == 0 || + mOutputSurfaceHeight == 0 || mOutputSurfaceWidth == 0) { mCropTask.end(null); return; } float scaleX = 1f, scaleY = 1f; - AspectRatio current = AspectRatio.of(mSurfaceWidth, mSurfaceHeight); - AspectRatio target = AspectRatio.of(mDesiredWidth, mDesiredHeight); + AspectRatio current = AspectRatio.of(mOutputSurfaceWidth, mOutputSurfaceHeight); + AspectRatio target = AspectRatio.of(mInputStreamWidth, mInputStreamHeight); if (current.toFloat() >= target.toFloat()) { // We are too short. Must increase height. scaleY = current.toFloat() / target.toFloat(); diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java index 24aad0ce..4127decc 100644 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java @@ -63,6 +63,7 @@ public enum Control { int boundary = this == WIDTH ? root.getWidth() : root.getHeight(); if (boundary == 0) boundary = 1000; int step = boundary / 10; + list.add(this == WIDTH ? 300 : 600); list.add(ViewGroup.LayoutParams.WRAP_CONTENT); list.add(ViewGroup.LayoutParams.MATCH_PARENT); for (int i = step; i < boundary; i += step) { diff --git a/demo/src/main/res/layout/activity_camera.xml b/demo/src/main/res/layout/activity_camera.xml index a159747d..83be7240 100644 --- a/demo/src/main/res/layout/activity_camera.xml +++ b/demo/src/main/res/layout/activity_camera.xml @@ -11,8 +11,8 @@