From 5656e6a3aeb6cebe4f21a7891ed7cfc36c13ffe2 Mon Sep 17 00:00:00 2001 From: Giacomo Randazzo Date: Sun, 16 Jun 2019 10:02:38 +0200 Subject: [PATCH] Use multiple OverlayLayout --- .../otaliastudios/cameraview/CameraView.java | 21 ++- .../cameraview/OverlayLayout.java | 63 +-------- .../cameraview/OverlayLayoutManager.java | 127 ++++++++++++++++++ .../cameraview/SnapshotPictureRecorder.java | 15 ++- .../cameraview/SnapshotVideoRecorder.java | 15 ++- .../cameraview/SurfaceDrawer.java | 6 +- .../cameraview/GlCameraPreview.java | 2 +- .../cameraview/SurfaceCameraPreview.java | 2 +- .../cameraview/TextureCameraPreview.java | 2 +- 9 files changed, 177 insertions(+), 76 deletions(-) create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayoutManager.java diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 96905f8f..6a898a29 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -71,7 +71,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver { PinchGestureLayout mPinchGestureLayout; TapGestureLayout mTapGestureLayout; ScrollGestureLayout mScrollGestureLayout; - OverlayLayout mOverlayLayout; + OverlayLayoutManager mOverlayLayoutManager; + OverlayLayoutManager mOverlayLayoutManagerBelow; private boolean mKeepScreenOn; private boolean mExperimental; @@ -197,12 +198,14 @@ public class CameraView extends FrameLayout implements LifecycleObserver { mPinchGestureLayout = new PinchGestureLayout(context); mTapGestureLayout = new TapGestureLayout(context); mScrollGestureLayout = new ScrollGestureLayout(context); - mOverlayLayout = new OverlayLayout(context); + mOverlayLayoutManager = new OverlayLayoutManager(context); + mOverlayLayoutManagerBelow = new OverlayLayoutManager(context); addView(mGridLinesLayout); addView(mPinchGestureLayout); addView(mTapGestureLayout); addView(mScrollGestureLayout); - addView(mOverlayLayout); + addView(mOverlayLayoutManager); + addView(mOverlayLayoutManagerBelow, 0); // Apply self managed setPlaySounds(playSounds); @@ -272,8 +275,10 @@ public class CameraView extends FrameLayout implements LifecycleObserver { // attached. That's why we instantiate the preview here. instantiatePreview(); - mCameraController.addPictureSurfaceDrawer(mOverlayLayout); - mCameraController.addVideoSurfaceDrawer(mOverlayLayout); + mCameraController.addPictureSurfaceDrawer(mOverlayLayoutManager); + mCameraController.addPictureSurfaceDrawer(mOverlayLayoutManagerBelow); + mCameraController.addVideoSurfaceDrawer(mOverlayLayoutManager); + mCameraController.addVideoSurfaceDrawer(mOverlayLayoutManagerBelow); } if (!isInEditMode()) { mOrientationHelper.enable(getContext()); @@ -291,7 +296,11 @@ public class CameraView extends FrameLayout implements LifecycleObserver { @Override public void addView(View child, ViewGroup.LayoutParams params) { if (params instanceof OverlayLayoutParams) { - mOverlayLayout.addView(child, params); + if (((OverlayLayoutParams) params).drawInPreview) { + mOverlayLayoutManager.addView(child, params); + } else { + mOverlayLayoutManagerBelow.addView(child, params); + } } else { super.addView(child, params); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayout.java b/cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayout.java index 47f0986f..56e38d99 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayout.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayout.java @@ -2,18 +2,13 @@ package com.otaliastudios.cameraview; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.PorterDuff; import android.util.AttributeSet; -import android.view.Surface; -import android.view.View; -import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -class OverlayLayout extends FrameLayout implements SurfaceDrawer { +class OverlayLayout extends FrameLayout { public OverlayLayout(@NonNull Context context) { super(context); @@ -25,60 +20,8 @@ class OverlayLayout extends FrameLayout implements SurfaceDrawer { setWillNotDraw(false); } - @Override - public void drawOnSurfaceForPictureSnapshot(Surface outputSurface) { - try { - final Canvas surfaceCanvas = outputSurface.lockCanvas(null); - - float xScale = surfaceCanvas.getWidth() / (float) getWidth(); - float yScale = surfaceCanvas.getHeight() / (float) getHeight(); - surfaceCanvas.scale(xScale, yScale); - surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (((CameraView.OverlayLayoutParams) child.getLayoutParams()).isDrawInPictureSnapshot()) { - drawChild(surfaceCanvas, child, getDrawingTime()); - } - } - - outputSurface.unlockCanvasAndPost(surfaceCanvas); - } catch (Surface.OutOfResourcesException e) { - e.printStackTrace(); - } - } - - @Override - public void drawOnSurfaceForVideoSnapshot(Surface outputSurface) { - try { - final Canvas surfaceCanvas = outputSurface.lockCanvas(null); - - // scale factor between canvas width and this View's width - float widthScale = surfaceCanvas.getWidth() / (float) getWidth(); - // scale factor between canvas height and this View's height - float heightScale = surfaceCanvas.getHeight() / (float) getHeight(); - surfaceCanvas.scale(widthScale, heightScale); - surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); if (((CameraView.OverlayLayoutParams) child.getLayoutParams()).isDrawInVideoSnapshot()) { - drawChild(surfaceCanvas, child, getDrawingTime()); - } - } - - outputSurface.unlockCanvasAndPost(surfaceCanvas); - } catch (Surface.OutOfResourcesException e) { - e.printStackTrace(); - } - } - - @Override - public void draw(Canvas canvas) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); if (((CameraView.OverlayLayoutParams) child.getLayoutParams()).isDrawInPreview()) { - drawChild(canvas, child, getDrawingTime()); - } - } + public void drawOverlay(Canvas canvas) { + super.draw(canvas); } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayoutManager.java b/cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayoutManager.java new file mode 100644 index 00000000..e8f349fb --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayoutManager.java @@ -0,0 +1,127 @@ +package com.otaliastudios.cameraview; + + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * This class manages the allocation of buffers and Frame objects. + * We are interested in both recycling byte[] buffers so they are not allocated for each + * preview frame, and in recycling Frame instances (so we don't instantiate a lot). + * + * For this, we keep a mPoolSize integer that defines the size of instances to keep. + * Whether this does make sense, it depends on how slow the frame processors are. + * If they are very slow, it is possible that some frames will be skipped. + * + * - byte[] buffer pool: + * this is not kept here, because Camera1 internals already have one that we can't control, but + * it should be OK. The only thing we do is allocate mPoolSize buffers when requested. + * - Frame pool: + * We keep a list of mPoolSize recycled instances, to be reused when a new buffer is available. + */ +class OverlayLayoutManager extends FrameLayout implements SurfaceDrawer { + + private Map mLayouts = new HashMap<>(); + + public OverlayLayoutManager(@NonNull Context context) { + super(context); + } + + public OverlayLayoutManager(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) { + // params must be instance of OverlayLayoutParams + if (!(params instanceof CameraView.OverlayLayoutParams)) { + return; + } + + OverlayType viewOverlayType = new OverlayType((CameraView.OverlayLayoutParams) params); + if (mLayouts.containsKey(viewOverlayType)) { + mLayouts.get(viewOverlayType).addView(child, params); + } else { + OverlayLayout newLayout = new OverlayLayout(getContext()); + newLayout.addView(child, params); + super.addView(newLayout); + mLayouts.put(viewOverlayType, newLayout); + } + } + + @Override + public void drawOnSurfaceForPictureSnapshot(Canvas surfaceCanvas) { + surfaceCanvas.save(); + // scale factor between canvas width and this View's width + float widthScale = surfaceCanvas.getWidth() / (float) getWidth(); + // scale factor between canvas height and this View's height + float heightScale = surfaceCanvas.getHeight() / (float) getHeight(); + surfaceCanvas.scale(widthScale, heightScale); + for (Map.Entry entry : mLayouts.entrySet()) { + if (entry.getKey().pictureSnapshot) { + entry.getValue().drawOverlay(surfaceCanvas); + } + } + surfaceCanvas.restore(); + } + + @Override + public void drawOnSurfaceForVideoSnapshot(Canvas surfaceCanvas) { + surfaceCanvas.save(); + // scale factor between canvas width and this View's width + float widthScale = surfaceCanvas.getWidth() / (float) getWidth(); + // scale factor between canvas height and this View's height + float heightScale = surfaceCanvas.getHeight() / (float) getHeight(); + surfaceCanvas.scale(widthScale, heightScale); + for (Map.Entry entry : mLayouts.entrySet()) { + if (entry.getKey().videoSnapshot) { + entry.getValue().drawOverlay(surfaceCanvas); + } + } + surfaceCanvas.restore(); + } + + private class OverlayType { + boolean preview = false; + boolean pictureSnapshot = false; + boolean videoSnapshot = false; + + OverlayType(CameraView.OverlayLayoutParams params) { + this.preview = params.isDrawInPreview(); + this.pictureSnapshot = params.isDrawInPictureSnapshot(); + this.videoSnapshot = params.isDrawInVideoSnapshot(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OverlayType that = (OverlayType) o; + return preview == that.preview && + pictureSnapshot == that.pictureSnapshot && + videoSnapshot == that.videoSnapshot; + } + + @Override + public int hashCode() { + int result = 0; + result = 31*result + (preview ? 1 : 0); + result = 31*result + (pictureSnapshot ? 1 : 0); + result = 31*result + (videoSnapshot ? 1 : 0); + return result; + } + } + +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java index 560b84be..8505bc87 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java @@ -2,7 +2,10 @@ package com.otaliastudios.cameraview; import android.annotation.SuppressLint; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ImageFormat; +import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.graphics.YuvImage; @@ -133,8 +136,16 @@ class SnapshotPictureRecorder extends PictureRecorder { mSurfaceTexture.getTransformMatrix(mTransform); Surface drawOnto = new Surface(mOverlaySurfaceTexture); if (mWithOverlay) { - for (SurfaceDrawer surfaceDrawer : mSurfaceDrawerList) { - surfaceDrawer.drawOnSurfaceForPictureSnapshot(drawOnto); + try { + final Canvas surfaceCanvas = drawOnto.lockCanvas(null); + surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + for (SurfaceDrawer surfaceDrawer : mSurfaceDrawerList) { + surfaceDrawer.drawOnSurfaceForPictureSnapshot(surfaceCanvas); + } + + drawOnto.unlockCanvasAndPost(surfaceCanvas); + } catch (Surface.OutOfResourcesException e) { + e.printStackTrace(); } mOverlaySurfaceTexture.updateTexImage(); mOverlaySurfaceTexture.getTransformMatrix(mOverlayTransform); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java index 669d004a..d704d483 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java @@ -1,5 +1,8 @@ package com.otaliastudios.cameraview; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.PorterDuff; import android.graphics.SurfaceTexture; import android.opengl.EGL14; import android.os.Build; @@ -131,8 +134,16 @@ class SnapshotVideoRecorder extends VideoRecorder implements GlCameraPreview.Ren // get overlay if (mWithOverlay) { - for (SurfaceDrawer surfaceDrawer : mSurfaceDrawerList) { - surfaceDrawer.drawOnSurfaceForVideoSnapshot(mOverlaySurface); + try { + final Canvas surfaceCanvas = mOverlaySurface.lockCanvas(null); + surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + for (SurfaceDrawer surfaceDrawer : mSurfaceDrawerList) { + surfaceDrawer.drawOnSurfaceForVideoSnapshot(surfaceCanvas); + } + + mOverlaySurface.unlockCanvasAndPost(surfaceCanvas); + } catch (Surface.OutOfResourcesException e) { + e.printStackTrace(); } mOverlaySurfaceTexture.updateTexImage(); mOverlaySurfaceTexture.getTransformMatrix(textureFrame.overlayTransform); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/SurfaceDrawer.java b/cameraview/src/main/java/com/otaliastudios/cameraview/SurfaceDrawer.java index d1a0973a..9686e9db 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/SurfaceDrawer.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/SurfaceDrawer.java @@ -1,8 +1,8 @@ package com.otaliastudios.cameraview; -import android.view.Surface; +import android.graphics.Canvas; interface SurfaceDrawer { - void drawOnSurfaceForPictureSnapshot(Surface surface); - void drawOnSurfaceForVideoSnapshot(Surface surface); + void drawOnSurfaceForPictureSnapshot(Canvas surfaceCanvas); + void drawOnSurfaceForVideoSnapshot(Canvas surfaceCanvas); } diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java index 4a454577..933ed11b 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java @@ -89,7 +89,7 @@ class GlCameraPreview extends CameraPreview imple mDispatched = false; } }); - parent.addView(root, 0); + parent.addView(root, 1); mRootView = root; return glView; } diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/SurfaceCameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/SurfaceCameraPreview.java index ac63767d..6f3f32f8 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/SurfaceCameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/SurfaceCameraPreview.java @@ -28,7 +28,7 @@ class SurfaceCameraPreview extends CameraPreview { @Override protected SurfaceView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) { View root = LayoutInflater.from(context).inflate(R.layout.cameraview_surface_view, parent, false); - parent.addView(root, 0); + parent.addView(root, 1); SurfaceView surfaceView = root.findViewById(R.id.surface_view); final SurfaceHolder holder = surfaceView.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java index 05b641fc..57d3a3ff 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java @@ -22,7 +22,7 @@ class TextureCameraPreview extends CameraPreview { @Override protected TextureView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) { View root = LayoutInflater.from(context).inflate(R.layout.cameraview_texture_view, parent, false); - parent.addView(root, 0); + parent.addView(root, 1); TextureView texture = root.findViewById(R.id.texture_view); texture.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {