blob: fafc3192717914be55d98d27833d135be208f2df [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
17#include <jpegrecoverymap/recoverymap.h>
Nick Deakinf6bca5a2022-11-04 10:43:43 -040018#include <jpegrecoverymap/jpegencoder.h>
19#include <jpegrecoverymap/jpegdecoder.h>
20#include <jpegrecoverymap/recoverymapmath.h>
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000021#include <jpegrecoverymap/recoverymaputils.h>
Nick Deakinf6bca5a2022-11-04 10:43:43 -040022
Dichen Zhanga8766262022-11-07 23:48:24 +000023#include <image_io/jpeg/jpeg_marker.h>
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000024#include <image_io/jpeg/jpeg_info.h>
25#include <image_io/jpeg/jpeg_scanner.h>
26#include <image_io/jpeg/jpeg_info_builder.h>
27#include <image_io/base/data_segment_data_source.h>
28#include <utils/Log.h>
Nick Deakinf6bca5a2022-11-04 10:43:43 -040029
30#include <memory>
Dichen Zhang72fd2b12022-11-01 06:11:50 +000031#include <sstream>
32#include <string>
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000033#include <cmath>
Dichen Zhang72fd2b12022-11-01 06:11:50 +000034
35using namespace std;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000036using namespace photos_editing_formats::image_io;
Dichen Zhang85b37562022-10-11 11:08:28 -070037
38namespace android::recoverymap {
39
Nick Deakinf6bca5a2022-11-04 10:43:43 -040040#define JPEGR_CHECK(x) \
41 { \
42 status_t status = (x); \
43 if ((status) != NO_ERROR) { \
44 return status; \
45 } \
46 }
47
Nick Deakin6bd90432022-11-20 16:26:37 -050048// The current JPEGR version that we encode to
49static const uint32_t kJpegrVersion = 1;
50
Nick Deakinf6bca5a2022-11-04 10:43:43 -040051// Map is quarter res / sixteenth size
52static const size_t kMapDimensionScaleFactor = 4;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +000053// JPEG compress quality (0 ~ 100) for recovery map
54static const int kMapCompressQuality = 85;
Nick Deakinf6bca5a2022-11-04 10:43:43 -040055
Nick Deakin6bd90432022-11-20 16:26:37 -050056// TODO: fill in st2086 metadata
57static const st2086_metadata kSt2086Metadata = {
58 {0.0f, 0.0f},
59 {0.0f, 0.0f},
60 {0.0f, 0.0f},
61 {0.0f, 0.0f},
62 0,
63 1.0f,
64};
Nick Deakinf6bca5a2022-11-04 10:43:43 -040065
Dichen Zhang72fd2b12022-11-01 06:11:50 +000066/*
Dichen Zhanga8766262022-11-07 23:48:24 +000067 * Helper function used for writing data to destination.
68 *
69 * @param destination destination of the data to be written.
70 * @param source source of data being written.
71 * @param length length of the data to be written.
72 * @param position cursor in desitination where the data is to be written.
73 * @return status of succeed or error code.
74 */
75status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) {
Dichen Zhang0b9f7de2022-11-18 06:52:46 +000076 if (position + length > destination->maxLength) {
Dichen Zhanga8766262022-11-07 23:48:24 +000077 return ERROR_JPEGR_BUFFER_TOO_SMALL;
78 }
79
80 memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
81 position += length;
82 return NO_ERROR;
83}
84
Dichen Zhang636f5242022-12-07 20:25:44 +000085/* Encode API-0 */
86status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
87 jpegr_transfer_function hdr_tf,
88 jr_compressed_ptr dest,
89 int quality,
90 jr_exif_ptr /* exif */) {
91 if (uncompressed_p010_image == nullptr || dest == nullptr) {
92 return ERROR_JPEGR_INVALID_NULL_PTR;
93 }
94
95 if (quality < 0 || quality > 100) {
96 return ERROR_JPEGR_INVALID_INPUT_TYPE;
97 }
98
99 jpegr_metadata metadata;
100 metadata.version = kJpegrVersion;
101 metadata.transferFunction = hdr_tf;
102 if (hdr_tf == JPEGR_TF_PQ) {
103 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
104 }
105
106 jpegr_uncompressed_struct uncompressed_yuv_420_image;
107 JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
108
109 jpegr_uncompressed_struct map;
110 JPEGR_CHECK(generateRecoveryMap(
111 &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
112 std::unique_ptr<uint8_t[]> map_data;
113 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
114
115 jpegr_compressed_struct compressed_map;
116 compressed_map.maxLength = map.width * map.height;
117 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
118 compressed_map.data = compressed_map_data.get();
119 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
120
121 JpegEncoder jpeg_encoder;
122 // TODO: determine ICC data based on color gamut information
123 if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
124 uncompressed_yuv_420_image.width,
125 uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
126 return ERROR_JPEGR_ENCODE_ERROR;
127 }
128 jpegr_compressed_struct jpeg;
129 jpeg.data = jpeg_encoder.getCompressedImagePtr();
130 jpeg.length = jpeg_encoder.getCompressedImageSize();
131
132 JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest));
133
134 return NO_ERROR;
135}
136
137/* Encode API-1 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000138status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
139 jr_uncompressed_ptr uncompressed_yuv_420_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500140 jpegr_transfer_function hdr_tf,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400141 jr_compressed_ptr dest,
Dichen Zhangffa34012022-11-03 23:21:13 +0000142 int quality,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000143 jr_exif_ptr /* exif */) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000144 if (uncompressed_p010_image == nullptr
145 || uncompressed_yuv_420_image == nullptr
146 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000147 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000148 }
149
Dichen Zhangffa34012022-11-03 23:21:13 +0000150 if (quality < 0 || quality > 100) {
151 return ERROR_JPEGR_INVALID_INPUT_TYPE;
152 }
153
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400154 if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
155 || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
156 return ERROR_JPEGR_RESOLUTION_MISMATCH;
157 }
158
Nick Deakin6bd90432022-11-20 16:26:37 -0500159 jpegr_metadata metadata;
160 metadata.version = kJpegrVersion;
161 metadata.transferFunction = hdr_tf;
162 if (hdr_tf == JPEGR_TF_PQ) {
163 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
164 }
165
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400166 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000167 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500168 uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400169 std::unique_ptr<uint8_t[]> map_data;
170 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
171
172 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000173 compressed_map.maxLength = map.width * map.height;
174 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400175 compressed_map.data = compressed_map_data.get();
176 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
177
178 JpegEncoder jpeg_encoder;
Nick Deakin6bd90432022-11-20 16:26:37 -0500179 // TODO: determine ICC data based on color gamut information
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400180 if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
181 uncompressed_yuv_420_image->width,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000182 uncompressed_yuv_420_image->height, quality, nullptr, 0)) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400183 return ERROR_JPEGR_ENCODE_ERROR;
184 }
185 jpegr_compressed_struct jpeg;
186 jpeg.data = jpeg_encoder.getCompressedImagePtr();
187 jpeg.length = jpeg_encoder.getCompressedImageSize();
188
Nick Deakin6bd90432022-11-20 16:26:37 -0500189 JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400190
Dichen Zhang6947d532022-10-22 02:16:21 +0000191 return NO_ERROR;
192}
193
Dichen Zhang636f5242022-12-07 20:25:44 +0000194/* Encode API-2 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000195status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
196 jr_uncompressed_ptr uncompressed_yuv_420_image,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400197 jr_compressed_ptr compressed_jpeg_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500198 jpegr_transfer_function hdr_tf,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000199 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000200 if (uncompressed_p010_image == nullptr
201 || uncompressed_yuv_420_image == nullptr
202 || compressed_jpeg_image == nullptr
203 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000204 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000205 }
206
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400207 if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
208 || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
209 return ERROR_JPEGR_RESOLUTION_MISMATCH;
210 }
211
Nick Deakin6bd90432022-11-20 16:26:37 -0500212 jpegr_metadata metadata;
213 metadata.version = kJpegrVersion;
214 metadata.transferFunction = hdr_tf;
215 if (hdr_tf == JPEGR_TF_PQ) {
216 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
217 }
218
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400219 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000220 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500221 uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400222 std::unique_ptr<uint8_t[]> map_data;
223 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
224
225 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000226 compressed_map.maxLength = map.width * map.height;
227 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400228 compressed_map.data = compressed_map_data.get();
229 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
230
Nick Deakin6bd90432022-11-20 16:26:37 -0500231 JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400232
Dichen Zhang6947d532022-10-22 02:16:21 +0000233 return NO_ERROR;
234}
235
Dichen Zhang636f5242022-12-07 20:25:44 +0000236/* Encode API-3 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000237status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400238 jr_compressed_ptr compressed_jpeg_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500239 jpegr_transfer_function hdr_tf,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000240 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000241 if (uncompressed_p010_image == nullptr
242 || compressed_jpeg_image == nullptr
243 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000244 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000245 }
246
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400247 JpegDecoder jpeg_decoder;
248 if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
249 return ERROR_JPEGR_DECODE_ERROR;
250 }
251 jpegr_uncompressed_struct uncompressed_yuv_420_image;
252 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
253 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
254 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
Nick Deakin6bd90432022-11-20 16:26:37 -0500255 uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400256
257 if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
258 || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
259 return ERROR_JPEGR_RESOLUTION_MISMATCH;
260 }
261
Nick Deakin6bd90432022-11-20 16:26:37 -0500262 jpegr_metadata metadata;
263 metadata.version = kJpegrVersion;
264 metadata.transferFunction = hdr_tf;
265 if (hdr_tf == JPEGR_TF_PQ) {
266 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
267 }
268
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400269 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000270 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500271 &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400272 std::unique_ptr<uint8_t[]> map_data;
273 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
274
275 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000276 compressed_map.maxLength = map.width * map.height;
277 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400278 compressed_map.data = compressed_map_data.get();
279 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
280
Nick Deakin6bd90432022-11-20 16:26:37 -0500281 JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400282
Dichen Zhang6947d532022-10-22 02:16:21 +0000283 return NO_ERROR;
284}
285
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000286status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
287 jr_info_ptr jpegr_info) {
288 if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
289 return ERROR_JPEGR_INVALID_NULL_PTR;
290 }
291
292 jpegr_compressed_struct primary_image, recovery_map;
293 JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image,
294 &primary_image, &recovery_map));
295
296 JpegDecoder jpeg_decoder;
297 if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
298 &jpegr_info->width, &jpegr_info->height,
299 jpegr_info->iccData, jpegr_info->exifData)) {
300 return ERROR_JPEGR_DECODE_ERROR;
301 }
302
303 return NO_ERROR;
304}
305
Dichen Zhang636f5242022-12-07 20:25:44 +0000306/* Decode API */
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400307status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
Dichen Zhangffa34012022-11-03 23:21:13 +0000308 jr_uncompressed_ptr dest,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000309 jr_exif_ptr exif,
310 bool request_sdr) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000311 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000312 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000313 }
314
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000315 // TODO: fill EXIF data
316 (void) exif;
317
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400318 jpegr_compressed_struct compressed_map;
Nick Deakin6bd90432022-11-20 16:26:37 -0500319 jpegr_metadata metadata;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000320 JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400321
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400322
323 JpegDecoder jpeg_decoder;
324 if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
325 return ERROR_JPEGR_DECODE_ERROR;
326 }
327
Fyodor Kyslovbf241572022-12-13 22:38:07 +0000328 JpegDecoder recovery_map_decoder;
329 if (!recovery_map_decoder.decompressImage(compressed_map.data,
330 compressed_map.length)) {
331 return ERROR_JPEGR_DECODE_ERROR;
332 }
333
334 jpegr_uncompressed_struct map;
335 map.data = recovery_map_decoder.getDecompressedImagePtr();
336 map.width = recovery_map_decoder.getDecompressedImageWidth();
337 map.height = recovery_map_decoder.getDecompressedImageHeight();
338
339
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400340 jpegr_uncompressed_struct uncompressed_yuv_420_image;
341 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
342 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
343 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
344
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000345 if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
346 jpeg_decoder.getXMPSize(), &metadata)) {
347 return ERROR_JPEGR_DECODE_ERROR;
348 }
349
350 if (request_sdr) {
351 memcpy(dest->data, uncompressed_yuv_420_image.data,
352 uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2);
353 dest->width = uncompressed_yuv_420_image.width;
354 dest->height = uncompressed_yuv_420_image.height;
355 } else {
356 JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
357 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400358
Dichen Zhang6947d532022-10-22 02:16:21 +0000359 return NO_ERROR;
360}
361
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400362status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
363 jr_compressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700364 if (uncompressed_recovery_map == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000365 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700366 }
367
Nick Deakin6bd90432022-11-20 16:26:37 -0500368 // TODO: should we have ICC data for the map?
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400369 JpegEncoder jpeg_encoder;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000370 if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data,
371 uncompressed_recovery_map->width,
372 uncompressed_recovery_map->height,
373 kMapCompressQuality,
374 nullptr,
375 0,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400376 true /* isSingleChannel */)) {
377 return ERROR_JPEGR_ENCODE_ERROR;
378 }
379
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000380 if (dest->maxLength < jpeg_encoder.getCompressedImageSize()) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400381 return ERROR_JPEGR_BUFFER_TOO_SMALL;
382 }
383
384 memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize());
385 dest->length = jpeg_encoder.getCompressedImageSize();
Nick Deakin6bd90432022-11-20 16:26:37 -0500386 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400387
Dichen Zhang6947d532022-10-22 02:16:21 +0000388 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700389}
390
Dichen Zhang6947d532022-10-22 02:16:21 +0000391status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
392 jr_uncompressed_ptr uncompressed_p010_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500393 jr_metadata_ptr metadata,
394 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700395 if (uncompressed_yuv_420_image == nullptr
396 || uncompressed_p010_image == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500397 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700398 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000399 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700400 }
401
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400402 if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width
403 || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) {
404 return ERROR_JPEGR_RESOLUTION_MISMATCH;
405 }
406
Nick Deakin6bd90432022-11-20 16:26:37 -0500407 if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED
408 || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) {
409 return ERROR_JPEGR_INVALID_COLORGAMUT;
410 }
411
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400412 size_t image_width = uncompressed_yuv_420_image->width;
413 size_t image_height = uncompressed_yuv_420_image->height;
414 size_t map_width = image_width / kMapDimensionScaleFactor;
415 size_t map_height = image_height / kMapDimensionScaleFactor;
416
417 dest->width = map_width;
418 dest->height = map_height;
Nick Deakin6bd90432022-11-20 16:26:37 -0500419 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400420 dest->data = new uint8_t[map_width * map_height];
421 std::unique_ptr<uint8_t[]> map_data;
422 map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
423
Nick Deakin6bd90432022-11-20 16:26:37 -0500424 ColorTransformFn hdrInvOetf = nullptr;
Nick Deakin65f492a2022-11-29 22:47:40 -0500425 float hdr_white_nits = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500426 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000427 case JPEGR_TF_LINEAR:
428 hdrInvOetf = identityConversion;
429 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500430 case JPEGR_TF_HLG:
431 hdrInvOetf = hlgInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500432 hdr_white_nits = kHlgMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500433 break;
434 case JPEGR_TF_PQ:
435 hdrInvOetf = pqInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500436 hdr_white_nits = kPqMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500437 break;
438 }
439
440 ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
441 uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
442
443 ColorCalculationFn luminanceFn = nullptr;
444 switch (uncompressed_yuv_420_image->colorGamut) {
445 case JPEGR_COLORGAMUT_BT709:
446 luminanceFn = srgbLuminance;
447 break;
448 case JPEGR_COLORGAMUT_P3:
449 luminanceFn = p3Luminance;
450 break;
451 case JPEGR_COLORGAMUT_BT2100:
452 luminanceFn = bt2100Luminance;
453 break;
454 case JPEGR_COLORGAMUT_UNSPECIFIED:
455 // Should be impossible to hit after input validation.
456 return ERROR_JPEGR_INVALID_COLORGAMUT;
457 }
458
Nick Deakin594a4ca2022-11-16 20:57:42 -0500459 float hdr_y_nits_max = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500460 double hdr_y_nits_avg = 0.0f;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400461 for (size_t y = 0; y < image_height; ++y) {
462 for (size_t x = 0; x < image_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500463 Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
464 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500465 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
466 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500467 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500468
Nick Deakin6bd90432022-11-20 16:26:37 -0500469 hdr_y_nits_avg += hdr_y_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500470 if (hdr_y_nits > hdr_y_nits_max) {
471 hdr_y_nits_max = hdr_y_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400472 }
473 }
474 }
Nick Deakin6bd90432022-11-20 16:26:37 -0500475 hdr_y_nits_avg /= image_width * image_height;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400476
Nick Deakin6bd90432022-11-20 16:26:37 -0500477 metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
478 if (metadata->transferFunction == JPEGR_TF_PQ) {
479 metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
480 metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
481 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400482
483 for (size_t y = 0; y < map_height; ++y) {
484 for (size_t x = 0; x < map_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500485 Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image,
486 kMapDimensionScaleFactor, x, y);
487 Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
488 Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
Nick Deakin65f492a2022-11-29 22:47:40 -0500489 float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400490
Nick Deakin594a4ca2022-11-16 20:57:42 -0500491 Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
492 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500493 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
494 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500495 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400496
497 size_t pixel_idx = x + y * map_width;
498 reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
Nick Deakin6bd90432022-11-20 16:26:37 -0500499 encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400500 }
501 }
502
503 map_data.release();
Dichen Zhang6947d532022-10-22 02:16:21 +0000504 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700505}
506
Dichen Zhang6947d532022-10-22 02:16:21 +0000507status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
508 jr_uncompressed_ptr uncompressed_recovery_map,
Nick Deakin6bd90432022-11-20 16:26:37 -0500509 jr_metadata_ptr metadata,
Dichen Zhang6947d532022-10-22 02:16:21 +0000510 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700511 if (uncompressed_yuv_420_image == nullptr
512 || uncompressed_recovery_map == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500513 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700514 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000515 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700516 }
517
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400518 size_t width = uncompressed_yuv_420_image->width;
519 size_t height = uncompressed_yuv_420_image->height;
520
521 dest->width = width;
522 dest->height = height;
523 size_t pixel_count = width * height;
524
Nick Deakin6bd90432022-11-20 16:26:37 -0500525 ColorTransformFn hdrOetf = nullptr;
526 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000527 case JPEGR_TF_LINEAR:
528 hdrOetf = identityConversion;
529 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500530 case JPEGR_TF_HLG:
531 hdrOetf = hlgOetf;
532 break;
533 case JPEGR_TF_PQ:
534 hdrOetf = pqOetf;
535 break;
536 }
537
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400538 for (size_t y = 0; y < height; ++y) {
539 for (size_t x = 0; x < width; ++x) {
Nick Deakin6bd90432022-11-20 16:26:37 -0500540 Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
541 Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr);
542 Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400543
Nick Deakin6bd90432022-11-20 16:26:37 -0500544 // TODO: determine map scaling factor based on actual map dims
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400545 float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y);
Nick Deakin6bd90432022-11-20 16:26:37 -0500546 Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400547
Nick Deakin38125332022-12-12 15:48:24 -0500548 Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
Nick Deakin6bd90432022-11-20 16:26:37 -0500549 uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400550
Nick Deakin6bd90432022-11-20 16:26:37 -0500551 size_t pixel_idx = x + y * width;
552 reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400553 }
554 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000555 return NO_ERROR;
556}
557
558status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
559 jr_compressed_ptr primary_image,
560 jr_compressed_ptr recovery_map) {
561 if (compressed_jpegr_image == nullptr) {
562 return ERROR_JPEGR_INVALID_NULL_PTR;
563 }
564
565 MessageHandler msg_handler;
566 std::shared_ptr<DataSegment> seg =
567 DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
568 static_cast<const uint8_t*>(compressed_jpegr_image->data),
569 DataSegment::BufferDispositionPolicy::kDontDelete);
570 DataSegmentDataSource data_source(seg);
571 JpegInfoBuilder jpeg_info_builder;
572 jpeg_info_builder.SetImageLimit(2);
573 JpegScanner jpeg_scanner(&msg_handler);
574 jpeg_scanner.Run(&data_source, &jpeg_info_builder);
575 data_source.Reset();
576
577 if (jpeg_scanner.HasError()) {
578 return ERROR_JPEGR_INVALID_INPUT_TYPE;
579 }
580
581 const auto& jpeg_info = jpeg_info_builder.GetInfo();
582 const auto& image_ranges = jpeg_info.GetImageRanges();
583 if (image_ranges.empty()) {
584 return ERROR_JPEGR_INVALID_INPUT_TYPE;
585 }
586
587 if (image_ranges.size() != 2) {
588 // Must be 2 JPEG Images
589 return ERROR_JPEGR_INVALID_INPUT_TYPE;
590 }
591
592 if (primary_image != nullptr) {
593 primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
594 image_ranges[0].GetBegin();
595 primary_image->length = image_ranges[0].GetLength();
596 }
597
598 if (recovery_map != nullptr) {
599 recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
600 image_ranges[1].GetBegin();
601 recovery_map->length = image_ranges[1].GetLength();
602 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400603
Dichen Zhang6947d532022-10-22 02:16:21 +0000604 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700605}
606
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000607
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400608status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000609 jr_compressed_ptr dest) {
610 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000611 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700612 }
613
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000614 return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest);
Dichen Zhang85b37562022-10-11 11:08:28 -0700615}
616
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400617status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
618 jr_compressed_ptr compressed_recovery_map,
Nick Deakin6bd90432022-11-20 16:26:37 -0500619 jr_metadata_ptr metadata,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400620 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000621 if (compressed_jpeg_image == nullptr
622 || compressed_recovery_map == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500623 || metadata == nullptr
Dichen Zhang6947d532022-10-22 02:16:21 +0000624 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000625 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700626 }
627
Dichen Zhangfbb687a2022-11-22 00:57:00 +0000628 const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
629 const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
630 const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
Dichen Zhanga8766262022-11-07 23:48:24 +0000631
632 // 2 bytes: APP1 sign (ff e1)
Dichen Zhangfbb687a2022-11-22 00:57:00 +0000633 // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
Dichen Zhanga8766262022-11-07 23:48:24 +0000634 // x bytes: length of xmp packet
Dichen Zhangfbb687a2022-11-22 00:57:00 +0000635
636 const int length = 3 + nameSpaceLength + xmp.size();
637 const uint8_t lengthH = ((length >> 8) & 0xff);
638 const uint8_t lengthL = (length & 0xff);
Dichen Zhanga8766262022-11-07 23:48:24 +0000639
640 int pos = 0;
641
642 // JPEG/R structure:
643 // SOI (ff d8)
644 // APP1 (ff e1)
645 // 2 bytes of length (2 + 29 + length of xmp packet)
646 // name space ("http://ns.adobe.com/xap/1.0/\0")
647 // xmp
648 // primary image (without the first two bytes, the SOI sign)
649 // secondary image (the recovery map)
650 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
651 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
652 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
653 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
654 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
655 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
Dichen Zhangfbb687a2022-11-22 00:57:00 +0000656 JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
Dichen Zhanga8766262022-11-07 23:48:24 +0000657 JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
658 JPEGR_CHECK(Write(dest,
659 (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
660 JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
661 dest->length = pos;
662
Dichen Zhang6947d532022-10-22 02:16:21 +0000663 return NO_ERROR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700664}
665
Dichen Zhang636f5242022-12-07 20:25:44 +0000666status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
667 jr_uncompressed_ptr dest) {
668 if (uncompressed_p010_image == nullptr || dest == nullptr) {
669 return ERROR_JPEGR_INVALID_NULL_PTR;
670 }
671
672 dest->width = uncompressed_p010_image->width;
673 dest->height = uncompressed_p010_image->height;
674 unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
675 dest->data = dest_data.get();
676
677 // TODO: Tone map algorighm here.
678
679 return NO_ERROR;
680}
681
Dichen Zhang85b37562022-10-11 11:08:28 -0700682} // namespace android::recoverymap