diff options
author | Xin Li <delphij@google.com> | 2020-08-31 21:21:38 -0700 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2020-08-31 21:21:38 -0700 |
commit | 628590d7ec80e10a3fc24b1c18a1afb55cca10a8 (patch) | |
tree | 4b1c3f52d86d7fb53afbe9e9438468588fa489f8 /libs/hwui/jni/YuvToJpegEncoder.cpp | |
parent | b11b8ec3aec8bb42f2c07e1c5ac7942da293baa8 (diff) | |
parent | d2d3a20624d968199353ccf6ddbae6f3ac39c9af (diff) |
Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507
Merged-In: I3d92a6de21a938f6b352ec26dc23420c0fe02b27
Change-Id: Ifdb80563ef042738778ebb8a7581a97c4e3d96e2
Diffstat (limited to 'libs/hwui/jni/YuvToJpegEncoder.cpp')
-rw-r--r-- | libs/hwui/jni/YuvToJpegEncoder.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/libs/hwui/jni/YuvToJpegEncoder.cpp b/libs/hwui/jni/YuvToJpegEncoder.cpp new file mode 100644 index 000000000000..689cf0bea741 --- /dev/null +++ b/libs/hwui/jni/YuvToJpegEncoder.cpp @@ -0,0 +1,268 @@ +#include "CreateJavaOutputStreamAdaptor.h" +#include "SkJPEGWriteUtility.h" +#include "YuvToJpegEncoder.h" +#include <ui/PixelFormat.h> +#include <hardware/hardware.h> + +#include "graphics_jni_helpers.h" + +YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) { + // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported + // for now. + if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { + return new Yuv420SpToJpegEncoder(strides); + } else if (format == HAL_PIXEL_FORMAT_YCbCr_422_I) { + return new Yuv422IToJpegEncoder(strides); + } else { + return NULL; + } +} + +YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) { +} + +struct ErrorMgr { + struct jpeg_error_mgr pub; + jmp_buf jmp; +}; + +void error_exit(j_common_ptr cinfo) { + ErrorMgr* err = (ErrorMgr*) cinfo->err; + (*cinfo->err->output_message) (cinfo); + longjmp(err->jmp, 1); +} + +bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, + int height, int* offsets, int jpegQuality) { + jpeg_compress_struct cinfo; + ErrorMgr err; + skjpeg_destination_mgr sk_wstream(stream); + + cinfo.err = jpeg_std_error(&err.pub); + err.pub.error_exit = error_exit; + + if (setjmp(err.jmp)) { + jpeg_destroy_compress(&cinfo); + return false; + } + jpeg_create_compress(&cinfo); + + cinfo.dest = &sk_wstream; + + setJpegCompressStruct(&cinfo, width, height, jpegQuality); + + jpeg_start_compress(&cinfo, TRUE); + + compress(&cinfo, (uint8_t*) inYuv, offsets); + + jpeg_finish_compress(&cinfo); + + jpeg_destroy_compress(&cinfo); + + return true; +} + +void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo, + int width, int height, int quality) { + cinfo->image_width = width; + cinfo->image_height = height; + cinfo->input_components = 3; + cinfo->in_color_space = JCS_YCbCr; + jpeg_set_defaults(cinfo); + + jpeg_set_quality(cinfo, quality, TRUE); + jpeg_set_colorspace(cinfo, JCS_YCbCr); + cinfo->raw_data_in = TRUE; + cinfo->dct_method = JDCT_IFAST; + configSamplingFactors(cinfo); +} + +/////////////////////////////////////////////////////////////////// +Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) : + YuvToJpegEncoder(strides) { + fNumPlanes = 2; +} + +void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, + uint8_t* yuv, int* offsets) { + SkDebugf("onFlyCompress"); + JSAMPROW y[16]; + JSAMPROW cb[8]; + JSAMPROW cr[8]; + JSAMPARRAY planes[3]; + planes[0] = y; + planes[1] = cb; + planes[2] = cr; + + int width = cinfo->image_width; + int height = cinfo->image_height; + uint8_t* yPlanar = yuv + offsets[0]; + uint8_t* vuPlanar = yuv + offsets[1]; //width * height; + uint8_t* uRows = new uint8_t [8 * (width >> 1)]; + uint8_t* vRows = new uint8_t [8 * (width >> 1)]; + + + // process 16 lines of Y and 8 lines of U/V each time. + while (cinfo->next_scanline < cinfo->image_height) { + //deitnerleave u and v + deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width, height); + + // Jpeg library ignores the rows whose indices are greater than height. + for (int i = 0; i < 16; i++) { + // y row + y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0]; + + // construct u row and v row + if ((i & 1) == 0) { + // height and width are both halved because of downsampling + int offset = (i >> 1) * (width >> 1); + cb[i/2] = uRows + offset; + cr[i/2] = vRows + offset; + } + } + jpeg_write_raw_data(cinfo, planes, 16); + } + delete [] uRows; + delete [] vRows; + +} + +void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows, + uint8_t* vRows, int rowIndex, int width, int height) { + int numRows = (height - rowIndex) / 2; + if (numRows > 8) numRows = 8; + for (int row = 0; row < numRows; ++row) { + int offset = ((rowIndex >> 1) + row) * fStrides[1]; + uint8_t* vu = vuPlanar + offset; + for (int i = 0; i < (width >> 1); ++i) { + int index = row * (width >> 1) + i; + uRows[index] = vu[1]; + vRows[index] = vu[0]; + vu += 2; + } + } +} + +void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { + // cb and cr are horizontally downsampled and vertically downsampled as well. + cinfo->comp_info[0].h_samp_factor = 2; + cinfo->comp_info[0].v_samp_factor = 2; + cinfo->comp_info[1].h_samp_factor = 1; + cinfo->comp_info[1].v_samp_factor = 1; + cinfo->comp_info[2].h_samp_factor = 1; + cinfo->comp_info[2].v_samp_factor = 1; +} + +/////////////////////////////////////////////////////////////////////////////// +Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) : + YuvToJpegEncoder(strides) { + fNumPlanes = 1; +} + +void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo, + uint8_t* yuv, int* offsets) { + SkDebugf("onFlyCompress_422"); + JSAMPROW y[16]; + JSAMPROW cb[16]; + JSAMPROW cr[16]; + JSAMPARRAY planes[3]; + planes[0] = y; + planes[1] = cb; + planes[2] = cr; + + int width = cinfo->image_width; + int height = cinfo->image_height; + uint8_t* yRows = new uint8_t [16 * width]; + uint8_t* uRows = new uint8_t [16 * (width >> 1)]; + uint8_t* vRows = new uint8_t [16 * (width >> 1)]; + + uint8_t* yuvOffset = yuv + offsets[0]; + + // process 16 lines of Y and 16 lines of U/V each time. + while (cinfo->next_scanline < cinfo->image_height) { + deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height); + + // Jpeg library ignores the rows whose indices are greater than height. + for (int i = 0; i < 16; i++) { + // y row + y[i] = yRows + i * width; + + // construct u row and v row + // width is halved because of downsampling + int offset = i * (width >> 1); + cb[i] = uRows + offset; + cr[i] = vRows + offset; + } + + jpeg_write_raw_data(cinfo, planes, 16); + } + delete [] yRows; + delete [] uRows; + delete [] vRows; +} + + +void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows, + uint8_t* vRows, int rowIndex, int width, int height) { + int numRows = height - rowIndex; + if (numRows > 16) numRows = 16; + for (int row = 0; row < numRows; ++row) { + uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0]; + for (int i = 0; i < (width >> 1); ++i) { + int indexY = row * width + (i << 1); + int indexU = row * (width >> 1) + i; + yRows[indexY] = yuvSeg[0]; + yRows[indexY + 1] = yuvSeg[2]; + uRows[indexU] = yuvSeg[1]; + vRows[indexU] = yuvSeg[3]; + yuvSeg += 4; + } + } +} + +void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { + // cb and cr are horizontally downsampled and vertically downsampled as well. + cinfo->comp_info[0].h_samp_factor = 2; + cinfo->comp_info[0].v_samp_factor = 2; + cinfo->comp_info[1].h_samp_factor = 1; + cinfo->comp_info[1].v_samp_factor = 2; + cinfo->comp_info[2].h_samp_factor = 1; + cinfo->comp_info[2].v_samp_factor = 2; +} +/////////////////////////////////////////////////////////////////////////////// + +static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv, + jint format, jint width, jint height, jintArray offsets, + jintArray strides, jint jpegQuality, jobject jstream, + jbyteArray jstorage) { + jbyte* yuv = env->GetByteArrayElements(inYuv, NULL); + SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); + + jint* imgOffsets = env->GetIntArrayElements(offsets, NULL); + jint* imgStrides = env->GetIntArrayElements(strides, NULL); + YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides); + jboolean result = JNI_FALSE; + if (encoder != NULL) { + encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality); + delete encoder; + result = JNI_TRUE; + } + + env->ReleaseByteArrayElements(inYuv, yuv, 0); + env->ReleaseIntArrayElements(offsets, imgOffsets, 0); + env->ReleaseIntArrayElements(strides, imgStrides, 0); + delete strm; + return result; +} +/////////////////////////////////////////////////////////////////////////////// + +static const JNINativeMethod gYuvImageMethods[] = { + { "nativeCompressToJpeg", "([BIII[I[IILjava/io/OutputStream;[B)Z", + (void*)YuvImage_compressToJpeg } +}; + +int register_android_graphics_YuvImage(JNIEnv* env) +{ + return android::RegisterMethodsOrDie(env, "android/graphics/YuvImage", gYuvImageMethods, + NELEM(gYuvImageMethods)); +} |