PS:2018/0807
不可以使用生产者模式 去不断的textureView.getBitmap, 否则RenderThread线程会 非常奇怪的抛出异常(疑似当前Frame被释放,再get的native层空指针)。应该在TextureView.SurfaceTextureListener的onSurfaceTextureUpdated(SurfaceTexture surface) 回调内去textureView.getBitmap
PS:2018/05/07
最近又发现TextureView的getBitmap方法 更快,比RenderScript还快。具体就是讲Camera 的Preview set到TextureView上,然后调用TextureView的getBitmap方法。推荐用生产者模式去getBitmap,我试验的是整幅图也就10+ms
Context
YUV格式转换为Bitmap比较耗时,使用RenderScript从50ms降到3、4ms(和像素值、手机相关,但比例差不多)
RenderScript耗时的地方是内存Allocation的分配,RenderScript的初始化,所以要设置为属性 缓存起来,不要重复创建。只要预览的宽高不变是不用变Allocation的大小的。
简单讲就是 Android系统为 解决高计算作业问题 采用了基于 C语言风格的RenderScript。具体:系统利用GPU、CPU多核来处理并发调度,使程序员只专注于算法。系统自己提供了几个常用的RenderScript,高斯模糊、直方图、yuv格式转换。
具体怎么看官网,不累述了,直接上代码
参考博客: Camera使用RenderScript
private void initRenderScript() {
renderScript = RenderScript.create(this);
yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(renderScript, Element.U8_4(renderScript));
script = new ScriptC_rotator(renderScript);
}
public Bitmap YUV_toRGB(byte[] yuvByteArray, int width, int height) {
Allocation in = getYuvAllocationIn(yuvByteArray);
Allocation out = getAllocationOut(width, height);
in.copyFrom(yuvByteArray);
yuvToRgbIntrinsic.setInput(in);
yuvToRgbIntrinsic.forEach(out);
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
out.copyTo(bmp);
bmp = rotate(bmp);
return bmp;
}
private Allocation getYuvAllocationIn(byte[] yuvByteArray) {
if (mInAllocation == null || yuvByteArray.length != preYuvInLength) {
preYuvInLength = yuvByteArray.length;
Type.Builder yuvType = new Type.Builder(renderScript, Element.U8(renderScript)).setX(yuvByteArray.length);
mInAllocation = Allocation.createTyped(renderScript, yuvType.create(), Allocation.USAGE_SCRIPT);
}
return mInAllocation;
}
private Allocation getAllocationOut(int W, int H) {
if (preW != W || preH != H) {
preW = W;
preH = H;
Type.Builder rgbaType = new Type.Builder(renderScript, Element.RGBA_8888(renderScript)).setX(W).setY(H);
outAllocation = Allocation.createTyped(renderScript, rgbaType.create(), Allocation.USAGE_SCRIPT);
}
return outAllocation;
}
public Bitmap rotate(Bitmap bitmap) {
Bitmap.Config config = bitmap.getConfig();
int targetHeight = bitmap.getWidth();
int targetWidth = bitmap.getHeight();
script.set_inWidth(bitmap.getWidth());
script.set_inHeight(bitmap.getHeight());
Allocation sourceAllocation = getFromRotateAllocation(bitmap);
sourceAllocation.copyFrom(bitmap);
script.set_inImage(sourceAllocation);
bitmap.recycle();
Bitmap target = Bitmap.createBitmap(targetWidth, targetHeight, config);
final Allocation targetAllocation = getToRotateAllocation(target);
script.forEach_rotate_270_clockwise(targetAllocation, targetAllocation);
targetAllocation.copyTo(target);
return target;
}
private Allocation getFromRotateAllocation(Bitmap bitmap) {
int targetHeight = bitmap.getWidth();
int targetWidth = bitmap.getHeight();
if (targetHeight != preRotateHeight || targetWidth != preRotateWidth) {
preRotateHeight = targetHeight;
preRotateWidth = targetWidth;
fromRotateAllocation = Allocation.createFromBitmap(renderScript, bitmap,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
}
return fromRotateAllocation;
}
private Allocation getToRotateAllocation(Bitmap bitmap) {
int targetHeight = bitmap.getWidth();
int targetWidth = bitmap.getHeight();
if (targetHeight != preRotateHeight || targetWidth != preRotateWidth) {
toRotateAllocation = Allocation.createFromBitmap(renderScript, bitmap,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
}
return toRotateAllocation;
}
Preview拿的是物理设备方向,所以用时需要旋转,旋转的RenderScript是自己写的。
main/rs/your.package.name(包名)/rotate.rs
#pragma version(1)
#pragma rs java_package_name(your.package.name)
rs_allocation inImage;
int inWidth;
int inHeight;
uchar4 __attribute__ ((kernel)) rotate_90_clockwise (uchar4 in, uint32_t x, uint32_t y) {
uint32_t inX = inWidth - 1 - y;
uint32_t inY = x;
const uchar4 *out = rsGetElementAt(inImage, inX, inY);
return *out;
}
uchar4 __attribute__ ((kernel)) rotate_270_clockwise (uchar4 in, uint32_t x, uint32_t y) {
uint32_t inX = y;
uint32_t inY = inHeight - 1 - x;
const uchar4 *out = rsGetElementAt(inImage, inX, inY);
return *out;
}
#pragma rs java_package_name(your.package.name) 这一行里的your.package.name需要替换你放置rotate.rs的地方
build后系统自动生成ScriptC_rotator 类,上文的script就是ScriptC_rotator的对象