blob: f362e00ff6a7eea4656a9abff00071373a13b428 [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 Zhang636f5242022-12-07 20:25:44 +0000194/* Encode API-0 */
195status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
196 jpegr_transfer_function hdr_tf,
197 jr_compressed_ptr dest,
198 int quality,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000199 jr_exif_ptr exif) {
Dichen Zhang636f5242022-12-07 20:25:44 +0000200 if (uncompressed_p010_image == nullptr || dest == nullptr) {
201 return ERROR_JPEGR_INVALID_NULL_PTR;
202 }
203
204 if (quality < 0 || quality > 100) {
205 return ERROR_JPEGR_INVALID_INPUT_TYPE;
206 }
207
208 jpegr_metadata metadata;
209 metadata.version = kJpegrVersion;
210 metadata.transferFunction = hdr_tf;
211 if (hdr_tf == JPEGR_TF_PQ) {
212 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
213 }
214
215 jpegr_uncompressed_struct uncompressed_yuv_420_image;
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800216 unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
217 uncompressed_p010_image->width * uncompressed_p010_image->height * 3 / 2);
218 uncompressed_yuv_420_image.data = uncompressed_yuv_420_image_data.get();
Dichen Zhang636f5242022-12-07 20:25:44 +0000219 JPEGR_CHECK(toneMap(uncompressed_p010_image, &uncompressed_yuv_420_image));
220
221 jpegr_uncompressed_struct map;
222 JPEGR_CHECK(generateRecoveryMap(
223 &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
224 std::unique_ptr<uint8_t[]> map_data;
225 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
226
227 jpegr_compressed_struct compressed_map;
228 compressed_map.maxLength = map.width * map.height;
229 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
230 compressed_map.data = compressed_map_data.get();
231 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
232
233 JpegEncoder jpeg_encoder;
234 // TODO: determine ICC data based on color gamut information
235 if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data,
236 uncompressed_yuv_420_image.width,
237 uncompressed_yuv_420_image.height, quality, nullptr, 0)) {
238 return ERROR_JPEGR_ENCODE_ERROR;
239 }
240 jpegr_compressed_struct jpeg;
241 jpeg.data = jpeg_encoder.getCompressedImagePtr();
242 jpeg.length = jpeg_encoder.getCompressedImageSize();
243
Dichen Zhangd18bc302022-12-16 20:55:24 +0000244 jpegr_exif_struct new_exif;
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800245 if (exif == nullptr || exif->data == nullptr) {
Dichen Zhangd18bc302022-12-16 20:55:24 +0000246 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
247 } else {
248 new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
249 }
250 new_exif.data = new uint8_t[new_exif.length];
251 std::unique_ptr<uint8_t[]> new_exif_data;
252 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
253 JPEGR_CHECK(updateExif(exif, &new_exif));
254
255 JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
Dichen Zhang636f5242022-12-07 20:25:44 +0000256
257 return NO_ERROR;
258}
259
260/* Encode API-1 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000261status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
262 jr_uncompressed_ptr uncompressed_yuv_420_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500263 jpegr_transfer_function hdr_tf,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400264 jr_compressed_ptr dest,
Dichen Zhangffa34012022-11-03 23:21:13 +0000265 int quality,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000266 jr_exif_ptr exif) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000267 if (uncompressed_p010_image == nullptr
268 || uncompressed_yuv_420_image == nullptr
269 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000270 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000271 }
272
Dichen Zhangffa34012022-11-03 23:21:13 +0000273 if (quality < 0 || quality > 100) {
274 return ERROR_JPEGR_INVALID_INPUT_TYPE;
275 }
276
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400277 if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
278 || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
279 return ERROR_JPEGR_RESOLUTION_MISMATCH;
280 }
281
Nick Deakin6bd90432022-11-20 16:26:37 -0500282 jpegr_metadata metadata;
283 metadata.version = kJpegrVersion;
284 metadata.transferFunction = hdr_tf;
285 if (hdr_tf == JPEGR_TF_PQ) {
286 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
287 }
288
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400289 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000290 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500291 uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400292 std::unique_ptr<uint8_t[]> map_data;
293 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
294
295 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000296 compressed_map.maxLength = map.width * map.height;
297 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400298 compressed_map.data = compressed_map_data.get();
299 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
300
301 JpegEncoder jpeg_encoder;
Nick Deakin6bd90432022-11-20 16:26:37 -0500302 // TODO: determine ICC data based on color gamut information
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400303 if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data,
304 uncompressed_yuv_420_image->width,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000305 uncompressed_yuv_420_image->height, quality, nullptr, 0)) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400306 return ERROR_JPEGR_ENCODE_ERROR;
307 }
308 jpegr_compressed_struct jpeg;
309 jpeg.data = jpeg_encoder.getCompressedImagePtr();
310 jpeg.length = jpeg_encoder.getCompressedImageSize();
311
Dichen Zhangd18bc302022-12-16 20:55:24 +0000312 jpegr_exif_struct new_exif;
313 if (exif == nullptr || exif->data == nullptr) {
314 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
315 } else {
316 new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
317 }
318
319 new_exif.data = new uint8_t[new_exif.length];
320 std::unique_ptr<uint8_t[]> new_exif_data;
321 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
322 JPEGR_CHECK(updateExif(exif, &new_exif));
323
324 JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400325
Dichen Zhang6947d532022-10-22 02:16:21 +0000326 return NO_ERROR;
327}
328
Dichen Zhang636f5242022-12-07 20:25:44 +0000329/* Encode API-2 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000330status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
331 jr_uncompressed_ptr uncompressed_yuv_420_image,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400332 jr_compressed_ptr compressed_jpeg_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500333 jpegr_transfer_function hdr_tf,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000334 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000335 if (uncompressed_p010_image == nullptr
336 || uncompressed_yuv_420_image == nullptr
337 || compressed_jpeg_image == nullptr
338 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000339 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000340 }
341
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400342 if (uncompressed_p010_image->width != uncompressed_yuv_420_image->width
343 || uncompressed_p010_image->height != uncompressed_yuv_420_image->height) {
344 return ERROR_JPEGR_RESOLUTION_MISMATCH;
345 }
346
Nick Deakin6bd90432022-11-20 16:26:37 -0500347 jpegr_metadata metadata;
348 metadata.version = kJpegrVersion;
349 metadata.transferFunction = hdr_tf;
350 if (hdr_tf == JPEGR_TF_PQ) {
351 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
352 }
353
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400354 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000355 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500356 uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400357 std::unique_ptr<uint8_t[]> map_data;
358 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
359
360 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000361 compressed_map.maxLength = map.width * map.height;
362 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400363 compressed_map.data = compressed_map_data.get();
364 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
365
Dichen Zhangd18bc302022-12-16 20:55:24 +0000366 // Extract EXIF from JPEG without decoding.
367 JpegDecoder jpeg_decoder;
368 if (!jpeg_decoder.extractEXIF(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
369 return ERROR_JPEGR_DECODE_ERROR;
370 }
371
372 jpegr_exif_struct exif;
373 exif.data = nullptr;
374 exif.length = 0;
375 // Delete EXIF package if it appears, and update exif.
376 if (jpeg_decoder.getEXIFPos() != 0) {
377 int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4;
378 memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4,
379 (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos()
380 + jpeg_decoder.getEXIFSize(),
381 compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize());
382 compressed_jpeg_image->length = new_length;
383 exif.data = jpeg_decoder.getEXIFPtr();
384 exif.length = jpeg_decoder.getEXIFSize();
385 }
386
387 jpegr_exif_struct new_exif;
388 if (exif.data == nullptr) {
389 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
390 } else {
391 new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
392 }
393
394 new_exif.data = new uint8_t[new_exif.length];
395 std::unique_ptr<uint8_t[]> new_exif_data;
396 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
397 JPEGR_CHECK(updateExif(&exif, &new_exif));
398
399 JPEGR_CHECK(appendRecoveryMap(
400 compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400401
Dichen Zhang6947d532022-10-22 02:16:21 +0000402 return NO_ERROR;
403}
404
Dichen Zhang636f5242022-12-07 20:25:44 +0000405/* Encode API-3 */
Dichen Zhang6947d532022-10-22 02:16:21 +0000406status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400407 jr_compressed_ptr compressed_jpeg_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500408 jpegr_transfer_function hdr_tf,
Dichen Zhang95cbb9f2022-11-07 18:32:05 +0000409 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000410 if (uncompressed_p010_image == nullptr
411 || compressed_jpeg_image == nullptr
412 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000413 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000414 }
415
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400416 JpegDecoder jpeg_decoder;
417 if (!jpeg_decoder.decompressImage(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
418 return ERROR_JPEGR_DECODE_ERROR;
419 }
420 jpegr_uncompressed_struct uncompressed_yuv_420_image;
421 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
422 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
423 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
Nick Deakin6bd90432022-11-20 16:26:37 -0500424 uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400425
Dichen Zhangd18bc302022-12-16 20:55:24 +0000426 jpegr_exif_struct exif;
427 exif.data = nullptr;
428 exif.length = 0;
429 // Delete EXIF package if it appears, and update exif.
430 if (jpeg_decoder.getEXIFPos() != 0) {
431 int new_length = compressed_jpeg_image->length - jpeg_decoder.getEXIFSize() - 4;
432 memcpy((uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos() - 4,
433 (uint8_t*)compressed_jpeg_image->data + jpeg_decoder.getEXIFPos()
434 + jpeg_decoder.getEXIFSize(),
435 compressed_jpeg_image->length - jpeg_decoder.getEXIFPos() - jpeg_decoder.getEXIFSize());
436 compressed_jpeg_image->length = new_length;
437 exif.data = jpeg_decoder.getEXIFPtr();
438 exif.length = jpeg_decoder.getEXIFSize();
439 }
440
441 jpegr_exif_struct new_exif;
442 if (exif.data == nullptr) {
443 new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
444 } else {
445 new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
446 }
447 new_exif.data = new uint8_t[new_exif.length];
448 std::unique_ptr<uint8_t[]> new_exif_data;
449 new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
450 JPEGR_CHECK(updateExif(&exif, &new_exif));
451
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400452 if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
453 || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
454 return ERROR_JPEGR_RESOLUTION_MISMATCH;
455 }
456
Nick Deakin6bd90432022-11-20 16:26:37 -0500457 jpegr_metadata metadata;
458 metadata.version = kJpegrVersion;
459 metadata.transferFunction = hdr_tf;
460 if (hdr_tf == JPEGR_TF_PQ) {
461 metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
462 }
463
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400464 jpegr_uncompressed_struct map;
Dichen Zhanga8766262022-11-07 23:48:24 +0000465 JPEGR_CHECK(generateRecoveryMap(
Nick Deakin6bd90432022-11-20 16:26:37 -0500466 &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400467 std::unique_ptr<uint8_t[]> map_data;
468 map_data.reset(reinterpret_cast<uint8_t*>(map.data));
469
470 jpegr_compressed_struct compressed_map;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000471 compressed_map.maxLength = map.width * map.height;
472 unique_ptr<uint8_t[]> compressed_map_data = make_unique<uint8_t[]>(compressed_map.maxLength);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400473 compressed_map.data = compressed_map_data.get();
474 JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));
475
Dichen Zhangd18bc302022-12-16 20:55:24 +0000476 JPEGR_CHECK(appendRecoveryMap(
477 compressed_jpeg_image, &compressed_map, &new_exif, &metadata, dest));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400478
Dichen Zhang6947d532022-10-22 02:16:21 +0000479 return NO_ERROR;
480}
481
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000482status_t RecoveryMap::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
483 jr_info_ptr jpegr_info) {
484 if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
485 return ERROR_JPEGR_INVALID_NULL_PTR;
486 }
487
488 jpegr_compressed_struct primary_image, recovery_map;
489 JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image,
490 &primary_image, &recovery_map));
491
492 JpegDecoder jpeg_decoder;
493 if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
494 &jpegr_info->width, &jpegr_info->height,
495 jpegr_info->iccData, jpegr_info->exifData)) {
496 return ERROR_JPEGR_DECODE_ERROR;
497 }
498
499 return NO_ERROR;
500}
501
Dichen Zhang636f5242022-12-07 20:25:44 +0000502/* Decode API */
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400503status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
Dichen Zhangffa34012022-11-03 23:21:13 +0000504 jr_uncompressed_ptr dest,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000505 jr_exif_ptr exif,
506 bool request_sdr) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000507 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000508 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang6947d532022-10-22 02:16:21 +0000509 }
510
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000511 // TODO: fill EXIF data
512 (void) exif;
513
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400514 jpegr_compressed_struct compressed_map;
Nick Deakin6bd90432022-11-20 16:26:37 -0500515 jpegr_metadata metadata;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000516 JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400517
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400518
519 JpegDecoder jpeg_decoder;
520 if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
521 return ERROR_JPEGR_DECODE_ERROR;
522 }
523
Fyodor Kyslovbf241572022-12-13 22:38:07 +0000524 JpegDecoder recovery_map_decoder;
525 if (!recovery_map_decoder.decompressImage(compressed_map.data,
526 compressed_map.length)) {
527 return ERROR_JPEGR_DECODE_ERROR;
528 }
529
530 jpegr_uncompressed_struct map;
531 map.data = recovery_map_decoder.getDecompressedImagePtr();
532 map.width = recovery_map_decoder.getDecompressedImageWidth();
533 map.height = recovery_map_decoder.getDecompressedImageHeight();
534
535
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400536 jpegr_uncompressed_struct uncompressed_yuv_420_image;
537 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
538 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
539 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
540
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000541 if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
542 jpeg_decoder.getXMPSize(), &metadata)) {
543 return ERROR_JPEGR_DECODE_ERROR;
544 }
545
546 if (request_sdr) {
547 memcpy(dest->data, uncompressed_yuv_420_image.data,
548 uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2);
549 dest->width = uncompressed_yuv_420_image.width;
550 dest->height = uncompressed_yuv_420_image.height;
551 } else {
552 JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
553 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400554
Dichen Zhang6947d532022-10-22 02:16:21 +0000555 return NO_ERROR;
556}
557
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400558status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
559 jr_compressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700560 if (uncompressed_recovery_map == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000561 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700562 }
563
Nick Deakin6bd90432022-11-20 16:26:37 -0500564 // TODO: should we have ICC data for the map?
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400565 JpegEncoder jpeg_encoder;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000566 if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data,
567 uncompressed_recovery_map->width,
568 uncompressed_recovery_map->height,
569 kMapCompressQuality,
570 nullptr,
571 0,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400572 true /* isSingleChannel */)) {
573 return ERROR_JPEGR_ENCODE_ERROR;
574 }
575
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000576 if (dest->maxLength < jpeg_encoder.getCompressedImageSize()) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400577 return ERROR_JPEGR_BUFFER_TOO_SMALL;
578 }
579
580 memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize());
581 dest->length = jpeg_encoder.getCompressedImageSize();
Nick Deakin6bd90432022-11-20 16:26:37 -0500582 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400583
Dichen Zhang6947d532022-10-22 02:16:21 +0000584 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700585}
586
Dichen Zhang6947d532022-10-22 02:16:21 +0000587status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
588 jr_uncompressed_ptr uncompressed_p010_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500589 jr_metadata_ptr metadata,
590 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700591 if (uncompressed_yuv_420_image == nullptr
592 || uncompressed_p010_image == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500593 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700594 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000595 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700596 }
597
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400598 if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width
599 || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) {
600 return ERROR_JPEGR_RESOLUTION_MISMATCH;
601 }
602
Nick Deakin6bd90432022-11-20 16:26:37 -0500603 if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED
604 || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) {
605 return ERROR_JPEGR_INVALID_COLORGAMUT;
606 }
607
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400608 size_t image_width = uncompressed_yuv_420_image->width;
609 size_t image_height = uncompressed_yuv_420_image->height;
610 size_t map_width = image_width / kMapDimensionScaleFactor;
611 size_t map_height = image_height / kMapDimensionScaleFactor;
612
613 dest->width = map_width;
614 dest->height = map_height;
Nick Deakin6bd90432022-11-20 16:26:37 -0500615 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400616 dest->data = new uint8_t[map_width * map_height];
617 std::unique_ptr<uint8_t[]> map_data;
618 map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
619
Nick Deakin6bd90432022-11-20 16:26:37 -0500620 ColorTransformFn hdrInvOetf = nullptr;
Nick Deakin65f492a2022-11-29 22:47:40 -0500621 float hdr_white_nits = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500622 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000623 case JPEGR_TF_LINEAR:
624 hdrInvOetf = identityConversion;
625 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500626 case JPEGR_TF_HLG:
627 hdrInvOetf = hlgInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500628 hdr_white_nits = kHlgMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500629 break;
630 case JPEGR_TF_PQ:
631 hdrInvOetf = pqInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500632 hdr_white_nits = kPqMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500633 break;
634 }
635
636 ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
637 uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
638
639 ColorCalculationFn luminanceFn = nullptr;
640 switch (uncompressed_yuv_420_image->colorGamut) {
641 case JPEGR_COLORGAMUT_BT709:
642 luminanceFn = srgbLuminance;
643 break;
644 case JPEGR_COLORGAMUT_P3:
645 luminanceFn = p3Luminance;
646 break;
647 case JPEGR_COLORGAMUT_BT2100:
648 luminanceFn = bt2100Luminance;
649 break;
650 case JPEGR_COLORGAMUT_UNSPECIFIED:
651 // Should be impossible to hit after input validation.
652 return ERROR_JPEGR_INVALID_COLORGAMUT;
653 }
654
Nick Deakin594a4ca2022-11-16 20:57:42 -0500655 float hdr_y_nits_max = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500656 double hdr_y_nits_avg = 0.0f;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400657 for (size_t y = 0; y < image_height; ++y) {
658 for (size_t x = 0; x < image_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500659 Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
660 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500661 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
662 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500663 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500664
Nick Deakin6bd90432022-11-20 16:26:37 -0500665 hdr_y_nits_avg += hdr_y_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500666 if (hdr_y_nits > hdr_y_nits_max) {
667 hdr_y_nits_max = hdr_y_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400668 }
669 }
670 }
Nick Deakin6bd90432022-11-20 16:26:37 -0500671 hdr_y_nits_avg /= image_width * image_height;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400672
Nick Deakin6bd90432022-11-20 16:26:37 -0500673 metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
674 if (metadata->transferFunction == JPEGR_TF_PQ) {
675 metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
676 metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
677 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400678
679 for (size_t y = 0; y < map_height; ++y) {
680 for (size_t x = 0; x < map_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500681 Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image,
682 kMapDimensionScaleFactor, x, y);
683 Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
684 Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
Nick Deakin65f492a2022-11-29 22:47:40 -0500685 float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400686
Nick Deakin594a4ca2022-11-16 20:57:42 -0500687 Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
688 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500689 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
690 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500691 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400692
693 size_t pixel_idx = x + y * map_width;
694 reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
Nick Deakin6bd90432022-11-20 16:26:37 -0500695 encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400696 }
697 }
698
699 map_data.release();
Dichen Zhang6947d532022-10-22 02:16:21 +0000700 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700701}
702
Dichen Zhang6947d532022-10-22 02:16:21 +0000703status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
704 jr_uncompressed_ptr uncompressed_recovery_map,
Nick Deakin6bd90432022-11-20 16:26:37 -0500705 jr_metadata_ptr metadata,
Dichen Zhang6947d532022-10-22 02:16:21 +0000706 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700707 if (uncompressed_yuv_420_image == nullptr
708 || uncompressed_recovery_map == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500709 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700710 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000711 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700712 }
713
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400714 size_t width = uncompressed_yuv_420_image->width;
715 size_t height = uncompressed_yuv_420_image->height;
716
717 dest->width = width;
718 dest->height = height;
719 size_t pixel_count = width * height;
720
Nick Deakin6bd90432022-11-20 16:26:37 -0500721 ColorTransformFn hdrOetf = nullptr;
722 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000723 case JPEGR_TF_LINEAR:
724 hdrOetf = identityConversion;
725 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500726 case JPEGR_TF_HLG:
727 hdrOetf = hlgOetf;
728 break;
729 case JPEGR_TF_PQ:
730 hdrOetf = pqOetf;
731 break;
732 }
733
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400734 for (size_t y = 0; y < height; ++y) {
735 for (size_t x = 0; x < width; ++x) {
Nick Deakin6bd90432022-11-20 16:26:37 -0500736 Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
737 Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr);
738 Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400739
Nick Deakin6bd90432022-11-20 16:26:37 -0500740 // TODO: determine map scaling factor based on actual map dims
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400741 float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y);
Nick Deakin6bd90432022-11-20 16:26:37 -0500742 Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400743
Nick Deakin38125332022-12-12 15:48:24 -0500744 Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
Nick Deakin6bd90432022-11-20 16:26:37 -0500745 uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400746
Nick Deakin6bd90432022-11-20 16:26:37 -0500747 size_t pixel_idx = x + y * width;
748 reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400749 }
750 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000751 return NO_ERROR;
752}
753
754status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
755 jr_compressed_ptr primary_image,
756 jr_compressed_ptr recovery_map) {
757 if (compressed_jpegr_image == nullptr) {
758 return ERROR_JPEGR_INVALID_NULL_PTR;
759 }
760
761 MessageHandler msg_handler;
762 std::shared_ptr<DataSegment> seg =
763 DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
764 static_cast<const uint8_t*>(compressed_jpegr_image->data),
765 DataSegment::BufferDispositionPolicy::kDontDelete);
766 DataSegmentDataSource data_source(seg);
767 JpegInfoBuilder jpeg_info_builder;
768 jpeg_info_builder.SetImageLimit(2);
769 JpegScanner jpeg_scanner(&msg_handler);
770 jpeg_scanner.Run(&data_source, &jpeg_info_builder);
771 data_source.Reset();
772
773 if (jpeg_scanner.HasError()) {
774 return ERROR_JPEGR_INVALID_INPUT_TYPE;
775 }
776
777 const auto& jpeg_info = jpeg_info_builder.GetInfo();
778 const auto& image_ranges = jpeg_info.GetImageRanges();
779 if (image_ranges.empty()) {
780 return ERROR_JPEGR_INVALID_INPUT_TYPE;
781 }
782
783 if (image_ranges.size() != 2) {
784 // Must be 2 JPEG Images
785 return ERROR_JPEGR_INVALID_INPUT_TYPE;
786 }
787
788 if (primary_image != nullptr) {
789 primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
790 image_ranges[0].GetBegin();
791 primary_image->length = image_ranges[0].GetLength();
792 }
793
794 if (recovery_map != nullptr) {
795 recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
796 image_ranges[1].GetBegin();
797 recovery_map->length = image_ranges[1].GetLength();
798 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400799
Dichen Zhang6947d532022-10-22 02:16:21 +0000800 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700801}
802
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000803
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400804status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000805 jr_compressed_ptr dest) {
806 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000807 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700808 }
809
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000810 return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest);
Dichen Zhang85b37562022-10-11 11:08:28 -0700811}
812
Dichen Zhangd18bc302022-12-16 20:55:24 +0000813// JPEG/R structure:
814// SOI (ff d8)
815// APP1 (ff e1)
816// 2 bytes of length (2 + length of exif package)
817// EXIF package (this includes the first two bytes representing the package length)
818// APP1 (ff e1)
819// 2 bytes of length (2 + 29 + length of xmp package)
820// name space ("http://ns.adobe.com/xap/1.0/\0")
821// xmp
822// primary image (without the first two bytes (SOI) and without EXIF, may have other packages)
823// secondary image (the recovery map)
824//
825// Metadata versions we are using:
826// ECMA TR-98 for JFIF marker
827// Exif 2.2 spec for EXIF marker
828// Adobe XMP spec part 3 for XMP marker
829// ICC v4.3 spec for ICC
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400830status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
831 jr_compressed_ptr compressed_recovery_map,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000832 jr_exif_ptr exif,
Nick Deakin6bd90432022-11-20 16:26:37 -0500833 jr_metadata_ptr metadata,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400834 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000835 if (compressed_jpeg_image == nullptr
836 || compressed_recovery_map == nullptr
Dichen Zhangd18bc302022-12-16 20:55:24 +0000837 || exif == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500838 || metadata == nullptr
Dichen Zhang6947d532022-10-22 02:16:21 +0000839 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000840 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700841 }
842
Dichen Zhanga8766262022-11-07 23:48:24 +0000843 int pos = 0;
844
Dichen Zhangd18bc302022-12-16 20:55:24 +0000845 // Write SOI
Dichen Zhanga8766262022-11-07 23:48:24 +0000846 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
847 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000848
849 // Write EXIF
850 {
851 const int length = 2 + exif->length;
852 const uint8_t lengthH = ((length >> 8) & 0xff);
853 const uint8_t lengthL = (length & 0xff);
854 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
855 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
856 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
857 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
858 JPEGR_CHECK(Write(dest, exif->data, exif->length, pos));
859 }
860
861 // Prepare and write XMP
862 {
863 const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
864 const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
865 const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
866 // 2 bytes: representing the length of the package
867 // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
868 // x bytes: length of xmp packet
869 const int length = 3 + nameSpaceLength + xmp.size();
870 const uint8_t lengthH = ((length >> 8) & 0xff);
871 const uint8_t lengthL = (length & 0xff);
872 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
873 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
874 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
875 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
876 JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
877 JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
878 }
879
880 // Write primary image
Dichen Zhanga8766262022-11-07 23:48:24 +0000881 JPEGR_CHECK(Write(dest,
882 (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000883
884 // Write secondary image
Dichen Zhanga8766262022-11-07 23:48:24 +0000885 JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000886
887 // Set back length
Dichen Zhanga8766262022-11-07 23:48:24 +0000888 dest->length = pos;
889
Dichen Zhangd18bc302022-12-16 20:55:24 +0000890 // Done!
Dichen Zhang6947d532022-10-22 02:16:21 +0000891 return NO_ERROR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700892}
893
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800894status_t RecoveryMap::toneMap(jr_uncompressed_ptr src,
Dichen Zhang636f5242022-12-07 20:25:44 +0000895 jr_uncompressed_ptr dest) {
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800896 if (src == nullptr || dest == nullptr) {
Dichen Zhang636f5242022-12-07 20:25:44 +0000897 return ERROR_JPEGR_INVALID_NULL_PTR;
898 }
899
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800900 dest->width = src->width;
901 dest->height = src->height;
Dichen Zhang636f5242022-12-07 20:25:44 +0000902
Dichen Zhangc3437ca2023-01-04 14:00:08 -0800903 size_t pixel_count = src->width * src->height;
904 for (size_t y = 0; y < src->height; ++y) {
905 for (size_t x = 0; x < src->width; ++x) {
906 size_t pixel_y_idx = x + y * src->width;
907 size_t pixel_uv_idx = x / 2 + (y / 2) * (src->width / 2);
908
909 uint16_t y_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_y_idx]
910 >> 6;
911 uint16_t u_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2]
912 >> 6;
913 uint16_t v_uint = reinterpret_cast<uint16_t*>(src->data)[pixel_count + pixel_uv_idx * 2 + 1]
914 >> 6;
915
916 uint8_t* y = &reinterpret_cast<uint8_t*>(dest->data)[pixel_y_idx];
917 uint8_t* u = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count + pixel_uv_idx];
918 uint8_t* v = &reinterpret_cast<uint8_t*>(dest->data)[pixel_count * 5 / 4 + pixel_uv_idx];
919
920 *y = static_cast<uint8_t>((y_uint >> 2) & 0xff);
921 *u = static_cast<uint8_t>((u_uint >> 2) & 0xff);
922 *v = static_cast<uint8_t>((v_uint >> 2) & 0xff);
923 }
924 }
925
926 dest->colorGamut = src->colorGamut;
Dichen Zhang636f5242022-12-07 20:25:44 +0000927
928 return NO_ERROR;
929}
930
Dichen Zhang85b37562022-10-11 11:08:28 -0700931} // namespace android::recoverymap