1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define FAILURE_DEBUG_PREFIX "jpeg"
18
19 #include <inttypes.h>
20 #include <setjmp.h>
21 #include <algorithm>
22 #include <vector>
23
24 extern "C" {
25 #include <jpeglib.h>
26 }
27 #include <libyuv/scale.h>
28 #include <system/camera_metadata.h>
29
30 #include "debug.h"
31 #include "exif.h"
32 #include "jpeg.h"
33 #include "yuv.h"
34
35 namespace android {
36 namespace hardware {
37 namespace camera {
38 namespace provider {
39 namespace implementation {
40 namespace jpeg {
41 namespace {
42 constexpr int kJpegMCUSize = 16; // we have to feed `jpeg_write_raw_data` in multiples of this
43
44 // compressYUVImplPixelsFast handles the case where the image width is a multiple
45 // of kJpegMCUSize. In this case no additional memcpy is required. See
46 // compressYUVImplPixelsSlow below for the cases where the image width is not
47 // a multiple of kJpegMCUSize.
compressYUVImplPixelsFast(const android_ycbcr & image,jpeg_compress_struct * cinfo)48 bool compressYUVImplPixelsFast(const android_ycbcr& image, jpeg_compress_struct* cinfo) {
49 const uint8_t* y[kJpegMCUSize];
50 const uint8_t* cb[kJpegMCUSize / 2];
51 const uint8_t* cr[kJpegMCUSize / 2];
52 const uint8_t** planes[] = { y, cb, cr };
53 const int height = cinfo->image_height;
54 const int height1 = height - 1;
55 const int ystride = image.ystride;
56 const int cstride = image.cstride;
57
58 while (true) {
59 const int nscl = cinfo->next_scanline;
60 if (nscl >= height) {
61 break;
62 }
63
64 for (int i = 0; i < kJpegMCUSize; ++i) {
65 const int nscli = std::min(nscl + i, height1);
66 y[i] = static_cast<const uint8_t*>(image.y) + nscli * ystride;
67 if ((i & 1) == 0) {
68 const int offset = (nscli / 2) * cstride;
69 cb[i / 2] = static_cast<const uint8_t*>(image.cb) + offset;
70 cr[i / 2] = static_cast<const uint8_t*>(image.cr) + offset;
71 }
72 }
73
74 if (!jpeg_write_raw_data(cinfo, const_cast<JSAMPIMAGE>(planes), kJpegMCUSize)) {
75 return FAILURE(false);
76 }
77 }
78
79 return true;
80 }
81
82 // Since JPEG processes everything in blocks of kJpegMCUSize, we have to make
83 // both width and height a multiple of kJpegMCUSize. The height is handled by
84 // repeating the last line. compressYUVImplPixelsSlow handles the case when the
85 // image width is not a multiple of kJpegMCUSize by allocating a memory block
86 // large enough to hold kJpegMCUSize rows of the image with width aligned up to
87 // the next multiple of kJpegMCUSize. The original image has to be copied
88 // chunk-by-chunk into this memory block.
compressYUVImplPixelsSlow(const android_ycbcr & image,jpeg_compress_struct * cinfo,const size_t alignedWidth,uint8_t * const alignedMemory)89 bool compressYUVImplPixelsSlow(const android_ycbcr& image, jpeg_compress_struct* cinfo,
90 const size_t alignedWidth, uint8_t* const alignedMemory) {
91 uint8_t* y[kJpegMCUSize];
92 uint8_t* cb[kJpegMCUSize / 2];
93 uint8_t* cr[kJpegMCUSize / 2];
94 uint8_t** planes[] = { y, cb, cr };
95
96 {
97 uint8_t* y0 = alignedMemory;
98 for (int i = 0; i < kJpegMCUSize; ++i, y0 += alignedWidth) {
99 y[i] = y0;
100 }
101
102 const size_t alignedWidth2 = alignedWidth / 2;
103 uint8_t* cb0 = y0;
104 uint8_t* cr0 = &cb0[kJpegMCUSize / 2 * alignedWidth2];
105
106 for (int i = 0; i < kJpegMCUSize / 2; ++i, cb0 += alignedWidth2, cr0 += alignedWidth2) {
107 cb[i] = cb0;
108 cr[i] = cr0;
109 }
110 }
111
112 const int width = cinfo->image_width;
113 const int width2 = width / 2;
114 const int height = cinfo->image_height;
115 const int height1 = height - 1;
116 const int ystride = image.ystride;
117 const int cstride = image.cstride;
118
119 while (true) {
120 const int nscl = cinfo->next_scanline;
121 if (nscl >= height) {
122 break;
123 }
124
125 for (int i = 0; i < kJpegMCUSize; ++i) {
126 const int nscli = std::min(nscl + i, height1);
127 memcpy(y[i], static_cast<const uint8_t*>(image.y) + nscli * ystride, width);
128 if ((i & 1) == 0) {
129 const int offset = (nscli / 2) * cstride;
130 memcpy(cb[i / 2], static_cast<const uint8_t*>(image.cb) + offset, width2);
131 memcpy(cr[i / 2], static_cast<const uint8_t*>(image.cr) + offset, width2);
132 }
133 }
134
135 if (!jpeg_write_raw_data(cinfo, const_cast<JSAMPIMAGE>(planes), kJpegMCUSize)) {
136 return FAILURE(false);
137 }
138 }
139
140 return true;
141 }
142
143 struct JpegErrorMgr : public jpeg_error_mgr {
JpegErrorMgrandroid::hardware::camera::provider::implementation::jpeg::__anon83f88db80111::JpegErrorMgr144 JpegErrorMgr() {
145 error_exit = &onJpegErrorS;
146 }
147
onJpegErrorandroid::hardware::camera::provider::implementation::jpeg::__anon83f88db80111::JpegErrorMgr148 void onJpegError(j_common_ptr cinfo) {
149 {
150 char errorMessage[JMSG_LENGTH_MAX];
151 memset(errorMessage, 0, sizeof(errorMessage));
152 (*format_message)(cinfo, errorMessage);
153 ALOGE("%s:%d: JPEG compression failed with '%s'",
154 __func__, __LINE__, errorMessage);
155 }
156
157 longjmp(jumpBuffer, 1);
158 }
159
onJpegErrorSandroid::hardware::camera::provider::implementation::jpeg::__anon83f88db80111::JpegErrorMgr160 static void onJpegErrorS(j_common_ptr cinfo) {
161 static_cast<JpegErrorMgr*>(cinfo->err)->onJpegError(cinfo);
162 }
163
164 jmp_buf jumpBuffer;
165 };
166
compressYUVImpl(const android_ycbcr & image,const Rect<uint16_t> imageSize,unsigned char * const rawExif,const unsigned rawExifSize,const int quality,jpeg_destination_mgr * sink)167 bool compressYUVImpl(const android_ycbcr& image, const Rect<uint16_t> imageSize,
168 unsigned char* const rawExif, const unsigned rawExifSize,
169 const int quality,
170 jpeg_destination_mgr* sink) {
171 if (image.chroma_step != 1) {
172 return FAILURE(false);
173 }
174
175 std::vector<uint8_t> alignedMemory;
176 jpeg_compress_struct cinfo;
177 JpegErrorMgr err;
178 bool result;
179
180 cinfo.err = jpeg_std_error(&err);
181 jpeg_create_compress(&cinfo);
182 cinfo.image_width = imageSize.width;
183 cinfo.image_height = imageSize.height;
184 cinfo.input_components = 3;
185 cinfo.in_color_space = JCS_YCbCr;
186 jpeg_set_defaults(&cinfo);
187 jpeg_set_quality(&cinfo, quality, TRUE);
188 jpeg_default_colorspace(&cinfo);
189 cinfo.raw_data_in = TRUE;
190 cinfo.dct_method = JDCT_IFAST;
191 cinfo.comp_info[0].h_samp_factor = 2;
192 cinfo.comp_info[0].v_samp_factor = 2;
193 cinfo.comp_info[1].h_samp_factor = 1;
194 cinfo.comp_info[1].v_samp_factor = 1;
195 cinfo.comp_info[2].h_samp_factor = 1;
196 cinfo.comp_info[2].v_samp_factor = 1;
197 cinfo.dest = sink;
198
199 if (setjmp(err.jumpBuffer)) {
200 jpeg_destroy_compress(&cinfo);
201 return FAILURE(false);
202 }
203
204 jpeg_start_compress(&cinfo, TRUE);
205
206 if (rawExif) {
207 jpeg_write_marker(&cinfo, JPEG_APP0 + 1, rawExif, rawExifSize);
208 }
209
210 if (imageSize.width % kJpegMCUSize) {
211 const size_t alignedWidth =
212 ((imageSize.width + kJpegMCUSize) / kJpegMCUSize) * kJpegMCUSize;
213 alignedMemory.resize(alignedWidth * kJpegMCUSize * 3 / 2);
214 result = compressYUVImplPixelsSlow(image, &cinfo, alignedWidth, alignedMemory.data());
215 } else {
216 result = compressYUVImplPixelsFast(image, &cinfo);
217 }
218
219 jpeg_finish_compress(&cinfo);
220 jpeg_destroy_compress(&cinfo);
221
222 return result;
223 }
224
resizeYUV(const android_ycbcr & srcYCbCr,const Rect<uint16_t> srcSize,const Rect<uint16_t> dstSize,std::vector<uint8_t> * pDstData)225 android_ycbcr resizeYUV(const android_ycbcr& srcYCbCr,
226 const Rect<uint16_t> srcSize,
227 const Rect<uint16_t> dstSize,
228 std::vector<uint8_t>* pDstData) {
229 if (srcYCbCr.chroma_step != 1) {
230 return FAILURE(android_ycbcr());
231 }
232
233 const size_t dstWidth = dstSize.width;
234 const size_t dstHeight = dstSize.height;
235 if ((dstWidth & 1) || (dstHeight & 1)) {
236 return FAILURE(android_ycbcr());
237 }
238
239 std::vector<uint8_t> dstData(yuv::NV21size(dstWidth, dstHeight));
240 const android_ycbcr dstYCbCr = yuv::NV21init(dstWidth, dstHeight, dstData.data());
241
242 const int result = libyuv::I420Scale(
243 static_cast<const uint8_t*>(srcYCbCr.y), srcYCbCr.ystride,
244 static_cast<const uint8_t*>(srcYCbCr.cb), srcYCbCr.cstride,
245 static_cast<const uint8_t*>(srcYCbCr.cr), srcYCbCr.cstride,
246 srcSize.width, srcSize.height,
247 static_cast<uint8_t*>(dstYCbCr.y), dstYCbCr.ystride,
248 static_cast<uint8_t*>(dstYCbCr.cb), dstYCbCr.cstride,
249 static_cast<uint8_t*>(dstYCbCr.cr), dstYCbCr.cstride,
250 dstWidth, dstHeight,
251 libyuv::kFilterBilinear);
252
253 if (result) {
254 return FAILURE_V(android_ycbcr(), "libyuv::I420Scale failed with %d", result);
255 } else {
256 *pDstData = std::move(dstData);
257 return dstYCbCr;
258 }
259 }
260
261 struct StaticBufferSink : public jpeg_destination_mgr {
StaticBufferSinkandroid::hardware::camera::provider::implementation::jpeg::__anon83f88db80111::StaticBufferSink262 StaticBufferSink(void* dst, const size_t dstCapacity) {
263 next_output_byte = static_cast<JOCTET*>(dst);
264 free_in_buffer = dstCapacity;
265 init_destination = &initDestinationS;
266 empty_output_buffer = &emptyOutputBufferS;
267 term_destination = &termDestinationS;
268 }
269
initDestinationSandroid::hardware::camera::provider::implementation::jpeg::__anon83f88db80111::StaticBufferSink270 static void initDestinationS(j_compress_ptr) {}
emptyOutputBufferSandroid::hardware::camera::provider::implementation::jpeg::__anon83f88db80111::StaticBufferSink271 static boolean emptyOutputBufferS(j_compress_ptr) { return 0; }
termDestinationSandroid::hardware::camera::provider::implementation::jpeg::__anon83f88db80111::StaticBufferSink272 static void termDestinationS(j_compress_ptr) {}
273 };
274
275 constexpr int kDefaultQuality = 85;
276
sanitizeJpegQuality(const int quality)277 int sanitizeJpegQuality(const int quality) {
278 if (quality <= 0) {
279 return kDefaultQuality;
280 } else if (quality > 100) {
281 return 100;
282 } else {
283 return quality;
284 }
285 }
286
287 } // namespace
288
compressYUV(const android_ycbcr & image,const Rect<uint16_t> imageSize,const CameraMetadata & metadata,void * const jpegData,const size_t jpegDataCapacity)289 size_t compressYUV(const android_ycbcr& image,
290 const Rect<uint16_t> imageSize,
291 const CameraMetadata& metadata,
292 void* const jpegData,
293 const size_t jpegDataCapacity) {
294 std::vector<uint8_t> nv21data;
295 const android_ycbcr imageNV21 =
296 yuv::toNV21Shallow(imageSize.width, imageSize.height,
297 image, &nv21data);
298
299 auto exifData = exif::createExifData(metadata, imageSize);
300 if (!exifData) {
301 return FAILURE(0);
302 }
303
304 const camera_metadata_t* const rawMetadata =
305 reinterpret_cast<const camera_metadata_t*>(metadata.metadata.data());
306 camera_metadata_ro_entry_t metadataEntry;
307
308 do {
309 Rect<uint16_t> thumbnailSize = {0, 0};
310 int thumbnailQuality = 0;
311
312 if (find_camera_metadata_ro_entry(rawMetadata, ANDROID_JPEG_THUMBNAIL_SIZE,
313 &metadataEntry)) {
314 break;
315 } else {
316 thumbnailSize.width = metadataEntry.data.i32[0];
317 thumbnailSize.height = metadataEntry.data.i32[1];
318 if ((thumbnailSize.width <= 0) || (thumbnailSize.height <= 0)) {
319 break;
320 }
321 }
322
323 if (find_camera_metadata_ro_entry(rawMetadata, ANDROID_JPEG_THUMBNAIL_QUALITY,
324 &metadataEntry)) {
325 thumbnailQuality = kDefaultQuality;
326 } else {
327 thumbnailQuality = sanitizeJpegQuality(metadataEntry.data.i32[0]);
328 }
329
330 std::vector<uint8_t> thumbnailData;
331 const android_ycbcr thumbmnail = resizeYUV(imageNV21, imageSize,
332 thumbnailSize, &thumbnailData);
333 if (!thumbmnail.y) {
334 return FAILURE(0);
335 }
336
337 StaticBufferSink sink(jpegData, jpegDataCapacity);
338 if (!compressYUVImpl(thumbmnail, thumbnailSize, nullptr, 0,
339 thumbnailQuality, &sink)) {
340 return FAILURE(0);
341 }
342
343 const size_t thumbnailJpegSize = jpegDataCapacity - sink.free_in_buffer;
344 void* exifThumbnailJpegDataPtr = exif::exifDataAllocThumbnail(
345 exifData.get(), thumbnailJpegSize);
346 if (!exifThumbnailJpegDataPtr) {
347 return FAILURE(0);
348 }
349
350 memcpy(exifThumbnailJpegDataPtr, jpegData, thumbnailJpegSize);
351 } while (false);
352
353 int quality;
354 if (find_camera_metadata_ro_entry(rawMetadata, ANDROID_JPEG_QUALITY,
355 &metadataEntry)) {
356 quality = kDefaultQuality;
357 } else {
358 quality = sanitizeJpegQuality(metadataEntry.data.i32[0]);
359 }
360
361 unsigned char* rawExif = nullptr;
362 unsigned rawExifSize = 0;
363 exif_data_save_data(const_cast<ExifData*>(exifData.get()),
364 &rawExif, &rawExifSize);
365 if (!rawExif) {
366 return FAILURE(0);
367 }
368
369 StaticBufferSink sink(jpegData, jpegDataCapacity);
370 const bool success = compressYUVImpl(imageNV21, imageSize, rawExif, rawExifSize,
371 quality, &sink);
372 free(rawExif);
373
374 return success ? (jpegDataCapacity - sink.free_in_buffer) : 0;
375 }
376
377 } // namespace jpeg
378 } // namespace implementation
379 } // namespace provider
380 } // namespace camera
381 } // namespace hardware
382 } // namespace android
383