1#include "CreateJavaOutputStreamAdaptor.h" 2#include "SkJPEGWriteUtility.h" 3#include "YuvToJpegEncoder.h" 4#include <ui/PixelFormat.h> 5#include <hardware/hardware.h> 6 7#include "core_jni_helpers.h" 8 9#include <jni.h> 10 11YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) { 12 // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported 13 // for now. 14 if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) { 15 return new Yuv420SpToJpegEncoder(strides); 16 } else if (format == HAL_PIXEL_FORMAT_YCbCr_422_I) { 17 return new Yuv422IToJpegEncoder(strides); 18 } else { 19 return NULL; 20 } 21} 22 23YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) { 24} 25 26struct ErrorMgr { 27 struct jpeg_error_mgr pub; 28 jmp_buf jmp; 29}; 30 31void error_exit(j_common_ptr cinfo) { 32 ErrorMgr* err = (ErrorMgr*) cinfo->err; 33 (*cinfo->err->output_message) (cinfo); 34 longjmp(err->jmp, 1); 35} 36 37bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, 38 int height, int* offsets, int jpegQuality) { 39 jpeg_compress_struct cinfo; 40 ErrorMgr err; 41 skjpeg_destination_mgr sk_wstream(stream); 42 43 cinfo.err = jpeg_std_error(&err.pub); 44 err.pub.error_exit = error_exit; 45 46 if (setjmp(err.jmp)) { 47 jpeg_destroy_compress(&cinfo); 48 return false; 49 } 50 jpeg_create_compress(&cinfo); 51 52 cinfo.dest = &sk_wstream; 53 54 setJpegCompressStruct(&cinfo, width, height, jpegQuality); 55 56 jpeg_start_compress(&cinfo, TRUE); 57 58 compress(&cinfo, (uint8_t*) inYuv, offsets); 59 60 jpeg_finish_compress(&cinfo); 61 62 jpeg_destroy_compress(&cinfo); 63 64 return true; 65} 66 67void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo, 68 int width, int height, int quality) { 69 cinfo->image_width = width; 70 cinfo->image_height = height; 71 cinfo->input_components = 3; 72 cinfo->in_color_space = JCS_YCbCr; 73 jpeg_set_defaults(cinfo); 74 75 jpeg_set_quality(cinfo, quality, TRUE); 76 jpeg_set_colorspace(cinfo, JCS_YCbCr); 77 cinfo->raw_data_in = TRUE; 78 cinfo->dct_method = JDCT_IFAST; 79 configSamplingFactors(cinfo); 80} 81 82/////////////////////////////////////////////////////////////////// 83Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) : 84 YuvToJpegEncoder(strides) { 85 fNumPlanes = 2; 86} 87 88void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo, 89 uint8_t* yuv, int* offsets) { 90 SkDebugf("onFlyCompress"); 91 JSAMPROW y[16]; 92 JSAMPROW cb[8]; 93 JSAMPROW cr[8]; 94 JSAMPARRAY planes[3]; 95 planes[0] = y; 96 planes[1] = cb; 97 planes[2] = cr; 98 99 int width = cinfo->image_width; 100 int height = cinfo->image_height; 101 uint8_t* yPlanar = yuv + offsets[0]; 102 uint8_t* vuPlanar = yuv + offsets[1]; //width * height; 103 uint8_t* uRows = new uint8_t [8 * (width >> 1)]; 104 uint8_t* vRows = new uint8_t [8 * (width >> 1)]; 105 106 107 // process 16 lines of Y and 8 lines of U/V each time. 108 while (cinfo->next_scanline < cinfo->image_height) { 109 //deitnerleave u and v 110 deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width, height); 111 112 // Jpeg library ignores the rows whose indices are greater than height. 113 for (int i = 0; i < 16; i++) { 114 // y row 115 y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0]; 116 117 // construct u row and v row 118 if ((i & 1) == 0) { 119 // height and width are both halved because of downsampling 120 int offset = (i >> 1) * (width >> 1); 121 cb[i/2] = uRows + offset; 122 cr[i/2] = vRows + offset; 123 } 124 } 125 jpeg_write_raw_data(cinfo, planes, 16); 126 } 127 delete [] uRows; 128 delete [] vRows; 129 130} 131 132void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows, 133 uint8_t* vRows, int rowIndex, int width, int height) { 134 int numRows = (height - rowIndex) / 2; 135 if (numRows > 8) numRows = 8; 136 for (int row = 0; row < numRows; ++row) { 137 int offset = ((rowIndex >> 1) + row) * fStrides[1]; 138 uint8_t* vu = vuPlanar + offset; 139 for (int i = 0; i < (width >> 1); ++i) { 140 int index = row * (width >> 1) + i; 141 uRows[index] = vu[1]; 142 vRows[index] = vu[0]; 143 vu += 2; 144 } 145 } 146} 147 148void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { 149 // cb and cr are horizontally downsampled and vertically downsampled as well. 150 cinfo->comp_info[0].h_samp_factor = 2; 151 cinfo->comp_info[0].v_samp_factor = 2; 152 cinfo->comp_info[1].h_samp_factor = 1; 153 cinfo->comp_info[1].v_samp_factor = 1; 154 cinfo->comp_info[2].h_samp_factor = 1; 155 cinfo->comp_info[2].v_samp_factor = 1; 156} 157 158/////////////////////////////////////////////////////////////////////////////// 159Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) : 160 YuvToJpegEncoder(strides) { 161 fNumPlanes = 1; 162} 163 164void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo, 165 uint8_t* yuv, int* offsets) { 166 SkDebugf("onFlyCompress_422"); 167 JSAMPROW y[16]; 168 JSAMPROW cb[16]; 169 JSAMPROW cr[16]; 170 JSAMPARRAY planes[3]; 171 planes[0] = y; 172 planes[1] = cb; 173 planes[2] = cr; 174 175 int width = cinfo->image_width; 176 int height = cinfo->image_height; 177 uint8_t* yRows = new uint8_t [16 * width]; 178 uint8_t* uRows = new uint8_t [16 * (width >> 1)]; 179 uint8_t* vRows = new uint8_t [16 * (width >> 1)]; 180 181 uint8_t* yuvOffset = yuv + offsets[0]; 182 183 // process 16 lines of Y and 16 lines of U/V each time. 184 while (cinfo->next_scanline < cinfo->image_height) { 185 deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height); 186 187 // Jpeg library ignores the rows whose indices are greater than height. 188 for (int i = 0; i < 16; i++) { 189 // y row 190 y[i] = yRows + i * width; 191 192 // construct u row and v row 193 // width is halved because of downsampling 194 int offset = i * (width >> 1); 195 cb[i] = uRows + offset; 196 cr[i] = vRows + offset; 197 } 198 199 jpeg_write_raw_data(cinfo, planes, 16); 200 } 201 delete [] yRows; 202 delete [] uRows; 203 delete [] vRows; 204} 205 206 207void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows, 208 uint8_t* vRows, int rowIndex, int width, int height) { 209 int numRows = height - rowIndex; 210 if (numRows > 16) numRows = 16; 211 for (int row = 0; row < numRows; ++row) { 212 uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0]; 213 for (int i = 0; i < (width >> 1); ++i) { 214 int indexY = row * width + (i << 1); 215 int indexU = row * (width >> 1) + i; 216 yRows[indexY] = yuvSeg[0]; 217 yRows[indexY + 1] = yuvSeg[2]; 218 uRows[indexU] = yuvSeg[1]; 219 vRows[indexU] = yuvSeg[3]; 220 yuvSeg += 4; 221 } 222 } 223} 224 225void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) { 226 // cb and cr are horizontally downsampled and vertically downsampled as well. 227 cinfo->comp_info[0].h_samp_factor = 2; 228 cinfo->comp_info[0].v_samp_factor = 2; 229 cinfo->comp_info[1].h_samp_factor = 1; 230 cinfo->comp_info[1].v_samp_factor = 2; 231 cinfo->comp_info[2].h_samp_factor = 1; 232 cinfo->comp_info[2].v_samp_factor = 2; 233} 234/////////////////////////////////////////////////////////////////////////////// 235 236static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv, 237 jint format, jint width, jint height, jintArray offsets, 238 jintArray strides, jint jpegQuality, jobject jstream, 239 jbyteArray jstorage) { 240 jbyte* yuv = env->GetByteArrayElements(inYuv, NULL); 241 SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); 242 243 jint* imgOffsets = env->GetIntArrayElements(offsets, NULL); 244 jint* imgStrides = env->GetIntArrayElements(strides, NULL); 245 YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides); 246 jboolean result = JNI_FALSE; 247 if (encoder != NULL) { 248 encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality); 249 delete encoder; 250 result = JNI_TRUE; 251 } 252 253 env->ReleaseByteArrayElements(inYuv, yuv, 0); 254 env->ReleaseIntArrayElements(offsets, imgOffsets, 0); 255 env->ReleaseIntArrayElements(strides, imgStrides, 0); 256 delete strm; 257 return result; 258} 259/////////////////////////////////////////////////////////////////////////////// 260 261static const JNINativeMethod gYuvImageMethods[] = { 262 { "nativeCompressToJpeg", "([BIII[I[IILjava/io/OutputStream;[B)Z", 263 (void*)YuvImage_compressToJpeg } 264}; 265 266int register_android_graphics_YuvImage(JNIEnv* env) 267{ 268 return android::RegisterMethodsOrDie(env, "android/graphics/YuvImage", gYuvImageMethods, 269 NELEM(gYuvImageMethods)); 270} 271