• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 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 #include <ultrahdr/jpegdecoderhelper.h>
18 
19 #include <utils/Log.h>
20 
21 #include <errno.h>
22 #include <setjmp.h>
23 #include <string>
24 
25 using namespace std;
26 
27 namespace android::ultrahdr {
28 
29 #define ALIGNM(x, m)  ((((x) + ((m) - 1)) / (m)) * (m))
30 
31 const uint32_t kAPP0Marker = JPEG_APP0;      // JFIF
32 const uint32_t kAPP1Marker = JPEG_APP0 + 1;  // EXIF, XMP
33 const uint32_t kAPP2Marker = JPEG_APP0 + 2;  // ICC
34 
35 const std::string kXmpNameSpace = "http://ns.adobe.com/xap/1.0/";
36 const std::string kExifIdCode = "Exif";
37 constexpr uint32_t kICCMarkerHeaderSize = 14;
38 constexpr uint8_t kICCSig[] = {
39         'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0',
40 };
41 
42 struct jpegr_source_mgr : jpeg_source_mgr {
43     jpegr_source_mgr(const uint8_t* ptr, int len);
44     ~jpegr_source_mgr();
45 
46     const uint8_t* mBufferPtr;
47     size_t mBufferLength;
48 };
49 
50 struct jpegrerror_mgr {
51     struct jpeg_error_mgr pub;
52     jmp_buf setjmp_buffer;
53 };
54 
jpegr_init_source(j_decompress_ptr cinfo)55 static void jpegr_init_source(j_decompress_ptr cinfo) {
56     jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
57     src->next_input_byte = static_cast<const JOCTET*>(src->mBufferPtr);
58     src->bytes_in_buffer = src->mBufferLength;
59 }
60 
jpegr_fill_input_buffer(j_decompress_ptr)61 static boolean jpegr_fill_input_buffer(j_decompress_ptr /* cinfo */) {
62     ALOGE("%s : should not get here", __func__);
63     return FALSE;
64 }
65 
jpegr_skip_input_data(j_decompress_ptr cinfo,long num_bytes)66 static void jpegr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
67     jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
68 
69     if (num_bytes > static_cast<long>(src->bytes_in_buffer)) {
70         ALOGE("jpegr_skip_input_data - num_bytes > (long)src->bytes_in_buffer");
71     } else {
72         src->next_input_byte += num_bytes;
73         src->bytes_in_buffer -= num_bytes;
74     }
75 }
76 
jpegr_term_source(j_decompress_ptr)77 static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {}
78 
jpegr_source_mgr(const uint8_t * ptr,int len)79 jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len) :
80         mBufferPtr(ptr), mBufferLength(len) {
81     init_source = jpegr_init_source;
82     fill_input_buffer = jpegr_fill_input_buffer;
83     skip_input_data = jpegr_skip_input_data;
84     resync_to_restart = jpeg_resync_to_restart;
85     term_source = jpegr_term_source;
86 }
87 
~jpegr_source_mgr()88 jpegr_source_mgr::~jpegr_source_mgr() {}
89 
jpegrerror_exit(j_common_ptr cinfo)90 static void jpegrerror_exit(j_common_ptr cinfo) {
91     jpegrerror_mgr* err = reinterpret_cast<jpegrerror_mgr*>(cinfo->err);
92     longjmp(err->setjmp_buffer, 1);
93 }
94 
JpegDecoderHelper()95 JpegDecoderHelper::JpegDecoderHelper() {
96 }
97 
~JpegDecoderHelper()98 JpegDecoderHelper::~JpegDecoderHelper() {
99 }
100 
decompressImage(const void * image,int length,bool decodeToRGBA)101 bool JpegDecoderHelper::decompressImage(const void* image, int length, bool decodeToRGBA) {
102     if (image == nullptr || length <= 0) {
103         ALOGE("Image size can not be handled: %d", length);
104         return false;
105     }
106 
107     mResultBuffer.clear();
108     mXMPBuffer.clear();
109     if (!decode(image, length, decodeToRGBA)) {
110         return false;
111     }
112 
113     return true;
114 }
115 
getDecompressedImagePtr()116 void* JpegDecoderHelper::getDecompressedImagePtr() {
117     return mResultBuffer.data();
118 }
119 
getDecompressedImageSize()120 size_t JpegDecoderHelper::getDecompressedImageSize() {
121     return mResultBuffer.size();
122 }
123 
getXMPPtr()124 void* JpegDecoderHelper::getXMPPtr() {
125     return mXMPBuffer.data();
126 }
127 
getXMPSize()128 size_t JpegDecoderHelper::getXMPSize() {
129     return mXMPBuffer.size();
130 }
131 
getEXIFPtr()132 void* JpegDecoderHelper::getEXIFPtr() {
133     return mEXIFBuffer.data();
134 }
135 
getEXIFSize()136 size_t JpegDecoderHelper::getEXIFSize() {
137     return mEXIFBuffer.size();
138 }
139 
getICCPtr()140 void* JpegDecoderHelper::getICCPtr() {
141     return mICCBuffer.data();
142 }
143 
getICCSize()144 size_t JpegDecoderHelper::getICCSize() {
145     return mICCBuffer.size();
146 }
147 
getDecompressedImageWidth()148 size_t JpegDecoderHelper::getDecompressedImageWidth() {
149     return mWidth;
150 }
151 
getDecompressedImageHeight()152 size_t JpegDecoderHelper::getDecompressedImageHeight() {
153     return mHeight;
154 }
155 
decode(const void * image,int length,bool decodeToRGBA)156 bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) {
157     jpeg_decompress_struct cinfo;
158     jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
159     jpegrerror_mgr myerr;
160     bool status = true;
161 
162     cinfo.err = jpeg_std_error(&myerr.pub);
163     myerr.pub.error_exit = jpegrerror_exit;
164 
165     if (setjmp(myerr.setjmp_buffer)) {
166         jpeg_destroy_decompress(&cinfo);
167         return false;
168     }
169     jpeg_create_decompress(&cinfo);
170 
171     jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
172     jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
173     jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
174 
175     cinfo.src = &mgr;
176     jpeg_read_header(&cinfo, TRUE);
177 
178     // Save XMP data, EXIF data, and ICC data.
179     // Here we only handle the first XMP / EXIF / ICC package.
180     // We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
181     // two bytes of package length which is stored in marker->original_length, and the real data
182     // which is stored in marker->data.
183     bool exifAppears = false;
184     bool xmpAppears = false;
185     bool iccAppears = false;
186     for (jpeg_marker_struct* marker = cinfo.marker_list;
187          marker && !(exifAppears && xmpAppears && iccAppears);
188          marker = marker->next) {
189 
190         if (marker->marker != kAPP1Marker && marker->marker != kAPP2Marker) {
191             continue;
192         }
193         const unsigned int len = marker->data_length;
194         if (!xmpAppears &&
195             len > kXmpNameSpace.size() &&
196             !strncmp(reinterpret_cast<const char*>(marker->data),
197                      kXmpNameSpace.c_str(),
198                      kXmpNameSpace.size())) {
199             mXMPBuffer.resize(len+1, 0);
200             memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len);
201             xmpAppears = true;
202         } else if (!exifAppears &&
203                    len > kExifIdCode.size() &&
204                    !strncmp(reinterpret_cast<const char*>(marker->data),
205                             kExifIdCode.c_str(),
206                             kExifIdCode.size())) {
207             mEXIFBuffer.resize(len, 0);
208             memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
209             exifAppears = true;
210         } else if (!iccAppears &&
211                    len > sizeof(kICCSig) &&
212                    !memcmp(marker->data, kICCSig, sizeof(kICCSig))) {
213             mICCBuffer.resize(len, 0);
214             memcpy(static_cast<void*>(mICCBuffer.data()), marker->data, len);
215             iccAppears = true;
216         }
217     }
218 
219     if (cinfo.image_width > kMaxWidth || cinfo.image_height > kMaxHeight) {
220         // constraint on max width and max height is only due to alloc constraints
221         // tune these values basing on the target device
222         status = false;
223         goto CleanUp;
224     }
225 
226     mWidth = cinfo.image_width;
227     mHeight = cinfo.image_height;
228 
229     if (decodeToRGBA) {
230         if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
231             // We don't intend to support decoding grayscale to RGBA
232             status = false;
233             ALOGE("%s: decoding grayscale to RGBA is unsupported", __func__);
234             goto CleanUp;
235         }
236         // 4 bytes per pixel
237         mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
238         cinfo.out_color_space = JCS_EXT_RGBA;
239     } else {
240         if (cinfo.jpeg_color_space == JCS_YCbCr) {
241             if (cinfo.comp_info[0].h_samp_factor != 2 ||
242                 cinfo.comp_info[1].h_samp_factor != 1 ||
243                 cinfo.comp_info[2].h_samp_factor != 1 ||
244                 cinfo.comp_info[0].v_samp_factor != 2 ||
245                 cinfo.comp_info[1].v_samp_factor != 1 ||
246                 cinfo.comp_info[2].v_samp_factor != 1) {
247                 status = false;
248                 ALOGE("%s: decoding to YUV only supports 4:2:0 subsampling", __func__);
249                 goto CleanUp;
250             }
251             mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
252         } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
253             mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
254         }
255         cinfo.out_color_space = cinfo.jpeg_color_space;
256         cinfo.raw_data_out = TRUE;
257     }
258 
259     cinfo.dct_method = JDCT_IFAST;
260 
261     jpeg_start_decompress(&cinfo);
262 
263     if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
264             cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
265         status = false;
266         goto CleanUp;
267     }
268 
269 CleanUp:
270     jpeg_finish_decompress(&cinfo);
271     jpeg_destroy_decompress(&cinfo);
272 
273     return status;
274 }
275 
decompress(jpeg_decompress_struct * cinfo,const uint8_t * dest,bool isSingleChannel)276 bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
277         bool isSingleChannel) {
278     if (isSingleChannel) {
279         return decompressSingleChannel(cinfo, dest);
280     }
281     if (cinfo->out_color_space == JCS_EXT_RGBA)
282         return decompressRGBA(cinfo, dest);
283     else
284         return decompressYUV(cinfo, dest);
285 }
286 
getCompressedImageParameters(const void * image,int length,size_t * pWidth,size_t * pHeight,std::vector<uint8_t> * iccData,std::vector<uint8_t> * exifData)287 bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length,
288                               size_t *pWidth, size_t *pHeight,
289                               std::vector<uint8_t> *iccData , std::vector<uint8_t> *exifData) {
290     jpeg_decompress_struct cinfo;
291     jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
292     jpegrerror_mgr myerr;
293     cinfo.err = jpeg_std_error(&myerr.pub);
294     myerr.pub.error_exit = jpegrerror_exit;
295 
296     if (setjmp(myerr.setjmp_buffer)) {
297         jpeg_destroy_decompress(&cinfo);
298         return false;
299     }
300     jpeg_create_decompress(&cinfo);
301 
302     jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
303     jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
304 
305     cinfo.src = &mgr;
306     if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
307         jpeg_destroy_decompress(&cinfo);
308         return false;
309     }
310 
311     if (pWidth != nullptr) {
312         *pWidth = cinfo.image_width;
313     }
314     if (pHeight != nullptr) {
315         *pHeight = cinfo.image_height;
316     }
317 
318     if (iccData != nullptr) {
319         for (jpeg_marker_struct* marker = cinfo.marker_list; marker;
320              marker = marker->next) {
321             if (marker->marker != kAPP2Marker) {
322                 continue;
323             }
324             if (marker->data_length <= kICCMarkerHeaderSize ||
325                 memcmp(marker->data, kICCSig, sizeof(kICCSig)) != 0) {
326                 continue;
327             }
328 
329             iccData->insert(iccData->end(), marker->data, marker->data + marker->data_length);
330         }
331     }
332 
333     if (exifData != nullptr) {
334         bool exifAppears = false;
335         for (jpeg_marker_struct* marker = cinfo.marker_list; marker && !exifAppears;
336              marker = marker->next) {
337             if (marker->marker != kAPP1Marker) {
338                 continue;
339             }
340 
341             const unsigned int len = marker->data_length;
342             if (len >= kExifIdCode.size() &&
343                 !strncmp(reinterpret_cast<const char*>(marker->data), kExifIdCode.c_str(),
344                          kExifIdCode.size())) {
345                 exifData->resize(len, 0);
346                 memcpy(static_cast<void*>(exifData->data()), marker->data, len);
347                 exifAppears = true;
348             }
349         }
350     }
351 
352     jpeg_destroy_decompress(&cinfo);
353     return true;
354 }
355 
decompressRGBA(jpeg_decompress_struct * cinfo,const uint8_t * dest)356 bool JpegDecoderHelper::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
357     JSAMPLE* decodeDst = (JSAMPLE*) dest;
358     uint32_t lines = 0;
359     // TODO: use batches for more effectiveness
360     while (lines < cinfo->image_height) {
361         uint32_t ret = jpeg_read_scanlines(cinfo, &decodeDst, 1);
362         if (ret == 0) {
363             break;
364         }
365         decodeDst += cinfo->image_width * 4;
366         lines++;
367     }
368     return lines == cinfo->image_height;
369 }
370 
decompressYUV(jpeg_decompress_struct * cinfo,const uint8_t * dest)371 bool JpegDecoderHelper::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
372     JSAMPROW y[kCompressBatchSize];
373     JSAMPROW cb[kCompressBatchSize / 2];
374     JSAMPROW cr[kCompressBatchSize / 2];
375     JSAMPARRAY planes[3] {y, cb, cr};
376 
377     size_t y_plane_size = cinfo->image_width * cinfo->image_height;
378     size_t uv_plane_size = y_plane_size / 4;
379     uint8_t* y_plane = const_cast<uint8_t*>(dest);
380     uint8_t* u_plane = const_cast<uint8_t*>(dest + y_plane_size);
381     uint8_t* v_plane = const_cast<uint8_t*>(dest + y_plane_size + uv_plane_size);
382     std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
383     memset(empty.get(), 0, cinfo->image_width);
384 
385     const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
386     bool is_width_aligned = (aligned_width == cinfo->image_width);
387     std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
388     uint8_t* y_plane_intrm = nullptr;
389     uint8_t* u_plane_intrm = nullptr;
390     uint8_t* v_plane_intrm = nullptr;
391     JSAMPROW y_intrm[kCompressBatchSize];
392     JSAMPROW cb_intrm[kCompressBatchSize / 2];
393     JSAMPROW cr_intrm[kCompressBatchSize / 2];
394     JSAMPARRAY planes_intrm[3] {y_intrm, cb_intrm, cr_intrm};
395     if (!is_width_aligned) {
396         size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
397         buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
398         y_plane_intrm = buffer_intrm.get();
399         u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize);
400         v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4;
401         for (int i = 0; i < kCompressBatchSize; ++i) {
402             y_intrm[i] = y_plane_intrm + i * aligned_width;
403         }
404         for (int i = 0; i < kCompressBatchSize / 2; ++i) {
405             int offset_intrm = i * (aligned_width / 2);
406             cb_intrm[i] = u_plane_intrm + offset_intrm;
407             cr_intrm[i] = v_plane_intrm + offset_intrm;
408         }
409     }
410 
411     while (cinfo->output_scanline < cinfo->image_height) {
412         for (int i = 0; i < kCompressBatchSize; ++i) {
413             size_t scanline = cinfo->output_scanline + i;
414             if (scanline < cinfo->image_height) {
415                 y[i] = y_plane + scanline * cinfo->image_width;
416             } else {
417                 y[i] = empty.get();
418             }
419         }
420         // cb, cr only have half scanlines
421         for (int i = 0; i < kCompressBatchSize / 2; ++i) {
422             size_t scanline = cinfo->output_scanline / 2 + i;
423             if (scanline < cinfo->image_height / 2) {
424                 int offset = scanline * (cinfo->image_width / 2);
425                 cb[i] = u_plane + offset;
426                 cr[i] = v_plane + offset;
427             } else {
428                 cb[i] = cr[i] = empty.get();
429             }
430         }
431 
432         int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
433                                            kCompressBatchSize);
434         if (processed != kCompressBatchSize) {
435             ALOGE("Number of processed lines does not equal input lines.");
436             return false;
437         }
438         if (!is_width_aligned) {
439             for (int i = 0; i < kCompressBatchSize; ++i) {
440                 memcpy(y[i], y_intrm[i], cinfo->image_width);
441             }
442             for (int i = 0; i < kCompressBatchSize / 2; ++i) {
443                 memcpy(cb[i], cb_intrm[i], cinfo->image_width / 2);
444                 memcpy(cr[i], cr_intrm[i], cinfo->image_width / 2);
445             }
446         }
447     }
448     return true;
449 }
450 
decompressSingleChannel(jpeg_decompress_struct * cinfo,const uint8_t * dest)451 bool JpegDecoderHelper::decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
452     JSAMPROW y[kCompressBatchSize];
453     JSAMPARRAY planes[1] {y};
454 
455     uint8_t* y_plane = const_cast<uint8_t*>(dest);
456     std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
457     memset(empty.get(), 0, cinfo->image_width);
458 
459     int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
460     bool is_width_aligned = (aligned_width == cinfo->image_width);
461     std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
462     uint8_t* y_plane_intrm = nullptr;
463     JSAMPROW y_intrm[kCompressBatchSize];
464     JSAMPARRAY planes_intrm[1] {y_intrm};
465     if (!is_width_aligned) {
466         size_t mcu_row_size = aligned_width * kCompressBatchSize;
467         buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
468         y_plane_intrm = buffer_intrm.get();
469         for (int i = 0; i < kCompressBatchSize; ++i) {
470             y_intrm[i] = y_plane_intrm + i * aligned_width;
471         }
472     }
473 
474     while (cinfo->output_scanline < cinfo->image_height) {
475         for (int i = 0; i < kCompressBatchSize; ++i) {
476             size_t scanline = cinfo->output_scanline + i;
477             if (scanline < cinfo->image_height) {
478                 y[i] = y_plane + scanline * cinfo->image_width;
479             } else {
480                 y[i] = empty.get();
481             }
482         }
483 
484         int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
485                                            kCompressBatchSize);
486         if (processed != kCompressBatchSize / 2) {
487             ALOGE("Number of processed lines does not equal input lines.");
488             return false;
489         }
490         if (!is_width_aligned) {
491             for (int i = 0; i < kCompressBatchSize; ++i) {
492                 memcpy(y[i], y_intrm[i], cinfo->image_width);
493             }
494         }
495     }
496     return true;
497 }
498 
499 } // namespace ultrahdr
500