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