【问题标题】:Applying a Mask to a Video Preview on Android在 Android 上将蒙版应用于视频预览
【发布时间】:2014-05-21 18:14:42
【问题描述】:

我正在尝试在 Android 上的视频供稿(预览)上应用蒙版。

这个想法是裁剪设备屏幕上的视频,使其看起来像圆形。

到目前为止,我已经完成了以下工作:

(忽略视频提要中的黑色圆形带,它是 xml 布局中的单独资源)

如您所见,我有视频,中间有一个圆孔。我需要相反的(即我应该看到“洞”内的视频,其余的应该是不可见的)。

我正在重写 SurfaceView 的绘制方法并应用 Porter-Duff 模式:

以下是相关代码:

package org.dornad.test;

import android.content.Context;
import android.graphics.*;
import android.hardware.Camera;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.io.IOException;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private Camera camera;
    private final SurfaceHolder holder;
    private Bitmap rounderBitmap;
    private Paint xferPaint;

    public CameraPreview(Context context, Camera camera) {
        super(context);

        this.camera = camera;
        holder = getHolder();
        holder.addCallback(this);

        this.setDrawingCacheEnabled(true);
    }

    @Override
    public void draw(Canvas canvas) {

        Bitmap currentBitmap = get();
        int w = currentBitmap.getWidth(), h = currentBitmap.getHeight();

        if (rounderBitmap == null) 
            rounderBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(rounderBitmap);

        if (xferPaint == null)  
            xferPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        xferPaint.setColor(Color.RED);

        c.drawCircle(w/2, h/2, 100, xferPaint);
        xferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

        canvas.drawBitmap(currentBitmap, 0, 0, null);
        canvas.drawBitmap(rounderBitmap, 0, 0, xferPaint);
    }

    private Bitmap get() {
        return this.getDrawingCache();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            setWillNotDraw(false);
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        } catch (IOException e) {
            L.d("Error setting camera preview : %s", e.getMessage());
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (holder.getSurface() == null) {
            return;
        }
        try {
            camera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop an non-existent preview
        }

        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        } catch (IOException e) {
            L.d("Error setting camera preview : %s", e.getMessage());
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // empty.  Take care of releasing the Camera preview in the Activity/Fragment.
    }
}

根据 Porter-Duff 混合模式的视觉指南:

我正在寻找的应该是 PorterDuff.Mode.XOR,但我得到的是带有红色填充的圆圈,而不是视频源。 (我知道红色来自 xferPaint.setColor 调用)

有什么想法吗?

【问题讨论】:

  • 所以相机正在向 Surface 发送帧,而你也在 Surface 上绘图?我的期望是,一旦你画了一次,你就再也看不到相机的输出了。您在 logcat 中看到任何投诉吗?
  • 我一回到办公室就去看看 logcat。但 AFAIK(或记得)没有抱怨。
  • 刚刚检查过。 logcat 没有任何投诉。
  • 我想我读错了你的代码......你正在向 Surface 发送帧,并渲染到视图中(通常只是一个透明的“洞”)。 View 和 Surface 之间的混合将由系统合成器完成,因此应用程序渲染没有任何要混合的先前内容。选择一种模式,将输出像素中的 Alpha 设置为适当的透明或不透明。

标签: android video surfaceview mask


【解决方案1】:

您将xferPaint 用于两件事:

  • 在缓冲区中画一个圆
  • 在预览顶部绘制缓冲区

并且您正在使用DST_IN 将每次渲染上的圆圈重新绘制到rounderBitmap

我认为,如果您重新排序代码,您可能会得到想要的结果(未经测试):

    Bitmap currentBitmap = get();
    int w = currentBitmap.getWidth(), h = currentBitmap.getHeight();

    if (rounderBitmap == null) {
        rounderBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(rounderBitmap);
        Paint holePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        holePaint.setColor(Color.RED);
        c.drawCircle(w / 2, h / 2, 100, holePaint);
    }

    if (xferPaint == null) {
        Paint xferPaint = new Paint();
        xferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    }

    canvas.drawBitmap(currentBitmap, 0, 0, null);
    canvas.drawBitmap(rounderBitmap, 0, 0, xferPaint);

如果它有效,那么你可能需要考虑:

  • rounderBitmap分配为200x200位图,而不是w x h
  • drawCircle(100, 100, 100
  • 位置rounderBitmapw/2-100h/2-100drawBitmap

一般来说,您可以尝试转储位图以查看它是否是您想要的(中间有一个圆圈的图像(具有正确的颜色/alpha):

static int counter;
// inside draw:
File out = new File(getContext().getExternalCacheDir(), "frame_"+ counter++ +".png");
Bitmap.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(out));
Log.d("DEBUG", "Written: " + out);

免责声明:我没有使用 PorterDuff 的经验,以上只是一般建议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多