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