blob: 27a756f2a1b4d995bf984700dc179796281df493 [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 Zhangd18bc302022-12-16 20:55:24 +000085status_t Write(jr_exif_ptr destination, const void* source, size_t length, int &position) {
86 memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
87 position += length;
88 return NO_ERROR;
89}
90
91// If the EXIF package doesn't exist in the input JPEG, we'll create one with one entry
92// where the length is represented by this value.
93const size_t PSEUDO_EXIF_PACKAGE_LENGTH = 28;
94// If the EXIF package exists in the input JPEG, we'll add an "JR" entry where the length is
95// represented by this value.
96const size_t EXIF_J_R_ENTRY_LENGTH = 12;
97
98/*
99 * Helper function
100 * Add J R entry to existing exif, or create a new one with J R entry if it's null.
101 * EXIF syntax / change:
102 * ori:
103 * FF E1 - APP1
104 * 01 FC - size of APP1 (to be calculated)
105 * -----------------------------------------------------
106 * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
107 * 49 49 2A 00 - TIFF Header
108 * 08 00 00 00 - offset to the IFD (image file directory)
109 * 06 00 - 6 entries
110 * 00 01 - Width Tag
111 * 03 00 - 'Short' type
112 * 01 00 00 00 - one entry
113 * 00 05 00 00 - image with 0x500
114 *--------------------------------------------------------------------------
115 * new:
116 * FF E1 - APP1
117 * 02 08 - new size, equals to old size + EXIF_J_R_ENTRY_LENGTH (12)
118 *-----------------------------------------------------
119 * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
120 * 49 49 2A 00 - TIFF Header
121 * 08 00 00 00 - offset to the IFD (image file directory)
122 * 07 00 - +1 entry
123 * 4A 52 Custom ('J''R') Tag
124 * 07 00 - Unknown type
125 * 01 00 00 00 - one element
126 * 00 00 00 00 - empty data
127 * 00 01 - Width Tag
128 * 03 00 - 'Short' type
129 * 01 00 00 00 - one entry
130 * 00 05 00 00 - image with 0x500
131 */
132status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest) {
133 if (exif == nullptr || exif->data == nullptr) {
134 uint8_t data[PSEUDO_EXIF_PACKAGE_LENGTH] = {
135 0x45, 0x78, 0x69, 0x66, 0x00, 0x00,
136 0x49, 0x49, 0x2A, 0x00,
137 0x08, 0x00, 0x00, 0x00,
138 0x01, 0x00,
139 0x4A, 0x52,
140 0x07, 0x00,
141 0x01, 0x00, 0x00, 0x00,
142 0x00, 0x00, 0x00, 0x00};
143 int pos = 0;
144 Write(dest, data, PSEUDO_EXIF_PACKAGE_LENGTH, pos);
145 return NO_ERROR;
146 }
147
148 int num_entry = 0;
149 uint8_t num_entry_low = 0;
150 uint8_t num_entry_high = 0;
151 bool use_big_endian = false;
152 if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4949) {
153 num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[14];
154 num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[15];
155 } else if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4d4d) {
156 use_big_endian = true;
157 num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[14];
158 num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[15];
159 } else {
160 return ERROR_JPEGR_METADATA_ERROR;
161 }
162 num_entry = (num_entry_high << 8) | num_entry_low;
163 num_entry += 1;
164 num_entry_low = num_entry & 0xff;
165 num_entry_high = (num_entry << 8) & 0xff;
166
167 int pos = 0;
168 Write(dest, (uint8_t*)exif->data, 14, pos);
169
170 if (use_big_endian) {
171 Write(dest, &num_entry_high, 1, pos);
172 Write(dest, &num_entry_low, 1, pos);
173 uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
174 0x4A, 0x52,
175 0x07, 0x00,
176 0x01, 0x00, 0x00, 0x00,
177 0x00, 0x00, 0x00, 0x00};
178 Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
179 } else {
180 Write(dest, &num_entry_low, 1, pos);
181 Write(dest, &num_entry_high, 1, pos);
182 uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
183 0x4A, 0x52,
184 0x00, 0x07,
185 0x00, 0x00, 0x00, 0x01,
186 0x00, 0x00, 0x00, 0x00};
187 Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
188 }
189
190 Write(dest, (uint8_t*)exif->data + 16, exif->length - 16, pos);
191
192 return NO_ERROR;
193}
194
Dichen Zhang636f5242022-12-07 20:25:44 +0000195/* Encode API-0 */
196status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
197 jpegr_transfer_function hdr_tf,
198 jr_compressed_ptr dest,
199 int quality,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000200 jr_exif_ptr exif) {
Dichen Zhang636f5242022-12-07 20:25:44 +0000201 if (uncompressed_p010_image == nullptr || dest == nullptr) {
202 return ERROR_JPEGR_INVALID_NULL_PTR;
203 }
204
205 if (quality < 0 || quality > 100) {
206 return ERROR_JPEGR_INVALID_INPUT_TYPE;
207 }
208
209 jpegr_metadata metadata;
210 metadata.version = kJpegrVersion;
211 metadata.transferFunction = hdr_tf;
212 if (hdr_tf == JPEGR_TF_PQ) {
213 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
214 }
215
216 jpegr_uncompressed_struct uncompressed_yuv_420_image;
217 JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
218
219 jpegr_uncompressed_struct map;
220 JPEGR_CHECK(generateRecoveryMap(
221 &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
222 std::unique_ptr<uint8_t[]> map_data;
223 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
224
225 jpegr_compressed_struct compressed_map;
226 compressed_map.maxLength = map.width * map.height;
227 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
228 compressed_map.data = compressed_map_data.get();
229 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
230
231 JpegEncoder jpeg_encoder;
232 // TODO: determine ICC data based on color gamut information
233 if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
234 uncompressed_yuv_420_image.width,
235 uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
236 return ERROR_JPEGR_ENCODE_ERROR;
237 }
238 jpegr_compressed_struct jpeg;
239 jpeg.data = jpeg_encoder.getCompressedImagePtr();
240 jpeg.length = jpeg_encoder.getCompressedImageSize();
241
Dichen Zhangd18bc302022-12-16 20:55:24 +0000242 jpegr_exif_struct new_exif;
243 if (exif->data == nullptr) {
244 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
245 } else {
246 new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
247 }
248 new_exif.data = new uint8_t[new_exif.length];
249 std::unique_ptr<uint8_t[]> new_exif_data;
250 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
251 JPEGR_CHECK(updateExif(exif, &new_exif));
252
253 JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
Dichen Zhang636f5242022-12-07 20:25:44 +0000254
255 return NO_ERROR;
256}
257
258/* Encode API-1 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000259status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
260 jr_uncompressed_ptr uncompressed_yuv_420_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500261 jpegr_transfer_function hdr_tf,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400262 jr_compressed_ptr dest,
Dichen Zhangffa34012022-11-03 23:21:13 +0000263 int quality,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000264 jr_exif_ptr exif) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000265 if (uncompressed_p010_image == nullptr
266 || uncompressed_yuv_420_image == nullptr
267 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000268 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000269 }
270
Dichen Zhangffa34012022-11-03 23:21:13 +0000271 if (quality < 0 || quality > 100) {
272 return ERROR_JPEGR_INVALID_INPUT_TYPE;
273 }
274
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400275 if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
276 || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
277 return ERROR_JPEGR_RESOLUTION_MISMATCH;
278 }
279
Nick Deakin6bd90432022-11-20 16:26:37 -0500280 jpegr_metadata metadata;
281 metadata.version = kJpegrVersion;
282 metadata.transferFunction = hdr_tf;
283 if (hdr_tf == JPEGR_TF_PQ) {
284 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
285 }
286
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400287 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000288 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500289 uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400290 std::unique_ptr<uint8_t[]> map_data;
291 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
292
293 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000294 compressed_map.maxLength = map.width * map.height;
295 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400296 compressed_map.data = compressed_map_data.get();
297 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
298
299 JpegEncoder jpeg_encoder;
Nick Deakin6bd90432022-11-20 16:26:37 -0500300 // TODO: determine ICC data based on color gamut information
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400301 if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
302 uncompressed_yuv_420_image->width,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000303 uncompressed_yuv_420_image->height, quality, nullptr, 0)) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400304 return ERROR_JPEGR_ENCODE_ERROR;
305 }
306 jpegr_compressed_struct jpeg;
307 jpeg.data = jpeg_encoder.getCompressedImagePtr();
308 jpeg.length = jpeg_encoder.getCompressedImageSize();
309
Dichen Zhangd18bc302022-12-16 20:55:24 +0000310 jpegr_exif_struct new_exif;
311 if (exif == nullptr || exif->data == nullptr) {
312 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
313 } else {
314 new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
315 }
316
317 new_exif.data = new uint8_t[new_exif.length];
318 std::unique_ptr<uint8_t[]> new_exif_data;
319 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
320 JPEGR_CHECK(updateExif(exif, &new_exif));
321
322 JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400323
Dichen Zhang6947d532022-10-22 02:16:21 +0000324 return NO_ERROR;
325}
326
Dichen Zhang636f5242022-12-07 20:25:44 +0000327/* Encode API-2 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000328status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
329 jr_uncompressed_ptr uncompressed_yuv_420_image,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400330 jr_compressed_ptr compressed_jpeg_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500331 jpegr_transfer_function hdr_tf,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000332 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000333 if (uncompressed_p010_image == nullptr
334 || uncompressed_yuv_420_image == nullptr
335 || compressed_jpeg_image == nullptr
336 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000337 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000338 }
339
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400340 if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
341 || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
342 return ERROR_JPEGR_RESOLUTION_MISMATCH;
343 }
344
Nick Deakin6bd90432022-11-20 16:26:37 -0500345 jpegr_metadata metadata;
346 metadata.version = kJpegrVersion;
347 metadata.transferFunction = hdr_tf;
348 if (hdr_tf == JPEGR_TF_PQ) {
349 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
350 }
351
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400352 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000353 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500354 uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400355 std::unique_ptr<uint8_t[]> map_data;
356 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
357
358 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000359 compressed_map.maxLength = map.width * map.height;
360 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400361 compressed_map.data = compressed_map_data.get();
362 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
363
Dichen Zhangd18bc302022-12-16 20:55:24 +0000364 // Extract EXIF from JPEG without decoding.
365 JpegDecoder jpeg_decoder;
366 if (!jpeg_decoder.extractEXIF(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
367 return ERROR_JPEGR_DECODE_ERROR;
368 }
369
370 jpegr_exif_struct exif;
371 exif.data = nullptr;
372 exif.length = 0;
373 // Delete EXIF package if it appears, and update exif.
374 if (jpeg_decoder.getEXIFPos() != 0) {
375 int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4;
376 memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4,
377 (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos()
378 + jpeg_decoder.getEXIFSize(),
379 compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize());
380 compressed_jpeg_image->length = new_length;
381 exif.data = jpeg_decoder.getEXIFPtr();
382 exif.length = jpeg_decoder.getEXIFSize();
383 }
384
385 jpegr_exif_struct new_exif;
386 if (exif.data == nullptr) {
387 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
388 } else {
389 new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
390 }
391
392 new_exif.data = new uint8_t[new_exif.length];
393 std::unique_ptr<uint8_t[]> new_exif_data;
394 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
395 JPEGR_CHECK(updateExif(&exif, &new_exif));
396
397 JPEGR_CHECK(appendRecoveryMap(
398 compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400399
Dichen Zhang6947d532022-10-22 02:16:21 +0000400 return NO_ERROR;
401}
402
Dichen Zhang636f5242022-12-07 20:25:44 +0000403/* Encode API-3 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000404status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400405 jr_compressed_ptr compressed_jpeg_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500406 jpegr_transfer_function hdr_tf,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000407 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000408 if (uncompressed_p010_image == nullptr
409 || compressed_jpeg_image == nullptr
410 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000411 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000412 }
413
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400414 JpegDecoder jpeg_decoder;
415 if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
416 return ERROR_JPEGR_DECODE_ERROR;
417 }
418 jpegr_uncompressed_struct uncompressed_yuv_420_image;
419 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
420 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
421 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
Nick Deakin6bd90432022-11-20 16:26:37 -0500422 uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400423
Dichen Zhangd18bc302022-12-16 20:55:24 +0000424 jpegr_exif_struct exif;
425 exif.data = nullptr;
426 exif.length = 0;
427 // Delete EXIF package if it appears, and update exif.
428 if (jpeg_decoder.getEXIFPos() != 0) {
429 int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4;
430 memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4,
431 (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos()
432 + jpeg_decoder.getEXIFSize(),
433 compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize());
434 compressed_jpeg_image->length = new_length;
435 exif.data = jpeg_decoder.getEXIFPtr();
436 exif.length = jpeg_decoder.getEXIFSize();
437 }
438
439 jpegr_exif_struct new_exif;
440 if (exif.data == nullptr) {
441 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
442 } else {
443 new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
444 }
445 new_exif.data = new uint8_t[new_exif.length];
446 std::unique_ptr<uint8_t[]> new_exif_data;
447 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
448 JPEGR_CHECK(updateExif(&exif, &new_exif));
449
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400450 if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
451 || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
452 return ERROR_JPEGR_RESOLUTION_MISMATCH;
453 }
454
Nick Deakin6bd90432022-11-20 16:26:37 -0500455 jpegr_metadata metadata;
456 metadata.version = kJpegrVersion;
457 metadata.transferFunction = hdr_tf;
458 if (hdr_tf == JPEGR_TF_PQ) {
459 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
460 }
461
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400462 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000463 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500464 &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400465 std::unique_ptr<uint8_t[]> map_data;
466 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
467
468 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000469 compressed_map.maxLength = map.width * map.height;
470 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400471 compressed_map.data = compressed_map_data.get();
472 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
473
Dichen Zhangd18bc302022-12-16 20:55:24 +0000474 JPEGR_CHECK(appendRecoveryMap(
475 compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400476
Dichen Zhang6947d532022-10-22 02:16:21 +0000477 return NO_ERROR;
478}
479
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000480status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
481 jr_info_ptr jpegr_info) {
482 if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
483 return ERROR_JPEGR_INVALID_NULL_PTR;
484 }
485
486 jpegr_compressed_struct primary_image, recovery_map;
487 JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image,
488 &primary_image, &recovery_map));
489
490 JpegDecoder jpeg_decoder;
491 if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
492 &jpegr_info->width, &jpegr_info->height,
493 jpegr_info->iccData, jpegr_info->exifData)) {
494 return ERROR_JPEGR_DECODE_ERROR;
495 }
496
497 return NO_ERROR;
498}
499
Dichen Zhang636f5242022-12-07 20:25:44 +0000500/* Decode API */
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400501status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
Dichen Zhangffa34012022-11-03 23:21:13 +0000502 jr_uncompressed_ptr dest,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000503 jr_exif_ptr exif,
504 bool request_sdr) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000505 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000506 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000507 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000508 // TODO: fill EXIF data
509 (void) exif;
510
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000511 if (request_sdr) {
512 JpegDecoder jpeg_decoder;
513 if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length,
514 true)) {
515 return ERROR_JPEGR_DECODE_ERROR;
516 }
517 jpegr_uncompressed_struct uncompressed_rgba_image;
518 uncompressed_rgba_image.data = jpeg_decoder.getDecompressedImagePtr();
519 uncompressed_rgba_image.width = jpeg_decoder.getDecompressedImageWidth();
520 uncompressed_rgba_image.height = jpeg_decoder.getDecompressedImageHeight();
521 memcpy(dest->data, uncompressed_rgba_image.data,
522 uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4);
523 dest->width = uncompressed_rgba_image.width;
524 dest->height = uncompressed_rgba_image.height;
525 return NO_ERROR;
526 }
527
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400528 jpegr_compressed_struct compressed_map;
Nick Deakin6bd90432022-11-20 16:26:37 -0500529 jpegr_metadata metadata;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000530 JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400531
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400532 JpegDecoder jpeg_decoder;
533 if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
534 return ERROR_JPEGR_DECODE_ERROR;
535 }
536
Fyodor Kyslovbf241572022-12-13 22:38:07 +0000537 JpegDecoder recovery_map_decoder;
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000538 if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) {
Fyodor Kyslovbf241572022-12-13 22:38:07 +0000539 return ERROR_JPEGR_DECODE_ERROR;
540 }
541
542 jpegr_uncompressed_struct map;
543 map.data = recovery_map_decoder.getDecompressedImagePtr();
544 map.width = recovery_map_decoder.getDecompressedImageWidth();
545 map.height = recovery_map_decoder.getDecompressedImageHeight();
546
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400547 jpegr_uncompressed_struct uncompressed_yuv_420_image;
548 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
549 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
550 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
551
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000552 if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000553 jpeg_decoder.getXMPSize(), &metadata)) {
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000554 return ERROR_JPEGR_DECODE_ERROR;
555 }
556
Fyodor Kyslovea9180f2023-01-06 01:11:43 +0000557 JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
Dichen Zhang6947d532022-10-22 02:16:21 +0000558 return NO_ERROR;
559}
560
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400561status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
562 jr_compressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700563 if (uncompressed_recovery_map == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000564 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700565 }
566
Nick Deakin6bd90432022-11-20 16:26:37 -0500567 // TODO: should we have ICC data for the map?
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400568 JpegEncoder jpeg_encoder;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000569 if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data,
570 uncompressed_recovery_map->width,
571 uncompressed_recovery_map->height,
572 kMapCompressQuality,
573 nullptr,
574 0,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400575 true /* isSingleChannel */)) {
576 return ERROR_JPEGR_ENCODE_ERROR;
577 }
578
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000579 if (dest->maxLength < jpeg_encoder.getCompressedImageSize()) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400580 return ERROR_JPEGR_BUFFER_TOO_SMALL;
581 }
582
583 memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize());
584 dest->length = jpeg_encoder.getCompressedImageSize();
Nick Deakin6bd90432022-11-20 16:26:37 -0500585 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400586
Dichen Zhang6947d532022-10-22 02:16:21 +0000587 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700588}
589
Dichen Zhang6947d532022-10-22 02:16:21 +0000590status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
591 jr_uncompressed_ptr uncompressed_p010_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500592 jr_metadata_ptr metadata,
593 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700594 if (uncompressed_yuv_420_image == nullptr
595 || uncompressed_p010_image == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500596 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700597 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000598 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700599 }
600
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400601 if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width
602 || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) {
603 return ERROR_JPEGR_RESOLUTION_MISMATCH;
604 }
605
Nick Deakin6bd90432022-11-20 16:26:37 -0500606 if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED
607 || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) {
608 return ERROR_JPEGR_INVALID_COLORGAMUT;
609 }
610
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400611 size_t image_width = uncompressed_yuv_420_image->width;
612 size_t image_height = uncompressed_yuv_420_image->height;
613 size_t map_width = image_width / kMapDimensionScaleFactor;
614 size_t map_height = image_height / kMapDimensionScaleFactor;
615
616 dest->width = map_width;
617 dest->height = map_height;
Nick Deakin6bd90432022-11-20 16:26:37 -0500618 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400619 dest->data = new uint8_t[map_width * map_height];
620 std::unique_ptr<uint8_t[]> map_data;
621 map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
622
Nick Deakin6bd90432022-11-20 16:26:37 -0500623 ColorTransformFn hdrInvOetf = nullptr;
Nick Deakin65f492a2022-11-29 22:47:40 -0500624 float hdr_white_nits = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500625 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000626 case JPEGR_TF_LINEAR:
627 hdrInvOetf = identityConversion;
628 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500629 case JPEGR_TF_HLG:
630 hdrInvOetf = hlgInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500631 hdr_white_nits = kHlgMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500632 break;
633 case JPEGR_TF_PQ:
634 hdrInvOetf = pqInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500635 hdr_white_nits = kPqMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500636 break;
637 }
638
639 ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
640 uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
641
642 ColorCalculationFn luminanceFn = nullptr;
643 switch (uncompressed_yuv_420_image->colorGamut) {
644 case JPEGR_COLORGAMUT_BT709:
645 luminanceFn = srgbLuminance;
646 break;
647 case JPEGR_COLORGAMUT_P3:
648 luminanceFn = p3Luminance;
649 break;
650 case JPEGR_COLORGAMUT_BT2100:
651 luminanceFn = bt2100Luminance;
652 break;
653 case JPEGR_COLORGAMUT_UNSPECIFIED:
654 // Should be impossible to hit after input validation.
655 return ERROR_JPEGR_INVALID_COLORGAMUT;
656 }
657
Nick Deakin594a4ca2022-11-16 20:57:42 -0500658 float hdr_y_nits_max = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500659 double hdr_y_nits_avg = 0.0f;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400660 for (size_t y = 0; y < image_height; ++y) {
661 for (size_t x = 0; x < image_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500662 Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
663 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500664 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
665 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500666 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500667
Nick Deakin6bd90432022-11-20 16:26:37 -0500668 hdr_y_nits_avg += hdr_y_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500669 if (hdr_y_nits > hdr_y_nits_max) {
670 hdr_y_nits_max = hdr_y_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400671 }
672 }
673 }
Nick Deakin6bd90432022-11-20 16:26:37 -0500674 hdr_y_nits_avg /= image_width * image_height;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400675
Nick Deakin6bd90432022-11-20 16:26:37 -0500676 metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
677 if (metadata->transferFunction == JPEGR_TF_PQ) {
678 metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
679 metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
680 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400681
682 for (size_t y = 0; y < map_height; ++y) {
683 for (size_t x = 0; x < map_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500684 Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image,
685 kMapDimensionScaleFactor, x, y);
686 Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
687 Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
Nick Deakin65f492a2022-11-29 22:47:40 -0500688 float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400689
Nick Deakin594a4ca2022-11-16 20:57:42 -0500690 Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
691 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500692 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
693 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500694 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400695
696 size_t pixel_idx = x + y * map_width;
697 reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
Nick Deakin6bd90432022-11-20 16:26:37 -0500698 encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400699 }
700 }
701
702 map_data.release();
Dichen Zhang6947d532022-10-22 02:16:21 +0000703 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700704}
705
Dichen Zhang6947d532022-10-22 02:16:21 +0000706status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
707 jr_uncompressed_ptr uncompressed_recovery_map,
Nick Deakin6bd90432022-11-20 16:26:37 -0500708 jr_metadata_ptr metadata,
Dichen Zhang6947d532022-10-22 02:16:21 +0000709 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700710 if (uncompressed_yuv_420_image == nullptr
711 || uncompressed_recovery_map == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500712 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700713 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000714 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700715 }
716
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400717 size_t width = uncompressed_yuv_420_image->width;
718 size_t height = uncompressed_yuv_420_image->height;
719
720 dest->width = width;
721 dest->height = height;
722 size_t pixel_count = width * height;
723
Nick Deakin6bd90432022-11-20 16:26:37 -0500724 ColorTransformFn hdrOetf = nullptr;
725 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000726 case JPEGR_TF_LINEAR:
727 hdrOetf = identityConversion;
728 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500729 case JPEGR_TF_HLG:
730 hdrOetf = hlgOetf;
731 break;
732 case JPEGR_TF_PQ:
733 hdrOetf = pqOetf;
734 break;
735 }
736
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400737 for (size_t y = 0; y < height; ++y) {
738 for (size_t x = 0; x < width; ++x) {
Nick Deakin6bd90432022-11-20 16:26:37 -0500739 Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
740 Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr);
741 Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400742
Nick Deakin6bd90432022-11-20 16:26:37 -0500743 // TODO: determine map scaling factor based on actual map dims
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400744 float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y);
Nick Deakin6bd90432022-11-20 16:26:37 -0500745 Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400746
Nick Deakin38125332022-12-12 15:48:24 -0500747 Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
Nick Deakin6bd90432022-11-20 16:26:37 -0500748 uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400749
Nick Deakin6bd90432022-11-20 16:26:37 -0500750 size_t pixel_idx = x + y * width;
751 reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400752 }
753 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000754 return NO_ERROR;
755}
756
757status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
758 jr_compressed_ptr primary_image,
759 jr_compressed_ptr recovery_map) {
760 if (compressed_jpegr_image == nullptr) {
761 return ERROR_JPEGR_INVALID_NULL_PTR;
762 }
763
764 MessageHandler msg_handler;
765 std::shared_ptr<DataSegment> seg =
766 DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
767 static_cast<const uint8_t*>(compressed_jpegr_image->data),
768 DataSegment::BufferDispositionPolicy::kDontDelete);
769 DataSegmentDataSource data_source(seg);
770 JpegInfoBuilder jpeg_info_builder;
771 jpeg_info_builder.SetImageLimit(2);
772 JpegScanner jpeg_scanner(&msg_handler);
773 jpeg_scanner.Run(&data_source, &jpeg_info_builder);
774 data_source.Reset();
775
776 if (jpeg_scanner.HasError()) {
777 return ERROR_JPEGR_INVALID_INPUT_TYPE;
778 }
779
780 const auto& jpeg_info = jpeg_info_builder.GetInfo();
781 const auto& image_ranges = jpeg_info.GetImageRanges();
782 if (image_ranges.empty()) {
783 return ERROR_JPEGR_INVALID_INPUT_TYPE;
784 }
785
786 if (image_ranges.size() != 2) {
787 // Must be 2 JPEG Images
788 return ERROR_JPEGR_INVALID_INPUT_TYPE;
789 }
790
791 if (primary_image != nullptr) {
792 primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
793 image_ranges[0].GetBegin();
794 primary_image->length = image_ranges[0].GetLength();
795 }
796
797 if (recovery_map != nullptr) {
798 recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
799 image_ranges[1].GetBegin();
800 recovery_map->length = image_ranges[1].GetLength();
801 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400802
Dichen Zhang6947d532022-10-22 02:16:21 +0000803 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700804}
805
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000806
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400807status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000808 jr_compressed_ptr dest) {
809 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000810 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700811 }
812
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000813 return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest);
Dichen Zhang85b37562022-10-11 11:08:28 -0700814}
815
Dichen Zhangd18bc302022-12-16 20:55:24 +0000816// JPEG/R structure:
817// SOI (ff d8)
818// APP1 (ff e1)
819// 2 bytes of length (2 + length of exif package)
820// EXIF package (this includes the first two bytes representing the package length)
821// APP1 (ff e1)
822// 2 bytes of length (2 + 29 + length of xmp package)
823// name space ("http://ns.adobe.com/xap/1.0/\0")
824// xmp
825// primary image (without the first two bytes (SOI) and without EXIF, may have other packages)
826// secondary image (the recovery map)
827//
828// Metadata versions we are using:
829// ECMA TR-98 for JFIF marker
830// Exif 2.2 spec for EXIF marker
831// Adobe XMP spec part 3 for XMP marker
832// ICC v4.3 spec for ICC
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400833status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
834 jr_compressed_ptr compressed_recovery_map,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000835 jr_exif_ptr exif,
Nick Deakin6bd90432022-11-20 16:26:37 -0500836 jr_metadata_ptr metadata,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400837 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000838 if (compressed_jpeg_image == nullptr
839 || compressed_recovery_map == nullptr
Dichen Zhangd18bc302022-12-16 20:55:24 +0000840 || exif == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500841 || metadata == nullptr
Dichen Zhang6947d532022-10-22 02:16:21 +0000842 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000843 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700844 }
845
Dichen Zhanga8766262022-11-07 23:48:24 +0000846 int pos = 0;
847
Dichen Zhangd18bc302022-12-16 20:55:24 +0000848 // Write SOI
Dichen Zhanga8766262022-11-07 23:48:24 +0000849 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
850 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000851
852 // Write EXIF
853 {
854 const int length = 2 + exif->length;
855 const uint8_t lengthH = ((length >> 8) & 0xff);
856 const uint8_t lengthL = (length & 0xff);
857 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
858 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
859 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
860 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
861 JPEGR_CHECK(Write(dest, exif->data, exif->length, pos));
862 }
863
864 // Prepare and write XMP
865 {
866 const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
867 const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
868 const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
869 // 2 bytes: representing the length of the package
870 // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
871 // x bytes: length of xmp packet
872 const int length = 3 + nameSpaceLength + xmp.size();
873 const uint8_t lengthH = ((length >> 8) & 0xff);
874 const uint8_t lengthL = (length & 0xff);
875 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
876 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
877 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
878 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
879 JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
880 JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
881 }
882
883 // Write primary image
Dichen Zhanga8766262022-11-07 23:48:24 +0000884 JPEGR_CHECK(Write(dest,
885 (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000886
887 // Write secondary image
Dichen Zhanga8766262022-11-07 23:48:24 +0000888 JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000889
890 // Set back length
Dichen Zhanga8766262022-11-07 23:48:24 +0000891 dest->length = pos;
892
Dichen Zhangd18bc302022-12-16 20:55:24 +0000893 // Done!
Dichen Zhang6947d532022-10-22 02:16:21 +0000894 return NO_ERROR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700895}
896
Dichen Zhang636f5242022-12-07 20:25:44 +0000897status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
898 jr_uncompressed_ptr dest) {
899 if (uncompressed_p010_image == nullptr || dest == nullptr) {
900 return ERROR_JPEGR_INVALID_NULL_PTR;
901 }
902
903 dest->width = uncompressed_p010_image->width;
904 dest->height = uncompressed_p010_image->height;
905 unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
906 dest->data = dest_data.get();
907
908 // TODO: Tone map algorighm here.
909
910 return NO_ERROR;
911}
912
Dichen Zhang85b37562022-10-11 11:08:28 -0700913} // namespace android::recoverymap