使用RenderScript实现相机模糊切换效果
实现原理
步骤:
1.得到相机每一帧数据的回调
2.将YUV格式转换为ARGB格式
3.缩小图片
4.模糊图片
5.显示到视图中。
具体实现代码
renderscript相关代码
YUV转换为ARGB代码如下:
// Needed directive for RS to work
#pragma version(1)
// The java_package_name directive needs to use your Activity's package path
#pragma rs java_package_name(cn.wps.moffice.main.scan.util.camera)
rs_allocation inputAllocation;
int wIn, hIn;
int numTotalPixels;
// Function to invoke before applying conversion
void setInputImageSize(int _w, int _h)
{
wIn = _w;
hIn = _h;
numTotalPixels = wIn * hIn;
}
// Kernel that converts a YUV element to a RGBA one
uchar4 __attribute__((kernel)) convert(uint32_t x, uint32_t y)
{
// YUV 4:2:0 planar image, with 8 bit Y samples, followed by
// interleaved V/U plane with 8bit 2x2 subsampled chroma samples
int baseIdx = x + y * wIn;
int baseUYIndex = numTotalPixels + (y >> 1) * wIn + (x & 0xfffffe);
uchar _y = rsGetElementAt_uchar(inputAllocation, baseIdx);
uchar _v = rsGetElementAt_uchar(inputAllocation, baseUYIndex);
uchar _u = rsGetElementAt_uchar(inputAllocation, baseUYIndex + 1);
_y = _y < 16 ? 16 : _y;
short Y = ((short)_y) - 16;
short U = ((short)_u) - 128;
short V = ((short)_v) - 128;
uchar4 out;
out.r = (uchar) clamp(((Y * 298 + V * 409 + 128) >> 8), 0, 255);
out.g = (uchar) clamp(((Y * 298 - U * 100 - V * 208 + 128) >> 8), 0, 255);
out.b = (uchar) clamp(((Y * 298 + U * 516 + 128) >> 8), 0, 255);
out.a = 255;
return out;
}
缩小图片代码如下:
// Needed directive for RS to work
#pragma version(1)
// Change java_package_name directive to match your Activity's package path
#pragma rs java_package_name(cn.wps.moffice.main.scan.util.camera)
rs_allocation inputAllocation;
// Sets image sizes and calculates the scale factor
static float scaleInv;
static int inputWidth, inputHeight, outputWidth, outputHeight;
void setInformation(int _inputWidth, int _inputHeight,
int _outputWidth, int _outputHeight){
inputWidth = _inputWidth;
inputHeight = _inputHeight;
outputWidth = _outputWidth;
outputHeight = _outputHeight;
// Calculates inverse scale factor, by which
// to round coordinates.
//
// Ex:
// Input size is 100
// Output desired size is 25
//
// Scale factor is 25 / 100 = 0.25
// Inverse scale factor is 1 / 0.25 = 4
//
// When iterating directly on the output
// allocation, to get input element it is needed
// to use the inverse scale factor.
//
// Current output element index is 20
// Respective input element index is 20 * 4 = 80
//
scaleInv = (float)inputWidth/(float)outputWidth;
}
uchar4 __attribute__((kernel)) resizeNearest(uint32_t x, uint32_t y) {
float fX = clamp(x*scaleInv, 0.0f, (float)inputWidth);
float fY = clamp(y*scaleInv, 0.0f, (float)inputHeight);
return rsGetElementAt_uchar4(inputAllocation, fX, fY);
}
Java层相关代码
获取相机每一帧的回调
private void prepareCamera() {
float perPixel = ImageFormat.getBitsPerPixel(mParameters.getPreviewFormat()) / 8f;
int previewBufferSize = (int) (mParameters.getPreviewSize().width * mParameters.getPreviewSize().height * perPixel);
mCameraDevice.addCallbackBuffer(new byte[previewBufferSize]);
mCameraDevice.addCallbackBuffer(new byte[previewBufferSize]);
mCameraDevice.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (isScreenShot) {
isScreenShot = false;
screenShot(data);
}
camera.addCallbackBuffer(data);
}
});
}
private void screenShot(final byte[] pixel) {
if(!Utils.hasKitKat()) return;
GlobalThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
final Bitmap bitmap = BitmapTrans.rotateBitmap(imgProcess.processImg(pixel, mParameters.getPreviewSize().width, mParameters.getPreviewSize().height), 90);
mHandler.post(new Runnable() {
@Override
public void run() {
iv_capture_view.setImageBitmap(bitmap);
iv_capture_view.setVisibility(View.VISIBLE);
AlphaAnimation inAnimation = new AlphaAnimation(0f, 1.0f);
inAnimation.setDuration(250);
iv_capture_view.startAnimation(inAnimation);
}
});
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
iv_capture_view.setVisibility(View.INVISIBLE);
AlphaAnimation outAnimation = new AlphaAnimation(1f, 0f);
outAnimation.setDuration(250);
iv_capture_view.startAnimation(outAnimation);
tv_camera_tip.setVisibility(View.VISIBLE);
AlphaAnimation inAnimation = new AlphaAnimation(0f, 1.0f);
inAnimation.setDuration(250);
tv_camera_tip.startAnimation(inAnimation);
}
}, 600);
}
});
}
执行Renderscript的Java工具类
package cn.wps.moffice.main.scan.util.img;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.ScriptIntrinsicBlur;
import android.renderscript.Type;
import cn.wps.moffice.main.scan.util.GlobalThreadPool;
import cn.wps.moffice.main.scan.util.camera.ScriptC_customYUVToRGBAConverter;
import cn.wps.moffice.main.scan.util.camera.ScriptC_resize_img;
import cn.wps.moffice.util.KSLog2;
/**
* Created by CW80JD2 on 2017/8/10.
*/
public class RSImgProcess {
private RenderScript mRS;
final float per = 0.2f;
private int outWidth, outHeight;
private ScriptC_customYUVToRGBAConverter customYUVToRGBAScript;
private ScriptC_resize_img resizeScript;
private ScriptIntrinsicBlur blurScript;
private Object lock = new Object();
private boolean isInit = false;
public RSImgProcess(final Context context) {
if(!Utils.hasKitKat()) return;
GlobalThreadPool.getInstance().execute(new Runnable() {
@Override
public void run() {
synchronized (lock) {
init(context);
isInit = true;
lock.notifyAll();
}
}
});
}
private void init(Context context) {
// Creates a RS context.
mRS = RenderScript.create(context);
customYUVToRGBAScript = new ScriptC_customYUVToRGBAConverter(mRS);
// Instantiates the blur script
blurScript = ScriptIntrinsicBlur.create(mRS, Element.RGBA_8888(mRS));
resizeScript = new ScriptC_resize_img(mRS);
}
private Allocation blur(Allocation inputAllocation, int width, int height) {
// Creates an output Allocation where to store the blur result
Type.Builder tbOutput = new Type.Builder(mRS, Element.RGBA_8888(mRS));
tbOutput.setX(width);
tbOutput.setY(height);
Allocation outputAllocation = Allocation.createTyped(mRS, tbOutput.create());
// Sets the blur radius (pixels)
blurScript.setRadius(25);
// Compute the blurred allocation
blurScript.setInput(inputAllocation);
blurScript.forEach(outputAllocation);
return outputAllocation;
}
private Allocation resize(Allocation inputAllocation, int width, int height) {
outWidth = (int) (width * per + 0.5);
outHeight = (int) (height * per + 0.5);
// Creates an output Allocation where to store the blur result
Type.Builder tbOutput = new Type.Builder(mRS, Element.RGBA_8888(mRS));
tbOutput.setX(outWidth);
tbOutput.setY(outHeight);
Allocation outputAllocation = Allocation.createTyped(mRS, tbOutput.create());
resizeScript.set_inputAllocation(inputAllocation);
resizeScript.invoke_setInformation(width, height, outWidth, outHeight);
resizeScript.forEach_resizeNearest(outputAllocation);
return outputAllocation;
}
private Allocation YUVToRGBA(byte[] dataIn, int width, int height) {
// Calculates expected YUV bytes count as YUV is not a human friendly way of storing data:
// https://en.wikipedia.org/wiki/YUV#Y.27UV420p_.28and_Y.27V12_or_YV12.29_to_RGB888_conversion
int expectedBytes = (int) (width * height * ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8f);
Type.Builder yuvTypeBuilder = new Type.Builder(mRS, Element.U8(mRS)).setX(expectedBytes);
Type yuvType = yuvTypeBuilder.create();
Allocation inputAllocation = Allocation.createTyped(mRS, yuvType, Allocation.USAGE_SCRIPT);
inputAllocation.copyFrom(dataIn);
customYUVToRGBAScript.invoke_setInputImageSize(width, height);
customYUVToRGBAScript.set_inputAllocation(inputAllocation);
// Creates temporary allocation that will match camera preview size
Type.Builder rgbaType = new Type.Builder(mRS, Element.RGBA_8888(mRS)).setX(width).setY(height);
Allocation midAllocation = Allocation.createTyped(mRS, rgbaType.create(), Allocation.USAGE_SCRIPT);
customYUVToRGBAScript.forEach_convert(midAllocation);
return midAllocation;
}
public Bitmap processImg(byte[] dataIn, int width, int height) {
if(!Utils.hasKitKat()) return null;
synchronized (lock) {
while (!isInit) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
long startTime = System.currentTimeMillis();
Allocation allocation = YUVToRGBA(dataIn, width, height);
Allocation resizeAllocation = resize(allocation, width, height);
Allocation outputAllocation = blur(resizeAllocation, outWidth, outHeight);
// Creates output Bitmap, matching input one size.
Bitmap outBitmap = Bitmap.createBitmap(outWidth, outHeight, Bitmap.Config.ARGB_8888);
// Copy calculation result to the output Bitmap.
outputAllocation.copyTo(outBitmap);
KSLog2.KSLog().d("processImg = " + (System.currentTimeMillis() - startTime));
return outBitmap;
}
}
效果
总结
使用Renderscript有什么优点?
1.通过GPU来计算从而减少CPU的使用来达到更高的用户响应(应为使主线程使用CPU来刷新界面)
2.计算速度更加快(GPU由于具有很多处理单元,能够更好地做并行计算)