Add cropping functionality to GLCameraPreview

v2
Mattia Iavarone 6 years ago
parent 6e6714dec4
commit 7df47cdf45
  1. 3
      MIGRATION.md
  2. 20
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java
  3. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
  4. 116
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera2.java
  5. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
  6. 47
      cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java
  7. 113
      cameraview/src/main/views/com/otaliastudios/cameraview/GLCameraPreview.java
  8. 13
      cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java
  9. 1
      demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java
  10. 4
      demo/src/main/res/layout/activity_camera.xml

@ -32,3 +32,6 @@
- VideoSizeSelector: added. It is needed to choose the capture size in VIDEO mode. - 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. Defaults to SizeSelectors.biggest(), but you can choose by aspect ratio or whatever.
- isTakingPicture(): added on top of isTakingVideo(). - isTakingPicture(): added on top of isTakingVideo().
TODO: improve gles stuff
TODO: takeVideoSnapshot

@ -86,25 +86,25 @@ public abstract class CameraPreviewTest extends BaseTest {
@Test @Test
public void testDesiredSize() { public void testDesiredSize() {
preview.setDesiredSize(160, 90); preview.setInputStreamSize(160, 90);
assertEquals(160, preview.getDesiredSize().getWidth()); assertEquals(160, preview.getInputStreamSize().getWidth());
assertEquals(90, preview.getDesiredSize().getHeight()); assertEquals(90, preview.getInputStreamSize().getHeight());
} }
@Test @Test
public void testSurfaceAvailable() { public void testSurfaceAvailable() {
ensureAvailable(); ensureAvailable();
verify(callback, times(1)).onSurfaceAvailable(); verify(callback, times(1)).onSurfaceAvailable();
assertEquals(surfaceSize.getWidth(), preview.getSurfaceSize().getWidth()); assertEquals(surfaceSize.getWidth(), preview.getOutputSurfaceSize().getWidth());
assertEquals(surfaceSize.getHeight(), preview.getSurfaceSize().getHeight()); assertEquals(surfaceSize.getHeight(), preview.getOutputSurfaceSize().getHeight());
} }
@Test @Test
public void testSurfaceDestroyed() { public void testSurfaceDestroyed() {
ensureAvailable(); ensureAvailable();
ensureDestroyed(); ensureDestroyed();
assertEquals(0, preview.getSurfaceSize().getWidth()); assertEquals(0, preview.getOutputSurfaceSize().getWidth());
assertEquals(0, preview.getSurfaceSize().getHeight()); assertEquals(0, preview.getOutputSurfaceSize().getHeight());
} }
@Test @Test
@ -136,19 +136,19 @@ public abstract class CameraPreviewTest extends BaseTest {
private void setDesiredAspectRatio(float desiredAspectRatio) { private void setDesiredAspectRatio(float desiredAspectRatio) {
preview.mCropTask.listen(); preview.mCropTask.listen();
preview.setDesiredSize((int) (10f * desiredAspectRatio), 10); // Wait... preview.setInputStreamSize((int) (10f * desiredAspectRatio), 10); // Wait...
preview.mCropTask.await(); preview.mCropTask.await();
assertEquals(desiredAspectRatio, getViewAspectRatioWithScale(), 0.01f); assertEquals(desiredAspectRatio, getViewAspectRatioWithScale(), 0.01f);
} }
private float getViewAspectRatio() { private float getViewAspectRatio() {
Size size = preview.getSurfaceSize(); Size size = preview.getOutputSurfaceSize();
return AspectRatio.of(size.getWidth(), size.getHeight()).toFloat(); return AspectRatio.of(size.getWidth(), size.getHeight()).toFloat();
} }
private float getViewAspectRatioWithScale() { private float getViewAspectRatioWithScale() {
Size size = preview.getSurfaceSize(); Size size = preview.getOutputSurfaceSize();
int newWidth = (int) (((float) size.getWidth()) * preview.getView().getScaleX()); int newWidth = (int) (((float) size.getWidth()) * preview.getView().getScaleX());
int newHeight = (int) (((float) size.getHeight()) * preview.getView().getScaleY()); int newHeight = (int) (((float) size.getHeight()) * preview.getView().getScaleY());
return AspectRatio.of(newWidth, newHeight).toFloat(); return AspectRatio.of(newWidth, newHeight).toFloat();

@ -8,8 +8,6 @@ import android.graphics.SurfaceTexture;
import android.graphics.YuvImage; import android.graphics.YuvImage;
import android.hardware.Camera; import android.hardware.Camera;
import android.location.Location; import android.location.Location;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Build; import android.os.Build;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; 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. // Preview surface is now available. If camera is open, set up.
@Override @Override
public void onSurfaceAvailable() { public void onSurfaceAvailable() {
LOG.i("onSurfaceAvailable:", "Size is", mPreview.getSurfaceSize()); LOG.i("onSurfaceAvailable:", "Size is", mPreview.getOutputSurfaceSize());
schedule(null, false, new Runnable() { schedule(null, false, new Runnable() {
@Override @Override
public void run() { public void run() {
@ -87,7 +85,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
// This requires stopping and restarting the preview. // This requires stopping and restarting the preview.
@Override @Override
public void onSurfaceChanged() { public void onSurfaceChanged() {
LOG.i("onSurfaceChanged, size is", mPreview.getSurfaceSize()); LOG.i("onSurfaceChanged, size is", mPreview.getOutputSurfaceSize());
schedule(null, true, new Runnable() { schedule(null, true, new Runnable() {
@Override @Override
public void run() { public void run() {
@ -139,7 +137,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera
mCameraCallbacks.onCameraPreviewSizeChanged(); mCameraCallbacks.onCameraPreviewSizeChanged();
Size previewSize = getPreviewSize(REF_VIEW); Size previewSize = getPreviewSize(REF_VIEW);
mPreview.setDesiredSize(previewSize.getWidth(), previewSize.getHeight()); mPreview.setInputStreamSize(previewSize.getWidth(), previewSize.getHeight());
Camera.Parameters params = mCamera.getParameters(); Camera.Parameters params = mCamera.getParameters();
mPreviewFormat = params.getPreviewFormat(); mPreviewFormat = params.getPreviewFormat();

@ -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) {
}
}

@ -4,8 +4,6 @@ import android.graphics.PointF;
import android.location.Location; import android.location.Location;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.support.annotation.NonNull; 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 // 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. // surface size from REF_VIEW to REF_SENSOR, and reflip at the end.
AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight()); AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight());
Size targetMinSize = mPreview.getSurfaceSize(); Size targetMinSize = mPreview.getOutputSurfaceSize();
boolean flip = flip(REF_VIEW, REF_SENSOR); boolean flip = flip(REF_VIEW, REF_SENSOR);
if (flip) targetMinSize = targetMinSize.flip(); if (flip) targetMinSize = targetMinSize.flip();
LOG.i("size:", "computePreviewSize:", "targetRatio:", targetRatio, "targetMinSize:", targetMinSize); LOG.i("size:", "computePreviewSize:", "targetRatio:", targetRatio, "targetMinSize:", targetMinSize);

@ -2,7 +2,6 @@ package com.otaliastudios.cameraview;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.view.Surface;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -25,12 +24,12 @@ abstract class CameraPreview<T extends View, Output> {
protected boolean mCropping; protected boolean mCropping;
// These are the surface dimensions in REF_VIEW. // These are the surface dimensions in REF_VIEW.
protected int mSurfaceWidth; protected int mOutputSurfaceWidth;
protected int mSurfaceHeight; protected int mOutputSurfaceHeight;
// These are the preview stream dimensions, in REF_VIEW. // These are the preview stream dimensions, in REF_VIEW.
protected int mDesiredWidth; protected int mInputStreamWidth;
protected int mDesiredHeight; protected int mInputStreamHeight;
CameraPreview(Context context, ViewGroup parent, SurfaceCallback callback) { CameraPreview(Context context, ViewGroup parent, SurfaceCallback callback) {
mView = onCreateView(context, parent); mView = onCreateView(context, parent);
@ -52,25 +51,25 @@ abstract class CameraPreview<T extends View, Output> {
// As far as I can see, these are the actual preview dimensions, as set in CameraParameters. // 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.
// These must be alredy rotated, if needed, to be consistent with surface/view sizes. // These must be alredy rotated, if needed, to be consistent with surface/view sizes.
void setDesiredSize(int width, int height) { void setInputStreamSize(int width, int height) {
LOG.i("setDesiredSize:", "desiredW=", width, "desiredH=", height); LOG.i("setInputStreamSize:", "desiredW=", width, "desiredH=", height);
mDesiredWidth = width; mInputStreamWidth = width;
mDesiredHeight = height; mInputStreamHeight = height;
crop(); crop();
} }
final Size getDesiredSize() { final Size getInputStreamSize() {
return new Size(mDesiredWidth, mDesiredHeight); return new Size(mInputStreamWidth, mInputStreamHeight);
} }
final Size getSurfaceSize() { final Size getOutputSurfaceSize() {
return new Size(mSurfaceWidth, mSurfaceHeight); return new Size(mOutputSurfaceWidth, mOutputSurfaceHeight);
} }
final void setSurfaceCallback(SurfaceCallback callback) { final void setSurfaceCallback(SurfaceCallback callback) {
mSurfaceCallback = callback; mSurfaceCallback = callback;
// If surface already available, dispatch. // If surface already available, dispatch.
if (mSurfaceWidth != 0 || mSurfaceHeight != 0) { if (mOutputSurfaceWidth != 0 || mOutputSurfaceHeight != 0) {
mSurfaceCallback.onSurfaceAvailable(); mSurfaceCallback.onSurfaceAvailable();
} }
} }
@ -78,8 +77,8 @@ abstract class CameraPreview<T extends View, Output> {
protected final void onSurfaceAvailable(int width, int height) { protected final void onSurfaceAvailable(int width, int height) {
LOG.i("onSurfaceAvailable:", "w=", width, "h=", height); LOG.i("onSurfaceAvailable:", "w=", width, "h=", height);
mSurfaceWidth = width; mOutputSurfaceWidth = width;
mSurfaceHeight = height; mOutputSurfaceHeight = height;
crop(); crop();
mSurfaceCallback.onSurfaceAvailable(); mSurfaceCallback.onSurfaceAvailable();
} }
@ -89,17 +88,17 @@ abstract class CameraPreview<T extends View, Output> {
// This is called by subclasses. // This is called by subclasses.
protected final void onSurfaceSizeChanged(int width, int height) { protected final void onSurfaceSizeChanged(int width, int height) {
LOG.i("onSurfaceSizeChanged:", "w=", width, "h=", height); LOG.i("onSurfaceSizeChanged:", "w=", width, "h=", height);
if (width != mSurfaceWidth || height != mSurfaceHeight) { if (width != mOutputSurfaceWidth || height != mOutputSurfaceHeight) {
mSurfaceWidth = width; mOutputSurfaceWidth = width;
mSurfaceHeight = height; mOutputSurfaceHeight = height;
crop(); crop();
mSurfaceCallback.onSurfaceChanged(); mSurfaceCallback.onSurfaceChanged();
} }
} }
protected final void onSurfaceDestroyed() { protected final void onSurfaceDestroyed() {
mSurfaceWidth = 0; mOutputSurfaceWidth = 0;
mSurfaceHeight = 0; mOutputSurfaceHeight = 0;
} }
void onResume() {} void onResume() {}
@ -109,13 +108,13 @@ abstract class CameraPreview<T extends View, Output> {
void onDestroy() {} void onDestroy() {}
final boolean isReady() { 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 * 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) * dimensions. This way our internal aspect ratio (mOutputSurfaceWidth / mOutputSurfaceHeight)
* will match the preview size aspect ratio (mDesiredWidth / mDesiredHeight). * will match the preview size aspect ratio (mInputStreamWidth / mInputStreamHeight).
* *
* There might still be some absolute difference (e.g. same ratio but bigger / smaller). * There might still be some absolute difference (e.g. same ratio but bigger / smaller).
* However that should be already managed by the framework. * However that should be already managed by the framework.

@ -3,7 +3,9 @@ package com.otaliastudios.cameraview;
import android.content.Context; import android.content.Context;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.View; import android.view.View;
@ -12,13 +14,47 @@ import android.view.ViewGroup;
import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; 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<GLSurfaceView, SurfaceTexture> implements GLSurfaceView.Renderer { class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> implements GLSurfaceView.Renderer {
private boolean mDispatched; private boolean mDispatched;
private final float[] mTransformMatrix = new float[16]; private final float[] mTransformMatrix = new float[16];
private int mTextureId = -1; private int mOutputTextureId = -1;
private SurfaceTexture mSurfaceTexture; private SurfaceTexture mInputSurfaceTexture;
private GLViewport mViewport; private GLViewport mOutputViewport;
GLCameraPreview(Context context, ViewGroup parent, SurfaceCallback callback) { GLCameraPreview(Context context, ViewGroup parent, SurfaceCallback callback) {
super(context, parent, callback); super(context, parent, callback);
@ -33,9 +69,12 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
glView.setEGLContextClientVersion(2); glView.setEGLContextClientVersion(2);
glView.setRenderer(this); glView.setRenderer(this);
glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
// glView.getHolder().setFixedSize(600, 300);
glView.getHolder().addCallback(new SurfaceHolder.Callback() { glView.getHolder().addCallback(new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {} 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 @Override
public void surfaceDestroyed(SurfaceHolder holder) { public void surfaceDestroyed(SurfaceHolder holder) {
@ -60,22 +99,26 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
@Override @Override
void onDestroy() { void onDestroy() {
super.onDestroy(); super.onDestroy();
if (mSurfaceTexture != null) { if (mInputSurfaceTexture != null) {
mSurfaceTexture.release(); mInputSurfaceTexture.release();
mSurfaceTexture = null; mInputSurfaceTexture = null;
} }
if (mViewport != null) { if (mOutputViewport != null) {
mViewport.release(); mOutputViewport.release();
mViewport = null; mOutputViewport = null;
} }
} }
// Renderer thread
@Override @Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) { public void onSurfaceCreated(GL10 gl, EGLConfig config) {
mViewport = new GLViewport(); mOutputViewport = new GLViewport();
mTextureId = mViewport.createTexture(); mOutputTextureId = mOutputViewport.createTexture();
mSurfaceTexture = new SurfaceTexture(mTextureId); mInputSurfaceTexture = new SurfaceTexture(mOutputTextureId);
mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
// 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 @Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) { public void onFrameAvailable(SurfaceTexture surfaceTexture) {
// requestRender is thread-safe. // requestRender is thread-safe.
@ -84,6 +127,7 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
}); });
} }
// Renderer thread
@Override @Override
public void onSurfaceChanged(GL10 gl, int width, int height) { public void onSurfaceChanged(GL10 gl, int width, int height) {
if (!mDispatched) { if (!mDispatched) {
@ -99,15 +143,16 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
public void onDrawFrame(GL10 gl) { public void onDrawFrame(GL10 gl) {
// Latch the latest frame. If there isn't anything new, // Latch the latest frame. If there isn't anything new,
// we'll just re-use whatever was there before. // we'll just re-use whatever was there before.
mSurfaceTexture.updateTexImage(); mInputSurfaceTexture.updateTexImage();
if (mDesiredWidth <= 0 || mDesiredHeight <= 0) { if (mInputStreamWidth <= 0 || mInputStreamHeight <= 0) {
// Skip drawing. Camera was not opened. // Skip drawing. Camera was not opened.
return; return;
} }
// Draw the video frame. // Draw the video frame.
mSurfaceTexture.getTransformMatrix(mTransformMatrix); mInputSurfaceTexture.getTransformMatrix(mTransformMatrix);
mViewport.drawFrame(mTextureId, mTransformMatrix); Matrix.scaleM(mTransformMatrix, 0, mScaleX, mScaleY, 1);
mOutputViewport.drawFrame(mOutputTextureId, mTransformMatrix);
} }
@Override @Override
@ -117,7 +162,7 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
@Override @Override
SurfaceTexture getOutput() { SurfaceTexture getOutput() {
return mSurfaceTexture; return mInputSurfaceTexture;
} }
@ -126,9 +171,39 @@ class GLCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
return true; 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 @Override
protected void crop() { protected void crop() {
mCropTask.start(); 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); mCropTask.end(null);
} }
} }

@ -5,7 +5,6 @@ import android.content.Context;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView; import android.view.TextureView;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -59,8 +58,8 @@ class TextureCameraPreview extends CameraPreview<TextureView, SurfaceTexture> {
@TargetApi(15) @TargetApi(15)
@Override @Override
void setDesiredSize(int width, int height) { void setInputStreamSize(int width, int height) {
super.setDesiredSize(width, height); super.setInputStreamSize(width, height);
if (getView().getSurfaceTexture() != null) { if (getView().getSurfaceTexture() != null) {
getView().getSurfaceTexture().setDefaultBufferSize(width, height); getView().getSurfaceTexture().setDefaultBufferSize(width, height);
} }
@ -77,14 +76,14 @@ class TextureCameraPreview extends CameraPreview<TextureView, SurfaceTexture> {
getView().post(new Runnable() { getView().post(new Runnable() {
@Override @Override
public void run() { public void run() {
if (mDesiredHeight == 0 || mDesiredWidth == 0 || if (mInputStreamHeight == 0 || mInputStreamWidth == 0 ||
mSurfaceHeight == 0 || mSurfaceWidth == 0) { mOutputSurfaceHeight == 0 || mOutputSurfaceWidth == 0) {
mCropTask.end(null); mCropTask.end(null);
return; return;
} }
float scaleX = 1f, scaleY = 1f; float scaleX = 1f, scaleY = 1f;
AspectRatio current = AspectRatio.of(mSurfaceWidth, mSurfaceHeight); AspectRatio current = AspectRatio.of(mOutputSurfaceWidth, mOutputSurfaceHeight);
AspectRatio target = AspectRatio.of(mDesiredWidth, mDesiredHeight); AspectRatio target = AspectRatio.of(mInputStreamWidth, mInputStreamHeight);
if (current.toFloat() >= target.toFloat()) { if (current.toFloat() >= target.toFloat()) {
// We are too short. Must increase height. // We are too short. Must increase height.
scaleY = current.toFloat() / target.toFloat(); scaleY = current.toFloat() / target.toFloat();

@ -63,6 +63,7 @@ public enum Control {
int boundary = this == WIDTH ? root.getWidth() : root.getHeight(); int boundary = this == WIDTH ? root.getWidth() : root.getHeight();
if (boundary == 0) boundary = 1000; if (boundary == 0) boundary = 1000;
int step = boundary / 10; int step = boundary / 10;
list.add(this == WIDTH ? 300 : 600);
list.add(ViewGroup.LayoutParams.WRAP_CONTENT); list.add(ViewGroup.LayoutParams.WRAP_CONTENT);
list.add(ViewGroup.LayoutParams.MATCH_PARENT); list.add(ViewGroup.LayoutParams.MATCH_PARENT);
for (int i = step; i < boundary; i += step) { for (int i = step; i < boundary; i += step) {

@ -11,8 +11,8 @@
<com.otaliastudios.cameraview.CameraView <com.otaliastudios.cameraview.CameraView
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/camera" android:id="@+id/camera"
android:layout_width="wrap_content" android:layout_width="300px"
android:layout_height="wrap_content" android:layout_height="600px"
android:layout_gravity="center" android:layout_gravity="center"
android:keepScreenOn="true" android:keepScreenOn="true"
app:cameraExperimental="true" app:cameraExperimental="true"

Loading…
Cancel
Save