0

I am currently having problems with multiple surface views and the clipping of one of the single views to get it to appear as a circle. It is best described in images:

So in this view i have 2 surface views, the full screen one which is showing a preview of the camera and then the top which is playing back a file with a MediaPlayer. I have gotten the top one to be above the preview with the following line:

surfaceView.setZOrderMediaOverlay(true);

Now as you can see i have attempted to mask this to a circle with the following code:

@Override
protected void dispatchDraw(Canvas canvas) {

    Path clipPath = new Path();
    clipPath.addCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2, Path.Direction.CCW);

    canvas.clipPath(clipPath);
    super.dispatchDraw(canvas);
}

But it doesn't appear to have quite worked out. I initially thought this was a problem with my masking code but turns out the if i remove the preview surface view, like in the following image, the masking works fine

Anyone have any ideas as to why this is occurring or how to fix it at all :S?

Thanks for your help

Joe Maher
  • 5,354
  • 5
  • 28
  • 44

1 Answers1

3

Bear in mind that a SurfaceView has two parts, the Surface and the View, and that the Android graphics system uses multiple layers.

All of your Views live on a single layer, usually referred to simply as "the View UI layer". Views can be placed on top of or below each other because they're all being rendered into the same buffer of pixels.

Surfaces appear on their own layers. By default, the SurfaceView's Surface is below (behind) the View UI layer. The SurfaceView's View part is just a transparent rectangle that the View system uses for layout. If you draw on that View (by subclassing SurfaceView and overriding a draw method), you're rendering pixels that overlap the Surface, making them opaque. This is an easy way to mask the Surface.

Your mini-player Surface uses setZOrderOnTop(), which sets the layer for that SurfaceView's Surface to be on top (in front) of the View UI. This is handy if your Surface is partially transparent, as it lets you see the Views behind it. However, anything you draw on the SurfaceView's View will also appear behind it, so it will no longer work as a mask.

The trouble with what you're trying to do is that you want to have the lower Surface visible through the upper Surface at the corners. You can't simply mask those pixels off with another layer -- you need to make them transparent on the video playback layer itself. This is a bit tricky, as you will need to feed the video into a GLES texture and then render it.

See also the graphics architecture doc.

fadden
  • 51,356
  • 5
  • 116
  • 166
  • Thanks a lot i suspected this way the case, i read another of your answers on another post that was having similar problems – Joe Maher Feb 26 '16 at 03:07
  • have you seen any good examples of both playing and masking a video in a GLSurfaceView? – Joe Maher Feb 26 '16 at 03:16
  • I don't have something specific to recommend. The "texture from camera" Activity in Grafika (https://github.com/google/grafika) demonstrates rendering a live camera feed with GLES, but it doesn't try to mask the image. The best approach might be to start with that and use a fragment shader to render transparency outside a certain radius and just keep rendering to a rect, though drawing a textured circle (approximated with a triangle fan) would work as well. (Grafika's camera filter demo has the key ingredients, but you'll need to play with GLSL a bit.) – fadden Feb 26 '16 at 05:33