blob: 9af5af75e521ebc2e0850656e0fc52154184fd19 [file] [log] [blame]
Dichen Zhang85b37562022-10-11 11:08:28 -07001/*
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/jpegr.h>
18#include <ultrahdr/jpegencoderhelper.h>
19#include <ultrahdr/jpegdecoderhelper.h>
20#include <ultrahdr/gainmapmath.h>
21#include <ultrahdr/jpegrutils.h>
22#include <ultrahdr/multipictureformat.h>
23#include <ultrahdr/icc.h>
Nick Deakinf6bca5a2022-11-04 10:43:43 -040024
Dichen Zhanga8766262022-11-07 23:48:24 +000025#include <image_io/jpeg/jpeg_marker.h>
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000026#include <image_io/jpeg/jpeg_info.h>
27#include <image_io/jpeg/jpeg_scanner.h>
28#include <image_io/jpeg/jpeg_info_builder.h>
29#include <image_io/base/data_segment_data_source.h>
Dichen Zhang53751272023-01-17 19:09:01 -080030#include <utils/Log.h>
Nick Deakinf6bca5a2022-11-04 10:43:43 -040031
Dichen Zhang6438a192023-01-29 07:51:15 +000032#include <map>
Nick Deakinf6bca5a2022-11-04 10:43:43 -040033#include <memory>
Dichen Zhang72fd2b12022-11-01 06:11:50 +000034#include <sstream>
35#include <string>
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000036#include <cmath>
Harish Mahendrakar72b6f302022-12-16 10:39:15 -080037#include <condition_variable>
38#include <deque>
39#include <mutex>
40#include <thread>
41#include <unistd.h>
Dichen Zhang72fd2b12022-11-01 06:11:50 +000042
43using namespace std;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000044using namespace photos_editing_formats::image_io;
Dichen Zhang85b37562022-10-11 11:08:28 -070045
Dichen Zhangdbceb0e2023-04-14 19:03:18 +000046namespace android::ultrahdr {
Dichen Zhang85b37562022-10-11 11:08:28 -070047
Harish Mahendrakar555a06b2022-12-14 09:37:27 -080048#define USE_SRGB_INVOETF_LUT 1
49#define USE_HLG_OETF_LUT 1
50#define USE_PQ_OETF_LUT 1
51#define USE_HLG_INVOETF_LUT 1
52#define USE_PQ_INVOETF_LUT 1
Dichen Zhang10959a42023-04-10 16:28:16 -070053#define USE_APPLY_GAIN_LUT 1
Harish Mahendrakar555a06b2022-12-14 09:37:27 -080054
Nick Deakinf6bca5a2022-11-04 10:43:43 -040055#define JPEGR_CHECK(x) \
56 { \
57 status_t status = (x); \
58 if ((status) != NO_ERROR) { \
59 return status; \
60 } \
61 }
62
Nick Deakin6bd90432022-11-20 16:26:37 -050063// The current JPEGR version that we encode to
Nick Deakin05ceebf2023-04-19 15:27:13 -040064static const char* const kJpegrVersion = "1.0";
Nick Deakin6bd90432022-11-20 16:26:37 -050065
Nick Deakinf6bca5a2022-11-04 10:43:43 -040066// Map is quarter res / sixteenth size
67static const size_t kMapDimensionScaleFactor = 4;
Ram Mohan033270a2023-05-18 15:26:33 +053068
69// Gain Map width is (image_width / kMapDimensionScaleFactor). If we were to
70// compress 420 GainMap in jpeg, then we need at least 2 samples. For Grayscale
71// 1 sample is sufficient. We are using 2 here anyways
72static const int kMinWidth = 2 * kMapDimensionScaleFactor;
73static const int kMinHeight = 2 * kMapDimensionScaleFactor;
74
Dichen Zhang53751272023-01-17 19:09:01 -080075// JPEG block size.
Dichen Zhang56a7d592023-04-14 16:57:34 +000076// JPEG encoding / decoding will require block based DCT transform 16 x 16 for luma,
77// and 8 x 8 for chroma.
78// Width must be 16 dividable for luma, and 8 dividable for chroma.
Ram Mohan4a72c192023-05-22 19:05:06 +053079// If this criteria is not facilitated, we will pad zeros based to each line on the
80// required block size.
Dichen Zhang56a7d592023-04-14 16:57:34 +000081static const size_t kJpegBlock = JpegEncoderHelper::kCompressBatchSize;
Dichen Zhang10959a42023-04-10 16:28:16 -070082// JPEG compress quality (0 ~ 100) for gain map
Dichen Zhang0b9f7de2022-11-18 06:52:46 +000083static const int kMapCompressQuality = 85;
Nick Deakinf6bca5a2022-11-04 10:43:43 -040084
Harish Mahendrakar72b6f302022-12-16 10:39:15 -080085#define CONFIG_MULTITHREAD 1
86int GetCPUCoreCount() {
87 int cpuCoreCount = 1;
88#if CONFIG_MULTITHREAD
89#if defined(_SC_NPROCESSORS_ONLN)
90 cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
91#else
92 // _SC_NPROC_ONLN must be defined...
93 cpuCoreCount = sysconf(_SC_NPROC_ONLN);
94#endif
95#endif
96 return cpuCoreCount;
97}
98
Ram Mohanac1cfec2023-05-18 14:41:15 +053099status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr uncompressed_p010_image,
100 jr_uncompressed_ptr uncompressed_yuv_420_image,
101 ultrahdr_transfer_function hdr_tf,
102 jr_compressed_ptr dest) {
103 if (uncompressed_p010_image == nullptr || uncompressed_p010_image->data == nullptr) {
104 ALOGE("received nullptr for uncompressed p010 image");
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700105 return ERROR_JPEGR_INVALID_NULL_PTR;
106 }
107
Ram Mohanac1cfec2023-05-18 14:41:15 +0530108 if (uncompressed_p010_image->width % 2 != 0
109 || uncompressed_p010_image->height % 2 != 0) {
110 ALOGE("Image dimensions cannot be odd, image dimensions %dx%d",
111 uncompressed_p010_image->width, uncompressed_p010_image->height);
112 return ERROR_JPEGR_INVALID_INPUT_TYPE;
113 }
114
Ram Mohan033270a2023-05-18 15:26:33 +0530115 if (uncompressed_p010_image->width < kMinWidth
116 || uncompressed_p010_image->height < kMinHeight) {
117 ALOGE("Image dimensions cannot be less than %dx%d, image dimensions %dx%d",
118 kMinWidth, kMinHeight, uncompressed_p010_image->width, uncompressed_p010_image->height);
Ram Mohanac1cfec2023-05-18 14:41:15 +0530119 return ERROR_JPEGR_INVALID_INPUT_TYPE;
120 }
121
Ram Mohand136b8a2023-06-02 09:06:40 +0530122 if (uncompressed_p010_image->width > kMaxWidth
123 || uncompressed_p010_image->height > kMaxHeight) {
124 ALOGE("Image dimensions cannot be larger than %dx%d, image dimensions %dx%d",
125 kMaxWidth, kMaxHeight, uncompressed_p010_image->width, uncompressed_p010_image->height);
126 return ERROR_JPEGR_INVALID_INPUT_TYPE;
127 }
128
Ram Mohanac1cfec2023-05-18 14:41:15 +0530129 if (uncompressed_p010_image->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED
130 || uncompressed_p010_image->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
131 ALOGE("Unrecognized p010 color gamut %d", uncompressed_p010_image->colorGamut);
132 return ERROR_JPEGR_INVALID_INPUT_TYPE;
133 }
134
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700135 if (uncompressed_p010_image->luma_stride != 0
136 && uncompressed_p010_image->luma_stride < uncompressed_p010_image->width) {
Ram Mohanac1cfec2023-05-18 14:41:15 +0530137 ALOGE("Luma stride can not be smaller than width, stride=%d, width=%d",
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700138 uncompressed_p010_image->luma_stride, uncompressed_p010_image->width);
139 return ERROR_JPEGR_INVALID_INPUT_TYPE;
140 }
141
Ram Mohanac1cfec2023-05-18 14:41:15 +0530142 if (uncompressed_p010_image->chroma_data != nullptr
143 && uncompressed_p010_image->chroma_stride < uncompressed_p010_image->width) {
144 ALOGE("Chroma stride can not be smaller than width, stride=%d, width=%d",
145 uncompressed_p010_image->chroma_stride,
146 uncompressed_p010_image->width);
147 return ERROR_JPEGR_INVALID_INPUT_TYPE;
148 }
149
150 if (dest == nullptr || dest->data == nullptr) {
151 ALOGE("received nullptr for destination");
152 return ERROR_JPEGR_INVALID_NULL_PTR;
153 }
154
Ram Mohan9b3d6852023-05-26 00:09:50 +0530155 if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX
156 || hdr_tf == ULTRAHDR_TF_SRGB) {
Ram Mohanac1cfec2023-05-18 14:41:15 +0530157 ALOGE("Invalid hdr transfer function %d", hdr_tf);
158 return ERROR_JPEGR_INVALID_INPUT_TYPE;
159 }
160
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700161 if (uncompressed_yuv_420_image == nullptr) {
162 return NO_ERROR;
163 }
164
Ram Mohanac1cfec2023-05-18 14:41:15 +0530165 if (uncompressed_yuv_420_image->data == nullptr) {
166 ALOGE("received nullptr for uncompressed 420 image");
167 return ERROR_JPEGR_INVALID_NULL_PTR;
168 }
169
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700170 if (uncompressed_yuv_420_image->luma_stride != 0) {
171 ALOGE("Stride is not supported for YUV420 image");
172 return ERROR_JPEGR_UNSUPPORTED_FEATURE;
173 }
174
175 if (uncompressed_yuv_420_image->chroma_data != nullptr) {
176 ALOGE("Pointer to chroma plane is not supported for YUV420 image, chroma data must"
177 "be immediately after the luma data.");
178 return ERROR_JPEGR_UNSUPPORTED_FEATURE;
179 }
180
181 if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
182 || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
183 ALOGE("Image resolutions mismatch: P010: %dx%d, YUV420: %dx%d",
184 uncompressed_p010_image->width,
185 uncompressed_p010_image->height,
186 uncompressed_yuv_420_image->width,
187 uncompressed_yuv_420_image->height);
188 return ERROR_JPEGR_RESOLUTION_MISMATCH;
189 }
190
Ram Mohanac1cfec2023-05-18 14:41:15 +0530191 if (uncompressed_yuv_420_image->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED
192 || uncompressed_yuv_420_image->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
193 ALOGE("Unrecognized 420 color gamut %d", uncompressed_yuv_420_image->colorGamut);
194 return ERROR_JPEGR_INVALID_INPUT_TYPE;
195 }
196
197 return NO_ERROR;
198}
199
200status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr uncompressed_p010_image,
201 jr_uncompressed_ptr uncompressed_yuv_420_image,
202 ultrahdr_transfer_function hdr_tf,
203 jr_compressed_ptr dest,
204 int quality) {
205 if (status_t ret = areInputArgumentsValid(
206 uncompressed_p010_image, uncompressed_yuv_420_image, hdr_tf, dest) != NO_ERROR) {
207 return ret;
208 }
209
210 if (quality < 0 || quality > 100) {
211 ALOGE("quality factor is out side range [0-100], quality factor : %d", quality);
212 return ERROR_JPEGR_INVALID_INPUT_TYPE;
213 }
214
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700215 return NO_ERROR;
216}
217
Dichen Zhang636f5242022-12-07 20:25:44 +0000218/* Encode API-0 */
Dichen Zhang761b52d2023-02-10 22:38:38 +0000219status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000220 ultrahdr_transfer_function hdr_tf,
Dichen Zhang61ede362023-02-22 18:50:13 +0000221 jr_compressed_ptr dest,
222 int quality,
223 jr_exif_ptr exif) {
Ram Mohanac1cfec2023-05-18 14:41:15 +0530224 if (status_t ret = areInputArgumentsValid(
225 uncompressed_p010_image, /* uncompressed_yuv_420_image */ nullptr,
226 hdr_tf, dest, quality) != NO_ERROR) {
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700227 return ret;
Dichen Zhang53751272023-01-17 19:09:01 -0800228 }
229
Ram Mohanac1cfec2023-05-18 14:41:15 +0530230 if (exif != nullptr && exif->data == nullptr) {
231 ALOGE("received nullptr for exif metadata");
232 return ERROR_JPEGR_INVALID_NULL_PTR;
233 }
234
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000235 ultrahdr_metadata_struct metadata;
Dichen Zhang636f5242022-12-07 20:25:44 +0000236 metadata.version = kJpegrVersion;
Dichen Zhang636f5242022-12-07 20:25:44 +0000237
238 jpegr_uncompressed_struct uncompressed_yuv_420_image;
Ram Mohan4a72c192023-05-22 19:05:06 +0530239 unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
240 uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2);
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800241 uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get();
Dichen Zhang636f5242022-12-07 20:25:44 +0000242 JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
243
244 jpegr_uncompressed_struct map;
Dichen Zhang10959a42023-04-10 16:28:16 -0700245 JPEGR_CHECK(generateGainMap(
Nick Deakin01759062023-02-02 18:21:43 -0500246 &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
Dichen Zhang636f5242022-12-07 20:25:44 +0000247 std::unique_ptr<uint8_t[]> map_data;
248 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
249
Ram Mohanf9540402023-05-18 20:08:55 +0530250 JpegEncoderHelper jpeg_encoder_gainmap;
251 JPEGR_CHECK(compressGainMap(&map, &jpeg_encoder_gainmap));
Dichen Zhang636f5242022-12-07 20:25:44 +0000252 jpegr_compressed_struct compressed_map;
Ram Mohanf9540402023-05-18 20:08:55 +0530253 compressed_map.maxLength = jpeg_encoder_gainmap.getCompressedImageSize();
254 compressed_map.length = compressed_map.maxLength;
255 compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
256 compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
Dichen Zhang636f5242022-12-07 20:25:44 +0000257
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000258 sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
Dichen Zhange46f9bb2023-02-23 19:34:53 +0000259 uncompressed_yuv_420_image.colorGamut);
Dichen Zhang6438a192023-01-29 07:51:15 +0000260
Nick Deakin0db53ee2023-05-19 17:14:45 -0400261 // Convert to Bt601 YUV encoding for JPEG encode
262 JPEGR_CHECK(convertYuv(&uncompressed_yuv_420_image, uncompressed_yuv_420_image.colorGamut,
263 ULTRAHDR_COLORGAMUT_P3));
264
Dichen Zhang02dd0592023-02-10 20:16:57 +0000265 JpegEncoderHelper jpeg_encoder;
Dichen Zhang636f5242022-12-07 20:25:44 +0000266 if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
267 uncompressed_yuv_420_image.width,
Dichen Zhang6438a192023-01-29 07:51:15 +0000268 uncompressed_yuv_420_image.height, quality,
Dichen Zhange46f9bb2023-02-23 19:34:53 +0000269 icc->getData(), icc->getLength())) {
Dichen Zhang636f5242022-12-07 20:25:44 +0000270 return ERROR_JPEGR_ENCODE_ERROR;
271 }
272 jpegr_compressed_struct jpeg;
273 jpeg.data = jpeg_encoder.getCompressedImagePtr();
274 jpeg.length = jpeg_encoder.getCompressedImageSize();
275
Nick Deakin0db53ee2023-05-19 17:14:45 -0400276 // No ICC since JPEG encode already did it
277 JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
278 &metadata, dest));
Dichen Zhang636f5242022-12-07 20:25:44 +0000279
280 return NO_ERROR;
281}
282
283/* Encode API-1 */
Dichen Zhang761b52d2023-02-10 22:38:38 +0000284status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
Dichen Zhang61ede362023-02-22 18:50:13 +0000285 jr_uncompressed_ptr uncompressed_yuv_420_image,
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000286 ultrahdr_transfer_function hdr_tf,
Dichen Zhang61ede362023-02-22 18:50:13 +0000287 jr_compressed_ptr dest,
288 int quality,
289 jr_exif_ptr exif) {
Ram Mohanac1cfec2023-05-18 14:41:15 +0530290 if (uncompressed_yuv_420_image == nullptr) {
291 ALOGE("received nullptr for uncompressed 420 image");
Dichen Zhang80b72482022-11-02 01:55:35 +0000292 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000293 }
294
Ram Mohanac1cfec2023-05-18 14:41:15 +0530295 if (exif != nullptr && exif->data == nullptr) {
296 ALOGE("received nullptr for exif metadata");
297 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhangffa34012022-11-03 23:21:13 +0000298 }
299
Ram Mohanac1cfec2023-05-18 14:41:15 +0530300 if (status_t ret = areInputArgumentsValid(
301 uncompressed_p010_image, uncompressed_yuv_420_image, hdr_tf,
302 dest, quality) != NO_ERROR) {
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700303 return ret;
Dichen Zhang53751272023-01-17 19:09:01 -0800304 }
305
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000306 ultrahdr_metadata_struct metadata;
Nick Deakin6bd90432022-11-20 16:26:37 -0500307 metadata.version = kJpegrVersion;
Nick Deakin6bd90432022-11-20 16:26:37 -0500308
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400309 jpegr_uncompressed_struct map;
Dichen Zhang10959a42023-04-10 16:28:16 -0700310 JPEGR_CHECK(generateGainMap(
Nick Deakin01759062023-02-02 18:21:43 -0500311 uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400312 std::unique_ptr<uint8_t[]> map_data;
313 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
314
Ram Mohanf9540402023-05-18 20:08:55 +0530315 JpegEncoderHelper jpeg_encoder_gainmap;
316 JPEGR_CHECK(compressGainMap(&map, &jpeg_encoder_gainmap));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400317 jpegr_compressed_struct compressed_map;
Ram Mohanf9540402023-05-18 20:08:55 +0530318 compressed_map.maxLength = jpeg_encoder_gainmap.getCompressedImageSize();
319 compressed_map.length = compressed_map.maxLength;
320 compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
321 compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400322
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000323 sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
Dichen Zhange46f9bb2023-02-23 19:34:53 +0000324 uncompressed_yuv_420_image->colorGamut);
Dichen Zhang6438a192023-01-29 07:51:15 +0000325
Nick Deakin0db53ee2023-05-19 17:14:45 -0400326 // Convert to Bt601 YUV encoding for JPEG encode; make a copy so as to no clobber client data
327 unique_ptr<uint8_t[]> yuv_420_bt601_data = make_unique<uint8_t[]>(
328 uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height * 3 / 2);
329 memcpy(yuv_420_bt601_data.get(), uncompressed_yuv_420_image->data,
330 uncompressed_yuv_420_image->width * uncompressed_yuv_420_image->height * 3 / 2);
331
332 jpegr_uncompressed_struct yuv_420_bt601_image = {
333 yuv_420_bt601_data.get(), uncompressed_yuv_420_image->width, uncompressed_yuv_420_image->height,
334 uncompressed_yuv_420_image->colorGamut };
335 JPEGR_CHECK(convertYuv(&yuv_420_bt601_image, yuv_420_bt601_image.colorGamut,
336 ULTRAHDR_COLORGAMUT_P3));
337
Dichen Zhang02dd0592023-02-10 20:16:57 +0000338 JpegEncoderHelper jpeg_encoder;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400339 if (!jpeg_encoder.compressImage(yuv_420_bt601_image.data,
340 yuv_420_bt601_image.width,
341 yuv_420_bt601_image.height, quality,
Dichen Zhange46f9bb2023-02-23 19:34:53 +0000342 icc->getData(), icc->getLength())) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400343 return ERROR_JPEGR_ENCODE_ERROR;
344 }
345 jpegr_compressed_struct jpeg;
346 jpeg.data = jpeg_encoder.getCompressedImagePtr();
347 jpeg.length = jpeg_encoder.getCompressedImageSize();
348
Nick Deakin0db53ee2023-05-19 17:14:45 -0400349 // No ICC since jpeg encode already did it
350 JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
351 &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400352
Dichen Zhang6947d532022-10-22 02:16:21 +0000353 return NO_ERROR;
354}
355
Dichen Zhang636f5242022-12-07 20:25:44 +0000356/* Encode API-2 */
Dichen Zhang761b52d2023-02-10 22:38:38 +0000357status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
Dichen Zhang61ede362023-02-22 18:50:13 +0000358 jr_uncompressed_ptr uncompressed_yuv_420_image,
359 jr_compressed_ptr compressed_jpeg_image,
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000360 ultrahdr_transfer_function hdr_tf,
Dichen Zhang61ede362023-02-22 18:50:13 +0000361 jr_compressed_ptr dest) {
Ram Mohanac1cfec2023-05-18 14:41:15 +0530362 if (uncompressed_yuv_420_image == nullptr) {
363 ALOGE("received nullptr for uncompressed 420 image");
Dichen Zhang80b72482022-11-02 01:55:35 +0000364 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000365 }
366
Ram Mohanac1cfec2023-05-18 14:41:15 +0530367 if (compressed_jpeg_image == nullptr || compressed_jpeg_image->data == nullptr) {
368 ALOGE("received nullptr for compressed jpeg image");
369 return ERROR_JPEGR_INVALID_NULL_PTR;
370 }
371
372 if (status_t ret = areInputArgumentsValid(
373 uncompressed_p010_image, uncompressed_yuv_420_image, hdr_tf, dest) != NO_ERROR) {
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700374 return ret;
Dichen Zhang53751272023-01-17 19:09:01 -0800375 }
376
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000377 ultrahdr_metadata_struct metadata;
Nick Deakin6bd90432022-11-20 16:26:37 -0500378 metadata.version = kJpegrVersion;
Nick Deakin6bd90432022-11-20 16:26:37 -0500379
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400380 jpegr_uncompressed_struct map;
Dichen Zhang10959a42023-04-10 16:28:16 -0700381 JPEGR_CHECK(generateGainMap(
Nick Deakin01759062023-02-02 18:21:43 -0500382 uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400383 std::unique_ptr<uint8_t[]> map_data;
384 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
385
Ram Mohanf9540402023-05-18 20:08:55 +0530386 JpegEncoderHelper jpeg_encoder_gainmap;
387 JPEGR_CHECK(compressGainMap(&map, &jpeg_encoder_gainmap));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400388 jpegr_compressed_struct compressed_map;
Ram Mohanf9540402023-05-18 20:08:55 +0530389 compressed_map.maxLength = jpeg_encoder_gainmap.getCompressedImageSize();
390 compressed_map.length = compressed_map.maxLength;
391 compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
392 compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400393
Nick Deakin0db53ee2023-05-19 17:14:45 -0400394 // We just want to check if ICC is present, so don't do a full decode. Note,
395 // this doesn't verify that the ICC is valid.
396 JpegDecoderHelper decoder;
397 std::vector<uint8_t> icc;
398 decoder.getCompressedImageParameters(compressed_jpeg_image->data, compressed_jpeg_image->length,
399 /* pWidth */ nullptr, /* pHeight */ nullptr,
400 &icc, /* exifData */ nullptr);
401
402 // Add ICC if not already present.
403 if (icc.size() > 0) {
404 JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
405 /* icc */ nullptr, /* icc size */ 0, &metadata, dest));
406 } else {
407 sp<DataStruct> newIcc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
408 uncompressed_yuv_420_image->colorGamut);
409 JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
410 newIcc->getData(), newIcc->getLength(), &metadata, dest));
411 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400412
Dichen Zhang6947d532022-10-22 02:16:21 +0000413 return NO_ERROR;
414}
415
Dichen Zhang636f5242022-12-07 20:25:44 +0000416/* Encode API-3 */
Dichen Zhang761b52d2023-02-10 22:38:38 +0000417status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
Dichen Zhang61ede362023-02-22 18:50:13 +0000418 jr_compressed_ptr compressed_jpeg_image,
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000419 ultrahdr_transfer_function hdr_tf,
Dichen Zhang61ede362023-02-22 18:50:13 +0000420 jr_compressed_ptr dest) {
Ram Mohanac1cfec2023-05-18 14:41:15 +0530421 if (compressed_jpeg_image == nullptr || compressed_jpeg_image->data == nullptr) {
422 ALOGE("received nullptr for compressed jpeg image");
Dichen Zhang80b72482022-11-02 01:55:35 +0000423 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000424 }
425
Ram Mohanac1cfec2023-05-18 14:41:15 +0530426 if (status_t ret = areInputArgumentsValid(
427 uncompressed_p010_image, /* uncompressed_yuv_420_image */ nullptr,
428 hdr_tf, dest) != NO_ERROR) {
Dichen Zhang66ca6e32023-04-05 12:22:54 -0700429 return ret;
Dichen Zhang53751272023-01-17 19:09:01 -0800430 }
431
Nick Deakin0db53ee2023-05-19 17:14:45 -0400432 // Note: output is Bt.601 YUV encoded regardless of gamut, due to jpeg decode.
Dichen Zhang02dd0592023-02-10 20:16:57 +0000433 JpegDecoderHelper jpeg_decoder;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400434 if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
435 return ERROR_JPEGR_DECODE_ERROR;
436 }
437 jpegr_uncompressed_struct uncompressed_yuv_420_image;
438 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
439 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
440 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
Nick Deakin6bd90432022-11-20 16:26:37 -0500441 uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400442
443 if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
444 || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
445 return ERROR_JPEGR_RESOLUTION_MISMATCH;
446 }
447
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000448 ultrahdr_metadata_struct metadata;
Nick Deakin6bd90432022-11-20 16:26:37 -0500449 metadata.version = kJpegrVersion;
Nick Deakin6bd90432022-11-20 16:26:37 -0500450
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400451 jpegr_uncompressed_struct map;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400452 // Indicate that the SDR image is Bt.601 YUV encoded.
Dichen Zhang10959a42023-04-10 16:28:16 -0700453 JPEGR_CHECK(generateGainMap(
Nick Deakin0db53ee2023-05-19 17:14:45 -0400454 &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map,
455 true /* sdr_is_601 */ ));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400456 std::unique_ptr<uint8_t[]> map_data;
457 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
458
Ram Mohanf9540402023-05-18 20:08:55 +0530459 JpegEncoderHelper jpeg_encoder_gainmap;
460 JPEGR_CHECK(compressGainMap(&map, &jpeg_encoder_gainmap));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400461 jpegr_compressed_struct compressed_map;
Ram Mohanf9540402023-05-18 20:08:55 +0530462 compressed_map.maxLength = jpeg_encoder_gainmap.getCompressedImageSize();
463 compressed_map.length = compressed_map.maxLength;
464 compressed_map.data = jpeg_encoder_gainmap.getCompressedImagePtr();
465 compressed_map.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400466
Nick Deakin0db53ee2023-05-19 17:14:45 -0400467 // We just want to check if ICC is present, so don't do a full decode. Note,
468 // this doesn't verify that the ICC is valid.
469 JpegDecoderHelper decoder;
470 std::vector<uint8_t> icc;
471 decoder.getCompressedImageParameters(compressed_jpeg_image->data, compressed_jpeg_image->length,
472 /* pWidth */ nullptr, /* pHeight */ nullptr,
473 &icc, /* exifData */ nullptr);
474
475 // Add ICC if not already present.
476 if (icc.size() > 0) {
477 JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
478 /* icc */ nullptr, /* icc size */ 0, &metadata, dest));
479 } else {
480 sp<DataStruct> newIcc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
481 uncompressed_yuv_420_image.colorGamut);
482 JPEGR_CHECK(appendGainMap(compressed_jpeg_image, &compressed_map, /* exif */ nullptr,
483 newIcc->getData(), newIcc->getLength(), &metadata, dest));
484 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400485
Dichen Zhang6947d532022-10-22 02:16:21 +0000486 return NO_ERROR;
487}
488
Dichen Zhang92e6c6b2023-04-14 20:20:14 +0000489/* Encode API-4 */
490status_t JpegR::encodeJPEGR(jr_compressed_ptr compressed_jpeg_image,
491 jr_compressed_ptr compressed_gainmap,
492 ultrahdr_metadata_ptr metadata,
493 jr_compressed_ptr dest) {
Ram Mohanac1cfec2023-05-18 14:41:15 +0530494 if (compressed_jpeg_image == nullptr || compressed_jpeg_image->data == nullptr) {
495 ALOGE("received nullptr for compressed jpeg image");
496 return ERROR_JPEGR_INVALID_NULL_PTR;
497 }
498
499 if (compressed_gainmap == nullptr || compressed_gainmap->data == nullptr) {
500 ALOGE("received nullptr for compressed gain map");
501 return ERROR_JPEGR_INVALID_NULL_PTR;
502 }
503
504 if (dest == nullptr || dest->data == nullptr) {
505 ALOGE("received nullptr for destination");
506 return ERROR_JPEGR_INVALID_NULL_PTR;
507 }
508
Nick Deakin0db53ee2023-05-19 17:14:45 -0400509 // We just want to check if ICC is present, so don't do a full decode. Note,
510 // this doesn't verify that the ICC is valid.
511 JpegDecoderHelper decoder;
512 std::vector<uint8_t> icc;
513 decoder.getCompressedImageParameters(compressed_jpeg_image->data, compressed_jpeg_image->length,
514 /* pWidth */ nullptr, /* pHeight */ nullptr,
515 &icc, /* exifData */ nullptr);
516
517 // Add ICC if not already present.
518 if (icc.size() > 0) {
519 JPEGR_CHECK(appendGainMap(compressed_jpeg_image, compressed_gainmap, /* exif */ nullptr,
520 /* icc */ nullptr, /* icc size */ 0, metadata, dest));
521 } else {
522 sp<DataStruct> newIcc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
523 compressed_jpeg_image->colorGamut);
524 JPEGR_CHECK(appendGainMap(compressed_jpeg_image, compressed_gainmap, /* exif */ nullptr,
525 newIcc->getData(), newIcc->getLength(), metadata, dest));
526 }
527
Dichen Zhang92e6c6b2023-04-14 20:20:14 +0000528 return NO_ERROR;
529}
530
Dichen Zhang61ede362023-02-22 18:50:13 +0000531status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_ptr jpegr_info) {
Ram Mohanac1cfec2023-05-18 14:41:15 +0530532 if (compressed_jpegr_image == nullptr || compressed_jpegr_image->data == nullptr) {
533 ALOGE("received nullptr for compressed jpegr image");
534 return ERROR_JPEGR_INVALID_NULL_PTR;
535 }
536
537 if (jpegr_info == nullptr) {
538 ALOGE("received nullptr for compressed jpegr info struct");
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000539 return ERROR_JPEGR_INVALID_NULL_PTR;
540 }
541
Dichen Zhang10959a42023-04-10 16:28:16 -0700542 jpegr_compressed_struct primary_image, gain_map;
543 JPEGR_CHECK(extractPrimaryImageAndGainMap(compressed_jpegr_image,
544 &primary_image, &gain_map));
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000545
Dichen Zhang02dd0592023-02-10 20:16:57 +0000546 JpegDecoderHelper jpeg_decoder;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000547 if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
548 &jpegr_info->width, &jpegr_info->height,
549 jpegr_info->iccData, jpegr_info->exifData)) {
550 return ERROR_JPEGR_DECODE_ERROR;
551 }
552
553 return NO_ERROR;
554}
555
Dichen Zhang636f5242022-12-07 20:25:44 +0000556/* Decode API */
Dichen Zhang761b52d2023-02-10 22:38:38 +0000557status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
Dichen Zhang61ede362023-02-22 18:50:13 +0000558 jr_uncompressed_ptr dest,
Dichen Zhangc6605702023-03-15 18:40:55 -0700559 float max_display_boost,
Dichen Zhang61ede362023-02-22 18:50:13 +0000560 jr_exif_ptr exif,
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000561 ultrahdr_output_format output_format,
Dichen Zhang10959a42023-04-10 16:28:16 -0700562 jr_uncompressed_ptr gain_map,
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000563 ultrahdr_metadata_ptr metadata) {
Ram Mohanb0375052023-05-20 04:03:48 +0530564 if (compressed_jpegr_image == nullptr || compressed_jpegr_image->data == nullptr) {
565 ALOGE("received nullptr for compressed jpegr image");
566 return ERROR_JPEGR_INVALID_NULL_PTR;
567 }
568
569 if (dest == nullptr || dest->data == nullptr) {
570 ALOGE("received nullptr for dest image");
Dichen Zhang80b72482022-11-02 01:55:35 +0000571 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000572 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000573
Dichen Zhangb96ba8f2023-03-21 23:33:41 +0000574 if (max_display_boost < 1.0f) {
Ram Mohanb0375052023-05-20 04:03:48 +0530575 ALOGE("received bad value for max_display_boost %f", max_display_boost);
576 return ERROR_JPEGR_INVALID_INPUT_TYPE;
577 }
578
579 if (exif != nullptr && exif->data == nullptr) {
580 ALOGE("received nullptr address for exif data");
581 return ERROR_JPEGR_INVALID_INPUT_TYPE;
582 }
583
584 if (output_format <= ULTRAHDR_OUTPUT_UNSPECIFIED || output_format > ULTRAHDR_OUTPUT_MAX) {
585 ALOGE("received bad value for output format %d", output_format);
586 return ERROR_JPEGR_INVALID_INPUT_TYPE;
587 }
588
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000589 if (output_format == ULTRAHDR_OUTPUT_SDR) {
Dichen Zhang02dd0592023-02-10 20:16:57 +0000590 JpegDecoderHelper jpeg_decoder;
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000591 if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
592 true)) {
593 return ERROR_JPEGR_DECODE_ERROR;
594 }
595 jpegr_uncompressed_struct uncompressed_rgba_image;
596 uncompressed_rgba_image.data = jpeg_decoder.getDecompressedImagePtr();
597 uncompressed_rgba_image.width = jpeg_decoder.getDecompressedImageWidth();
598 uncompressed_rgba_image.height = jpeg_decoder.getDecompressedImageHeight();
599 memcpy(dest->data, uncompressed_rgba_image.data,
600 uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
601 dest->width = uncompressed_rgba_image.width;
602 dest->height = uncompressed_rgba_image.height;
Dichen Zhang14f3c472023-03-08 07:24:48 +0000603
Dichen Zhang10959a42023-04-10 16:28:16 -0700604 if (gain_map == nullptr && exif == nullptr) {
Dichen Zhang14f3c472023-03-08 07:24:48 +0000605 return NO_ERROR;
606 }
607
608 if (exif != nullptr) {
609 if (exif->data == nullptr) {
610 return ERROR_JPEGR_INVALID_NULL_PTR;
611 }
612 if (exif->length < jpeg_decoder.getEXIFSize()) {
613 return ERROR_JPEGR_BUFFER_TOO_SMALL;
614 }
615 memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize());
616 exif->length = jpeg_decoder.getEXIFSize();
617 }
Dichen Zhang10959a42023-04-10 16:28:16 -0700618 if (gain_map == nullptr) {
Dichen Zhang14f3c472023-03-08 07:24:48 +0000619 return NO_ERROR;
620 }
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000621 }
622
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400623 jpegr_compressed_struct compressed_map;
Dichen Zhang10959a42023-04-10 16:28:16 -0700624 JPEGR_CHECK(extractGainMap(compressed_jpegr_image, &compressed_map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400625
Dichen Zhang10959a42023-04-10 16:28:16 -0700626 JpegDecoderHelper gain_map_decoder;
627 if (!gain_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
Dichen Zhang14f3c472023-03-08 07:24:48 +0000628 return ERROR_JPEGR_DECODE_ERROR;
629 }
Ram Mohan9b3d6852023-05-26 00:09:50 +0530630 if ((gain_map_decoder.getDecompressedImageWidth() *
631 gain_map_decoder.getDecompressedImageHeight()) >
632 gain_map_decoder.getDecompressedImageSize()) {
633 return ERROR_JPEGR_CALCULATION_ERROR;
634 }
Dichen Zhang14f3c472023-03-08 07:24:48 +0000635
Dichen Zhang10959a42023-04-10 16:28:16 -0700636 if (gain_map != nullptr) {
637 gain_map->width = gain_map_decoder.getDecompressedImageWidth();
638 gain_map->height = gain_map_decoder.getDecompressedImageHeight();
639 int size = gain_map->width * gain_map->height;
640 gain_map->data = malloc(size);
641 memcpy(gain_map->data, gain_map_decoder.getDecompressedImagePtr(), size);
Dichen Zhang14f3c472023-03-08 07:24:48 +0000642 }
643
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000644 ultrahdr_metadata_struct uhdr_metadata;
Dichen Zhang10959a42023-04-10 16:28:16 -0700645 if (!getMetadataFromXMP(static_cast<uint8_t*>(gain_map_decoder.getXMPPtr()),
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000646 gain_map_decoder.getXMPSize(), &uhdr_metadata)) {
Dichen Zhange286f1c2023-03-14 00:22:00 +0000647 return ERROR_JPEGR_DECODE_ERROR;
648 }
649
650 if (metadata != nullptr) {
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000651 metadata->version = uhdr_metadata.version;
652 metadata->minContentBoost = uhdr_metadata.minContentBoost;
653 metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
Dichen Zhange286f1c2023-03-14 00:22:00 +0000654 }
655
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000656 if (output_format == ULTRAHDR_OUTPUT_SDR) {
Dichen Zhang14f3c472023-03-08 07:24:48 +0000657 return NO_ERROR;
658 }
659
Dichen Zhang02dd0592023-02-10 20:16:57 +0000660 JpegDecoderHelper jpeg_decoder;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400661 if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
662 return ERROR_JPEGR_DECODE_ERROR;
663 }
Ram Mohan9b3d6852023-05-26 00:09:50 +0530664 if ((jpeg_decoder.getDecompressedImageWidth() *
665 jpeg_decoder.getDecompressedImageHeight() * 3 / 2) >
666 jpeg_decoder.getDecompressedImageSize()) {
667 return ERROR_JPEGR_CALCULATION_ERROR;
668 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400669
Dichen Zhangb80e2262023-03-08 06:59:51 +0000670 if (exif != nullptr) {
671 if (exif->data == nullptr) {
672 return ERROR_JPEGR_INVALID_NULL_PTR;
673 }
674 if (exif->length < jpeg_decoder.getEXIFSize()) {
675 return ERROR_JPEGR_BUFFER_TOO_SMALL;
676 }
677 memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize());
678 exif->length = jpeg_decoder.getEXIFSize();
679 }
680
Fyodor Kyslovbf241572022-12-13 22:38:07 +0000681 jpegr_uncompressed_struct map;
Dichen Zhang10959a42023-04-10 16:28:16 -0700682 map.data = gain_map_decoder.getDecompressedImagePtr();
683 map.width = gain_map_decoder.getDecompressedImageWidth();
684 map.height = gain_map_decoder.getDecompressedImageHeight();
Fyodor Kyslovbf241572022-12-13 22:38:07 +0000685
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400686 jpegr_uncompressed_struct uncompressed_yuv_420_image;
687 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
688 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
689 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
Nick Deakin0db53ee2023-05-19 17:14:45 -0400690 uncompressed_yuv_420_image.colorGamut = IccHelper::readIccColorGamut(
691 jpeg_decoder.getICCPtr(), jpeg_decoder.getICCSize());
692
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000693 JPEGR_CHECK(applyGainMap(&uncompressed_yuv_420_image, &map, &uhdr_metadata, output_format,
Dichen Zhang10959a42023-04-10 16:28:16 -0700694 max_display_boost, dest));
Dichen Zhang6947d532022-10-22 02:16:21 +0000695 return NO_ERROR;
696}
697
Dichen Zhang10959a42023-04-10 16:28:16 -0700698status_t JpegR::compressGainMap(jr_uncompressed_ptr uncompressed_gain_map,
Ram Mohanf9540402023-05-18 20:08:55 +0530699 JpegEncoderHelper* jpeg_encoder) {
700 if (uncompressed_gain_map == nullptr || jpeg_encoder == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000701 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700702 }
703
Nick Deakin0db53ee2023-05-19 17:14:45 -0400704 // Don't need to convert YUV to Bt601 since single channel
Ram Mohanf9540402023-05-18 20:08:55 +0530705 if (!jpeg_encoder->compressImage(uncompressed_gain_map->data,
706 uncompressed_gain_map->width,
707 uncompressed_gain_map->height,
708 kMapCompressQuality,
709 nullptr,
710 0,
711 true /* isSingleChannel */)) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400712 return ERROR_JPEGR_ENCODE_ERROR;
713 }
714
Dichen Zhang6947d532022-10-22 02:16:21 +0000715 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700716}
717
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800718const int kJobSzInRows = 16;
719static_assert(kJobSzInRows > 0 && kJobSzInRows % kMapDimensionScaleFactor == 0,
720 "align job size to kMapDimensionScaleFactor");
721
722class JobQueue {
723 public:
724 bool dequeueJob(size_t& rowStart, size_t& rowEnd);
725 void enqueueJob(size_t rowStart, size_t rowEnd);
726 void markQueueForEnd();
727 void reset();
728
729 private:
730 bool mQueuedAllJobs = false;
731 std::deque<std::tuple<size_t, size_t>> mJobs;
732 std::mutex mMutex;
733 std::condition_variable mCv;
734};
735
736bool JobQueue::dequeueJob(size_t& rowStart, size_t& rowEnd) {
737 std::unique_lock<std::mutex> lock{mMutex};
738 while (true) {
739 if (mJobs.empty()) {
740 if (mQueuedAllJobs) {
741 return false;
742 } else {
Ram Mohand39fb082023-05-09 17:25:49 +0000743 mCv.wait_for(lock, std::chrono::milliseconds(100));
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800744 }
745 } else {
746 auto it = mJobs.begin();
747 rowStart = std::get<0>(*it);
748 rowEnd = std::get<1>(*it);
749 mJobs.erase(it);
750 return true;
751 }
752 }
753 return false;
754}
755
756void JobQueue::enqueueJob(size_t rowStart, size_t rowEnd) {
757 std::unique_lock<std::mutex> lock{mMutex};
758 mJobs.push_back(std::make_tuple(rowStart, rowEnd));
759 lock.unlock();
760 mCv.notify_one();
761}
762
763void JobQueue::markQueueForEnd() {
764 std::unique_lock<std::mutex> lock{mMutex};
765 mQueuedAllJobs = true;
Ram Mohand39fb082023-05-09 17:25:49 +0000766 lock.unlock();
767 mCv.notify_all();
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800768}
769
770void JobQueue::reset() {
771 std::unique_lock<std::mutex> lock{mMutex};
772 mJobs.clear();
773 mQueuedAllJobs = false;
774}
775
Dichen Zhang10959a42023-04-10 16:28:16 -0700776status_t JpegR::generateGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
777 jr_uncompressed_ptr uncompressed_p010_image,
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000778 ultrahdr_transfer_function hdr_tf,
779 ultrahdr_metadata_ptr metadata,
Nick Deakin0db53ee2023-05-19 17:14:45 -0400780 jr_uncompressed_ptr dest,
781 bool sdr_is_601) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700782 if (uncompressed_yuv_420_image == nullptr
783 || uncompressed_p010_image == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500784 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700785 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000786 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700787 }
788
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400789 if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width
790 || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) {
791 return ERROR_JPEGR_RESOLUTION_MISMATCH;
792 }
793
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000794 if (uncompressed_yuv_420_image->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED
795 || uncompressed_p010_image->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
Nick Deakin6bd90432022-11-20 16:26:37 -0500796 return ERROR_JPEGR_INVALID_COLORGAMUT;
797 }
798
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400799 size_t image_width = uncompressed_yuv_420_image->width;
800 size_t image_height = uncompressed_yuv_420_image->height;
801 size_t map_width = image_width / kMapDimensionScaleFactor;
802 size_t map_height = image_height / kMapDimensionScaleFactor;
Dichen Zhang53751272023-01-17 19:09:01 -0800803 size_t map_stride = static_cast<size_t>(
804 floor((map_width + kJpegBlock - 1) / kJpegBlock)) * kJpegBlock;
805 size_t map_height_aligned = ((map_height + 1) >> 1) << 1;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400806
Dichen Zhang53751272023-01-17 19:09:01 -0800807 dest->width = map_stride;
808 dest->height = map_height_aligned;
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000809 dest->colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
Dichen Zhang53751272023-01-17 19:09:01 -0800810 dest->data = new uint8_t[map_stride * map_height_aligned];
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400811 std::unique_ptr<uint8_t[]> map_data;
812 map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
813
Nick Deakin6bd90432022-11-20 16:26:37 -0500814 ColorTransformFn hdrInvOetf = nullptr;
Ram Mohancd3f6372023-06-02 15:16:15 +0530815 float hdr_white_nits = kSdrWhiteNits;
Nick Deakin01759062023-02-02 18:21:43 -0500816 switch (hdr_tf) {
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000817 case ULTRAHDR_TF_LINEAR:
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000818 hdrInvOetf = identityConversion;
819 break;
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000820 case ULTRAHDR_TF_HLG:
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800821#if USE_HLG_INVOETF_LUT
822 hdrInvOetf = hlgInvOetfLUT;
823#else
Nick Deakin6bd90432022-11-20 16:26:37 -0500824 hdrInvOetf = hlgInvOetf;
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800825#endif
Nick Deakin65f492a2022-11-29 22:47:40 -0500826 hdr_white_nits = kHlgMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500827 break;
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000828 case ULTRAHDR_TF_PQ:
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800829#if USE_PQ_INVOETF_LUT
830 hdrInvOetf = pqInvOetfLUT;
831#else
Nick Deakin6bd90432022-11-20 16:26:37 -0500832 hdrInvOetf = pqInvOetf;
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800833#endif
Nick Deakin65f492a2022-11-29 22:47:40 -0500834 hdr_white_nits = kPqMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500835 break;
Dichen Zhang6438a192023-01-29 07:51:15 +0000836 default:
Dichen Zhangb27d06d2022-12-14 19:57:50 +0000837 // Should be impossible to hit after input validation.
838 return ERROR_JPEGR_INVALID_TRANS_FUNC;
Nick Deakin6bd90432022-11-20 16:26:37 -0500839 }
840
Nick Deakina2215292023-02-14 21:40:06 -0500841 metadata->maxContentBoost = hdr_white_nits / kSdrWhiteNits;
842 metadata->minContentBoost = 1.0f;
Dichen Zhangf7a9e172023-04-06 18:23:47 -0700843 float log2MinBoost = log2(metadata->minContentBoost);
844 float log2MaxBoost = log2(metadata->maxContentBoost);
Nick Deakina2215292023-02-14 21:40:06 -0500845
Nick Deakin6bd90432022-11-20 16:26:37 -0500846 ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
847 uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
848
849 ColorCalculationFn luminanceFn = nullptr;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400850 ColorTransformFn sdrYuvToRgbFn = nullptr;
Nick Deakin6bd90432022-11-20 16:26:37 -0500851 switch (uncompressed_yuv_420_image->colorGamut) {
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000852 case ULTRAHDR_COLORGAMUT_BT709:
Nick Deakin6bd90432022-11-20 16:26:37 -0500853 luminanceFn = srgbLuminance;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400854 sdrYuvToRgbFn = srgbYuvToRgb;
Nick Deakin6bd90432022-11-20 16:26:37 -0500855 break;
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000856 case ULTRAHDR_COLORGAMUT_P3:
Nick Deakin6bd90432022-11-20 16:26:37 -0500857 luminanceFn = p3Luminance;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400858 sdrYuvToRgbFn = p3YuvToRgb;
Nick Deakin6bd90432022-11-20 16:26:37 -0500859 break;
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000860 case ULTRAHDR_COLORGAMUT_BT2100:
Nick Deakin6bd90432022-11-20 16:26:37 -0500861 luminanceFn = bt2100Luminance;
Nick Deakin0db53ee2023-05-19 17:14:45 -0400862 sdrYuvToRgbFn = bt2100YuvToRgb;
863 break;
864 case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
865 // Should be impossible to hit after input validation.
866 return ERROR_JPEGR_INVALID_COLORGAMUT;
867 }
868 if (sdr_is_601) {
869 sdrYuvToRgbFn = p3YuvToRgb;
870 }
871
872 ColorTransformFn hdrYuvToRgbFn = nullptr;
873 switch (uncompressed_p010_image->colorGamut) {
874 case ULTRAHDR_COLORGAMUT_BT709:
875 hdrYuvToRgbFn = srgbYuvToRgb;
876 break;
877 case ULTRAHDR_COLORGAMUT_P3:
878 hdrYuvToRgbFn = p3YuvToRgb;
879 break;
880 case ULTRAHDR_COLORGAMUT_BT2100:
881 hdrYuvToRgbFn = bt2100YuvToRgb;
Nick Deakin6bd90432022-11-20 16:26:37 -0500882 break;
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000883 case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
Nick Deakin6bd90432022-11-20 16:26:37 -0500884 // Should be impossible to hit after input validation.
885 return ERROR_JPEGR_INVALID_COLORGAMUT;
886 }
887
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800888 std::mutex mutex;
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800889 const int threads = std::clamp(GetCPUCoreCount(), 1, 4);
890 size_t rowStep = threads == 1 ? image_height : kJobSzInRows;
891 JobQueue jobQueue;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500892
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800893 std::function<void()> generateMap = [uncompressed_yuv_420_image, uncompressed_p010_image,
894 metadata, dest, hdrInvOetf, hdrGamutConversionFn,
Nick Deakin0db53ee2023-05-19 17:14:45 -0400895 luminanceFn, sdrYuvToRgbFn, hdrYuvToRgbFn, hdr_white_nits,
896 log2MinBoost, log2MaxBoost, &jobQueue]() -> void {
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800897 size_t rowStart, rowEnd;
Dichen Zhang24b4a392023-02-02 22:54:01 +0000898 size_t dest_map_width = uncompressed_yuv_420_image->width / kMapDimensionScaleFactor;
899 size_t dest_map_stride = dest->width;
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800900 while (jobQueue.dequeueJob(rowStart, rowEnd)) {
901 for (size_t y = rowStart; y < rowEnd; ++y) {
Dichen Zhang24b4a392023-02-02 22:54:01 +0000902 for (size_t x = 0; x < dest_map_width; ++x) {
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800903 Color sdr_yuv_gamma =
904 sampleYuv420(uncompressed_yuv_420_image, kMapDimensionScaleFactor, x, y);
Nick Deakin0db53ee2023-05-19 17:14:45 -0400905 Color sdr_rgb_gamma = sdrYuvToRgbFn(sdr_yuv_gamma);
906 // We are assuming the SDR input is always sRGB transfer.
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800907#if USE_SRGB_INVOETF_LUT
908 Color sdr_rgb = srgbInvOetfLUT(sdr_rgb_gamma);
909#else
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800910 Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800911#endif
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800912 float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
913
914 Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
Nick Deakin0db53ee2023-05-19 17:14:45 -0400915 Color hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800916 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
917 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
918 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
919
Dichen Zhang24b4a392023-02-02 22:54:01 +0000920 size_t pixel_idx = x + y * dest_map_stride;
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800921 reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
Dichen Zhang10959a42023-04-10 16:28:16 -0700922 encodeGain(sdr_y_nits, hdr_y_nits, metadata, log2MinBoost, log2MaxBoost);
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800923 }
924 }
925 }
926 };
927
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800928 // generate map
Nick Deakina2215292023-02-14 21:40:06 -0500929 std::vector<std::thread> workers;
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800930 for (int th = 0; th < threads - 1; th++) {
931 workers.push_back(std::thread(generateMap));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400932 }
933
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800934 rowStep = (threads == 1 ? image_height : kJobSzInRows) / kMapDimensionScaleFactor;
935 for (size_t rowStart = 0; rowStart < map_height;) {
936 size_t rowEnd = std::min(rowStart + rowStep, map_height);
937 jobQueue.enqueueJob(rowStart, rowEnd);
938 rowStart = rowEnd;
939 }
940 jobQueue.markQueueForEnd();
941 generateMap();
942 std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
943
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400944 map_data.release();
Dichen Zhang6947d532022-10-22 02:16:21 +0000945 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700946}
947
Dichen Zhang10959a42023-04-10 16:28:16 -0700948status_t JpegR::applyGainMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
949 jr_uncompressed_ptr uncompressed_gain_map,
Dichen Zhangdbceb0e2023-04-14 19:03:18 +0000950 ultrahdr_metadata_ptr metadata,
951 ultrahdr_output_format output_format,
Dichen Zhang10959a42023-04-10 16:28:16 -0700952 float max_display_boost,
953 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700954 if (uncompressed_yuv_420_image == nullptr
Dichen Zhang10959a42023-04-10 16:28:16 -0700955 || uncompressed_gain_map == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500956 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700957 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000958 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700959 }
960
Ram Mohan9b3d6852023-05-26 00:09:50 +0530961 // TODO: remove once map scaling factor is computed based on actual map dims
962 size_t image_width = uncompressed_yuv_420_image->width;
963 size_t image_height = uncompressed_yuv_420_image->height;
964 size_t map_width = image_width / kMapDimensionScaleFactor;
965 size_t map_height = image_height / kMapDimensionScaleFactor;
966 map_width = static_cast<size_t>(
967 floor((map_width + kJpegBlock - 1) / kJpegBlock)) * kJpegBlock;
968 map_height = ((map_height + 1) >> 1) << 1;
969 if (map_width != uncompressed_gain_map->width
970 || map_height != uncompressed_gain_map->height) {
971 ALOGE("gain map dimensions and primary image dimensions are not to scale");
972 return ERROR_JPEGR_INVALID_INPUT_TYPE;
973 }
974
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800975 dest->width = uncompressed_yuv_420_image->width;
976 dest->height = uncompressed_yuv_420_image->height;
Ram Mohanfe723d62022-12-15 00:59:11 +0530977 ShepardsIDW idwTable(kMapDimensionScaleFactor);
Dichen Zhangb96ba8f2023-03-21 23:33:41 +0000978 float display_boost = std::min(max_display_boost, metadata->maxContentBoost);
Dichen Zhang10959a42023-04-10 16:28:16 -0700979 GainLUT gainLUT(metadata, display_boost);
Ram Mohanfe723d62022-12-15 00:59:11 +0530980
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800981 JobQueue jobQueue;
Dichen Zhang10959a42023-04-10 16:28:16 -0700982 std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_gain_map,
Dichen Zhang3e5798c2023-03-01 22:30:43 +0000983 metadata, dest, &jobQueue, &idwTable, output_format,
Dichen Zhang10959a42023-04-10 16:28:16 -0700984 &gainLUT, display_boost]() -> void {
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800985 size_t width = uncompressed_yuv_420_image->width;
986 size_t height = uncompressed_yuv_420_image->height;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400987
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800988 size_t rowStart, rowEnd;
989 while (jobQueue.dequeueJob(rowStart, rowEnd)) {
990 for (size_t y = rowStart; y < rowEnd; ++y) {
991 for (size_t x = 0; x < width; ++x) {
992 Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
Nick Deakin0db53ee2023-05-19 17:14:45 -0400993 // Assuming the sdr image is a decoded JPEG, we should always use Rec.601 YUV coefficients
994 Color rgb_gamma_sdr = p3YuvToRgb(yuv_gamma_sdr);
995 // We are assuming the SDR base image is always sRGB transfer.
Harish Mahendrakar555a06b2022-12-14 09:37:27 -0800996#if USE_SRGB_INVOETF_LUT
997 Color rgb_sdr = srgbInvOetfLUT(rgb_gamma_sdr);
998#else
Harish Mahendrakar72b6f302022-12-16 10:39:15 -0800999 Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
Harish Mahendrakar555a06b2022-12-14 09:37:27 -08001000#endif
Dichen Zhang10959a42023-04-10 16:28:16 -07001001 float gain;
Harish Mahendrakar72b6f302022-12-16 10:39:15 -08001002 // TODO: determine map scaling factor based on actual map dims
1003 size_t map_scale_factor = kMapDimensionScaleFactor;
1004 // TODO: If map_scale_factor is guaranteed to be an integer, then remove the following.
1005 // Currently map_scale_factor is of type size_t, but it could be changed to a float
1006 // later.
1007 if (map_scale_factor != floorf(map_scale_factor)) {
Dichen Zhang10959a42023-04-10 16:28:16 -07001008 gain = sampleMap(uncompressed_gain_map, map_scale_factor, x, y);
Harish Mahendrakar72b6f302022-12-16 10:39:15 -08001009 } else {
Dichen Zhang10959a42023-04-10 16:28:16 -07001010 gain = sampleMap(uncompressed_gain_map, map_scale_factor, x, y, idwTable);
Harish Mahendrakar72b6f302022-12-16 10:39:15 -08001011 }
Dichen Zhangc6605702023-03-15 18:40:55 -07001012
Dichen Zhang10959a42023-04-10 16:28:16 -07001013#if USE_APPLY_GAIN_LUT
1014 Color rgb_hdr = applyGainLUT(rgb_sdr, gain, gainLUT);
Harish Mahendrakarf25991f2022-12-16 11:57:44 -08001015#else
Dichen Zhang10959a42023-04-10 16:28:16 -07001016 Color rgb_hdr = applyGain(rgb_sdr, gain, metadata, display_boost);
Harish Mahendrakarf25991f2022-12-16 11:57:44 -08001017#endif
Dichen Zhangc6605702023-03-15 18:40:55 -07001018 rgb_hdr = rgb_hdr / display_boost;
Harish Mahendrakar72b6f302022-12-16 10:39:15 -08001019 size_t pixel_idx = x + y * width;
Dichen Zhang3e5798c2023-03-01 22:30:43 +00001020
1021 switch (output_format) {
Dichen Zhangdbceb0e2023-04-14 19:03:18 +00001022 case ULTRAHDR_OUTPUT_HDR_LINEAR:
Dichen Zhang3e5798c2023-03-01 22:30:43 +00001023 {
1024 uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr);
1025 reinterpret_cast<uint64_t*>(dest->data)[pixel_idx] = rgba_f16;
1026 break;
1027 }
Dichen Zhangdbceb0e2023-04-14 19:03:18 +00001028 case ULTRAHDR_OUTPUT_HDR_HLG:
Dichen Zhang3e5798c2023-03-01 22:30:43 +00001029 {
1030#if USE_HLG_OETF_LUT
1031 ColorTransformFn hdrOetf = hlgOetfLUT;
1032#else
1033 ColorTransformFn hdrOetf = hlgOetf;
1034#endif
1035 Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
1036 uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
1037 reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
1038 break;
1039 }
Dichen Zhangdbceb0e2023-04-14 19:03:18 +00001040 case ULTRAHDR_OUTPUT_HDR_PQ:
Dichen Zhang3e5798c2023-03-01 22:30:43 +00001041 {
1042#if USE_HLG_OETF_LUT
1043 ColorTransformFn hdrOetf = pqOetfLUT;
1044#else
1045 ColorTransformFn hdrOetf = pqOetf;
1046#endif
1047 Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
1048 uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
1049 reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
1050 break;
1051 }
1052 default:
1053 {}
1054 // Should be impossible to hit after input validation.
1055 }
Harish Mahendrakar72b6f302022-12-16 10:39:15 -08001056 }
1057 }
1058 }
1059 };
1060
1061 const int threads = std::clamp(GetCPUCoreCount(), 1, 4);
1062 std::vector<std::thread> workers;
1063 for (int th = 0; th < threads - 1; th++) {
1064 workers.push_back(std::thread(applyRecMap));
Nick Deakinf6bca5a2022-11-04 10:43:43 -04001065 }
Harish Mahendrakar72b6f302022-12-16 10:39:15 -08001066 const int rowStep = threads == 1 ? uncompressed_yuv_420_image->height : kJobSzInRows;
1067 for (int rowStart = 0; rowStart < uncompressed_yuv_420_image->height;) {
1068 int rowEnd = std::min(rowStart + rowStep, uncompressed_yuv_420_image->height);
1069 jobQueue.enqueueJob(rowStart, rowEnd);
1070 rowStart = rowEnd;
1071 }
1072 jobQueue.markQueueForEnd();
1073 applyRecMap();
1074 std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +00001075 return NO_ERROR;
1076}
1077
Dichen Zhang10959a42023-04-10 16:28:16 -07001078status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr compressed_jpegr_image,
1079 jr_compressed_ptr primary_image,
1080 jr_compressed_ptr gain_map) {
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +00001081 if (compressed_jpegr_image == nullptr) {
1082 return ERROR_JPEGR_INVALID_NULL_PTR;
1083 }
1084
1085 MessageHandler msg_handler;
1086 std::shared_ptr<DataSegment> seg =
1087 DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
1088 static_cast<const uint8_t*>(compressed_jpegr_image->data),
1089 DataSegment::BufferDispositionPolicy::kDontDelete);
1090 DataSegmentDataSource data_source(seg);
1091 JpegInfoBuilder jpeg_info_builder;
1092 jpeg_info_builder.SetImageLimit(2);
1093 JpegScanner jpeg_scanner(&msg_handler);
1094 jpeg_scanner.Run(&data_source, &jpeg_info_builder);
1095 data_source.Reset();
1096
1097 if (jpeg_scanner.HasError()) {
1098 return ERROR_JPEGR_INVALID_INPUT_TYPE;
1099 }
1100
1101 const auto& jpeg_info = jpeg_info_builder.GetInfo();
1102 const auto& image_ranges = jpeg_info.GetImageRanges();
1103 if (image_ranges.empty()) {
1104 return ERROR_JPEGR_INVALID_INPUT_TYPE;
1105 }
1106
1107 if (image_ranges.size() != 2) {
1108 // Must be 2 JPEG Images
1109 return ERROR_JPEGR_INVALID_INPUT_TYPE;
1110 }
1111
1112 if (primary_image != nullptr) {
1113 primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
1114 image_ranges[0].GetBegin();
1115 primary_image->length = image_ranges[0].GetLength();
1116 }
1117
Dichen Zhang10959a42023-04-10 16:28:16 -07001118 if (gain_map != nullptr) {
1119 gain_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +00001120 image_ranges[1].GetBegin();
Dichen Zhang10959a42023-04-10 16:28:16 -07001121 gain_map->length = image_ranges[1].GetLength();
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +00001122 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -04001123
Dichen Zhang6947d532022-10-22 02:16:21 +00001124 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -07001125}
1126
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +00001127
Dichen Zhang10959a42023-04-10 16:28:16 -07001128status_t JpegR::extractGainMap(jr_compressed_ptr compressed_jpegr_image,
1129 jr_compressed_ptr dest) {
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +00001130 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +00001131 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -07001132 }
1133
Dichen Zhang10959a42023-04-10 16:28:16 -07001134 return extractPrimaryImageAndGainMap(compressed_jpegr_image, nullptr, dest);
Dichen Zhang85b37562022-10-11 11:08:28 -07001135}
1136
Dichen Zhangd18bc302022-12-16 20:55:24 +00001137// JPEG/R structure:
1138// SOI (ff d8)
Dichen Zhang50ff1292023-01-27 18:03:43 +00001139//
1140// (Optional, only if EXIF package is from outside)
Dichen Zhangd18bc302022-12-16 20:55:24 +00001141// APP1 (ff e1)
1142// 2 bytes of length (2 + length of exif package)
1143// EXIF package (this includes the first two bytes representing the package length)
Dichen Zhang50ff1292023-01-27 18:03:43 +00001144//
1145// (Required, XMP package) APP1 (ff e1)
Dichen Zhangd18bc302022-12-16 20:55:24 +00001146// 2 bytes of length (2 + 29 + length of xmp package)
1147// name space ("http://ns.adobe.com/xap/1.0/\0")
Dichen Zhang61ede362023-02-22 18:50:13 +00001148// XMP
1149//
1150// (Required, MPF package) APP2 (ff e2)
1151// 2 bytes of length
1152// MPF
Dichen Zhang50ff1292023-01-27 18:03:43 +00001153//
1154// (Required) primary image (without the first two bytes (SOI), may have other packages)
1155//
Dichen Zhang61ede362023-02-22 18:50:13 +00001156// SOI (ff d8)
1157//
1158// (Required, XMP package) APP1 (ff e1)
1159// 2 bytes of length (2 + 29 + length of xmp package)
1160// name space ("http://ns.adobe.com/xap/1.0/\0")
1161// XMP
1162//
Dichen Zhang10959a42023-04-10 16:28:16 -07001163// (Required) secondary image (the gain map, without the first two bytes (SOI))
Dichen Zhangd18bc302022-12-16 20:55:24 +00001164//
1165// Metadata versions we are using:
1166// ECMA TR-98 for JFIF marker
1167// Exif 2.2 spec for EXIF marker
1168// Adobe XMP spec part 3 for XMP marker
1169// ICC v4.3 spec for ICC
Dichen Zhang10959a42023-04-10 16:28:16 -07001170status_t JpegR::appendGainMap(jr_compressed_ptr compressed_jpeg_image,
1171 jr_compressed_ptr compressed_gain_map,
1172 jr_exif_ptr exif,
Nick Deakin0db53ee2023-05-19 17:14:45 -04001173 void* icc, size_t icc_size,
Dichen Zhangdbceb0e2023-04-14 19:03:18 +00001174 ultrahdr_metadata_ptr metadata,
Dichen Zhang10959a42023-04-10 16:28:16 -07001175 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +00001176 if (compressed_jpeg_image == nullptr
Dichen Zhang10959a42023-04-10 16:28:16 -07001177 || compressed_gain_map == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -05001178 || metadata == nullptr
Dichen Zhang6947d532022-10-22 02:16:21 +00001179 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +00001180 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -07001181 }
1182
Ram Mohancd3f6372023-06-02 15:16:15 +05301183 if (metadata->minContentBoost < 1.0f || metadata->maxContentBoost < metadata->minContentBoost) {
1184 ALOGE("received bad value for content boost min %f, max %f", metadata->minContentBoost,
1185 metadata->maxContentBoost);
1186 return ERROR_JPEGR_INVALID_INPUT_TYPE;
1187 }
1188
Dichen Zhang15345ea2023-02-23 23:54:32 +00001189 const string nameSpace = "http://ns.adobe.com/xap/1.0/";
1190 const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
Dichen Zhanga8766262022-11-07 23:48:24 +00001191
Dichen Zhang15345ea2023-02-23 23:54:32 +00001192 // calculate secondary image length first, because the length will be written into the primary
1193 // image xmp
Dichen Zhang61ede362023-02-22 18:50:13 +00001194 const string xmp_secondary = generateXmpForSecondaryImage(*metadata);
Dichen Zhang15345ea2023-02-23 23:54:32 +00001195 const int xmp_secondary_length = 2 /* 2 bytes representing the length of the package */
1196 + nameSpaceLength /* 29 bytes length of name space including \0 */
1197 + xmp_secondary.size(); /* length of xmp packet */
1198 const int secondary_image_size = 2 /* 2 bytes length of APP1 sign */
1199 + xmp_secondary_length
Dichen Zhang10959a42023-04-10 16:28:16 -07001200 + compressed_gain_map->length;
Dichen Zhang15345ea2023-02-23 23:54:32 +00001201 // primary image
Nick Deakin05ceebf2023-04-19 15:27:13 -04001202 const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size, *metadata);
Dichen Zhang15345ea2023-02-23 23:54:32 +00001203 // same as primary
1204 const int xmp_primary_length = 2 + nameSpaceLength + xmp_primary.size();
Dichen Zhang61ede362023-02-22 18:50:13 +00001205
Dichen Zhang15345ea2023-02-23 23:54:32 +00001206 int pos = 0;
Dichen Zhang61ede362023-02-22 18:50:13 +00001207 // Begin primary image
Dichen Zhangd18bc302022-12-16 20:55:24 +00001208 // Write SOI
Dichen Zhanga8766262022-11-07 23:48:24 +00001209 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1210 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +00001211
1212 // Write EXIF
Dichen Zhang50ff1292023-01-27 18:03:43 +00001213 if (exif != nullptr) {
Dichen Zhangd18bc302022-12-16 20:55:24 +00001214 const int length = 2 + exif->length;
1215 const uint8_t lengthH = ((length >> 8) & 0xff);
1216 const uint8_t lengthL = (length & 0xff);
1217 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1218 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
1219 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
1220 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
1221 JPEGR_CHECK(Write(dest, exif->data, exif->length, pos));
1222 }
1223
1224 // Prepare and write XMP
1225 {
Dichen Zhang15345ea2023-02-23 23:54:32 +00001226 const int length = xmp_primary_length;
Dichen Zhangd18bc302022-12-16 20:55:24 +00001227 const uint8_t lengthH = ((length >> 8) & 0xff);
1228 const uint8_t lengthL = (length & 0xff);
1229 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1230 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
1231 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
1232 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
1233 JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
Dichen Zhang61ede362023-02-22 18:50:13 +00001234 JPEGR_CHECK(Write(dest, (void*)xmp_primary.c_str(), xmp_primary.size(), pos));
1235 }
1236
Nick Deakin0db53ee2023-05-19 17:14:45 -04001237 // Write ICC
1238 if (icc != nullptr && icc_size > 0) {
1239 const int length = icc_size + 2;
1240 const uint8_t lengthH = ((length >> 8) & 0xff);
1241 const uint8_t lengthL = (length & 0xff);
1242 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1243 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
1244 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
1245 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
1246 JPEGR_CHECK(Write(dest, icc, icc_size, pos));
1247 }
1248
Dichen Zhang61ede362023-02-22 18:50:13 +00001249 // Prepare and write MPF
1250 {
1251 const int length = 2 + calculateMpfSize();
1252 const uint8_t lengthH = ((length >> 8) & 0xff);
1253 const uint8_t lengthL = (length & 0xff);
1254 int primary_image_size = pos + length + compressed_jpeg_image->length;
Dichen Zhang15345ea2023-02-23 23:54:32 +00001255 // between APP2 + package size + signature
1256 // ff e2 00 58 4d 50 46 00
1257 // 2 + 2 + 4 = 8 (bytes)
1258 // and ff d8 sign of the secondary image
1259 int secondary_image_offset = primary_image_size - pos - 8;
1260 sp<DataStruct> mpf = generateMpf(primary_image_size,
1261 0, /* primary_image_offset */
1262 secondary_image_size,
1263 secondary_image_offset);
Dichen Zhang61ede362023-02-22 18:50:13 +00001264 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1265 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
1266 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
1267 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
1268 JPEGR_CHECK(Write(dest, (void*)mpf->getData(), mpf->getLength(), pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +00001269 }
1270
1271 // Write primary image
Dichen Zhanga8766262022-11-07 23:48:24 +00001272 JPEGR_CHECK(Write(dest,
1273 (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
Dichen Zhang61ede362023-02-22 18:50:13 +00001274 // Finish primary image
1275
Dichen Zhang10959a42023-04-10 16:28:16 -07001276 // Begin secondary image (gain map)
Dichen Zhang61ede362023-02-22 18:50:13 +00001277 // Write SOI
1278 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1279 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
1280
1281 // Prepare and write XMP
1282 {
Dichen Zhang15345ea2023-02-23 23:54:32 +00001283 const int length = xmp_secondary_length;
Dichen Zhang61ede362023-02-22 18:50:13 +00001284 const uint8_t lengthH = ((length >> 8) & 0xff);
1285 const uint8_t lengthL = (length & 0xff);
1286 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
1287 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
1288 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
1289 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
1290 JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
1291 JPEGR_CHECK(Write(dest, (void*)xmp_secondary.c_str(), xmp_secondary.size(), pos));
1292 }
Dichen Zhangd18bc302022-12-16 20:55:24 +00001293
1294 // Write secondary image
Dichen Zhang61ede362023-02-22 18:50:13 +00001295 JPEGR_CHECK(Write(dest,
Dichen Zhang10959a42023-04-10 16:28:16 -07001296 (uint8_t*)compressed_gain_map->data + 2, compressed_gain_map->length - 2, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +00001297
1298 // Set back length
Dichen Zhanga8766262022-11-07 23:48:24 +00001299 dest->length = pos;
1300
Dichen Zhangd18bc302022-12-16 20:55:24 +00001301 // Done!
Dichen Zhang6947d532022-10-22 02:16:21 +00001302 return NO_ERROR;
Dichen Zhang85b37562022-10-11 11:08:28 -07001303}
1304
Dichen Zhang61ede362023-02-22 18:50:13 +00001305status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) {
Dichen Zhangc3437ca2023-01-04 14:00:08 -08001306 if (src == nullptr || dest == nullptr) {
Dichen Zhang636f5242022-12-07 20:25:44 +00001307 return ERROR_JPEGR_INVALID_NULL_PTR;
1308 }
1309
Dichen Zhang66ca6e32023-04-05 12:22:54 -07001310 uint16_t* src_luma_data = reinterpret_cast<uint16_t*>(src->data);
Dichen Zhang98b06852023-05-17 16:56:51 +00001311 size_t src_luma_stride = src->luma_stride == 0 ? src->width : src->luma_stride;
Dichen Zhang66ca6e32023-04-05 12:22:54 -07001312
Dichen Zhang98b06852023-05-17 16:56:51 +00001313 uint16_t* src_chroma_data;
1314 size_t src_chroma_stride;
1315 if (src->chroma_data == nullptr) {
1316 src_chroma_stride = src_luma_stride;
1317 src_chroma_data = &reinterpret_cast<uint16_t*>(src->data)[src_luma_stride * src->height];
1318 } else {
1319 src_chroma_stride = src->chroma_stride;
1320 src_chroma_data = reinterpret_cast<uint16_t*>(src->chroma_data);
Dichen Zhang66ca6e32023-04-05 12:22:54 -07001321 }
Dichen Zhangc3437ca2023-01-04 14:00:08 -08001322 dest->width = src->width;
1323 dest->height = src->height;
Dichen Zhang636f5242022-12-07 20:25:44 +00001324
Dichen Zhang66ca6e32023-04-05 12:22:54 -07001325 size_t dest_luma_pixel_count = dest->width * dest->height;
1326
Dichen Zhangc3437ca2023-01-04 14:00:08 -08001327 for (size_t y = 0; y < src->height; ++y) {
1328 for (size_t x = 0; x < src->width; ++x) {
Dichen Zhang66ca6e32023-04-05 12:22:54 -07001329 size_t src_y_idx = y * src_luma_stride + x;
1330 size_t src_u_idx = (y >> 1) * src_chroma_stride + (x & ~0x1);
1331 size_t src_v_idx = src_u_idx + 1;
Dichen Zhangc3437ca2023-01-04 14:00:08 -08001332
Dichen Zhang66ca6e32023-04-05 12:22:54 -07001333 uint16_t y_uint = src_luma_data[src_y_idx] >> 6;
1334 uint16_t u_uint = src_chroma_data[src_u_idx] >> 6;
1335 uint16_t v_uint = src_chroma_data[src_v_idx] >> 6;
Dichen Zhangc3437ca2023-01-04 14:00:08 -08001336
Dichen Zhang66ca6e32023-04-05 12:22:54 -07001337 size_t dest_y_idx = x + y * dest->width;
1338 size_t dest_uv_idx = x / 2 + (y / 2) * (dest->width / 2);
1339
1340 uint8_t* y = &reinterpret_cast<uint8_t*>(dest->data)[dest_y_idx];
1341 uint8_t* u = &reinterpret_cast<uint8_t*>(dest->data)[dest_luma_pixel_count + dest_uv_idx];
1342 uint8_t* v = &reinterpret_cast<uint8_t*>(
1343 dest->data)[dest_luma_pixel_count * 5 / 4 + dest_uv_idx];
Dichen Zhangc3437ca2023-01-04 14:00:08 -08001344
1345 *y = static_cast<uint8_t>((y_uint >> 2) & 0xff);
1346 *u = static_cast<uint8_t>((u_uint >> 2) & 0xff);
1347 *v = static_cast<uint8_t>((v_uint >> 2) & 0xff);
1348 }
1349 }
1350
1351 dest->colorGamut = src->colorGamut;
Dichen Zhang636f5242022-12-07 20:25:44 +00001352
1353 return NO_ERROR;
1354}
1355
Nick Deakin0db53ee2023-05-19 17:14:45 -04001356status_t JpegR::convertYuv(jr_uncompressed_ptr image,
1357 ultrahdr_color_gamut src_encoding,
1358 ultrahdr_color_gamut dest_encoding) {
1359 if (image == nullptr) {
1360 return ERROR_JPEGR_INVALID_NULL_PTR;
1361 }
1362
1363 if (src_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED
1364 || dest_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
1365 return ERROR_JPEGR_INVALID_COLORGAMUT;
1366 }
1367
1368 ColorTransformFn conversionFn = nullptr;
1369 switch (src_encoding) {
1370 case ULTRAHDR_COLORGAMUT_BT709:
1371 switch (dest_encoding) {
1372 case ULTRAHDR_COLORGAMUT_BT709:
1373 return NO_ERROR;
1374 case ULTRAHDR_COLORGAMUT_P3:
1375 conversionFn = yuv709To601;
1376 break;
1377 case ULTRAHDR_COLORGAMUT_BT2100:
1378 conversionFn = yuv709To2100;
1379 break;
1380 default:
1381 // Should be impossible to hit after input validation
1382 return ERROR_JPEGR_INVALID_COLORGAMUT;
1383 }
1384 break;
1385 case ULTRAHDR_COLORGAMUT_P3:
1386 switch (dest_encoding) {
1387 case ULTRAHDR_COLORGAMUT_BT709:
1388 conversionFn = yuv601To709;
1389 break;
1390 case ULTRAHDR_COLORGAMUT_P3:
1391 return NO_ERROR;
1392 case ULTRAHDR_COLORGAMUT_BT2100:
1393 conversionFn = yuv601To2100;
1394 break;
1395 default:
1396 // Should be impossible to hit after input validation
1397 return ERROR_JPEGR_INVALID_COLORGAMUT;
1398 }
1399 break;
1400 case ULTRAHDR_COLORGAMUT_BT2100:
1401 switch (dest_encoding) {
1402 case ULTRAHDR_COLORGAMUT_BT709:
1403 conversionFn = yuv2100To709;
1404 break;
1405 case ULTRAHDR_COLORGAMUT_P3:
1406 conversionFn = yuv2100To601;
1407 break;
1408 case ULTRAHDR_COLORGAMUT_BT2100:
1409 return NO_ERROR;
1410 default:
1411 // Should be impossible to hit after input validation
1412 return ERROR_JPEGR_INVALID_COLORGAMUT;
1413 }
1414 break;
1415 default:
1416 // Should be impossible to hit after input validation
1417 return ERROR_JPEGR_INVALID_COLORGAMUT;
1418 }
1419
1420 if (conversionFn == nullptr) {
1421 // Should be impossible to hit after input validation
1422 return ERROR_JPEGR_INVALID_COLORGAMUT;
1423 }
1424
1425 for (size_t y = 0; y < image->height / 2; ++y) {
1426 for (size_t x = 0; x < image->width / 2; ++x) {
1427 transformYuv420(image, x, y, conversionFn);
1428 }
1429 }
1430
1431 return NO_ERROR;
1432}
1433
Dichen Zhangdbceb0e2023-04-14 19:03:18 +00001434} // namespace android::ultrahdr