diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java index 30be1f6f..70c33162 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java @@ -913,6 +913,14 @@ public class CameraViewTest extends BaseTest { cameraView.setFrameProcessingExecutors(0); } + @Test + public void testDrawHardwareOverlays() { + cameraView.setDrawHardwareOverlays(true); + assertTrue(cameraView.getDrawHardwareOverlays()); + cameraView.setDrawHardwareOverlays(false); + assertFalse(cameraView.getDrawHardwareOverlays()); + } + //endregion //region Lists of listeners and processors diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 656a2a43..dee95caa 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -227,6 +227,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver { int frameExecutors = a.getInteger(R.styleable.CameraView_cameraFrameProcessingExecutors, DEFAULT_FRAME_PROCESSING_EXECUTORS); + boolean drawHardwareOverlays = a.getBoolean(R.styleable.CameraView_cameraDrawHardwareOverlays, false); + // Size selectors and gestures SizeSelectorParser sizeSelectors = new SizeSelectorParser(a); GestureParser gestures = new GestureParser(a); @@ -260,6 +262,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { setUseDeviceOrientation(useDeviceOrientation); setGrid(controls.getGrid()); setGridColor(gridColor); + setDrawHardwareOverlays(drawHardwareOverlays); // Apply camera engine params // Adding new ones? See setEngine(). @@ -2156,6 +2159,25 @@ public class CameraView extends FrameLayout implements LifecycleObserver { return mCameraEngine.isTakingPicture(); } + /** + * Sets the overlay layout hardware canvas capture mode to allow hardware + * accelerated views to be captured in snapshots + * + * @param on true if enabled + */ + public void setDrawHardwareOverlays(boolean on) { + mOverlayLayout.setHardwareCanvasEnabled(on); + } + + /** + * Returns true if the overlay layout is set to capture the hardware canvas + * of child views + * + * @return boolean indicating hardware canvas capture is enabled + */ + public boolean getDrawHardwareOverlays() { + return mOverlayLayout.getHardwareCanvasEnabled(); + } //endregion //region Callbacks and dispatching diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/Overlay.java b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/Overlay.java index 44e1661d..038702af 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/Overlay.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/Overlay.java @@ -30,4 +30,18 @@ public interface Overlay { * @return true to draw on it */ boolean drawsOn(@NonNull Target target); + + /** + * Sets the overlay renderer to lock and capture the hardware canvas in order + * to capture hardware accelerated views such as video players + * + * @param on enabled + */ + void setHardwareCanvasEnabled(boolean on); + + /** + * Returns true if hardware canvas capture is enabled, false by default + * @return true if capturing hardware surfaces + */ + boolean getHardwareCanvasEnabled(); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java index 88f72668..940b80ba 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayDrawer.java @@ -6,6 +6,7 @@ import android.graphics.PorterDuff; import android.graphics.SurfaceTexture; import android.opengl.GLES11Ext; import android.opengl.GLES20; +import android.os.Build; import android.view.Surface; import androidx.annotation.NonNull; @@ -63,7 +64,12 @@ public class OverlayDrawer { */ public void draw(@NonNull Overlay.Target target) { try { - final Canvas surfaceCanvas = mSurface.lockCanvas(null); + final Canvas surfaceCanvas; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && mOverlay.getHardwareCanvasEnabled()) { + surfaceCanvas = mSurface.lockHardwareCanvas(); + } else { + surfaceCanvas = mSurface.lockCanvas(null); + } surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); mOverlay.drawOn(target, surfaceCanvas); mSurface.unlockCanvasAndPost(surfaceCanvas); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java index 3b2bd782..ba5dec36 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java @@ -26,6 +26,8 @@ public class OverlayLayout extends FrameLayout implements Overlay { @VisibleForTesting Target currentTarget = Target.PREVIEW; + private boolean mHardwareCanvasEnabled; + /** * We set {@link #setWillNotDraw(boolean)} to false even if we don't draw anything. * This ensures that the View system will call {@link #draw(Canvas)} on us instead @@ -99,6 +101,16 @@ public class OverlayLayout extends FrameLayout implements Overlay { return false; } + @Override + public void setHardwareCanvasEnabled(boolean on) { + mHardwareCanvasEnabled = on; + } + + @Override + public boolean getHardwareCanvasEnabled() { + return mHardwareCanvasEnabled; + } + /** * For {@link Target#PREVIEW}, this method is called by the View hierarchy. We will * just forward the call to super. @@ -132,7 +144,8 @@ public class OverlayLayout extends FrameLayout implements Overlay { "canvas:", canvas.getWidth() + "x" + canvas.getHeight(), "view:", getWidth() + "x" + getHeight(), "widthScale:", widthScale, - "heightScale:", heightScale + "heightScale:", heightScale, + "hardwareCanvasMode:", mHardwareCanvasEnabled ); canvas.scale(widthScale, heightScale); dispatchDraw(canvas); diff --git a/cameraview/src/main/res/values/attrs.xml b/cameraview/src/main/res/values/attrs.xml index 9a27f115..1b4418da 100644 --- a/cameraview/src/main/res/values/attrs.xml +++ b/cameraview/src/main/res/values/attrs.xml @@ -169,6 +169,8 @@ + + diff --git a/docs/_docs/watermarks-and-overlays.md b/docs/_docs/watermarks-and-overlays.md index a770c699..751dc65b 100644 --- a/docs/_docs/watermarks-and-overlays.md +++ b/docs/_docs/watermarks-and-overlays.md @@ -82,3 +82,19 @@ params.drawOnVideoSnapshot = false; // do not draw on video snapshots // When done, apply overlay.setLayoutParams(params); ``` + +To capture a hardware rendered View such as a video rendered to a TextureView, enable the +`cameraDrawHardwareOverlays` flag: + +```xml + +``` + +Alternatively you can enable it in code with `setDrawHardwareOverlays()`: + +```java +cameraView.setDrawHardwareOverlays(true); +``` \ No newline at end of file