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