blob: ee68043c794e898631c74b115004fdde53631430 [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>
Nick Deakinf6bca5a2022-11-04 10:43:43 -040028
29#include <memory>
Dichen Zhang72fd2b12022-11-01 06:11:50 +000030#include <sstream>
31#include <string>
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000032#include <cmath>
Dichen Zhang72fd2b12022-11-01 06:11:50 +000033
34using namespace std;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000035using namespace photos_editing_formats::image_io;
Dichen Zhang85b37562022-10-11 11:08:28 -070036
37namespace android::recoverymap {
38
Nick Deakinf6bca5a2022-11-04 10:43:43 -040039#define JPEGR_CHECK(x) \
40 { \
41 status_t status = (x); \
42 if ((status) != NO_ERROR) { \
43 return status; \
44 } \
45 }
46
Nick Deakin6bd90432022-11-20 16:26:37 -050047// The current JPEGR version that we encode to
48static const uint32_t kJpegrVersion = 1;
49
Nick Deakinf6bca5a2022-11-04 10:43:43 -040050// Map is quarter res / sixteenth size
51static const size_t kMapDimensionScaleFactor = 4;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +000052// JPEG compress quality (0 ~ 100) for recovery map
53static const int kMapCompressQuality = 85;
Nick Deakinf6bca5a2022-11-04 10:43:43 -040054
Nick Deakin6bd90432022-11-20 16:26:37 -050055// TODO: fill in st2086 metadata
56static const st2086_metadata kSt2086Metadata = {
57 {0.0f, 0.0f},
58 {0.0f, 0.0f},
59 {0.0f, 0.0f},
60 {0.0f, 0.0f},
61 0,
62 1.0f,
63};
Nick Deakinf6bca5a2022-11-04 10:43:43 -040064
Dichen Zhang72fd2b12022-11-01 06:11:50 +000065/*
Dichen Zhanga8766262022-11-07 23:48:24 +000066 * Helper function used for writing data to destination.
67 *
68 * @param destination destination of the data to be written.
69 * @param source source of data being written.
70 * @param length length of the data to be written.
71 * @param position cursor in desitination where the data is to be written.
72 * @return status of succeed or error code.
73 */
74status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) {
Dichen Zhang0b9f7de2022-11-18 06:52:46 +000075 if (position + length > destination->maxLength) {
Dichen Zhanga8766262022-11-07 23:48:24 +000076 return ERROR_JPEGR_BUFFER_TOO_SMALL;
77 }
78
79 memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
80 position += length;
81 return NO_ERROR;
82}
83
Dichen Zhangd18bc302022-12-16 20:55:24 +000084status_t Write(jr_exif_ptr destination, const void* source, size_t length, int &position) {
85 memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
86 position += length;
87 return NO_ERROR;
88}
89
90// If the EXIF package doesn't exist in the input JPEG, we'll create one with one entry
91// where the length is represented by this value.
92const size_t PSEUDO_EXIF_PACKAGE_LENGTH = 28;
93// If the EXIF package exists in the input JPEG, we'll add an "JR" entry where the length is
94// represented by this value.
95const size_t EXIF_J_R_ENTRY_LENGTH = 12;
96
97/*
98 * Helper function
99 * Add J R entry to existing exif, or create a new one with J R entry if it's null.
100 * EXIF syntax / change:
101 * ori:
102 * FF E1 - APP1
103 * 01 FC - size of APP1 (to be calculated)
104 * -----------------------------------------------------
105 * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
106 * 49 49 2A 00 - TIFF Header
107 * 08 00 00 00 - offset to the IFD (image file directory)
108 * 06 00 - 6 entries
109 * 00 01 - Width Tag
110 * 03 00 - 'Short' type
111 * 01 00 00 00 - one entry
112 * 00 05 00 00 - image with 0x500
113 *--------------------------------------------------------------------------
114 * new:
115 * FF E1 - APP1
116 * 02 08 - new size, equals to old size + EXIF_J_R_ENTRY_LENGTH (12)
117 *-----------------------------------------------------
118 * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
119 * 49 49 2A 00 - TIFF Header
120 * 08 00 00 00 - offset to the IFD (image file directory)
121 * 07 00 - +1 entry
122 * 4A 52 Custom ('J''R') Tag
123 * 07 00 - Unknown type
124 * 01 00 00 00 - one element
125 * 00 00 00 00 - empty data
126 * 00 01 - Width Tag
127 * 03 00 - 'Short' type
128 * 01 00 00 00 - one entry
129 * 00 05 00 00 - image with 0x500
130 */
131status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest) {
132 if (exif == nullptr || exif->data == nullptr) {
133 uint8_t data[PSEUDO_EXIF_PACKAGE_LENGTH] = {
134 0x45, 0x78, 0x69, 0x66, 0x00, 0x00,
135 0x49, 0x49, 0x2A, 0x00,
136 0x08, 0x00, 0x00, 0x00,
137 0x01, 0x00,
138 0x4A, 0x52,
139 0x07, 0x00,
140 0x01, 0x00, 0x00, 0x00,
141 0x00, 0x00, 0x00, 0x00};
142 int pos = 0;
143 Write(dest, data, PSEUDO_EXIF_PACKAGE_LENGTH, pos);
144 return NO_ERROR;
145 }
146
147 int num_entry = 0;
148 uint8_t num_entry_low = 0;
149 uint8_t num_entry_high = 0;
150 bool use_big_endian = false;
151 if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4949) {
152 num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[14];
153 num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[15];
154 } else if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4d4d) {
155 use_big_endian = true;
156 num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[14];
157 num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[15];
158 } else {
159 return ERROR_JPEGR_METADATA_ERROR;
160 }
161 num_entry = (num_entry_high << 8) | num_entry_low;
162 num_entry += 1;
163 num_entry_low = num_entry & 0xff;
164 num_entry_high = (num_entry << 8) & 0xff;
165
166 int pos = 0;
167 Write(dest, (uint8_t*)exif->data, 14, pos);
168
169 if (use_big_endian) {
170 Write(dest, &num_entry_high, 1, pos);
171 Write(dest, &num_entry_low, 1, pos);
172 uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
173 0x4A, 0x52,
174 0x07, 0x00,
175 0x01, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00};
177 Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
178 } else {
179 Write(dest, &num_entry_low, 1, pos);
180 Write(dest, &num_entry_high, 1, pos);
181 uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
182 0x4A, 0x52,
183 0x00, 0x07,
184 0x00, 0x00, 0x00, 0x01,
185 0x00, 0x00, 0x00, 0x00};
186 Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
187 }
188
189 Write(dest, (uint8_t*)exif->data + 16, exif->length - 16, pos);
190
191 return NO_ERROR;
192}
193
Dichen Zhang63d92512023-01-04 12:01:16 -0800194/*
195 * Helper function copies the JPEG image from without EXIF.
196 *
197 * @param dest destination of the data to be written.
198 * @param source source of data being written.
199 * @param exif_pos position of the EXIF package, which is aligned with jpegdecoder.getEXIFPos().
200 * (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>).
201 * @param exif_size exif size without the initial 4 bytes, aligned with jpegdecoder.getEXIFSize().
202 */
203void copyJpegWithoutExif(jr_compressed_ptr dest,
204 jr_compressed_ptr source,
205 size_t exif_pos,
206 size_t exif_size) {
207 memcpy(dest, source, sizeof(jpegr_compressed_struct));
208
209 const size_t exif_offset = 4; //exif_pos has 4 bypes offset to the FF sign
210 dest->length = source->length - exif_size - exif_offset;
211 dest->data = malloc(dest->length);
212
213 memcpy(dest->data, source->data, exif_pos - exif_offset);
214 memcpy((uint8_t*)dest->data + exif_pos - exif_offset,
215 (uint8_t*)source->data + exif_pos + exif_size,
216 source->length - exif_pos - exif_size);
217}
218
Dichen Zhang636f5242022-12-07 20:25:44 +0000219/* Encode API-0 */
220status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
221 jpegr_transfer_function hdr_tf,
222 jr_compressed_ptr dest,
223 int quality,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000224 jr_exif_ptr exif) {
Dichen Zhang636f5242022-12-07 20:25:44 +0000225 if (uncompressed_p010_image == nullptr || dest == nullptr) {
226 return ERROR_JPEGR_INVALID_NULL_PTR;
227 }
228
229 if (quality < 0 || quality > 100) {
230 return ERROR_JPEGR_INVALID_INPUT_TYPE;
231 }
232
233 jpegr_metadata metadata;
234 metadata.version = kJpegrVersion;
235 metadata.transferFunction = hdr_tf;
236 if (hdr_tf == JPEGR_TF_PQ) {
237 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
238 }
239
240 jpegr_uncompressed_struct uncompressed_yuv_420_image;
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800241 unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
242 uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2);
243 uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get();
Dichen Zhang636f5242022-12-07 20:25:44 +0000244 JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
245
246 jpegr_uncompressed_struct map;
247 JPEGR_CHECK(generateRecoveryMap(
248 &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
249 std::unique_ptr<uint8_t[]> map_data;
250 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
251
252 jpegr_compressed_struct compressed_map;
253 compressed_map.maxLength = map.width * map.height;
254 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
255 compressed_map.data = compressed_map_data.get();
256 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
257
258 JpegEncoder jpeg_encoder;
259 // TODO: determine ICC data based on color gamut information
260 if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
261 uncompressed_yuv_420_image.width,
262 uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
263 return ERROR_JPEGR_ENCODE_ERROR;
264 }
265 jpegr_compressed_struct jpeg;
266 jpeg.data = jpeg_encoder.getCompressedImagePtr();
267 jpeg.length = jpeg_encoder.getCompressedImageSize();
268
Dichen Zhangd18bc302022-12-16 20:55:24 +0000269 jpegr_exif_struct new_exif;
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800270 if (exif == nullptr || exif->data == nullptr) {
Dichen Zhangd18bc302022-12-16 20:55:24 +0000271 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
272 } else {
273 new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
274 }
275 new_exif.data = new uint8_t[new_exif.length];
276 std::unique_ptr<uint8_t[]> new_exif_data;
277 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
278 JPEGR_CHECK(updateExif(exif, &new_exif));
279
280 JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
Dichen Zhang636f5242022-12-07 20:25:44 +0000281
282 return NO_ERROR;
283}
284
285/* Encode API-1 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000286status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
287 jr_uncompressed_ptr uncompressed_yuv_420_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500288 jpegr_transfer_function hdr_tf,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400289 jr_compressed_ptr dest,
Dichen Zhangffa34012022-11-03 23:21:13 +0000290 int quality,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000291 jr_exif_ptr exif) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000292 if (uncompressed_p010_image == nullptr
293 || uncompressed_yuv_420_image == nullptr
294 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000295 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000296 }
297
Dichen Zhangffa34012022-11-03 23:21:13 +0000298 if (quality < 0 || quality > 100) {
299 return ERROR_JPEGR_INVALID_INPUT_TYPE;
300 }
301
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400302 if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
303 || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
304 return ERROR_JPEGR_RESOLUTION_MISMATCH;
305 }
306
Nick Deakin6bd90432022-11-20 16:26:37 -0500307 jpegr_metadata metadata;
308 metadata.version = kJpegrVersion;
309 metadata.transferFunction = hdr_tf;
310 if (hdr_tf == JPEGR_TF_PQ) {
311 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
312 }
313
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400314 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000315 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500316 uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400317 std::unique_ptr<uint8_t[]> map_data;
318 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
319
320 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000321 compressed_map.maxLength = map.width * map.height;
322 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400323 compressed_map.data = compressed_map_data.get();
324 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
325
326 JpegEncoder jpeg_encoder;
Nick Deakin6bd90432022-11-20 16:26:37 -0500327 // TODO: determine ICC data based on color gamut information
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400328 if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
329 uncompressed_yuv_420_image->width,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000330 uncompressed_yuv_420_image->height, quality, nullptr, 0)) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400331 return ERROR_JPEGR_ENCODE_ERROR;
332 }
333 jpegr_compressed_struct jpeg;
334 jpeg.data = jpeg_encoder.getCompressedImagePtr();
335 jpeg.length = jpeg_encoder.getCompressedImageSize();
336
Dichen Zhangd18bc302022-12-16 20:55:24 +0000337 jpegr_exif_struct new_exif;
338 if (exif == nullptr || exif->data == nullptr) {
339 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
340 } else {
341 new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
342 }
343
344 new_exif.data = new uint8_t[new_exif.length];
345 std::unique_ptr<uint8_t[]> new_exif_data;
346 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
347 JPEGR_CHECK(updateExif(exif, &new_exif));
348
349 JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400350
Dichen Zhang6947d532022-10-22 02:16:21 +0000351 return NO_ERROR;
352}
353
Dichen Zhang636f5242022-12-07 20:25:44 +0000354/* Encode API-2 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000355status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
356 jr_uncompressed_ptr uncompressed_yuv_420_image,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400357 jr_compressed_ptr compressed_jpeg_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500358 jpegr_transfer_function hdr_tf,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000359 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000360 if (uncompressed_p010_image == nullptr
361 || uncompressed_yuv_420_image == nullptr
362 || compressed_jpeg_image == nullptr
363 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000364 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000365 }
366
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400367 if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
368 || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
369 return ERROR_JPEGR_RESOLUTION_MISMATCH;
370 }
371
Nick Deakin6bd90432022-11-20 16:26:37 -0500372 jpegr_metadata metadata;
373 metadata.version = kJpegrVersion;
374 metadata.transferFunction = hdr_tf;
375 if (hdr_tf == JPEGR_TF_PQ) {
376 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
377 }
378
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400379 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000380 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500381 uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400382 std::unique_ptr<uint8_t[]> map_data;
383 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
384
385 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000386 compressed_map.maxLength = map.width * map.height;
387 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400388 compressed_map.data = compressed_map_data.get();
389 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
390
Dichen Zhangd18bc302022-12-16 20:55:24 +0000391 // Extract EXIF from JPEG without decoding.
392 JpegDecoder jpeg_decoder;
393 if (!jpeg_decoder.extractEXIF(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
394 return ERROR_JPEGR_DECODE_ERROR;
395 }
396
Dichen Zhang63d92512023-01-04 12:01:16 -0800397 // Update exif.
Dichen Zhangd18bc302022-12-16 20:55:24 +0000398 jpegr_exif_struct exif;
399 exif.data = nullptr;
400 exif.length = 0;
Dichen Zhang63d92512023-01-04 12:01:16 -0800401 jpegr_compressed_struct new_jpeg_image;
402 new_jpeg_image.data = nullptr;
403 new_jpeg_image.length = 0;
Dichen Zhangd18bc302022-12-16 20:55:24 +0000404 if (jpeg_decoder.getEXIFPos() != 0) {
Dichen Zhang63d92512023-01-04 12:01:16 -0800405 copyJpegWithoutExif(&new_jpeg_image,
406 compressed_jpeg_image,
407 jpeg_decoder.getEXIFPos(),
408 jpeg_decoder.getEXIFSize());
Dichen Zhangd18bc302022-12-16 20:55:24 +0000409 exif.data = jpeg_decoder.getEXIFPtr();
410 exif.length = jpeg_decoder.getEXIFSize();
411 }
412
413 jpegr_exif_struct new_exif;
414 if (exif.data == nullptr) {
415 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
416 } else {
417 new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
418 }
419
420 new_exif.data = new uint8_t[new_exif.length];
421 std::unique_ptr<uint8_t[]> new_exif_data;
422 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
423 JPEGR_CHECK(updateExif(&exif, &new_exif));
424
425 JPEGR_CHECK(appendRecoveryMap(
Dichen Zhang63d92512023-01-04 12:01:16 -0800426 new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
427 &compressed_map, &new_exif, &metadata, dest));
428
429 if (new_jpeg_image.data != nullptr) {
430 free(new_jpeg_image.data);
431 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400432
Dichen Zhang6947d532022-10-22 02:16:21 +0000433 return NO_ERROR;
434}
435
Dichen Zhang636f5242022-12-07 20:25:44 +0000436/* Encode API-3 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000437status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400438 jr_compressed_ptr compressed_jpeg_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500439 jpegr_transfer_function hdr_tf,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000440 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000441 if (uncompressed_p010_image == nullptr
442 || compressed_jpeg_image == nullptr
443 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000444 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000445 }
446
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400447 JpegDecoder jpeg_decoder;
448 if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
449 return ERROR_JPEGR_DECODE_ERROR;
450 }
451 jpegr_uncompressed_struct uncompressed_yuv_420_image;
452 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
453 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
454 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
Nick Deakin6bd90432022-11-20 16:26:37 -0500455 uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400456
Dichen Zhang63d92512023-01-04 12:01:16 -0800457 // Update exif.
Dichen Zhangd18bc302022-12-16 20:55:24 +0000458 jpegr_exif_struct exif;
459 exif.data = nullptr;
460 exif.length = 0;
Dichen Zhang63d92512023-01-04 12:01:16 -0800461 jpegr_compressed_struct new_jpeg_image;
462 new_jpeg_image.data = nullptr;
463 new_jpeg_image.length = 0;
Dichen Zhangd18bc302022-12-16 20:55:24 +0000464 if (jpeg_decoder.getEXIFPos() != 0) {
Dichen Zhang63d92512023-01-04 12:01:16 -0800465 copyJpegWithoutExif(&new_jpeg_image,
466 compressed_jpeg_image,
467 jpeg_decoder.getEXIFPos(),
468 jpeg_decoder.getEXIFSize());
Dichen Zhangd18bc302022-12-16 20:55:24 +0000469 exif.data = jpeg_decoder.getEXIFPtr();
470 exif.length = jpeg_decoder.getEXIFSize();
471 }
472
473 jpegr_exif_struct new_exif;
474 if (exif.data == nullptr) {
475 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
476 } else {
477 new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
478 }
479 new_exif.data = new uint8_t[new_exif.length];
480 std::unique_ptr<uint8_t[]> new_exif_data;
481 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
482 JPEGR_CHECK(updateExif(&exif, &new_exif));
483
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400484 if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
485 || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
486 return ERROR_JPEGR_RESOLUTION_MISMATCH;
487 }
488
Nick Deakin6bd90432022-11-20 16:26:37 -0500489 jpegr_metadata metadata;
490 metadata.version = kJpegrVersion;
491 metadata.transferFunction = hdr_tf;
492 if (hdr_tf == JPEGR_TF_PQ) {
493 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
494 }
495
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400496 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000497 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500498 &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400499 std::unique_ptr<uint8_t[]> map_data;
500 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
501
502 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000503 compressed_map.maxLength = map.width * map.height;
504 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400505 compressed_map.data = compressed_map_data.get();
506 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
507
Dichen Zhangd18bc302022-12-16 20:55:24 +0000508 JPEGR_CHECK(appendRecoveryMap(
Dichen Zhang63d92512023-01-04 12:01:16 -0800509 new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
510 &compressed_map, &new_exif, &metadata, dest));
511
512 if (new_jpeg_image.data != nullptr) {
513 free(new_jpeg_image.data);
514 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400515
Dichen Zhang6947d532022-10-22 02:16:21 +0000516 return NO_ERROR;
517}
518
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000519status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
520 jr_info_ptr jpegr_info) {
521 if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
522 return ERROR_JPEGR_INVALID_NULL_PTR;
523 }
524
525 jpegr_compressed_struct primary_image, recovery_map;
526 JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image,
527 &primary_image, &recovery_map));
528
529 JpegDecoder jpeg_decoder;
530 if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
531 &jpegr_info->width, &jpegr_info->height,
532 jpegr_info->iccData, jpegr_info->exifData)) {
533 return ERROR_JPEGR_DECODE_ERROR;
534 }
535
536 return NO_ERROR;
537}
538
Dichen Zhang636f5242022-12-07 20:25:44 +0000539/* Decode API */
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400540status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
Dichen Zhangffa34012022-11-03 23:21:13 +0000541 jr_uncompressed_ptr dest,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000542 jr_exif_ptr exif,
543 bool request_sdr) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000544 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000545 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000546 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000547 // TODO: fill EXIF data
548 (void) exif;
549
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000550 if (request_sdr) {
551 JpegDecoder jpeg_decoder;
552 if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
553 true)) {
554 return ERROR_JPEGR_DECODE_ERROR;
555 }
556 jpegr_uncompressed_struct uncompressed_rgba_image;
557 uncompressed_rgba_image.data = jpeg_decoder.getDecompressedImagePtr();
558 uncompressed_rgba_image.width = jpeg_decoder.getDecompressedImageWidth();
559 uncompressed_rgba_image.height = jpeg_decoder.getDecompressedImageHeight();
560 memcpy(dest->data, uncompressed_rgba_image.data,
561 uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
562 dest->width = uncompressed_rgba_image.width;
563 dest->height = uncompressed_rgba_image.height;
564 return NO_ERROR;
565 }
566
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400567 jpegr_compressed_struct compressed_map;
Nick Deakin6bd90432022-11-20 16:26:37 -0500568 jpegr_metadata metadata;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000569 JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400570
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400571 JpegDecoder jpeg_decoder;
572 if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
573 return ERROR_JPEGR_DECODE_ERROR;
574 }
575
Fyodor Kyslovbf241572022-12-13 22:38:07 +0000576 JpegDecoder recovery_map_decoder;
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000577 if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
Fyodor Kyslovbf241572022-12-13 22:38:07 +0000578 return ERROR_JPEGR_DECODE_ERROR;
579 }
580
581 jpegr_uncompressed_struct map;
582 map.data = recovery_map_decoder.getDecompressedImagePtr();
583 map.width = recovery_map_decoder.getDecompressedImageWidth();
584 map.height = recovery_map_decoder.getDecompressedImageHeight();
585
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400586 jpegr_uncompressed_struct uncompressed_yuv_420_image;
587 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
588 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
589 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
590
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000591 if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000592 jpeg_decoder.getXMPSize(), &metadata)) {
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000593 return ERROR_JPEGR_DECODE_ERROR;
594 }
595
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000596 JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
Dichen Zhang6947d532022-10-22 02:16:21 +0000597 return NO_ERROR;
598}
599
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400600status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
601 jr_compressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700602 if (uncompressed_recovery_map == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000603 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700604 }
605
Nick Deakin6bd90432022-11-20 16:26:37 -0500606 // TODO: should we have ICC data for the map?
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400607 JpegEncoder jpeg_encoder;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000608 if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data,
609 uncompressed_recovery_map->width,
610 uncompressed_recovery_map->height,
611 kMapCompressQuality,
612 nullptr,
613 0,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400614 true /* isSingleChannel */)) {
615 return ERROR_JPEGR_ENCODE_ERROR;
616 }
617
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000618 if (dest->maxLength < jpeg_encoder.getCompressedImageSize()) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400619 return ERROR_JPEGR_BUFFER_TOO_SMALL;
620 }
621
622 memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize());
623 dest->length = jpeg_encoder.getCompressedImageSize();
Nick Deakin6bd90432022-11-20 16:26:37 -0500624 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400625
Dichen Zhang6947d532022-10-22 02:16:21 +0000626 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700627}
628
Dichen Zhang6947d532022-10-22 02:16:21 +0000629status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
630 jr_uncompressed_ptr uncompressed_p010_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500631 jr_metadata_ptr metadata,
632 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700633 if (uncompressed_yuv_420_image == nullptr
634 || uncompressed_p010_image == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500635 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700636 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000637 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700638 }
639
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400640 if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width
641 || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) {
642 return ERROR_JPEGR_RESOLUTION_MISMATCH;
643 }
644
Nick Deakin6bd90432022-11-20 16:26:37 -0500645 if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED
646 || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) {
647 return ERROR_JPEGR_INVALID_COLORGAMUT;
648 }
649
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400650 size_t image_width = uncompressed_yuv_420_image->width;
651 size_t image_height = uncompressed_yuv_420_image->height;
652 size_t map_width = image_width / kMapDimensionScaleFactor;
653 size_t map_height = image_height / kMapDimensionScaleFactor;
654
655 dest->width = map_width;
656 dest->height = map_height;
Nick Deakin6bd90432022-11-20 16:26:37 -0500657 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400658 dest->data = new uint8_t[map_width * map_height];
659 std::unique_ptr<uint8_t[]> map_data;
660 map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
661
Nick Deakin6bd90432022-11-20 16:26:37 -0500662 ColorTransformFn hdrInvOetf = nullptr;
Nick Deakin65f492a2022-11-29 22:47:40 -0500663 float hdr_white_nits = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500664 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000665 case JPEGR_TF_LINEAR:
666 hdrInvOetf = identityConversion;
667 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500668 case JPEGR_TF_HLG:
669 hdrInvOetf = hlgInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500670 hdr_white_nits = kHlgMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500671 break;
672 case JPEGR_TF_PQ:
673 hdrInvOetf = pqInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500674 hdr_white_nits = kPqMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500675 break;
676 }
677
678 ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
679 uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
680
681 ColorCalculationFn luminanceFn = nullptr;
682 switch (uncompressed_yuv_420_image->colorGamut) {
683 case JPEGR_COLORGAMUT_BT709:
684 luminanceFn = srgbLuminance;
685 break;
686 case JPEGR_COLORGAMUT_P3:
687 luminanceFn = p3Luminance;
688 break;
689 case JPEGR_COLORGAMUT_BT2100:
690 luminanceFn = bt2100Luminance;
691 break;
692 case JPEGR_COLORGAMUT_UNSPECIFIED:
693 // Should be impossible to hit after input validation.
694 return ERROR_JPEGR_INVALID_COLORGAMUT;
695 }
696
Nick Deakin594a4ca2022-11-16 20:57:42 -0500697 float hdr_y_nits_max = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500698 double hdr_y_nits_avg = 0.0f;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400699 for (size_t y = 0; y < image_height; ++y) {
700 for (size_t x = 0; x < image_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500701 Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
702 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500703 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
704 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500705 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500706
Nick Deakin6bd90432022-11-20 16:26:37 -0500707 hdr_y_nits_avg += hdr_y_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500708 if (hdr_y_nits > hdr_y_nits_max) {
709 hdr_y_nits_max = hdr_y_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400710 }
711 }
712 }
Nick Deakin6bd90432022-11-20 16:26:37 -0500713 hdr_y_nits_avg /= image_width * image_height;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400714
Nick Deakin6bd90432022-11-20 16:26:37 -0500715 metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
716 if (metadata->transferFunction == JPEGR_TF_PQ) {
717 metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
718 metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
719 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400720
721 for (size_t y = 0; y < map_height; ++y) {
722 for (size_t x = 0; x < map_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500723 Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image,
724 kMapDimensionScaleFactor, x, y);
725 Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
726 Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
Nick Deakin65f492a2022-11-29 22:47:40 -0500727 float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400728
Nick Deakin594a4ca2022-11-16 20:57:42 -0500729 Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
730 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500731 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
732 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500733 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400734
735 size_t pixel_idx = x + y * map_width;
736 reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
Nick Deakin6bd90432022-11-20 16:26:37 -0500737 encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400738 }
739 }
740
741 map_data.release();
Dichen Zhang6947d532022-10-22 02:16:21 +0000742 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700743}
744
Dichen Zhang6947d532022-10-22 02:16:21 +0000745status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
746 jr_uncompressed_ptr uncompressed_recovery_map,
Nick Deakin6bd90432022-11-20 16:26:37 -0500747 jr_metadata_ptr metadata,
Dichen Zhang6947d532022-10-22 02:16:21 +0000748 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700749 if (uncompressed_yuv_420_image == nullptr
750 || uncompressed_recovery_map == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500751 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700752 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000753 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700754 }
755
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400756 size_t width = uncompressed_yuv_420_image->width;
757 size_t height = uncompressed_yuv_420_image->height;
758
759 dest->width = width;
760 dest->height = height;
761 size_t pixel_count = width * height;
762
Nick Deakin6bd90432022-11-20 16:26:37 -0500763 ColorTransformFn hdrOetf = nullptr;
764 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000765 case JPEGR_TF_LINEAR:
766 hdrOetf = identityConversion;
767 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500768 case JPEGR_TF_HLG:
769 hdrOetf = hlgOetf;
770 break;
771 case JPEGR_TF_PQ:
772 hdrOetf = pqOetf;
773 break;
774 }
775
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400776 for (size_t y = 0; y < height; ++y) {
777 for (size_t x = 0; x < width; ++x) {
Nick Deakin6bd90432022-11-20 16:26:37 -0500778 Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
779 Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr);
780 Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400781
Nick Deakin6bd90432022-11-20 16:26:37 -0500782 // TODO: determine map scaling factor based on actual map dims
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400783 float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y);
Nick Deakin6bd90432022-11-20 16:26:37 -0500784 Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400785
Nick Deakin38125332022-12-12 15:48:24 -0500786 Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
Nick Deakin6bd90432022-11-20 16:26:37 -0500787 uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400788
Nick Deakin6bd90432022-11-20 16:26:37 -0500789 size_t pixel_idx = x + y * width;
790 reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400791 }
792 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000793 return NO_ERROR;
794}
795
796status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
797 jr_compressed_ptr primary_image,
798 jr_compressed_ptr recovery_map) {
799 if (compressed_jpegr_image == nullptr) {
800 return ERROR_JPEGR_INVALID_NULL_PTR;
801 }
802
803 MessageHandler msg_handler;
804 std::shared_ptr<DataSegment> seg =
805 DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
806 static_cast<const uint8_t*>(compressed_jpegr_image->data),
807 DataSegment::BufferDispositionPolicy::kDontDelete);
808 DataSegmentDataSource data_source(seg);
809 JpegInfoBuilder jpeg_info_builder;
810 jpeg_info_builder.SetImageLimit(2);
811 JpegScanner jpeg_scanner(&msg_handler);
812 jpeg_scanner.Run(&data_source, &jpeg_info_builder);
813 data_source.Reset();
814
815 if (jpeg_scanner.HasError()) {
816 return ERROR_JPEGR_INVALID_INPUT_TYPE;
817 }
818
819 const auto& jpeg_info = jpeg_info_builder.GetInfo();
820 const auto& image_ranges = jpeg_info.GetImageRanges();
821 if (image_ranges.empty()) {
822 return ERROR_JPEGR_INVALID_INPUT_TYPE;
823 }
824
825 if (image_ranges.size() != 2) {
826 // Must be 2 JPEG Images
827 return ERROR_JPEGR_INVALID_INPUT_TYPE;
828 }
829
830 if (primary_image != nullptr) {
831 primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
832 image_ranges[0].GetBegin();
833 primary_image->length = image_ranges[0].GetLength();
834 }
835
836 if (recovery_map != nullptr) {
837 recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
838 image_ranges[1].GetBegin();
839 recovery_map->length = image_ranges[1].GetLength();
840 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400841
Dichen Zhang6947d532022-10-22 02:16:21 +0000842 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700843}
844
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000845
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400846status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000847 jr_compressed_ptr dest) {
848 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000849 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700850 }
851
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000852 return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest);
Dichen Zhang85b37562022-10-11 11:08:28 -0700853}
854
Dichen Zhangd18bc302022-12-16 20:55:24 +0000855// JPEG/R structure:
856// SOI (ff d8)
857// APP1 (ff e1)
858// 2 bytes of length (2 + length of exif package)
859// EXIF package (this includes the first two bytes representing the package length)
860// APP1 (ff e1)
861// 2 bytes of length (2 + 29 + length of xmp package)
862// name space ("http://ns.adobe.com/xap/1.0/\0")
863// xmp
864// primary image (without the first two bytes (SOI) and without EXIF, may have other packages)
865// secondary image (the recovery map)
866//
867// Metadata versions we are using:
868// ECMA TR-98 for JFIF marker
869// Exif 2.2 spec for EXIF marker
870// Adobe XMP spec part 3 for XMP marker
871// ICC v4.3 spec for ICC
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400872status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
873 jr_compressed_ptr compressed_recovery_map,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000874 jr_exif_ptr exif,
Nick Deakin6bd90432022-11-20 16:26:37 -0500875 jr_metadata_ptr metadata,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400876 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000877 if (compressed_jpeg_image == nullptr
878 || compressed_recovery_map == nullptr
Dichen Zhangd18bc302022-12-16 20:55:24 +0000879 || exif == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500880 || metadata == nullptr
Dichen Zhang6947d532022-10-22 02:16:21 +0000881 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000882 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700883 }
884
Dichen Zhanga8766262022-11-07 23:48:24 +0000885 int pos = 0;
886
Dichen Zhangd18bc302022-12-16 20:55:24 +0000887 // Write SOI
Dichen Zhanga8766262022-11-07 23:48:24 +0000888 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
889 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000890
891 // Write EXIF
892 {
893 const int length = 2 + exif->length;
894 const uint8_t lengthH = ((length >> 8) & 0xff);
895 const uint8_t lengthL = (length & 0xff);
896 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
897 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
898 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
899 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
900 JPEGR_CHECK(Write(dest, exif->data, exif->length, pos));
901 }
902
903 // Prepare and write XMP
904 {
905 const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
906 const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
907 const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
908 // 2 bytes: representing the length of the package
909 // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
910 // x bytes: length of xmp packet
Dichen Zhang25df9c82023-01-03 17:04:10 -0800911 const int length = 2 + nameSpaceLength + xmp.size();
Dichen Zhangd18bc302022-12-16 20:55:24 +0000912 const uint8_t lengthH = ((length >> 8) & 0xff);
913 const uint8_t lengthL = (length & 0xff);
914 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
915 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
916 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
917 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
918 JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
919 JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
920 }
921
922 // Write primary image
Dichen Zhanga8766262022-11-07 23:48:24 +0000923 JPEGR_CHECK(Write(dest,
924 (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000925
926 // Write secondary image
Dichen Zhanga8766262022-11-07 23:48:24 +0000927 JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000928
929 // Set back length
Dichen Zhanga8766262022-11-07 23:48:24 +0000930 dest->length = pos;
931
Dichen Zhangd18bc302022-12-16 20:55:24 +0000932 // Done!
Dichen Zhang6947d532022-10-22 02:16:21 +0000933 return NO_ERROR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700934}
935
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800936status_t RecoveryMap::toneMap(jr_uncompressed_ptr src,
Dichen Zhang636f5242022-12-07 20:25:44 +0000937 jr_uncompressed_ptr dest) {
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800938 if (src == nullptr || dest == nullptr) {
Dichen Zhang636f5242022-12-07 20:25:44 +0000939 return ERROR_JPEGR_INVALID_NULL_PTR;
940 }
941
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800942 dest->width = src->width;
943 dest->height = src->height;
Dichen Zhang636f5242022-12-07 20:25:44 +0000944
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800945 size_t pixel_count = src->width * src->height;
946 for (size_t y = 0; y < src->height; ++y) {
947 for (size_t x = 0; x < src->width; ++x) {
948 size_t pixel_y_idx = x + y * src->width;
949 size_t pixel_uv_idx = x / 2 + (y / 2) * (src->width / 2);
950
951 uint16_t y_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_y_idx]
952 >> 6;
953 uint16_t u_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2]
954 >> 6;
955 uint16_t v_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2 + 1]
956 >> 6;
957
958 uint8_t* y = &reinterpret_cast<uint8_t*>(dest->data)[pixel_y_idx];
959 uint8_t* u = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count + pixel_uv_idx];
960 uint8_t* v = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count * 5 / 4 + pixel_uv_idx];
961
962 *y = static_cast<uint8_t>((y_uint >> 2) & 0xff);
963 *u = static_cast<uint8_t>((u_uint >> 2) & 0xff);
964 *v = static_cast<uint8_t>((v_uint >> 2) & 0xff);
965 }
966 }
967
968 dest->colorGamut = src->colorGamut;
Dichen Zhang636f5242022-12-07 20:25:44 +0000969
970 return NO_ERROR;
971}
972
Dichen Zhang85b37562022-10-11 11:08:28 -0700973} // namespace android::recoverymap