blob: d22f4eca9a6781854a867c64f3ac6d7ecc0e5fd6 [file] [log] [blame]
Dichen Zhang0d12ba82022-10-19 20:44:51 +00001/*
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
Dichen Zhangdbceb0e2023-04-14 19:03:18 +000017#include <ultrahdr/jpegdecoderhelper.h>
Dichen Zhang0d12ba82022-10-19 20:44:51 +000018
Dichen Zhangb27d06d2022-12-14 19:57:50 +000019#include <utils/Log.h>
Dichen Zhang0d12ba82022-10-19 20:44:51 +000020
21#include <errno.h>
22#include <setjmp.h>
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000023#include <string>
24
25using namespace std;
Dichen Zhang0d12ba82022-10-19 20:44:51 +000026
Dichen Zhangdbceb0e2023-04-14 19:03:18 +000027namespace android::ultrahdr {
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000028
Ram Mohan2838bec2023-05-19 02:46:49 +053029#define ALIGNM(x, m) ((((x) + ((m) - 1)) / (m)) * (m))
30
Dichen Zhangd18bc302022-12-16 20:55:24 +000031const uint32_t kAPP0Marker = JPEG_APP0; // JFIF
32const uint32_t kAPP1Marker = JPEG_APP0 + 1; // EXIF, XMP
33const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC
34
35const std::string kXmpNameSpace = "http://ns.adobe.com/xap/1.0/";
36const std::string kExifIdCode = "Exif";
Fyodor Kyslove00916d2023-01-16 22:12:02 +000037constexpr uint32_t kICCMarkerHeaderSize = 14;
38constexpr uint8_t kICCSig[] = {
39 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0',
40};
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000041
Dichen Zhang0d12ba82022-10-19 20:44:51 +000042struct jpegr_source_mgr : jpeg_source_mgr {
43 jpegr_source_mgr(const uint8_t* ptr, int len);
44 ~jpegr_source_mgr();
45
46 const uint8_t* mBufferPtr;
47 size_t mBufferLength;
48};
49
50struct jpegrerror_mgr {
51 struct jpeg_error_mgr pub;
52 jmp_buf setjmp_buffer;
53};
54
55static void jpegr_init_source(j_decompress_ptr cinfo) {
56 jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
57 src->next_input_byte = static_cast<const JOCTET*>(src->mBufferPtr);
58 src->bytes_in_buffer = src->mBufferLength;
59}
60
61static boolean jpegr_fill_input_buffer(j_decompress_ptr /* cinfo */) {
62 ALOGE("%s : should not get here", __func__);
63 return FALSE;
64}
65
66static void jpegr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
67 jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
68
69 if (num_bytes > static_cast<long>(src->bytes_in_buffer)) {
70 ALOGE("jpegr_skip_input_data - num_bytes > (long)src->bytes_in_buffer");
71 } else {
72 src->next_input_byte += num_bytes;
73 src->bytes_in_buffer -= num_bytes;
74 }
75}
76
77static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {}
78
79jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len) :
80 mBufferPtr(ptr), mBufferLength(len) {
81 init_source = jpegr_init_source;
82 fill_input_buffer = jpegr_fill_input_buffer;
83 skip_input_data = jpegr_skip_input_data;
84 resync_to_restart = jpeg_resync_to_restart;
85 term_source = jpegr_term_source;
86}
87
88jpegr_source_mgr::~jpegr_source_mgr() {}
89
90static void jpegrerror_exit(j_common_ptr cinfo) {
91 jpegrerror_mgr* err = reinterpret_cast<jpegrerror_mgr*>(cinfo->err);
92 longjmp(err->setjmp_buffer, 1);
93}
94
Dichen Zhang02dd0592023-02-10 20:16:57 +000095JpegDecoderHelper::JpegDecoderHelper() {
Dichen Zhang0d12ba82022-10-19 20:44:51 +000096}
97
Dichen Zhang02dd0592023-02-10 20:16:57 +000098JpegDecoderHelper::~JpegDecoderHelper() {
Dichen Zhang0d12ba82022-10-19 20:44:51 +000099}
100
Dichen Zhang02dd0592023-02-10 20:16:57 +0000101bool JpegDecoderHelper::decompressImage(const void* image, int length, bool decodeToRGBA) {
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000102 if (image == nullptr || length <= 0) {
103 ALOGE("Image size can not be handled: %d", length);
104 return false;
105 }
106
107 mResultBuffer.clear();
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000108 mXMPBuffer.clear();
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000109 if (!decode(image, length, decodeToRGBA)) {
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000110 return false;
111 }
112
113 return true;
114}
115
Dichen Zhang02dd0592023-02-10 20:16:57 +0000116void* JpegDecoderHelper::getDecompressedImagePtr() {
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000117 return mResultBuffer.data();
118}
119
Dichen Zhang02dd0592023-02-10 20:16:57 +0000120size_t JpegDecoderHelper::getDecompressedImageSize() {
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000121 return mResultBuffer.size();
122}
123
Dichen Zhang02dd0592023-02-10 20:16:57 +0000124void* JpegDecoderHelper::getXMPPtr() {
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000125 return mXMPBuffer.data();
126}
127
Dichen Zhang02dd0592023-02-10 20:16:57 +0000128size_t JpegDecoderHelper::getXMPSize() {
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000129 return mXMPBuffer.size();
130}
131
Dichen Zhang02dd0592023-02-10 20:16:57 +0000132void* JpegDecoderHelper::getEXIFPtr() {
Dichen Zhangd18bc302022-12-16 20:55:24 +0000133 return mEXIFBuffer.data();
134}
135
Dichen Zhang02dd0592023-02-10 20:16:57 +0000136size_t JpegDecoderHelper::getEXIFSize() {
Dichen Zhangd18bc302022-12-16 20:55:24 +0000137 return mEXIFBuffer.size();
138}
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000139
Nick Deakin0db53ee2023-05-19 17:14:45 -0400140void* JpegDecoderHelper::getICCPtr() {
141 return mICCBuffer.data();
142}
143
144size_t JpegDecoderHelper::getICCSize() {
145 return mICCBuffer.size();
146}
147
Dichen Zhang02dd0592023-02-10 20:16:57 +0000148size_t JpegDecoderHelper::getDecompressedImageWidth() {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400149 return mWidth;
150}
151
Dichen Zhang02dd0592023-02-10 20:16:57 +0000152size_t JpegDecoderHelper::getDecompressedImageHeight() {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400153 return mHeight;
154}
155
Dichen Zhang02dd0592023-02-10 20:16:57 +0000156bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) {
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000157 jpeg_decompress_struct cinfo;
158 jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
159 jpegrerror_mgr myerr;
Ram Mohane69d9d22023-06-02 17:44:45 +0530160 bool status = true;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000161
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000162 cinfo.err = jpeg_std_error(&myerr.pub);
163 myerr.pub.error_exit = jpegrerror_exit;
164
165 if (setjmp(myerr.setjmp_buffer)) {
166 jpeg_destroy_decompress(&cinfo);
167 return false;
168 }
169 jpeg_create_decompress(&cinfo);
170
Dichen Zhangd18bc302022-12-16 20:55:24 +0000171 jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
172 jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
173 jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000174
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000175 cinfo.src = &mgr;
176 jpeg_read_header(&cinfo, TRUE);
177
Nick Deakin0db53ee2023-05-19 17:14:45 -0400178 // Save XMP data, EXIF data, and ICC data.
179 // Here we only handle the first XMP / EXIF / ICC package.
Dichen Zhangd18bc302022-12-16 20:55:24 +0000180 // We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
181 // two bytes of package length which is stored in marker->original_length, and the real data
Nick Deakin0db53ee2023-05-19 17:14:45 -0400182 // which is stored in marker->data.
Dichen Zhangd18bc302022-12-16 20:55:24 +0000183 bool exifAppears = false;
184 bool xmpAppears = false;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400185 bool iccAppears = false;
Dichen Zhangd18bc302022-12-16 20:55:24 +0000186 for (jpeg_marker_struct* marker = cinfo.marker_list;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400187 marker && !(exifAppears && xmpAppears && iccAppears);
Dichen Zhangd18bc302022-12-16 20:55:24 +0000188 marker = marker->next) {
189
Nick Deakin0db53ee2023-05-19 17:14:45 -0400190 if (marker->marker != kAPP1Marker && marker->marker != kAPP2Marker) {
Dichen Zhangd18bc302022-12-16 20:55:24 +0000191 continue;
192 }
Dichen Zhangd18bc302022-12-16 20:55:24 +0000193 const unsigned int len = marker->data_length;
194 if (!xmpAppears &&
195 len > kXmpNameSpace.size() &&
196 !strncmp(reinterpret_cast<const char*>(marker->data),
197 kXmpNameSpace.c_str(),
198 kXmpNameSpace.size())) {
199 mXMPBuffer.resize(len+1, 0);
200 memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len);
201 xmpAppears = true;
202 } else if (!exifAppears &&
203 len > kExifIdCode.size() &&
204 !strncmp(reinterpret_cast<const char*>(marker->data),
205 kExifIdCode.c_str(),
206 kExifIdCode.size())) {
207 mEXIFBuffer.resize(len, 0);
208 memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
209 exifAppears = true;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400210 } else if (!iccAppears &&
211 len > sizeof(kICCSig) &&
212 !memcmp(marker->data, kICCSig, sizeof(kICCSig))) {
213 mICCBuffer.resize(len, 0);
214 memcpy(static_cast<void*>(mICCBuffer.data()), marker->data, len);
215 iccAppears = true;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000216 }
217 }
218
Ram Mohand136b8a2023-06-02 09:06:40 +0530219 if (cinfo.image_width > kMaxWidth || cinfo.image_height > kMaxHeight) {
220 // constraint on max width and max height is only due to alloc constraints
221 // tune these values basing on the target device
Ram Mohane69d9d22023-06-02 17:44:45 +0530222 status = false;
223 goto CleanUp;
Ram Mohand136b8a2023-06-02 09:06:40 +0530224 }
225
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400226 mWidth = cinfo.image_width;
227 mHeight = cinfo.image_height;
228
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000229 if (decodeToRGBA) {
Dichen Zhangf63125c2023-07-31 22:27:27 +0000230 // The primary image is expected to be yuv420 sampling
231 if (cinfo.jpeg_color_space != JCS_YCbCr) {
232 status = false;
233 ALOGE("%s: decodeToRGBA unexpected jpeg color space ", __func__);
234 goto CleanUp;
235 }
236 if (cinfo.comp_info[0].h_samp_factor != 2 ||
237 cinfo.comp_info[1].h_samp_factor != 1 ||
238 cinfo.comp_info[2].h_samp_factor != 1 ||
239 cinfo.comp_info[0].v_samp_factor != 2 ||
240 cinfo.comp_info[1].v_samp_factor != 1 ||
241 cinfo.comp_info[2].v_samp_factor != 1 ) {
242 status = false;
243 ALOGE("%s: decodeToRGBA unexpected primary image sub-sampling", __func__);
Ram Mohane69d9d22023-06-02 17:44:45 +0530244 goto CleanUp;
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000245 }
246 // 4 bytes per pixel
247 mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
248 cinfo.out_color_space = JCS_EXT_RGBA;
249 } else {
250 if (cinfo.jpeg_color_space == JCS_YCbCr) {
Ram Mohan2838bec2023-05-19 02:46:49 +0530251 if (cinfo.comp_info[0].h_samp_factor != 2 ||
252 cinfo.comp_info[1].h_samp_factor != 1 ||
253 cinfo.comp_info[2].h_samp_factor != 1 ||
254 cinfo.comp_info[0].v_samp_factor != 2 ||
255 cinfo.comp_info[1].v_samp_factor != 1 ||
256 cinfo.comp_info[2].v_samp_factor != 1) {
Ram Mohane69d9d22023-06-02 17:44:45 +0530257 status = false;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400258 ALOGE("%s: decoding to YUV only supports 4:2:0 subsampling", __func__);
Ram Mohane69d9d22023-06-02 17:44:45 +0530259 goto CleanUp;
Ram Mohan2838bec2023-05-19 02:46:49 +0530260 }
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000261 mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
262 } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
263 mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
Dichen Zhangf63125c2023-07-31 22:27:27 +0000264 } else {
265 status = false;
266 ALOGE("%s: decodeToYUV unexpected jpeg color space", __func__);
267 goto CleanUp;
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000268 }
269 cinfo.out_color_space = cinfo.jpeg_color_space;
270 cinfo.raw_data_out = TRUE;
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000271 }
272
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000273 cinfo.dct_method = JDCT_IFAST;
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000274
275 jpeg_start_decompress(&cinfo);
276
277 if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
278 cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
Ram Mohane69d9d22023-06-02 17:44:45 +0530279 status = false;
280 goto CleanUp;
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000281 }
282
Ram Mohane69d9d22023-06-02 17:44:45 +0530283CleanUp:
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000284 jpeg_finish_decompress(&cinfo);
285 jpeg_destroy_decompress(&cinfo);
286
Ram Mohane69d9d22023-06-02 17:44:45 +0530287 return status;
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000288}
289
Dichen Zhang02dd0592023-02-10 20:16:57 +0000290bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000291 bool isSingleChannel) {
292 if (isSingleChannel) {
293 return decompressSingleChannel(cinfo, dest);
294 }
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000295 if (cinfo->out_color_space == JCS_EXT_RGBA)
296 return decompressRGBA(cinfo, dest);
297 else
298 return decompressYUV(cinfo, dest);
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000299}
300
Dichen Zhang02dd0592023-02-10 20:16:57 +0000301bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000302 size_t *pWidth, size_t *pHeight,
Fyodor Kyslova90ee002023-01-10 23:41:41 +0000303 std::vector<uint8_t> *iccData , std::vector<uint8_t> *exifData) {
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000304 jpeg_decompress_struct cinfo;
305 jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
306 jpegrerror_mgr myerr;
307 cinfo.err = jpeg_std_error(&myerr.pub);
308 myerr.pub.error_exit = jpegrerror_exit;
309
310 if (setjmp(myerr.setjmp_buffer)) {
311 jpeg_destroy_decompress(&cinfo);
312 return false;
313 }
314 jpeg_create_decompress(&cinfo);
315
Dichen Zhangd18bc302022-12-16 20:55:24 +0000316 jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
317 jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000318
319 cinfo.src = &mgr;
320 if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
321 jpeg_destroy_decompress(&cinfo);
322 return false;
323 }
324
Nick Deakin0db53ee2023-05-19 17:14:45 -0400325 if (pWidth != nullptr) {
326 *pWidth = cinfo.image_width;
327 }
328 if (pHeight != nullptr) {
329 *pHeight = cinfo.image_height;
330 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000331
Fyodor Kyslove00916d2023-01-16 22:12:02 +0000332 if (iccData != nullptr) {
333 for (jpeg_marker_struct* marker = cinfo.marker_list; marker;
334 marker = marker->next) {
335 if (marker->marker != kAPP2Marker) {
336 continue;
337 }
338 if (marker->data_length <= kICCMarkerHeaderSize ||
339 memcmp(marker->data, kICCSig, sizeof(kICCSig)) != 0) {
340 continue;
341 }
342
Nick Deakin0db53ee2023-05-19 17:14:45 -0400343 iccData->insert(iccData->end(), marker->data, marker->data + marker->data_length);
Fyodor Kyslove00916d2023-01-16 22:12:02 +0000344 }
345 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000346
Fyodor Kyslova90ee002023-01-10 23:41:41 +0000347 if (exifData != nullptr) {
348 bool exifAppears = false;
349 for (jpeg_marker_struct* marker = cinfo.marker_list; marker && !exifAppears;
350 marker = marker->next) {
351 if (marker->marker != kAPP1Marker) {
352 continue;
353 }
354
355 const unsigned int len = marker->data_length;
356 if (len >= kExifIdCode.size() &&
357 !strncmp(reinterpret_cast<const char*>(marker->data), kExifIdCode.c_str(),
358 kExifIdCode.size())) {
359 exifData->resize(len, 0);
360 memcpy(static_cast<void*>(exifData->data()), marker->data, len);
361 exifAppears = true;
362 }
363 }
364 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000365
366 jpeg_destroy_decompress(&cinfo);
367 return true;
368}
369
Dichen Zhang02dd0592023-02-10 20:16:57 +0000370bool JpegDecoderHelper::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000371 JSAMPLE* decodeDst = (JSAMPLE*) dest;
372 uint32_t lines = 0;
373 // TODO: use batches for more effectiveness
374 while (lines < cinfo->image_height) {
375 uint32_t ret = jpeg_read_scanlines(cinfo, &decodeDst, 1);
376 if (ret == 0) {
377 break;
378 }
379 decodeDst += cinfo->image_width * 4;
380 lines++;
381 }
382 return lines == cinfo->image_height;
383}
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000384
Dichen Zhang02dd0592023-02-10 20:16:57 +0000385bool JpegDecoderHelper::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000386 JSAMPROW y[kCompressBatchSize];
387 JSAMPROW cb[kCompressBatchSize / 2];
388 JSAMPROW cr[kCompressBatchSize / 2];
389 JSAMPARRAY planes[3] {y, cb, cr};
390
391 size_t y_plane_size = cinfo->image_width * cinfo->image_height;
392 size_t uv_plane_size = y_plane_size / 4;
393 uint8_t* y_plane = const_cast<uint8_t*>(dest);
394 uint8_t* u_plane = const_cast<uint8_t*>(dest + y_plane_size);
395 uint8_t* v_plane = const_cast<uint8_t*>(dest + y_plane_size + uv_plane_size);
Ram Mohane69d9d22023-06-02 17:44:45 +0530396 std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000397 memset(empty.get(), 0, cinfo->image_width);
398
Ram Mohan2838bec2023-05-19 02:46:49 +0530399 const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
400 bool is_width_aligned = (aligned_width == cinfo->image_width);
401 std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
402 uint8_t* y_plane_intrm = nullptr;
403 uint8_t* u_plane_intrm = nullptr;
404 uint8_t* v_plane_intrm = nullptr;
405 JSAMPROW y_intrm[kCompressBatchSize];
406 JSAMPROW cb_intrm[kCompressBatchSize / 2];
407 JSAMPROW cr_intrm[kCompressBatchSize / 2];
408 JSAMPARRAY planes_intrm[3] {y_intrm, cb_intrm, cr_intrm};
409 if (!is_width_aligned) {
410 size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
411 buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
412 y_plane_intrm = buffer_intrm.get();
413 u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize);
414 v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4;
415 for (int i = 0; i < kCompressBatchSize; ++i) {
416 y_intrm[i] = y_plane_intrm + i * aligned_width;
417 }
418 for (int i = 0; i < kCompressBatchSize / 2; ++i) {
419 int offset_intrm = i * (aligned_width / 2);
420 cb_intrm[i] = u_plane_intrm + offset_intrm;
421 cr_intrm[i] = v_plane_intrm + offset_intrm;
422 }
423 }
424
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000425 while (cinfo->output_scanline < cinfo->image_height) {
426 for (int i = 0; i < kCompressBatchSize; ++i) {
427 size_t scanline = cinfo->output_scanline + i;
428 if (scanline < cinfo->image_height) {
429 y[i] = y_plane + scanline * cinfo->image_width;
430 } else {
431 y[i] = empty.get();
432 }
433 }
434 // cb, cr only have half scanlines
435 for (int i = 0; i < kCompressBatchSize / 2; ++i) {
436 size_t scanline = cinfo->output_scanline / 2 + i;
437 if (scanline < cinfo->image_height / 2) {
438 int offset = scanline * (cinfo->image_width / 2);
439 cb[i] = u_plane + offset;
440 cr[i] = v_plane + offset;
441 } else {
442 cb[i] = cr[i] = empty.get();
443 }
444 }
445
Ram Mohan2838bec2023-05-19 02:46:49 +0530446 int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
447 kCompressBatchSize);
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000448 if (processed != kCompressBatchSize) {
449 ALOGE("Number of processed lines does not equal input lines.");
450 return false;
451 }
Ram Mohan2838bec2023-05-19 02:46:49 +0530452 if (!is_width_aligned) {
453 for (int i = 0; i < kCompressBatchSize; ++i) {
454 memcpy(y[i], y_intrm[i], cinfo->image_width);
455 }
456 for (int i = 0; i < kCompressBatchSize / 2; ++i) {
457 memcpy(cb[i], cb_intrm[i], cinfo->image_width / 2);
458 memcpy(cr[i], cr_intrm[i], cinfo->image_width / 2);
459 }
460 }
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000461 }
462 return true;
463}
464
Dichen Zhang02dd0592023-02-10 20:16:57 +0000465bool JpegDecoderHelper::decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000466 JSAMPROW y[kCompressBatchSize];
467 JSAMPARRAY planes[1] {y};
468
469 uint8_t* y_plane = const_cast<uint8_t*>(dest);
Ram Mohane69d9d22023-06-02 17:44:45 +0530470 std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000471 memset(empty.get(), 0, cinfo->image_width);
472
Ram Mohan2838bec2023-05-19 02:46:49 +0530473 int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
474 bool is_width_aligned = (aligned_width == cinfo->image_width);
475 std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
476 uint8_t* y_plane_intrm = nullptr;
477 JSAMPROW y_intrm[kCompressBatchSize];
478 JSAMPARRAY planes_intrm[1] {y_intrm};
479 if (!is_width_aligned) {
480 size_t mcu_row_size = aligned_width * kCompressBatchSize;
481 buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
482 y_plane_intrm = buffer_intrm.get();
483 for (int i = 0; i < kCompressBatchSize; ++i) {
484 y_intrm[i] = y_plane_intrm + i * aligned_width;
485 }
486 }
487
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000488 while (cinfo->output_scanline < cinfo->image_height) {
489 for (int i = 0; i < kCompressBatchSize; ++i) {
490 size_t scanline = cinfo->output_scanline + i;
491 if (scanline < cinfo->image_height) {
492 y[i] = y_plane + scanline * cinfo->image_width;
493 } else {
494 y[i] = empty.get();
495 }
496 }
497
Ram Mohan2838bec2023-05-19 02:46:49 +0530498 int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
499 kCompressBatchSize);
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000500 if (processed != kCompressBatchSize / 2) {
501 ALOGE("Number of processed lines does not equal input lines.");
502 return false;
503 }
Ram Mohan2838bec2023-05-19 02:46:49 +0530504 if (!is_width_aligned) {
505 for (int i = 0; i < kCompressBatchSize; ++i) {
506 memcpy(y[i], y_intrm[i], cinfo->image_width);
507 }
508 }
Dichen Zhang0d12ba82022-10-19 20:44:51 +0000509 }
510 return true;
511}
512
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000513} // namespace ultrahdr