diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 12afbd77..f42dcad0 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -244,13 +244,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver { * {@link #setEngine(Engine)} is called. */ private void doInstantiateEngine() { - if (mCameraEngine != null) { - mCameraEngine.removePictureOverlay(mOverlayLayout); - mCameraEngine.removeVideoOverlay(mOverlayLayout); - } mCameraEngine = instantiateCameraEngine(mEngine, mCameraCallbacks); - mCameraEngine.addPictureOverlay(mOverlayLayout); - mCameraEngine.addVideoOverlay(mOverlayLayout); + mCameraEngine.setOverlay(mOverlayLayout); } /** diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java index 6438055c..1eaff96b 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera1Engine.java @@ -312,7 +312,7 @@ public class Camera1Engine extends CameraEngine implements AspectRatio outputRatio = getAngles().flip(Reference.OUTPUT, Reference.VIEW) ? viewAspectRatio.flip() : viewAspectRatio; if (mPreview instanceof GlCameraPreview && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - mPictureRecorder = new SnapshotGlPictureRecorder(stub, this, (GlCameraPreview) mPreview, outputRatio, getPictureOverlays()); + mPictureRecorder = new SnapshotGlPictureRecorder(stub, this, (GlCameraPreview) mPreview, outputRatio, getOverlay()); } else { mPictureRecorder = new Snapshot1PictureRecorder(stub, this, mCamera, outputRatio); } @@ -362,7 +362,7 @@ public class Camera1Engine extends CameraEngine implements stub.rotation = getAngles().offset(Reference.VIEW, Reference.OUTPUT, Axis.ABSOLUTE); // Start. - mVideoRecorder = new SnapshotVideoRecorder(Camera1Engine.this, glPreview, getVideoOverlays()); + mVideoRecorder = new SnapshotVideoRecorder(Camera1Engine.this, glPreview, getOverlay()); mVideoRecorder.start(stub); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java index 7fe8b9af..0cdfb452 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -615,7 +615,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv stub.rotation = getAngles().offset(Reference.SENSOR, Reference.OUTPUT, Axis.RELATIVE_TO_SENSOR); // Actually it will be rotated and set to 0. AspectRatio outputRatio = getAngles().flip(Reference.OUTPUT, Reference.VIEW) ? viewAspectRatio.flip() : viewAspectRatio; if (mPreview instanceof GlCameraPreview) { - mPictureRecorder = new SnapshotGlPictureRecorder(stub, this, (GlCameraPreview) mPreview, outputRatio, getPictureOverlays()); + mPictureRecorder = new SnapshotGlPictureRecorder(stub, this, (GlCameraPreview) mPreview, outputRatio, getOverlay()); } else { throw new RuntimeException("takePictureSnapshot with Camera2 is only supported with Preview.GL_SURFACE"); } @@ -709,7 +709,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv stub.rotation = getAngles().offset(Reference.VIEW, Reference.OUTPUT, Axis.ABSOLUTE); // Start. - mVideoRecorder = new SnapshotVideoRecorder(this, glPreview, getVideoOverlays()); + mVideoRecorder = new SnapshotVideoRecorder(this, glPreview, getOverlay()); mVideoRecorder.start(stub); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java index c9f26c93..50670d48 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CameraEngine.java @@ -185,8 +185,7 @@ public abstract class CameraEngine implements private long mAutoFocusResetDelayMillis; private int mSnapshotMaxWidth = Integer.MAX_VALUE; // in REF_VIEW for consistency with SizeSelectors private int mSnapshotMaxHeight = Integer.MAX_VALUE; // in REF_VIEW for consistency with SizeSelectors - private final List pictureOverlays = new ArrayList<>(); - private final List videoOverlays = new ArrayList<>(); + private Overlay overlay; // Steps private final Step.Callback mStepCallback = new Step.Callback() { @@ -784,6 +783,15 @@ public abstract class CameraEngine implements //region Final setters and getters + public final void setOverlay(@Nullable Overlay overlay) { + this.overlay = overlay; + } + + @Nullable + public final Overlay getOverlay() { + return overlay; + } + @SuppressWarnings("WeakerAccess") public final Angles getAngles() { return mAngles; @@ -1389,41 +1397,4 @@ public abstract class CameraEngine implements } //endregion - - //region Overlays - - - public void addPictureOverlay(@NonNull Overlay overlay) { - if (!pictureOverlays.contains(overlay)) { - pictureOverlays.add(overlay); - } - } - - public void removePictureOverlay(@NonNull Overlay overlay) { - pictureOverlays.remove(overlay); - } - - public void addVideoOverlay(@NonNull Overlay overlay) { - if (!videoOverlays.contains(overlay)) { - videoOverlays.add(overlay); - } - } - - public void removeVideoOverlay(@NonNull Overlay overlay) { - videoOverlays.remove(overlay); - } - - @SuppressWarnings("WeakerAccess") - @NonNull - protected List getPictureOverlays() { - return pictureOverlays; - } - - @SuppressWarnings("WeakerAccess") - @NonNull - protected List getVideoOverlays() { - return videoOverlays; - } - - //endregion } 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 d614a875..212495cf 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/Overlay.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/Overlay.java @@ -4,7 +4,19 @@ import android.graphics.Canvas; import androidx.annotation.NonNull; +/** + * Base interface for overlays. + */ public interface Overlay { - void drawOnPicture(@NonNull Canvas canvas); - void drawOnVideo(@NonNull Canvas canvas); + + enum Target { + PREVIEW, PICTURE_SNAPSHOT, VIDEO_SNAPSHOT + } + + /** + * Called for this overlay to draw itself on the specified target and canvas. + * @param target target + * @param canvas target canvas + */ + void draw(@NonNull Target target, @NonNull Canvas canvas); } 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 33c47037..3327fde9 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/overlay/OverlayLayout.java @@ -23,11 +23,7 @@ public class OverlayLayout extends FrameLayout implements Overlay { private static final String TAG = OverlayLayout.class.getSimpleName(); private static final CameraLogger LOG = CameraLogger.create(TAG); - private static final int DRAWING_PREVIEW = 0; - private static final int DRAWING_PICTURE = 1; - private static final int DRAWING_VIDEO = 2; - - private int target = DRAWING_PREVIEW; + private Target currentTarget = Target.PREVIEW; /** * We set {@link #setWillNotDraw(boolean)} to false even if we don't draw anything. @@ -81,63 +77,52 @@ public class OverlayLayout extends FrameLayout implements Overlay { * likely drawing on the preview. * @param canvas View canvas */ + @SuppressLint("MissingSuperCall") @Override public void draw(Canvas canvas) { - LOG.i("draw called. Setting DRAWING_PREVIEW and calling super."); - synchronized (this) { - target = DRAWING_PREVIEW; - super.draw(canvas); - } + LOG.i("normal draw called."); + draw(Target.PREVIEW, canvas); } /** - * This is called by the overlay drawer. - * We call {@link #dispatchDraw(Canvas)} to draw our children. + * For {@link Target#PREVIEW}, this method is called by the View hierarchy. We will + * just forward the call to super. * - * The input canvas has the Surface dimensions which means it is guaranteed - * to have our own aspect ratio. But we might still have to apply some scale. + * For {@link Target#PICTURE_SNAPSHOT} and {@link Target#VIDEO_SNAPSHOT}, + * this method is called by the overlay drawer. We call {@link #dispatchDraw(Canvas)} + * to draw our children only. * - * @param canvas the overlay canvas + * @param target the draw target + * @param canvas the canvas */ @Override - public void drawOnPicture(@NonNull Canvas canvas) { - canvas.save(); - float widthScale = canvas.getWidth() / (float) getWidth(); - float heightScale = canvas.getHeight() / (float) getHeight(); - LOG.i("drawOnPicture", - "widthScale:", widthScale, - "heightScale:", heightScale, - "canvasWidth:", canvas.getWidth(), - "canvasHeight:", canvas.getHeight()); - canvas.scale(widthScale, heightScale); + public void draw(@NonNull Target target, @NonNull Canvas canvas) { synchronized (this) { - target = DRAWING_PICTURE; - dispatchDraw(canvas); - } - canvas.restore(); - } - - /** - * This is called by the overlay drawer. - * We call {@link #dispatchDraw(Canvas)} to draw our children. - * @param canvas the overlay canvas - */ - @Override - public void drawOnVideo(@NonNull Canvas canvas) { - canvas.save(); - float widthScale = canvas.getWidth() / (float) getWidth(); - float heightScale = canvas.getHeight() / (float) getHeight(); - LOG.i("drawOnVideo", - "widthScale:", widthScale, - "heightScale:", heightScale, - "canvasWidth:", canvas.getWidth(), - "canvasHeight:", canvas.getHeight()); - canvas.scale(widthScale, heightScale); - synchronized (this) { - target = DRAWING_VIDEO; - dispatchDraw(canvas); + currentTarget = target; + switch (target) { + case PREVIEW: + super.draw(canvas); + break; + case VIDEO_SNAPSHOT: + case PICTURE_SNAPSHOT: + canvas.save(); + // The input canvas has the Surface dimensions which means it is guaranteed + // to have our own aspect ratio. But we might still have to apply some scale. + float widthScale = canvas.getWidth() / (float) getWidth(); + float heightScale = canvas.getHeight() / (float) getHeight(); + LOG.i("draw", + "target:", target, + "canvas:", canvas.getWidth() + "x" + canvas.getHeight(), + "view:", getWidth() + "x" + getHeight(), + "widthScale:", widthScale, + "heightScale:", heightScale + ); + canvas.scale(widthScale, heightScale); + dispatchDraw(canvas); + canvas.restore(); + break; + } } - canvas.restore(); } /** @@ -147,18 +132,18 @@ public class OverlayLayout extends FrameLayout implements Overlay { @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { LayoutParams params = (LayoutParams) child.getLayoutParams(); - boolean draw = ((target == DRAWING_PREVIEW && params.drawOnPreview) - || (target == DRAWING_VIDEO && params.drawOnVideoSnapshot) - || (target == DRAWING_PICTURE && params.drawOnPictureSnapshot) + boolean draw = ((currentTarget == Target.PREVIEW && params.drawOnPreview) + || (currentTarget == Target.VIDEO_SNAPSHOT && params.drawOnVideoSnapshot) + || (currentTarget == Target.PICTURE_SNAPSHOT && params.drawOnPictureSnapshot) ); if (draw) { LOG.v("Performing drawing for view:", child.getClass().getSimpleName(), - "target:", target, + "target:", currentTarget, "params:", params); return super.drawChild(canvas, child, drawingTime); } else { LOG.v("Skipping drawing for view:", child.getClass().getSimpleName(), - "target:", target, + "target:", currentTarget, "params:", params); return false; } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java index ddca7f86..53c1fa9b 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/SnapshotGlPictureRecorder.java @@ -47,23 +47,21 @@ public class SnapshotGlPictureRecorder extends PictureRecorder { private GlCameraPreview mPreview; private AspectRatio mOutputRatio; - private List mOverlays; - private boolean mHasOverlays; + private Overlay mOverlay; + private boolean mHasOverlay; public SnapshotGlPictureRecorder( @NonNull PictureResult.Stub stub, @NonNull CameraEngine engine, @NonNull GlCameraPreview preview, @NonNull AspectRatio outputRatio, - @Nullable List overlays) { + @Nullable Overlay overlay) { super(stub, engine); mEngine = engine; mPreview = preview; mOutputRatio = outputRatio; - mHasOverlays = overlays != null && !overlays.isEmpty(); - if (mHasOverlays) { - mOverlays = new ArrayList<>(overlays); - } + mOverlay = overlay; + mHasOverlay = overlay != null; } @TargetApi(Build.VERSION_CODES.KITKAT) @@ -93,7 +91,7 @@ public class SnapshotGlPictureRecorder extends PictureRecorder { mSurfaceTexture.setDefaultBufferSize(mResult.size.getWidth(), mResult.size.getHeight()); mTransform = new float[16]; - if (mHasOverlays) { + if (mHasOverlay) { mOverlayTextureId = mViewport.createTexture(); mOverlaySurfaceTexture = new SurfaceTexture(mOverlayTextureId, true); mOverlaySurfaceTexture.setDefaultBufferSize(mResult.size.getWidth(), mResult.size.getHeight()); @@ -165,14 +163,12 @@ public class SnapshotGlPictureRecorder extends PictureRecorder { // 7. Do pretty much the same for overlays, though with // some differences. - if (mHasOverlays) { + if (mHasOverlay) { // 1. First we must draw on the texture and get latest image. try { final Canvas surfaceCanvas = mOverlaySurface.lockCanvas(null); surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - for (Overlay overlay : mOverlays) { - overlay.drawOnPicture(surfaceCanvas); - } + mOverlay.draw(Overlay.Target.PICTURE_SNAPSHOT, surfaceCanvas); mOverlaySurface.unlockCanvasAndPost(surfaceCanvas); } catch (Surface.OutOfResourcesException e) { LOG.w("Got Surface.OutOfResourcesException while drawing picture overlays", e); @@ -195,7 +191,7 @@ public class SnapshotGlPictureRecorder extends PictureRecorder { // 8. Draw and save mViewport.drawFrame(mTextureId, mTransform); - if (mHasOverlays) mViewport.drawFrame(mOverlayTextureId, mOverlayTransform); + if (mHasOverlay) mViewport.drawFrame(mOverlayTextureId, mOverlayTransform); // don't - surface.swapBuffers(); mResult.data = surface.saveFrameTo(Bitmap.CompressFormat.JPEG); mResult.format = PictureResult.FORMAT_JPEG; @@ -205,7 +201,7 @@ public class SnapshotGlPictureRecorder extends PictureRecorder { surface.release(); mViewport.release(); mSurfaceTexture.release(); - if (mHasOverlays) { + if (mHasOverlay) { mOverlaySurface.release(); mOverlaySurfaceTexture.release(); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java index 2dd585c6..637613bc 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/video/SnapshotVideoRecorder.java @@ -60,20 +60,17 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram private int mOverlayTextureId = 0; private SurfaceTexture mOverlaySurfaceTexture; private Surface mOverlaySurface; - private List mOverlays; - private boolean mHasOverlays; + private Overlay mOverlay; + private boolean mHasOverlay; public SnapshotVideoRecorder(@NonNull CameraEngine engine, @NonNull GlCameraPreview preview, - @Nullable List overlays) { + @Nullable Overlay overlay) { super(engine); mPreview = preview; mEngine = engine; - - mHasOverlays = overlays != null && !overlays.isEmpty(); - if (mHasOverlays) { - mOverlays = new ArrayList<>(overlays); - } + mOverlay = overlay; + mHasOverlay = overlay != null; } @Override @@ -93,7 +90,7 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram @Override public void onRendererTextureCreated(int textureId) { mTextureId = textureId; - if (mHasOverlays) { + if (mHasOverlay) { EglViewport temp = new EglViewport(); mOverlayTextureId = temp.createTexture(); mOverlaySurfaceTexture = new SurfaceTexture(mOverlayTextureId); @@ -131,7 +128,7 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram mResult.videoFrameRate, mResult.rotation, type, mTextureId, - mHasOverlays ? mOverlayTextureId : TextureMediaEncoder.NO_TEXTURE, + mHasOverlay ? mOverlayTextureId : TextureMediaEncoder.NO_TEXTURE, scaleX, scaleY, mFlipped, EGL14.eglGetCurrentContext() @@ -160,14 +157,11 @@ public class SnapshotVideoRecorder extends VideoRecorder implements RendererFram surfaceTexture.getTransformMatrix(textureFrame.transform); // get overlay - if (mHasOverlays) { + if (mHasOverlay) { try { final Canvas surfaceCanvas = mOverlaySurface.lockCanvas(null); surfaceCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - for (Overlay overlay : mOverlays) { - overlay.drawOnVideo(surfaceCanvas); - } - + mOverlay.draw(Overlay.Target.VIDEO_SNAPSHOT, surfaceCanvas); mOverlaySurface.unlockCanvasAndPost(surfaceCanvas); } catch (Surface.OutOfResourcesException e) { LOG.w("Got Surface.OutOfResourcesException while drawing video overlays", e); diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java index 39e5c39c..47e09f67 100644 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java @@ -83,8 +83,6 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis b.setState(BottomSheetBehavior.STATE_HIDDEN); } }); - - syncWatermark(); } private void message(String content, boolean important) { @@ -220,16 +218,6 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis message("Switched to front camera!", false); break; } - syncWatermark(); - } - - @SuppressLint("SetTextI18n") - private void syncWatermark() { - TextView watermark = findViewById(R.id.watermark_title); - switch (camera.getFacing()) { - case FRONT: watermark.setText("Front Camera"); break; - case BACK: watermark.setText("Back Camera"); break; - } } @Override diff --git a/demo/src/main/res/layout/activity_camera.xml b/demo/src/main/res/layout/activity_camera.xml index d58a30f1..cd4773d9 100644 --- a/demo/src/main/res/layout/activity_camera.xml +++ b/demo/src/main/res/layout/activity_camera.xml @@ -51,6 +51,7 @@ android:id="@+id/watermark_title" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="CameraView" android:textColor="@android:color/white" />