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 <errno.h>
18 #include <setjmp.h>
19
20 #include <cmath>
21 #include <cstring>
22
23 #include "ultrahdr/ultrahdrcommon.h"
24 #include "ultrahdr/ultrahdr.h"
25 #include "ultrahdr/jpegdecoderhelper.h"
26
27 using namespace std;
28
29 namespace ultrahdr {
30
31 static const uint32_t kAPP0Marker = JPEG_APP0; // JFIF
32 static const uint32_t kAPP1Marker = JPEG_APP0 + 1; // EXIF, XMP
33 static const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC, ISO Metadata
34
35 static constexpr uint8_t kICCSig[] = {
36 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0',
37 };
38
39 static constexpr uint8_t kXmpNameSpace[] = {
40 'h', 't', 't', 'p', ':', '/', '/', 'n', 's', '.', 'a', 'd', 'o', 'b', 'e',
41 '.', 'c', 'o', 'm', '/', 'x', 'a', 'p', '/', '1', '.', '0', '/', '\0',
42 };
43
44 static constexpr uint8_t kExifIdCode[] = {
45 'E', 'x', 'i', 'f', '\0', '\0',
46 };
47
48 static constexpr uint8_t kIsoMetadataNameSpace[] = {
49 'u', 'r', 'n', ':', 'i', 's', 'o', ':', 's', 't', 'd', ':', 'i', 's',
50 'o', ':', 't', 's', ':', '2', '1', '4', '9', '6', ':', '-', '1', '\0',
51 };
52
53 /*!\brief module for managing input */
54 struct jpeg_source_mgr_impl : jpeg_source_mgr {
55 jpeg_source_mgr_impl(const uint8_t* ptr, int len);
56 ~jpeg_source_mgr_impl() = default;
57
58 const uint8_t* mBufferPtr;
59 size_t mBufferLength;
60 };
61
62 /*!\brief module for managing error */
63 struct jpeg_error_mgr_impl : jpeg_error_mgr {
64 jmp_buf setjmp_buffer;
65 };
66
jpegr_init_source(j_decompress_ptr cinfo)67 static void jpegr_init_source(j_decompress_ptr cinfo) {
68 jpeg_source_mgr_impl* src = static_cast<jpeg_source_mgr_impl*>(cinfo->src);
69 src->next_input_byte = static_cast<const JOCTET*>(src->mBufferPtr);
70 src->bytes_in_buffer = src->mBufferLength;
71 }
72
jpegr_fill_input_buffer(j_decompress_ptr)73 static boolean jpegr_fill_input_buffer(j_decompress_ptr /* cinfo */) {
74 ALOGE("%s : should not reach here", __func__);
75 return FALSE;
76 }
77
jpegr_skip_input_data(j_decompress_ptr cinfo,long num_bytes)78 static void jpegr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
79 jpeg_source_mgr_impl* src = static_cast<jpeg_source_mgr_impl*>(cinfo->src);
80
81 if (num_bytes > static_cast<long>(src->bytes_in_buffer)) {
82 ALOGE("jpegr_skip_input_data - num_bytes > (long)src->bytes_in_buffer");
83 } else {
84 src->next_input_byte += num_bytes;
85 src->bytes_in_buffer -= num_bytes;
86 }
87 }
88
jpegr_term_source(j_decompress_ptr)89 static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {}
90
jpeg_source_mgr_impl(const uint8_t * ptr,int len)91 jpeg_source_mgr_impl::jpeg_source_mgr_impl(const uint8_t* ptr, int len)
92 : mBufferPtr(ptr), mBufferLength(len) {
93 init_source = jpegr_init_source;
94 fill_input_buffer = jpegr_fill_input_buffer;
95 skip_input_data = jpegr_skip_input_data;
96 resync_to_restart = jpeg_resync_to_restart;
97 term_source = jpegr_term_source;
98 }
99
jpegrerror_exit(j_common_ptr cinfo)100 static void jpegrerror_exit(j_common_ptr cinfo) {
101 jpeg_error_mgr_impl* err = reinterpret_cast<jpeg_error_mgr_impl*>(cinfo->err);
102 longjmp(err->setjmp_buffer, 1);
103 }
104
output_message(j_common_ptr cinfo)105 static void output_message(j_common_ptr cinfo) {
106 char buffer[JMSG_LENGTH_MAX];
107
108 (*cinfo->err->format_message)(cinfo, buffer);
109 ALOGE("%s\n", buffer);
110 }
111
jpeg_extract_marker_payload(const j_decompress_ptr cinfo,const uint32_t marker_code,const uint8_t * marker_fourcc_code,const uint32_t fourcc_length,std::vector<JOCTET> & destination,int & markerPayloadOffsetRelativeToSourceBuffer)112 static void jpeg_extract_marker_payload(const j_decompress_ptr cinfo, const uint32_t marker_code,
113 const uint8_t* marker_fourcc_code,
114 const uint32_t fourcc_length,
115 std::vector<JOCTET>& destination,
116 int& markerPayloadOffsetRelativeToSourceBuffer) {
117 size_t pos = 2; /* position after reading SOI marker (0xffd8) */
118 markerPayloadOffsetRelativeToSourceBuffer = -1;
119
120 for (jpeg_marker_struct* marker = cinfo->marker_list; marker; marker = marker->next) {
121 pos += 4; /* position after reading next marker and its size (0xFFXX, [SIZE = 2 bytes]) */
122
123 if (marker->marker == marker_code && marker->data_length > fourcc_length &&
124 !memcmp(marker->data, marker_fourcc_code, fourcc_length)) {
125 destination.resize(marker->data_length);
126 memcpy(static_cast<void*>(destination.data()), marker->data, marker->data_length);
127 markerPayloadOffsetRelativeToSourceBuffer = pos;
128 return;
129 }
130 pos += marker->original_length; /* position after marker's payload */
131 }
132 }
133
getOutputSamplingFormat(const j_decompress_ptr cinfo)134 static uhdr_img_fmt_t getOutputSamplingFormat(const j_decompress_ptr cinfo) {
135 if (cinfo->num_components == 1)
136 return UHDR_IMG_FMT_8bppYCbCr400;
137 else {
138 int a = cinfo->max_h_samp_factor / cinfo->comp_info[1].h_samp_factor;
139 int b = cinfo->max_v_samp_factor / cinfo->comp_info[1].v_samp_factor;
140 if (a == 1 && b == 1)
141 return UHDR_IMG_FMT_24bppYCbCr444;
142 else if (a == 1 && b == 2)
143 return UHDR_IMG_FMT_16bppYCbCr440;
144 else if (a == 2 && b == 1)
145 return UHDR_IMG_FMT_16bppYCbCr422;
146 else if (a == 2 && b == 2)
147 return UHDR_IMG_FMT_12bppYCbCr420;
148 else if (a == 4 && b == 1)
149 return UHDR_IMG_FMT_12bppYCbCr411;
150 else if (a == 4 && b == 2)
151 return UHDR_IMG_FMT_10bppYCbCr410;
152 }
153 return UHDR_IMG_FMT_UNSPECIFIED;
154 }
155
decompressImage(const void * image,int length,decode_mode_t mode)156 bool JpegDecoderHelper::decompressImage(const void* image, int length, decode_mode_t mode) {
157 if (image == nullptr) {
158 ALOGE("received nullptr for compressed image data");
159 return false;
160 }
161 if (length <= 0) {
162 ALOGE("received bad compressed image size %d", length);
163 return false;
164 }
165
166 // reset context
167 mResultBuffer.clear();
168 mXMPBuffer.clear();
169 mEXIFBuffer.clear();
170 mICCBuffer.clear();
171 mIsoMetadataBuffer.clear();
172 mOutFormat = UHDR_IMG_FMT_UNSPECIFIED;
173 for (int i = 0; i < kMaxNumComponents; i++) {
174 mPlanesMCURow[i].reset();
175 mPlaneWidth[i] = 0;
176 mPlaneHeight[i] = 0;
177 }
178 mExifPayLoadOffset = -1;
179
180 return decode(image, length, mode);
181 }
182
decode(const void * image,int length,decode_mode_t mode)183 bool JpegDecoderHelper::decode(const void* image, int length, decode_mode_t mode) {
184 jpeg_source_mgr_impl mgr(static_cast<const uint8_t*>(image), length);
185 jpeg_decompress_struct cinfo;
186 jpeg_error_mgr_impl myerr;
187
188 cinfo.err = jpeg_std_error(&myerr);
189 myerr.error_exit = jpegrerror_exit;
190 myerr.output_message = output_message;
191
192 if (0 == setjmp(myerr.setjmp_buffer)) {
193 jpeg_create_decompress(&cinfo);
194 cinfo.src = &mgr;
195 jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
196 jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
197 jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
198 int ret_val = jpeg_read_header(&cinfo, TRUE /* require an image to be present */);
199 if (JPEG_HEADER_OK != ret_val) {
200 ALOGE("jpeg_read_header(...) returned %d, expected %d", ret_val, JPEG_HEADER_OK);
201 jpeg_destroy_decompress(&cinfo);
202 return false;
203 }
204 int payloadOffset = -1;
205 jpeg_extract_marker_payload(&cinfo, kAPP1Marker, kXmpNameSpace,
206 sizeof kXmpNameSpace / sizeof kXmpNameSpace[0], mXMPBuffer,
207 payloadOffset);
208 jpeg_extract_marker_payload(&cinfo, kAPP1Marker, kExifIdCode,
209 sizeof kExifIdCode / sizeof kExifIdCode[0], mEXIFBuffer,
210 mExifPayLoadOffset);
211 jpeg_extract_marker_payload(&cinfo, kAPP2Marker, kICCSig, sizeof kICCSig / sizeof kICCSig[0],
212 mICCBuffer, payloadOffset);
213 jpeg_extract_marker_payload(&cinfo, kAPP2Marker, kIsoMetadataNameSpace,
214 sizeof kIsoMetadataNameSpace / sizeof kIsoMetadataNameSpace[0],
215 mIsoMetadataBuffer, payloadOffset);
216
217 if (cinfo.image_width < 1 || cinfo.image_height < 1) {
218 ALOGE("received bad image width or height, wd = %d, ht = %d. wd and height shall be >= 1",
219 cinfo.image_width, cinfo.image_height);
220 jpeg_destroy_decompress(&cinfo);
221 return false;
222 }
223 if (cinfo.image_width > kMaxWidth || cinfo.image_height > kMaxHeight) {
224 ALOGE(
225 "max width, max supported by library are %d, %d respectively. Current image width and "
226 "height are %d, %d. Recompile library with updated max supported dimensions to proceed",
227 kMaxWidth, kMaxHeight, cinfo.image_width, cinfo.image_height);
228 jpeg_destroy_decompress(&cinfo);
229 return false;
230 }
231 if (cinfo.num_components != 1 && cinfo.num_components != 3) {
232 ALOGE(
233 "ultrahdr primary image and supplimentary images are images encoded with 1 component "
234 "(grayscale) or 3 components (YCbCr / RGB). Unrecognized number of components %d",
235 cinfo.num_components);
236 jpeg_destroy_decompress(&cinfo);
237 return false;
238 }
239
240 for (int i = 0, product = 0; i < cinfo.num_components; i++) {
241 if (cinfo.comp_info[i].h_samp_factor < 1 || cinfo.comp_info[i].h_samp_factor > 4) {
242 ALOGE(
243 "received bad horizontal sampling factor for component index %d, sample factor h = %d, "
244 "this is expected to be with in range [1-4]",
245 i, cinfo.comp_info[i].h_samp_factor);
246 jpeg_destroy_decompress(&cinfo);
247 return false;
248 }
249 if (cinfo.comp_info[i].v_samp_factor < 1 || cinfo.comp_info[i].v_samp_factor > 4) {
250 ALOGE(
251 "received bad vertical sampling factor for component index %d, sample factor v = %d, "
252 "this is expected to be with in range [1-4]",
253 i, cinfo.comp_info[i].v_samp_factor);
254 jpeg_destroy_decompress(&cinfo);
255 return false;
256 }
257 product += cinfo.comp_info[i].h_samp_factor * cinfo.comp_info[i].v_samp_factor;
258 if (product > 10) {
259 ALOGE(
260 "received bad sampling factors for components, sum of product of h_samp_factor, "
261 "v_samp_factor across all components exceeds 10");
262 jpeg_destroy_decompress(&cinfo);
263 return false;
264 }
265 }
266
267 for (int i = 0; i < cinfo.num_components; i++) {
268 mPlaneWidth[i] = std::ceil(((float)cinfo.image_width * cinfo.comp_info[i].h_samp_factor) /
269 cinfo.max_h_samp_factor);
270 mPlaneHeight[i] = std::ceil(((float)cinfo.image_height * cinfo.comp_info[i].v_samp_factor) /
271 cinfo.max_v_samp_factor);
272 }
273
274 if (cinfo.num_components == 3 &&
275 (mPlaneWidth[1] != mPlaneWidth[2] || mPlaneHeight[1] != mPlaneHeight[2])) {
276 ALOGE(
277 "cb, cr planes are not sampled identically. cb width %d, cb height %d, cr width %d, cr "
278 "height %d",
279 (int)mPlaneWidth[1], (int)mPlaneWidth[2], (int)mPlaneHeight[1], (int)mPlaneHeight[2]);
280 jpeg_destroy_decompress(&cinfo);
281 return false;
282 }
283
284 if (PARSE_STREAM == mode) {
285 jpeg_destroy_decompress(&cinfo);
286 return true;
287 }
288
289 if (DECODE_STREAM == mode) {
290 mode = cinfo.num_components == 1 ? DECODE_TO_YCBCR_CS : DECODE_TO_RGB_CS;
291 }
292
293 if (DECODE_TO_RGB_CS == mode) {
294 if (cinfo.jpeg_color_space != JCS_YCbCr && cinfo.jpeg_color_space != JCS_RGB) {
295 ALOGE("expected input color space to be JCS_YCbCr or JCS_RGB but got %d",
296 cinfo.jpeg_color_space);
297 jpeg_destroy_decompress(&cinfo);
298 return false;
299 }
300 #ifdef JCS_ALPHA_EXTENSIONS
301 mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
302 cinfo.out_color_space = JCS_EXT_RGBA;
303 #else
304 mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3);
305 cinfo.out_color_space = JCS_RGB;
306 #endif
307 } else if (DECODE_TO_YCBCR_CS == mode) {
308 if (cinfo.jpeg_color_space != JCS_YCbCr && cinfo.jpeg_color_space != JCS_GRAYSCALE) {
309 ALOGE("expected input color space to be JCS_YCbCr or JCS_GRAYSCALE but got %d",
310 cinfo.jpeg_color_space);
311 jpeg_destroy_decompress(&cinfo);
312 return false;
313 }
314 if (cinfo.jpeg_color_space == JCS_YCbCr) {
315 if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
316 cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
317 cinfo.comp_info[2].h_samp_factor != 1 || cinfo.comp_info[2].v_samp_factor != 1) {
318 ALOGE("apply gainmap supports only 4:2:0 sub sampling format, stopping image decode");
319 jpeg_destroy_decompress(&cinfo);
320 return false;
321 }
322 }
323 int size = 0;
324 for (int i = 0; i < cinfo.num_components; i++) {
325 size += mPlaneWidth[i] * mPlaneHeight[i];
326 }
327 mResultBuffer.resize(size);
328 cinfo.out_color_space = cinfo.jpeg_color_space;
329 cinfo.raw_data_out = TRUE;
330 }
331 cinfo.dct_method = JDCT_ISLOW;
332 jpeg_start_decompress(&cinfo);
333 if (!decode(&cinfo, static_cast<uint8_t*>(mResultBuffer.data()))) {
334 jpeg_destroy_decompress(&cinfo);
335 return false;
336 }
337 } else {
338 cinfo.err->output_message((j_common_ptr)&cinfo);
339 jpeg_destroy_decompress(&cinfo);
340 return false;
341 }
342 jpeg_finish_decompress(&cinfo);
343 jpeg_destroy_decompress(&cinfo);
344 return true;
345 }
346
decode(jpeg_decompress_struct * cinfo,uint8_t * dest)347 bool JpegDecoderHelper::decode(jpeg_decompress_struct* cinfo, uint8_t* dest) {
348 switch (cinfo->out_color_space) {
349 case JCS_GRAYSCALE:
350 [[fallthrough]];
351 case JCS_YCbCr:
352 mOutFormat = getOutputSamplingFormat(cinfo);
353 return decodeToCSYCbCr(cinfo, dest);
354 #ifdef JCS_ALPHA_EXTENSIONS
355 case JCS_EXT_RGBA:
356 mOutFormat = UHDR_IMG_FMT_32bppRGBA8888;
357 return decodeToCSRGB(cinfo, dest);
358 #endif
359 case JCS_RGB:
360 mOutFormat = UHDR_IMG_FMT_24bppRGB888;
361 return decodeToCSRGB(cinfo, dest);
362 default:
363 ALOGE("unrecognized output color space %d", cinfo->out_color_space);
364 }
365 return false;
366 }
367
decodeToCSRGB(jpeg_decompress_struct * cinfo,uint8_t * dest)368 bool JpegDecoderHelper::decodeToCSRGB(jpeg_decompress_struct* cinfo, uint8_t* dest) {
369 JSAMPLE* out = (JSAMPLE*)dest;
370
371 while (cinfo->output_scanline < cinfo->image_height) {
372 JDIMENSION read_lines = jpeg_read_scanlines(cinfo, &out, 1);
373 if (1 != read_lines) {
374 ALOGE("jpeg_read_scanlines returned %d, expected %d", read_lines, 1);
375 return false;
376 }
377 #ifdef JCS_ALPHA_EXTENSIONS
378 out += cinfo->image_width * 4;
379 #else
380 out += cinfo->image_width * 3;
381 #endif
382 }
383 return true;
384 }
385
decodeToCSYCbCr(jpeg_decompress_struct * cinfo,uint8_t * dest)386 bool JpegDecoderHelper::decodeToCSYCbCr(jpeg_decompress_struct* cinfo, uint8_t* dest) {
387 JSAMPROW mcuRows[kMaxNumComponents][4 * DCTSIZE];
388 JSAMPROW mcuRowsTmp[kMaxNumComponents][4 * DCTSIZE];
389 uint8_t* planes[kMaxNumComponents]{};
390 size_t alignedPlaneWidth[kMaxNumComponents]{};
391 JSAMPARRAY subImage[kMaxNumComponents];
392
393 for (int i = 0, plane_offset = 0; i < cinfo->num_components; i++) {
394 planes[i] = dest + plane_offset;
395 plane_offset += mPlaneWidth[i] * mPlaneHeight[i];
396 alignedPlaneWidth[i] = ALIGNM(mPlaneWidth[i], DCTSIZE);
397 if (mPlaneWidth[i] != alignedPlaneWidth[i]) {
398 mPlanesMCURow[i] = std::make_unique<uint8_t[]>(alignedPlaneWidth[i] * DCTSIZE *
399 cinfo->comp_info[i].v_samp_factor);
400 uint8_t* mem = mPlanesMCURow[i].get();
401 for (int j = 0; j < DCTSIZE * cinfo->comp_info[i].v_samp_factor;
402 j++, mem += alignedPlaneWidth[i]) {
403 mcuRowsTmp[i][j] = mem;
404 }
405 } else if (mPlaneHeight[i] % DCTSIZE != 0) {
406 mPlanesMCURow[i] = std::make_unique<uint8_t[]>(alignedPlaneWidth[i]);
407 }
408 subImage[i] = mPlaneWidth[i] == alignedPlaneWidth[i] ? mcuRows[i] : mcuRowsTmp[i];
409 }
410
411 while (cinfo->output_scanline < cinfo->image_height) {
412 JDIMENSION mcu_scanline_start[kMaxNumComponents];
413
414 for (int i = 0; i < cinfo->num_components; i++) {
415 mcu_scanline_start[i] =
416 std::ceil(((float)cinfo->output_scanline * cinfo->comp_info[i].v_samp_factor) /
417 cinfo->max_v_samp_factor);
418
419 for (int j = 0; j < cinfo->comp_info[i].v_samp_factor * DCTSIZE; j++) {
420 JDIMENSION scanline = mcu_scanline_start[i] + j;
421
422 if (scanline < mPlaneHeight[i]) {
423 mcuRows[i][j] = planes[i] + scanline * mPlaneWidth[i];
424 } else {
425 mcuRows[i][j] = mPlanesMCURow[i].get();
426 }
427 }
428 }
429
430 int processed = jpeg_read_raw_data(cinfo, subImage, DCTSIZE * cinfo->max_v_samp_factor);
431 if (processed != DCTSIZE * cinfo->max_v_samp_factor) {
432 ALOGE("number of scan lines read %d does not equal requested scan lines %d ", processed,
433 DCTSIZE * cinfo->max_v_samp_factor);
434 return false;
435 }
436
437 for (int i = 0; i < cinfo->num_components; i++) {
438 if (mPlaneWidth[i] != alignedPlaneWidth[i]) {
439 for (int j = 0; j < cinfo->comp_info[i].v_samp_factor * DCTSIZE; j++) {
440 JDIMENSION scanline = mcu_scanline_start[i] + j;
441 if (scanline < mPlaneHeight[i]) {
442 memcpy(mcuRows[i][j], mcuRowsTmp[i][j], mPlaneWidth[i]);
443 }
444 }
445 }
446 }
447 }
448 return true;
449 }
450
451 } // namespace ultrahdr
452