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