blob: 53fa8ce9e3771936b4cb3a59ab7fb117beb5d527 [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 }
508
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000509 // TODO: fill EXIF data
510 (void) exif;
511
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400512 jpegr_compressed_struct compressed_map;
Nick Deakin6bd90432022-11-20 16:26:37 -0500513 jpegr_metadata metadata;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000514 JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400515
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400516
517 JpegDecoder jpeg_decoder;
518 if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) {
519 return ERROR_JPEGR_DECODE_ERROR;
520 }
521
Fyodor Kyslovbf241572022-12-13 22:38:07 +0000522 JpegDecoder recovery_map_decoder;
523 if (!recovery_map_decoder.decompressImage(compressed_map.data,
524 compressed_map.length)) {
525 return ERROR_JPEGR_DECODE_ERROR;
526 }
527
528 jpegr_uncompressed_struct map;
529 map.data = recovery_map_decoder.getDecompressedImagePtr();
530 map.width = recovery_map_decoder.getDecompressedImageWidth();
531 map.height = recovery_map_decoder.getDecompressedImageHeight();
532
533
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400534 jpegr_uncompressed_struct uncompressed_yuv_420_image;
535 uncompressed_yuv_420_image.data = jpeg_decoder.getDecompressedImagePtr();
536 uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
537 uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
538
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000539 if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
540 jpeg_decoder.getXMPSize(), &metadata)) {
541 return ERROR_JPEGR_DECODE_ERROR;
542 }
543
544 if (request_sdr) {
545 memcpy(dest->data, uncompressed_yuv_420_image.data,
546 uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2);
547 dest->width = uncompressed_yuv_420_image.width;
548 dest->height = uncompressed_yuv_420_image.height;
549 } else {
550 JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
551 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400552
Dichen Zhang6947d532022-10-22 02:16:21 +0000553 return NO_ERROR;
554}
555
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400556status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
557 jr_compressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700558 if (uncompressed_recovery_map == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000559 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700560 }
561
Nick Deakin6bd90432022-11-20 16:26:37 -0500562 // TODO: should we have ICC data for the map?
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400563 JpegEncoder jpeg_encoder;
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000564 if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data,
565 uncompressed_recovery_map->width,
566 uncompressed_recovery_map->height,
567 kMapCompressQuality,
568 nullptr,
569 0,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400570 true /* isSingleChannel */)) {
571 return ERROR_JPEGR_ENCODE_ERROR;
572 }
573
Dichen Zhang0b9f7de2022-11-18 06:52:46 +0000574 if (dest->maxLength < jpeg_encoder.getCompressedImageSize()) {
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400575 return ERROR_JPEGR_BUFFER_TOO_SMALL;
576 }
577
578 memcpy(dest->data, jpeg_encoder.getCompressedImagePtr(), jpeg_encoder.getCompressedImageSize());
579 dest->length = jpeg_encoder.getCompressedImageSize();
Nick Deakin6bd90432022-11-20 16:26:37 -0500580 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400581
Dichen Zhang6947d532022-10-22 02:16:21 +0000582 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700583}
584
Dichen Zhang6947d532022-10-22 02:16:21 +0000585status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
586 jr_uncompressed_ptr uncompressed_p010_image,
Nick Deakin6bd90432022-11-20 16:26:37 -0500587 jr_metadata_ptr metadata,
588 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700589 if (uncompressed_yuv_420_image == nullptr
590 || uncompressed_p010_image == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500591 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700592 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000593 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700594 }
595
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400596 if (uncompressed_yuv_420_image->width != uncompressed_p010_image->width
597 || uncompressed_yuv_420_image->height != uncompressed_p010_image->height) {
598 return ERROR_JPEGR_RESOLUTION_MISMATCH;
599 }
600
Nick Deakin6bd90432022-11-20 16:26:37 -0500601 if (uncompressed_yuv_420_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED
602 || uncompressed_p010_image->colorGamut == JPEGR_COLORGAMUT_UNSPECIFIED) {
603 return ERROR_JPEGR_INVALID_COLORGAMUT;
604 }
605
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400606 size_t image_width = uncompressed_yuv_420_image->width;
607 size_t image_height = uncompressed_yuv_420_image->height;
608 size_t map_width = image_width / kMapDimensionScaleFactor;
609 size_t map_height = image_height / kMapDimensionScaleFactor;
610
611 dest->width = map_width;
612 dest->height = map_height;
Nick Deakin6bd90432022-11-20 16:26:37 -0500613 dest->colorGamut = JPEGR_COLORGAMUT_UNSPECIFIED;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400614 dest->data = new uint8_t[map_width * map_height];
615 std::unique_ptr<uint8_t[]> map_data;
616 map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
617
Nick Deakin6bd90432022-11-20 16:26:37 -0500618 ColorTransformFn hdrInvOetf = nullptr;
Nick Deakin65f492a2022-11-29 22:47:40 -0500619 float hdr_white_nits = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500620 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000621 case JPEGR_TF_LINEAR:
622 hdrInvOetf = identityConversion;
623 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500624 case JPEGR_TF_HLG:
625 hdrInvOetf = hlgInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500626 hdr_white_nits = kHlgMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500627 break;
628 case JPEGR_TF_PQ:
629 hdrInvOetf = pqInvOetf;
Nick Deakin65f492a2022-11-29 22:47:40 -0500630 hdr_white_nits = kPqMaxNits;
Nick Deakin6bd90432022-11-20 16:26:37 -0500631 break;
632 }
633
634 ColorTransformFn hdrGamutConversionFn = getHdrConversionFn(
635 uncompressed_yuv_420_image->colorGamut, uncompressed_p010_image->colorGamut);
636
637 ColorCalculationFn luminanceFn = nullptr;
638 switch (uncompressed_yuv_420_image->colorGamut) {
639 case JPEGR_COLORGAMUT_BT709:
640 luminanceFn = srgbLuminance;
641 break;
642 case JPEGR_COLORGAMUT_P3:
643 luminanceFn = p3Luminance;
644 break;
645 case JPEGR_COLORGAMUT_BT2100:
646 luminanceFn = bt2100Luminance;
647 break;
648 case JPEGR_COLORGAMUT_UNSPECIFIED:
649 // Should be impossible to hit after input validation.
650 return ERROR_JPEGR_INVALID_COLORGAMUT;
651 }
652
Nick Deakin594a4ca2022-11-16 20:57:42 -0500653 float hdr_y_nits_max = 0.0f;
Nick Deakin6bd90432022-11-20 16:26:37 -0500654 double hdr_y_nits_avg = 0.0f;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400655 for (size_t y = 0; y < image_height; ++y) {
656 for (size_t x = 0; x < image_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500657 Color hdr_yuv_gamma = getP010Pixel(uncompressed_p010_image, x, y);
658 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500659 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
660 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500661 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500662
Nick Deakin6bd90432022-11-20 16:26:37 -0500663 hdr_y_nits_avg += hdr_y_nits;
Nick Deakin594a4ca2022-11-16 20:57:42 -0500664 if (hdr_y_nits > hdr_y_nits_max) {
665 hdr_y_nits_max = hdr_y_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400666 }
667 }
668 }
Nick Deakin6bd90432022-11-20 16:26:37 -0500669 hdr_y_nits_avg /= image_width * image_height;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400670
Nick Deakin6bd90432022-11-20 16:26:37 -0500671 metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
672 if (metadata->transferFunction == JPEGR_TF_PQ) {
673 metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
674 metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
675 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400676
677 for (size_t y = 0; y < map_height; ++y) {
678 for (size_t x = 0; x < map_width; ++x) {
Nick Deakin594a4ca2022-11-16 20:57:42 -0500679 Color sdr_yuv_gamma = sampleYuv420(uncompressed_yuv_420_image,
680 kMapDimensionScaleFactor, x, y);
681 Color sdr_rgb_gamma = srgbYuvToRgb(sdr_yuv_gamma);
682 Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
Nick Deakin65f492a2022-11-29 22:47:40 -0500683 float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400684
Nick Deakin594a4ca2022-11-16 20:57:42 -0500685 Color hdr_yuv_gamma = sampleP010(uncompressed_p010_image, kMapDimensionScaleFactor, x, y);
686 Color hdr_rgb_gamma = bt2100YuvToRgb(hdr_yuv_gamma);
Nick Deakin6bd90432022-11-20 16:26:37 -0500687 Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
688 hdr_rgb = hdrGamutConversionFn(hdr_rgb);
Nick Deakin65f492a2022-11-29 22:47:40 -0500689 float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400690
691 size_t pixel_idx = x + y * map_width;
692 reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
Nick Deakin6bd90432022-11-20 16:26:37 -0500693 encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400694 }
695 }
696
697 map_data.release();
Dichen Zhang6947d532022-10-22 02:16:21 +0000698 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700699}
700
Dichen Zhang6947d532022-10-22 02:16:21 +0000701status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
702 jr_uncompressed_ptr uncompressed_recovery_map,
Nick Deakin6bd90432022-11-20 16:26:37 -0500703 jr_metadata_ptr metadata,
Dichen Zhang6947d532022-10-22 02:16:21 +0000704 jr_uncompressed_ptr dest) {
Dichen Zhang596a7562022-10-12 14:57:05 -0700705 if (uncompressed_yuv_420_image == nullptr
706 || uncompressed_recovery_map == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500707 || metadata == nullptr
Dichen Zhang596a7562022-10-12 14:57:05 -0700708 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000709 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700710 }
711
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400712 size_t width = uncompressed_yuv_420_image->width;
713 size_t height = uncompressed_yuv_420_image->height;
714
715 dest->width = width;
716 dest->height = height;
717 size_t pixel_count = width * height;
718
Nick Deakin6bd90432022-11-20 16:26:37 -0500719 ColorTransformFn hdrOetf = nullptr;
720 switch (metadata->transferFunction) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000721 case JPEGR_TF_LINEAR:
722 hdrOetf = identityConversion;
723 break;
Nick Deakin6bd90432022-11-20 16:26:37 -0500724 case JPEGR_TF_HLG:
725 hdrOetf = hlgOetf;
726 break;
727 case JPEGR_TF_PQ:
728 hdrOetf = pqOetf;
729 break;
730 }
731
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400732 for (size_t y = 0; y < height; ++y) {
733 for (size_t x = 0; x < width; ++x) {
Nick Deakin6bd90432022-11-20 16:26:37 -0500734 Color yuv_gamma_sdr = getYuv420Pixel(uncompressed_yuv_420_image, x, y);
735 Color rgb_gamma_sdr = srgbYuvToRgb(yuv_gamma_sdr);
736 Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400737
Nick Deakin6bd90432022-11-20 16:26:37 -0500738 // TODO: determine map scaling factor based on actual map dims
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400739 float recovery = sampleMap(uncompressed_recovery_map, kMapDimensionScaleFactor, x, y);
Nick Deakin6bd90432022-11-20 16:26:37 -0500740 Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata->rangeScalingFactor);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400741
Nick Deakin38125332022-12-12 15:48:24 -0500742 Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
Nick Deakin6bd90432022-11-20 16:26:37 -0500743 uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400744
Nick Deakin6bd90432022-11-20 16:26:37 -0500745 size_t pixel_idx = x + y * width;
746 reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400747 }
748 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000749 return NO_ERROR;
750}
751
752status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
753 jr_compressed_ptr primary_image,
754 jr_compressed_ptr recovery_map) {
755 if (compressed_jpegr_image == nullptr) {
756 return ERROR_JPEGR_INVALID_NULL_PTR;
757 }
758
759 MessageHandler msg_handler;
760 std::shared_ptr<DataSegment> seg =
761 DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
762 static_cast<const uint8_t*>(compressed_jpegr_image->data),
763 DataSegment::BufferDispositionPolicy::kDontDelete);
764 DataSegmentDataSource data_source(seg);
765 JpegInfoBuilder jpeg_info_builder;
766 jpeg_info_builder.SetImageLimit(2);
767 JpegScanner jpeg_scanner(&msg_handler);
768 jpeg_scanner.Run(&data_source, &jpeg_info_builder);
769 data_source.Reset();
770
771 if (jpeg_scanner.HasError()) {
772 return ERROR_JPEGR_INVALID_INPUT_TYPE;
773 }
774
775 const auto& jpeg_info = jpeg_info_builder.GetInfo();
776 const auto& image_ranges = jpeg_info.GetImageRanges();
777 if (image_ranges.empty()) {
778 return ERROR_JPEGR_INVALID_INPUT_TYPE;
779 }
780
781 if (image_ranges.size() != 2) {
782 // Must be 2 JPEG Images
783 return ERROR_JPEGR_INVALID_INPUT_TYPE;
784 }
785
786 if (primary_image != nullptr) {
787 primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
788 image_ranges[0].GetBegin();
789 primary_image->length = image_ranges[0].GetLength();
790 }
791
792 if (recovery_map != nullptr) {
793 recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
794 image_ranges[1].GetBegin();
795 recovery_map->length = image_ranges[1].GetLength();
796 }
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400797
Dichen Zhang6947d532022-10-22 02:16:21 +0000798 return NO_ERROR;
Dichen Zhang596a7562022-10-12 14:57:05 -0700799}
800
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000801
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400802status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000803 jr_compressed_ptr dest) {
804 if (compressed_jpegr_image == nullptr || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000805 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700806 }
807
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000808 return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest);
Dichen Zhang85b37562022-10-11 11:08:28 -0700809}
810
Dichen Zhangd18bc302022-12-16 20:55:24 +0000811// JPEG/R structure:
812// SOI (ff d8)
813// APP1 (ff e1)
814// 2 bytes of length (2 + length of exif package)
815// EXIF package (this includes the first two bytes representing the package length)
816// APP1 (ff e1)
817// 2 bytes of length (2 + 29 + length of xmp package)
818// name space ("http://ns.adobe.com/xap/1.0/\0")
819// xmp
820// primary image (without the first two bytes (SOI) and without EXIF, may have other packages)
821// secondary image (the recovery map)
822//
823// Metadata versions we are using:
824// ECMA TR-98 for JFIF marker
825// Exif 2.2 spec for EXIF marker
826// Adobe XMP spec part 3 for XMP marker
827// ICC v4.3 spec for ICC
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400828status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
829 jr_compressed_ptr compressed_recovery_map,
Dichen Zhangd18bc302022-12-16 20:55:24 +0000830 jr_exif_ptr exif,
Nick Deakin6bd90432022-11-20 16:26:37 -0500831 jr_metadata_ptr metadata,
Nick Deakinf6bca5a2022-11-04 10:43:43 -0400832 jr_compressed_ptr dest) {
Dichen Zhang6947d532022-10-22 02:16:21 +0000833 if (compressed_jpeg_image == nullptr
834 || compressed_recovery_map == nullptr
Dichen Zhangd18bc302022-12-16 20:55:24 +0000835 || exif == nullptr
Nick Deakin6bd90432022-11-20 16:26:37 -0500836 || metadata == nullptr
Dichen Zhang6947d532022-10-22 02:16:21 +0000837 || dest == nullptr) {
Dichen Zhang80b72482022-11-02 01:55:35 +0000838 return ERROR_JPEGR_INVALID_NULL_PTR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700839 }
840
Dichen Zhanga8766262022-11-07 23:48:24 +0000841 int pos = 0;
842
Dichen Zhangd18bc302022-12-16 20:55:24 +0000843 // Write SOI
Dichen Zhanga8766262022-11-07 23:48:24 +0000844 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
845 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000846
847 // Write EXIF
848 {
849 const int length = 2 + exif->length;
850 const uint8_t lengthH = ((length >> 8) & 0xff);
851 const uint8_t lengthL = (length & 0xff);
852 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
853 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
854 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
855 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
856 JPEGR_CHECK(Write(dest, exif->data, exif->length, pos));
857 }
858
859 // Prepare and write XMP
860 {
861 const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
862 const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
863 const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
864 // 2 bytes: representing the length of the package
865 // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
866 // x bytes: length of xmp packet
867 const int length = 3 + nameSpaceLength + xmp.size();
868 const uint8_t lengthH = ((length >> 8) & 0xff);
869 const uint8_t lengthL = (length & 0xff);
870 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
871 JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
872 JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
873 JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
874 JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
875 JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
876 }
877
878 // Write primary image
Dichen Zhanga8766262022-11-07 23:48:24 +0000879 JPEGR_CHECK(Write(dest,
880 (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000881
882 // Write secondary image
Dichen Zhanga8766262022-11-07 23:48:24 +0000883 JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
Dichen Zhangd18bc302022-12-16 20:55:24 +0000884
885 // Set back length
Dichen Zhanga8766262022-11-07 23:48:24 +0000886 dest->length = pos;
887
Dichen Zhangd18bc302022-12-16 20:55:24 +0000888 // Done!
Dichen Zhang6947d532022-10-22 02:16:21 +0000889 return NO_ERROR;
Dichen Zhang85b37562022-10-11 11:08:28 -0700890}
891
Dichen Zhang636f5242022-12-07 20:25:44 +0000892status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image,
893 jr_uncompressed_ptr dest) {
894 if (uncompressed_p010_image == nullptr || dest == nullptr) {
895 return ERROR_JPEGR_INVALID_NULL_PTR;
896 }
897
898 dest->width = uncompressed_p010_image->width;
899 dest->height = uncompressed_p010_image->height;
900 unique_ptr<uint8_t[]> dest_data = make_unique<uint8_t[]>(dest->width * dest->height * 3 / 2);
901 dest->data = dest_data.get();
902
903 // TODO: Tone map algorighm here.
904
905 return NO_ERROR;
906}
907
Dichen Zhang85b37562022-10-11 11:08:28 -0700908} // namespace android::recoverymap