blob: 1c5f126d86724cf25f34f7895b69930ea120f9c4 [file] [log] [blame]
Wei-Ta Chenbca2d612009-11-30 17:52:05 +08001#include "CreateJavaOutputStreamAdaptor.h"
Matt Sarett79ad7be2016-03-25 14:28:40 -04002#include "SkJPEGWriteUtility.h"
Kevin Lubick1175dc02022-02-28 12:41:27 -05003#include "SkStream.h"
4#include "SkTypes.h"
Wei-Ta Chenbca2d612009-11-30 17:52:05 +08005#include "YuvToJpegEncoder.h"
Mathias Agopian8f2423e2010-02-16 17:33:37 -08006#include <ui/PixelFormat.h>
7#include <hardware/hardware.h>
Wei-Ta Chenbca2d612009-11-30 17:52:05 +08008
Derek Sollenbergerc5882c42019-10-25 11:11:32 -04009#include "graphics_jni_helpers.h"
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080010
Kevin Lubickba253602022-08-09 21:13:39 +000011#include <csetjmp>
12
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080013YuvToJpegEncoder* YuvToJpegEncoder::create(int format, int* strides) {
Mathias Agopiana696f5d2010-02-17 17:53:09 -080014 // Only ImageFormat.NV21 and ImageFormat.YUY2 are supported
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080015 // for now.
Mathias Agopiana696f5d2010-02-17 17:53:09 -080016 if (format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080017 return new Yuv420SpToJpegEncoder(strides);
Mathias Agopian8f2423e2010-02-16 17:33:37 -080018 } else if (format == HAL_PIXEL_FORMAT_YCbCr_422_I) {
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080019 return new Yuv422IToJpegEncoder(strides);
20 } else {
21 return NULL;
22 }
23}
24
25YuvToJpegEncoder::YuvToJpegEncoder(int* strides) : fStrides(strides) {
26}
27
Leon Scroggins III1c3ded392018-02-28 13:23:32 -050028struct ErrorMgr {
29 struct jpeg_error_mgr pub;
30 jmp_buf jmp;
31};
32
33void error_exit(j_common_ptr cinfo) {
34 ErrorMgr* err = (ErrorMgr*) cinfo->err;
35 (*cinfo->err->output_message) (cinfo);
36 longjmp(err->jmp, 1);
37}
38
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080039bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
40 int height, int* offsets, int jpegQuality) {
41 jpeg_compress_struct cinfo;
Leon Scroggins III1c3ded392018-02-28 13:23:32 -050042 ErrorMgr err;
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080043 skjpeg_destination_mgr sk_wstream(stream);
44
Leon Scroggins III1c3ded392018-02-28 13:23:32 -050045 cinfo.err = jpeg_std_error(&err.pub);
46 err.pub.error_exit = error_exit;
47
48 if (setjmp(err.jmp)) {
49 jpeg_destroy_compress(&cinfo);
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080050 return false;
51 }
52 jpeg_create_compress(&cinfo);
53
54 cinfo.dest = &sk_wstream;
55
56 setJpegCompressStruct(&cinfo, width, height, jpegQuality);
57
58 jpeg_start_compress(&cinfo, TRUE);
59
60 compress(&cinfo, (uint8_t*) inYuv, offsets);
61
62 jpeg_finish_compress(&cinfo);
63
Leon Scroggins III1c3ded392018-02-28 13:23:32 -050064 jpeg_destroy_compress(&cinfo);
65
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080066 return true;
67}
68
69void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo,
70 int width, int height, int quality) {
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080071 cinfo->image_width = width;
72 cinfo->image_height = height;
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080073 cinfo->input_components = 3;
74 cinfo->in_color_space = JCS_YCbCr;
75 jpeg_set_defaults(cinfo);
Chia-chi Yehaa868592010-03-10 17:08:58 +080076
77 jpeg_set_quality(cinfo, quality, TRUE);
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080078 jpeg_set_colorspace(cinfo, JCS_YCbCr);
79 cinfo->raw_data_in = TRUE;
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080080 cinfo->dct_method = JDCT_IFAST;
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080081 configSamplingFactors(cinfo);
82}
83
84///////////////////////////////////////////////////////////////////
85Yuv420SpToJpegEncoder::Yuv420SpToJpegEncoder(int* strides) :
86 YuvToJpegEncoder(strides) {
87 fNumPlanes = 2;
88}
89
90void Yuv420SpToJpegEncoder::compress(jpeg_compress_struct* cinfo,
91 uint8_t* yuv, int* offsets) {
Sally Qi7e3f93b2021-07-15 00:00:54 +000092 ALOGD("onFlyCompress");
Wei-Ta Chenbca2d612009-11-30 17:52:05 +080093 JSAMPROW y[16];
94 JSAMPROW cb[8];
95 JSAMPROW cr[8];
96 JSAMPARRAY planes[3];
97 planes[0] = y;
98 planes[1] = cb;
99 planes[2] = cr;
100
101 int width = cinfo->image_width;
102 int height = cinfo->image_height;
103 uint8_t* yPlanar = yuv + offsets[0];
104 uint8_t* vuPlanar = yuv + offsets[1]; //width * height;
105 uint8_t* uRows = new uint8_t [8 * (width >> 1)];
106 uint8_t* vRows = new uint8_t [8 * (width >> 1)];
107
108
109 // process 16 lines of Y and 8 lines of U/V each time.
110 while (cinfo->next_scanline < cinfo->image_height) {
111 //deitnerleave u and v
Wu-cheng Li4b63f142012-10-16 23:40:03 +0800112 deinterleave(vuPlanar, uRows, vRows, cinfo->next_scanline, width, height);
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800113
Wu-cheng Li4b63f142012-10-16 23:40:03 +0800114 // Jpeg library ignores the rows whose indices are greater than height.
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800115 for (int i = 0; i < 16; i++) {
116 // y row
117 y[i] = yPlanar + (cinfo->next_scanline + i) * fStrides[0];
118
119 // construct u row and v row
120 if ((i & 1) == 0) {
121 // height and width are both halved because of downsampling
122 int offset = (i >> 1) * (width >> 1);
123 cb[i/2] = uRows + offset;
124 cr[i/2] = vRows + offset;
125 }
126 }
127 jpeg_write_raw_data(cinfo, planes, 16);
128 }
129 delete [] uRows;
130 delete [] vRows;
131
132}
133
134void Yuv420SpToJpegEncoder::deinterleave(uint8_t* vuPlanar, uint8_t* uRows,
Wu-cheng Li4b63f142012-10-16 23:40:03 +0800135 uint8_t* vRows, int rowIndex, int width, int height) {
136 int numRows = (height - rowIndex) / 2;
137 if (numRows > 8) numRows = 8;
138 for (int row = 0; row < numRows; ++row) {
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800139 int offset = ((rowIndex >> 1) + row) * fStrides[1];
140 uint8_t* vu = vuPlanar + offset;
141 for (int i = 0; i < (width >> 1); ++i) {
142 int index = row * (width >> 1) + i;
143 uRows[index] = vu[1];
144 vRows[index] = vu[0];
145 vu += 2;
146 }
147 }
148}
149
150void Yuv420SpToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) {
151 // cb and cr are horizontally downsampled and vertically downsampled as well.
152 cinfo->comp_info[0].h_samp_factor = 2;
153 cinfo->comp_info[0].v_samp_factor = 2;
154 cinfo->comp_info[1].h_samp_factor = 1;
155 cinfo->comp_info[1].v_samp_factor = 1;
156 cinfo->comp_info[2].h_samp_factor = 1;
157 cinfo->comp_info[2].v_samp_factor = 1;
158}
159
160///////////////////////////////////////////////////////////////////////////////
161Yuv422IToJpegEncoder::Yuv422IToJpegEncoder(int* strides) :
162 YuvToJpegEncoder(strides) {
163 fNumPlanes = 1;
164}
165
166void Yuv422IToJpegEncoder::compress(jpeg_compress_struct* cinfo,
167 uint8_t* yuv, int* offsets) {
Sally Qi7e3f93b2021-07-15 00:00:54 +0000168 ALOGD("onFlyCompress_422");
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800169 JSAMPROW y[16];
170 JSAMPROW cb[16];
171 JSAMPROW cr[16];
172 JSAMPARRAY planes[3];
173 planes[0] = y;
174 planes[1] = cb;
175 planes[2] = cr;
176
177 int width = cinfo->image_width;
178 int height = cinfo->image_height;
179 uint8_t* yRows = new uint8_t [16 * width];
180 uint8_t* uRows = new uint8_t [16 * (width >> 1)];
181 uint8_t* vRows = new uint8_t [16 * (width >> 1)];
182
183 uint8_t* yuvOffset = yuv + offsets[0];
184
185 // process 16 lines of Y and 16 lines of U/V each time.
186 while (cinfo->next_scanline < cinfo->image_height) {
187 deinterleave(yuvOffset, yRows, uRows, vRows, cinfo->next_scanline, width, height);
188
Wu-cheng Li4b63f142012-10-16 23:40:03 +0800189 // Jpeg library ignores the rows whose indices are greater than height.
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800190 for (int i = 0; i < 16; i++) {
191 // y row
192 y[i] = yRows + i * width;
193
194 // construct u row and v row
195 // width is halved because of downsampling
196 int offset = i * (width >> 1);
197 cb[i] = uRows + offset;
198 cr[i] = vRows + offset;
199 }
200
201 jpeg_write_raw_data(cinfo, planes, 16);
202 }
203 delete [] yRows;
204 delete [] uRows;
205 delete [] vRows;
206}
207
208
209void Yuv422IToJpegEncoder::deinterleave(uint8_t* yuv, uint8_t* yRows, uint8_t* uRows,
210 uint8_t* vRows, int rowIndex, int width, int height) {
Wu-cheng Li4b63f142012-10-16 23:40:03 +0800211 int numRows = height - rowIndex;
212 if (numRows > 16) numRows = 16;
213 for (int row = 0; row < numRows; ++row) {
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800214 uint8_t* yuvSeg = yuv + (rowIndex + row) * fStrides[0];
215 for (int i = 0; i < (width >> 1); ++i) {
216 int indexY = row * width + (i << 1);
217 int indexU = row * (width >> 1) + i;
218 yRows[indexY] = yuvSeg[0];
219 yRows[indexY + 1] = yuvSeg[2];
220 uRows[indexU] = yuvSeg[1];
221 vRows[indexU] = yuvSeg[3];
222 yuvSeg += 4;
223 }
224 }
225}
226
227void Yuv422IToJpegEncoder::configSamplingFactors(jpeg_compress_struct* cinfo) {
228 // cb and cr are horizontally downsampled and vertically downsampled as well.
229 cinfo->comp_info[0].h_samp_factor = 2;
230 cinfo->comp_info[0].v_samp_factor = 2;
231 cinfo->comp_info[1].h_samp_factor = 1;
232 cinfo->comp_info[1].v_samp_factor = 2;
233 cinfo->comp_info[2].h_samp_factor = 1;
234 cinfo->comp_info[2].v_samp_factor = 2;
235}
236///////////////////////////////////////////////////////////////////////////////
237
238static jboolean YuvImage_compressToJpeg(JNIEnv* env, jobject, jbyteArray inYuv,
Ashok Bhat39029b22014-01-10 16:24:38 +0000239 jint format, jint width, jint height, jintArray offsets,
240 jintArray strides, jint jpegQuality, jobject jstream,
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800241 jbyteArray jstorage) {
242 jbyte* yuv = env->GetByteArrayElements(inYuv, NULL);
243 SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
244
245 jint* imgOffsets = env->GetIntArrayElements(offsets, NULL);
246 jint* imgStrides = env->GetIntArrayElements(strides, NULL);
247 YuvToJpegEncoder* encoder = YuvToJpegEncoder::create(format, imgStrides);
Pawel Augustyn7c68a4072012-07-16 11:23:54 +0200248 jboolean result = JNI_FALSE;
249 if (encoder != NULL) {
250 encoder->encode(strm, yuv, width, height, imgOffsets, jpegQuality);
251 delete encoder;
252 result = JNI_TRUE;
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800253 }
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800254
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800255 env->ReleaseByteArrayElements(inYuv, yuv, 0);
256 env->ReleaseIntArrayElements(offsets, imgOffsets, 0);
257 env->ReleaseIntArrayElements(strides, imgStrides, 0);
Martin Wallgrend8659002014-08-20 14:58:58 +0200258 delete strm;
Pawel Augustyn7c68a4072012-07-16 11:23:54 +0200259 return result;
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800260}
261///////////////////////////////////////////////////////////////////////////////
262
Daniel Micay76f6a862015-09-19 17:31:01 -0400263static const JNINativeMethod gYuvImageMethods[] = {
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800264 { "nativeCompressToJpeg", "([BIII[I[IILjava/io/OutputStream;[B)Z",
265 (void*)YuvImage_compressToJpeg }
266};
267
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800268int register_android_graphics_YuvImage(JNIEnv* env)
269{
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800270 return android::RegisterMethodsOrDie(env, "android/graphics/YuvImage", gYuvImageMethods,
271 NELEM(gYuvImageMethods));
Wei-Ta Chenbca2d612009-11-30 17:52:05 +0800272}