在Android中使用Camera避免不了操作YUV,但是我们经常会遇到在不同手机上Camera出来的尺寸不是我们想要的,这个时候就需要裁剪了,最优办法当然是从源头YUV进行处理,一是YUV相对来说数据量较小,二是在源头上处理了,可以为后续流程节省CPU算力开销。如果你们在整个工程中用的是Texture,我也有解决方案,稍后放出来。
源码地址与Demo:https://github.com/bookzhan/bzyuvlib
首先我们要知道Android 中常见的YUV数据结构:
YV12:YYYYYYYY VVUU
NV21:YYYYYYYY UVUV
YUV420:YYYYYYYY UUVV
我们裁剪的原理就是通过一个矩形区域startX,startY,disWidth,disHeight来操作指针,copy出这个矩形区域的数据,话不多说直接上代码:
extern "C"
JNIEXPORT jint JNICALL
Java_com_luoye_bzyuvlib_BZYUVUtil_cropNV21(JNIEnv *env, jclass type, jbyteArray src_,
jbyteArray dis_, jint srcWidth, jint srcHeight,
jint startX, jint startY, jint disWidth,
jint disHeight) {
if (NULL == src_ || NULL == dis_ || startX < 0 || startY < 0 ||
startX + disWidth > srcWidth || startY + disHeight > srcHeight) {
BZLogUtil::logE(
"cropNV21 param is error NULL == src_ || NULL == dis_ || startX < 0 || startY < 0 ||startX + disWidth > srcWidth || startY + disHeight > srcHeight");
return -1;
}
jsize srcLength = env->GetArrayLength(src_);
if (srcLength < srcWidth * srcHeight * 3 / 2) {
BZLogUtil::logE("srcLength < srcWidth * srcHeight * 3 / 2");
return -1;
}
jsize disLength = env->GetArrayLength(dis_);
if (disLength < disWidth * disHeight * 3 / 2) {
BZLogUtil::logE("disLength < disWidth * disHeight * 3 / 2");
return -1;
}
jbyte *src = env->GetByteArrayElements(src_, NULL);
jbyte *dis = env->GetByteArrayElements(dis_, NULL);
if (nullptr == src || nullptr == dis) {
BZLogUtil::logE("nullptr == src || nullptr == dis");
return -1;
}
//Align
startY = startY / 2 * 2;
startX = startX / 2 * 2;
disWidth = disWidth / 2 * 2;
disHeight = disHeight / 2 * 2;
//The x and y values are reversed
// startY = srcHeight - disHeight - startY;
// startX = srcWidth - disWidth - startX;
signed char *srcStartP = src + srcWidth * startY;
signed char *disStartP = dis;
//copyY
for (int i = 0; i < disHeight; ++i) {
memcpy(disStartP, srcStartP + startX, (size_t) disWidth);
disStartP += disWidth;
srcStartP += srcWidth;
}
//This is where the UV starts
srcStartP = src + srcWidth * srcHeight + srcWidth * startY / 2;
//copyUV
for (int i = 0; i < disHeight / 2; ++i) {
memcpy(disStartP, srcStartP + startX, (size_t) disWidth);
disStartP += disWidth;
srcStartP += srcWidth;
}
env->ReleaseByteArrayElements(src_, src, 0);
env->ReleaseByteArrayElements(dis_, dis, 0);
return 0;
}
YU12与YUV420(I420)只是UV顺序不同这里不影响我们裁剪,可以合并成一个函数
extern "C"
JNIEXPORT jint JNICALL
Java_com_luoye_bzyuvlib_BZYUVUtil_cropYUV420(JNIEnv *env, jclass type, jbyteArray src_,
jbyteArray dis_, jint srcWidth, jint srcHeight,
jint startX, jint startY, jint disWidth,
jint disHeight) {
if (NULL == src_ || NULL == dis_ || startX < 0 || startY < 0 ||
startX + disWidth > srcWidth || startY + disHeight > srcHeight) {
BZLogUtil::logE(
"cropYV12 param is error NULL == src_ || NULL == dis_ || startX < 0 || startY < 0 ||startX + disWidth > srcWidth || startY + disHeight > srcHeight");
return -1;
}
jsize srcLength = env->GetArrayLength(src_);
if (srcLength < srcWidth * srcHeight * 3 / 2) {
BZLogUtil::logE("srcLength < srcWidth * srcHeight * 3 / 2");
return -1;
}
jsize disLength = env->GetArrayLength(dis_);
if (disLength < disWidth * disHeight * 3 / 2) {
BZLogUtil::logE("disLength < disWidth * disHeight * 3 / 2");
return -1;
}
jbyte *src = env->GetByteArrayElements(src_, NULL);
jbyte *dis = env->GetByteArrayElements(dis_, NULL);
if (nullptr == src || nullptr == dis) {
BZLogUtil::logE("nullptr == src || nullptr == dis");
return -1;
}
//Align
startY = startY / 2 * 2;
startX = startX / 2 * 2;
disWidth = disWidth / 2 * 2;
disHeight = disHeight / 2 * 2;
//The x and y values are reversed
// startY = srcHeight - disHeight - startY;
// startX = srcWidth - disWidth - startX;
signed char *srcStartP = src + srcWidth * startY;
signed char *disStartP = dis;
//copyY
for (int i = 0; i < disHeight; ++i) {
memcpy(disStartP, srcStartP + startX, (size_t) disWidth);
disStartP += disWidth;
srcStartP += srcWidth;
}
//copy Y or V
srcStartP = src + srcWidth * srcHeight + srcWidth * startY / 4;
for (int i = 0; i < disHeight / 2; ++i) {
memcpy(disStartP, srcStartP + startX / 2, (size_t) disWidth);
disStartP += disWidth / 2;
srcStartP += srcWidth / 2;
}
//copy Y or V
srcStartP = src + srcWidth * srcHeight * 5 / 4 + srcWidth * startY / 4;
for (int i = 0; i < disHeight / 2; ++i) {
memcpy(disStartP, srcStartP + startX / 2, (size_t) disWidth);
disStartP += disWidth / 2;
srcStartP += srcWidth / 2;
}
env->ReleaseByteArrayElements(src_, src, 0);
env->ReleaseByteArrayElements(dis_, dis, 0);
return 0;
}