Use multiple OverlayLayout

pull/421/head
Giacomo Randazzo 6 years ago
parent 19ddbfde29
commit 5656e6a3ae
  1. 21
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  2. 63
      cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayout.java
  3. 127
      cameraview/src/main/java/com/otaliastudios/cameraview/OverlayLayoutManager.java
  4. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java
  5. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java
  6. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/SurfaceDrawer.java
  7. 2
      cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java
  8. 2
      cameraview/src/main/views/com/otaliastudios/cameraview/SurfaceCameraPreview.java
  9. 2
      cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java

@ -71,7 +71,8 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
PinchGestureLayout mPinchGestureLayout; PinchGestureLayout mPinchGestureLayout;
TapGestureLayout mTapGestureLayout; TapGestureLayout mTapGestureLayout;
ScrollGestureLayout mScrollGestureLayout; ScrollGestureLayout mScrollGestureLayout;
OverlayLayout mOverlayLayout; OverlayLayoutManager mOverlayLayoutManager;
OverlayLayoutManager mOverlayLayoutManagerBelow;
private boolean mKeepScreenOn; private boolean mKeepScreenOn;
private boolean mExperimental; private boolean mExperimental;
@ -197,12 +198,14 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mPinchGestureLayout = new PinchGestureLayout(context); mPinchGestureLayout = new PinchGestureLayout(context);
mTapGestureLayout = new TapGestureLayout(context); mTapGestureLayout = new TapGestureLayout(context);
mScrollGestureLayout = new ScrollGestureLayout(context); mScrollGestureLayout = new ScrollGestureLayout(context);
mOverlayLayout = new OverlayLayout(context); mOverlayLayoutManager = new OverlayLayoutManager(context);
mOverlayLayoutManagerBelow = new OverlayLayoutManager(context);
addView(mGridLinesLayout); addView(mGridLinesLayout);
addView(mPinchGestureLayout); addView(mPinchGestureLayout);
addView(mTapGestureLayout); addView(mTapGestureLayout);
addView(mScrollGestureLayout); addView(mScrollGestureLayout);
addView(mOverlayLayout); addView(mOverlayLayoutManager);
addView(mOverlayLayoutManagerBelow, 0);
// Apply self managed // Apply self managed
setPlaySounds(playSounds); setPlaySounds(playSounds);
@ -272,8 +275,10 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// attached. That's why we instantiate the preview here. // attached. That's why we instantiate the preview here.
instantiatePreview(); instantiatePreview();
mCameraController.addPictureSurfaceDrawer(mOverlayLayout); mCameraController.addPictureSurfaceDrawer(mOverlayLayoutManager);
mCameraController.addVideoSurfaceDrawer(mOverlayLayout); mCameraController.addPictureSurfaceDrawer(mOverlayLayoutManagerBelow);
mCameraController.addVideoSurfaceDrawer(mOverlayLayoutManager);
mCameraController.addVideoSurfaceDrawer(mOverlayLayoutManagerBelow);
} }
if (!isInEditMode()) { if (!isInEditMode()) {
mOrientationHelper.enable(getContext()); mOrientationHelper.enable(getContext());
@ -291,7 +296,11 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@Override @Override
public void addView(View child, ViewGroup.LayoutParams params) { public void addView(View child, ViewGroup.LayoutParams params) {
if (params instanceof OverlayLayoutParams) { if (params instanceof OverlayLayoutParams) {
mOverlayLayout.addView(child, params); if (((OverlayLayoutParams) params).drawInPreview) {
mOverlayLayoutManager.addView(child, params);
} else {
mOverlayLayoutManagerBelow.addView(child, params);
}
} else { } else {
super.addView(child, params); super.addView(child, params);
} }

@ -2,18 +2,13 @@ package com.otaliastudios.cameraview;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
class OverlayLayout extends FrameLayout implements SurfaceDrawer { class OverlayLayout extends FrameLayout {
public OverlayLayout(@NonNull Context context) { public OverlayLayout(@NonNull Context context) {
super(context); super(context);
@ -25,60 +20,8 @@ class OverlayLayout extends FrameLayout implements SurfaceDrawer {
setWillNotDraw(false); setWillNotDraw(false);
} }
@Override public void drawOverlay(Canvas canvas) {
public void drawOnSurfaceForPictureSnapshot(Surface outputSurface) { super.draw(canvas);
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());
}
}
} }
} }

@ -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<OverlayType, OverlayLayout> 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<OverlayType, OverlayLayout> 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<OverlayType, OverlayLayout> 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;
}
}
}

