Coverage Report

Created: 2026-01-10 06:57

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