通过RenderScript 实现 NV21转Bitmap、两张Bitmap按照透明度混合的工具类

之前写的一篇文章中我通过提取bitmap的数组,然后通过位移运算提取两个数组的alpha值通过比例混合为一张新的图片,这样对于大图片来说会很卡。所以我在查阅资料后:

安卓开发手册 ScriptIntrinsicBlend

通过RenderScript使用GPU加速来获得非常好的转换效果,只要使用安卓自身提供的脚本工具类ScriptIntrinsicBlend,配置好参数和输入输出,还有混合模式,即可快速完成该任务。

4k×2k的两张Bitmap可以在我的手机上只要30ms就可以混合完成。下面贴出我写的工具类:


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlend;
import android.renderscript.ScriptIntrinsicYuvToRGB;
import android.renderscript.Type;
import android.util.Log;

import java.io.OutputStream;

/**@author 陈杰柱**/
public class YuvUtil {
    private Context mContext;
    private RenderScript rs;

    private ScriptIntrinsicYuvToRGB mYuvToRgbIntrinsic;
    private ScriptIntrinsicBlend mScriptIntrinsicBlend;

    private Type.Builder mYuvType, mRgbaType;
    private Type.Builder mRgbType1, mRgbType2;

    private Allocation mYuvin, mYuvOut;
    private Allocation mARGBPic1, mARGBPic2;

    private Bitmap mBmpout;

    public YuvUtil(Context context) {
        this.mContext = context;
        rs = RenderScript.create(context);
        mYuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
        mScriptIntrinsicBlend = ScriptIntrinsicBlend.create(rs, Element.RGBA_8888(rs));
    }

    /**nv21转bmp**/
    public Bitmap nv21ToBitmap(byte[] nv21, int width, int height) {
        long start = System.currentTimeMillis();
        if (mYuvType == null) {
            mYuvType = new Type.Builder(rs, Element.U8(rs)).setX(nv21.length);
            mYuvin = Allocation.createTyped(rs, mYuvType.create(), Allocation.USAGE_SCRIPT);
            mRgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
            mYuvOut = Allocation.createTyped(rs, mRgbaType.create(), Allocation.USAGE_SCRIPT);
        }
        mYuvin.copyFrom(nv21);
        mYuvToRgbIntrinsic.setInput(mYuvin);
        mYuvToRgbIntrinsic.forEach(mYuvOut);
        if (mBmpout == null) {
            mBmpout = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        } else if (mBmpout.getWidth() != width || mBmpout.getHeight() != height) {
            mBmpout = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        }
        mYuvOut.copyTo(mBmpout);
        Log.i("cjztest", "nv21转bmp" + (System.currentTimeMillis() - start) + "ms");
        return mBmpout;

    }

    /**两个bmp按照透明度混合**/
    public Bitmap bitmapBlendBitmap(Bitmap bottom, Bitmap top) {
        long start = System.currentTimeMillis();
        if (mRgbType1 == null || mRgbType2 == null) {
            mRgbType1 = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(top.getWidth()).setY(top.getHeight());
            mRgbType2 = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(bottom.getWidth()).setY(bottom.getHeight());
            mARGBPic1 = Allocation.createTyped(rs, mRgbType1.create(), Allocation.USAGE_SCRIPT);
            mARGBPic2 = Allocation.createTyped(rs, mRgbType2.create(), Allocation.USAGE_SCRIPT);
        }
        mARGBPic1.copyFrom(top);
        mARGBPic2.copyFrom(bottom);
//        mScriptIntrinsicBlend.forEachAdd(mARGBPic1, mYuvOut);
//        mScriptIntrinsicBlend.forEachDstAtop(mARGBPic1, mARGBPic2);
//        mScriptIntrinsicBlend.forEachDstIn(mARGBPic1, mARGBPic2);
//        mScriptIntrinsicBlend.forEachDstOut(mARGBPic2, mARGBPic1);
        mScriptIntrinsicBlend.forEachDstOver(mARGBPic2, mARGBPic1);
        if (mBmpout == null) {
            mBmpout = Bitmap.createBitmap(top.getWidth(), top.getHeight(), Bitmap.Config.ARGB_8888);
        } else if (mBmpout.getWidth() != top.getWidth() || mBmpout.getHeight() != top.getHeight()) {
            mBmpout = Bitmap.createBitmap(top.getWidth(), top.getHeight(), Bitmap.Config.ARGB_8888);
        }
        mARGBPic1.copyTo(mBmpout);
        Log.i("cjztest", "两个bmp按照透明度混合使用的时间" + (System.currentTimeMillis() - start) + "ms");
        return mBmpout;
    }