@ -2,7 +2,10 @@ package com.otaliastudios.cameraview;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageFormat; import android.graphics.ImageFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.graphics.YuvImage; import android.graphics.YuvImage;
@ -133,8 +136,16 @@ class SnapshotPictureRecorder extends PictureRecorder {
mSurfaceTexture.getTransformMatrix(mTransform); mSurfaceTexture.getTransformMatrix(mTransform);
Surface drawOnto = new Surface(mOverlaySurfaceTexture); Surface drawOnto = new Surface(mOverlaySurfaceTexture);
if (mWithOverlay) { if (mWithOverlay) {
for (SurfaceDrawer surfaceDrawer : mSurfaceDrawerList) { try {
surfaceDrawer.drawOnSurfaceForPictureSnapshot(drawOnto); 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.updateTexImage();
mOverlaySurfaceTexture.getTransformMatrix(mOverlayTransform); mOverlaySurfaceTexture.getTransformMatrix(mOverlayTransform);

@ -1,5 +1,8 @@
package com.otaliastudios.cameraview; package com.otaliastudios.cameraview;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.opengl.EGL14; import android.opengl.EGL14;
import android.os.Build; import android.os.Build;
@ -131,8 +134,16 @@ class SnapshotVideoRecorder extends VideoRecorder implements GlCameraPreview.Ren
// get overlay // get overlay
if (mWithOverlay) { if (mWithOverlay) {
for (SurfaceDrawer surfaceDrawer : mSurfaceDrawerList) { try {
surfaceDrawer.drawOnSurfaceForVideoSnapshot(mOverlaySurface); 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.updateTexImage();
mOverlaySurfaceTexture.getTransformMatrix(textureFrame.overlayTransform); mOverlaySurfaceTexture.getTransformMatrix(textureFrame.overlayTransform);

@ -1,8 +1,8 @@
package com.otaliastudios.cameraview; package com.otaliastudios.cameraview;
import android.view.Surface; import android.graphics.Canvas;
interface SurfaceDrawer { interface SurfaceDrawer {
void drawOnSurfaceForPictureSnapshot(Surface surface); void drawOnSurfaceForPictureSnapshot(Canvas surfaceCanvas);
void drawOnSurfaceForVideoSnapshot(Surface surface); void drawOnSurfaceForVideoSnapshot(Canvas surfaceCanvas);
} }

@ -89,7 +89,7 @@ class GlCameraPreview extends CameraPreview<GLSurfaceView, SurfaceTexture> imple
mDispatched = false; mDispatched = false;
} }
}); });
parent.addView(root, 0); parent.addView(root, 1);
mRootView = root; mRootView = root;
return glView; return glView;
} }

@ -28,7 +28,7 @@ class SurfaceCameraPreview extends CameraPreview<SurfaceView, SurfaceHolder> {
@Override @Override
protected SurfaceView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) { protected SurfaceView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) {
View root = LayoutInflater.from(context).inflate(R.layout.cameraview_surface_view, parent, false); 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); SurfaceView surfaceView = root.findViewById(R.id.surface_view);
final SurfaceHolder holder = surfaceView.getHolder(); final SurfaceHolder holder = surfaceView.getHolder();
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

@ -22,7 +22,7 @@ class TextureCameraPreview extends CameraPreview<TextureView, SurfaceTexture> {
@Override @Override
protected TextureView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) { protected TextureView onCreateView(@NonNull Context context, @NonNull ViewGroup parent) {
View root = LayoutInflater.from(context).inflate(R.layout.cameraview_texture_view, parent, false); 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); TextureView texture = root.findViewById(R.id.texture_view);
texture.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { texture.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {

Loading…
Cancel
Save