    /**传统方法把YUV转jpg**/
    public void nv21ToBitmapDirectSave(byte[] nv21, int width, int height, OutputStream os) {
        YuvImage im = new YuvImage(nv21, ImageFormat.NV21, width, height, new int[]{width, width, width});
        im.compressToJpeg(new Rect(0, 0, width, height), 80, os);
    }
}

另外可以符合使用,例如如果你希望实现在NV21上混合一个同样宽高的bmp,可以这么使用,也就是NV21先变成BMP再混合:

Bitmap resultBmp = mYuvUtil.bitmapBlendBitmap(mYuvUtil.nv21ToBitmap(photoBuffer, width, height), waterMarkBmp);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于高效率的YUV420_888NV21,我们可以使用Android提供的RenderScript库进行加速。RenderScript是Android中的高性能计算框架,可以在各种设备上进行优化的计算操作。以下是使用RenderScript进行高效率YUV420_888NV21的示例代码: ```java public static void convertYUV420888ToNV21(Image image, byte[] nv21) { RenderScript rs = RenderScript.create(context); ScriptC_yuv420888 script = new ScriptC_yuv420888(rs); int width = image.getWidth(); int height = image.getHeight(); Type.Builder yuvType = new Type.Builder(rs, Element.YUV(rs)); yuvType.setX(width).setY(height).setYuvFormat(ImageFormat.YUV_420_888); Allocation yuvAllocation = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT); Type.Builder nv21Type = new Type.Builder(rs, Element.createPixel(rs, Element.DataType.UNSIGNED_8, Element.DataKind.PIXEL_YUV)); nv21Type.setX(width).setY(height).setYuvFormat(ImageFormat.NV21); Allocation nv21Allocation = Allocation.createTyped(rs, nv21Type.create(), Allocation.USAGE_SCRIPT); ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); int ySize = yBuffer.remaining(); int uSize = uBuffer.remaining(); int vSize = vBuffer.remaining(); byte[] yBytes = new byte[ySize]; byte[] uBytes = new byte[uSize]; byte[] vBytes = new byte[vSize]; yBuffer.get(yBytes); uBuffer.get(uBytes); vBuffer.get(vBytes); yuvAllocation.copyFromUnchecked(new byte[][] {yBytes, uBytes, vBytes}); script.invoke_setup(yuvAllocation, nv21Allocation); script.invoke_yuv420888_to_nv21(); nv21Allocation.copyTo(nv21); yuvAllocation.destroy(); nv21Allocation.destroy(); script.destroy(); rs.destroy(); } ``` 这个方法首先创建一个RenderScript实例和一个ScriptC_yuv420888实例。然后,创建一个YUV420_888格式的Allocation和一个NV21格式的Allocation。接下来,将Y、U、V分量的ByteBuffer换为对应的字节数组,并将它们复制到YUV420_888的Allocation中。调用ScriptC_yuv420888中的`invoke_setup`方法将YUV420_888和NV21的Allocation传递给RenderScript。接下来,调用`invoke_yuv420888_to_nv21`方法进行换。最后,将换后的字节流写入nv21数组中。 使用RenderScript可以大大提高YUV420_888NV21的效率,特别是处理大型图像时。但是,使用RenderScript需要一定的学习成本,并且需要在您的应用中添加RenderScript库的依赖。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值