blob: d5ad9a51c49f4dc2277bced153d9beceb824f77d [file] [log] [blame]
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +00001/*
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/recoverymaputils.h>
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000018#include <image_io/xml/xml_reader.h>
Dichen Zhangdc8452b2022-11-23 17:17:56 +000019#include <image_io/xml/xml_writer.h>
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000020#include <image_io/base/message_handler.h>
21#include <image_io/xml/xml_element_rules.h>
22#include <image_io/xml/xml_handler.h>
23#include <image_io/xml/xml_rule.h>
24
Dichen Zhang0daf5f82023-01-19 13:44:23 -080025#include <utils/Log.h>
26
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000027using namespace photos_editing_formats::image_io;
28using namespace std;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000029
30namespace android::recoverymap {
31
Dichen Zhangdc8452b2022-11-23 17:17:56 +000032/*
33 * Helper function used for generating XMP metadata.
34 *
35 * @param prefix The prefix part of the name.
36 * @param suffix The suffix part of the name.
37 * @return A name of the form "prefix:suffix".
38 */
39string Name(const string &prefix, const string &suffix) {
40 std::stringstream ss;
41 ss << prefix << ":" << suffix;
42 return ss.str();
43}
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000044
Dichen Zhang91dfc572023-01-19 11:22:43 -080045/*
46 * Helper function used for writing data to destination.
47 */
48status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) {
49 if (position + length > destination->maxLength) {
50 return ERROR_JPEGR_BUFFER_TOO_SMALL;
51 }
52
53 memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
54 position += length;
55 return NO_ERROR;
56}
57
58status_t Write(jr_exif_ptr destination, const void* source, size_t length, int &position) {
59 memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
60 position += length;
61 return NO_ERROR;
62}
63
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000064// Extremely simple XML Handler - just searches for interesting elements
65class XMPXmlHandler : public XmlHandler {
66public:
67
68 XMPXmlHandler() : XmlHandler() {
Fyodor Kyslovad58a342022-12-12 02:10:35 +000069 gContainerItemState = NotStrarted;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000070 }
71
72 enum ParseState {
73 NotStrarted,
74 Started,
75 Done
76 };
77
78 virtual DataMatchResult StartElement(const XmlTokenContext& context) {
79 string val;
80 if (context.BuildTokenValue(&val)) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +000081 if (!val.compare(gContainerItemName)) {
82 gContainerItemState = Started;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000083 } else {
Fyodor Kyslovad58a342022-12-12 02:10:35 +000084 if (gContainerItemState != Done) {
85 gContainerItemState = NotStrarted;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000086 }
87 }
88 }
89 return context.GetResult();
90 }
91
92 virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +000093 if (gContainerItemState == Started) {
94 gContainerItemState = Done;
95 lastAttributeName = "";
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000096 }
97 return context.GetResult();
98 }
99
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000100 virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000101 string val;
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000102 if (gContainerItemState == Started) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000103 if (context.BuildTokenValue(&val)) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000104 if (!val.compare(rangeScalingFactorAttrName)) {
105 lastAttributeName = rangeScalingFactorAttrName;
106 } else if (!val.compare(transferFunctionAttrName)) {
107 lastAttributeName = transferFunctionAttrName;
108 } else {
109 lastAttributeName = "";
110 }
111 }
112 }
113 return context.GetResult();
114 }
115
116 virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
117 string val;
118 if (gContainerItemState == Started) {
119 if (context.BuildTokenValue(&val, true)) {
120 if (!lastAttributeName.compare(rangeScalingFactorAttrName)) {
121 rangeScalingFactorStr = val;
122 } else if (!lastAttributeName.compare(transferFunctionAttrName)) {
123 transferFunctionStr = val;
124 }
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000125 }
126 }
127 return context.GetResult();
128 }
129
130 bool getRangeScalingFactor(float* scaling_factor) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000131 if (gContainerItemState == Done) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000132 stringstream ss(rangeScalingFactorStr);
133 float val;
134 if (ss >> val) {
135 *scaling_factor = val;
136 return true;
137 } else {
138 return false;
139 }
140 } else {
141 return false;
142 }
143 }
144
145 bool getTransferFunction(jpegr_transfer_function* transfer_function) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000146 if (gContainerItemState == Done) {
147 stringstream ss(transferFunctionStr);
148 int val;
149 if (ss >> val) {
150 *transfer_function = static_cast<jpegr_transfer_function>(val);
151 return true;
152 } else {
153 return false;
154 }
155 } else {
156 return false;
157 }
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000158 return true;
159 }
160
161private:
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000162 static const string gContainerItemName;
163 static const string rangeScalingFactorAttrName;
164 static const string transferFunctionAttrName;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000165 string rangeScalingFactorStr;
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000166 string transferFunctionStr;
167 string lastAttributeName;
168 ParseState gContainerItemState;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000169};
170
Nick Deakin7dfe0162023-01-19 18:27:09 -0500171// GContainer XMP constants - URI and namespace prefix
172const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
173const string kContainerPrefix = "GContainer";
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000174
Nick Deakin7dfe0162023-01-19 18:27:09 -0500175// GContainer XMP constants - element and attribute names
176const string kConDirectory = Name(kContainerPrefix, "Directory");
177const string kConItem = Name(kContainerPrefix, "Item");
178const string kConItemLength = Name(kContainerPrefix, "ItemLength");
179const string kConItemMime = Name(kContainerPrefix, "ItemMime");
180const string kConItemSemantic = Name(kContainerPrefix, "ItemSemantic");
181const string kConVersion = Name(kContainerPrefix, "Version");
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000182
Nick Deakin7dfe0162023-01-19 18:27:09 -0500183// GContainer XMP constants - element and attribute values
184const string kSemanticPrimary = "Primary";
185const string kSemanticRecoveryMap = "RecoveryMap";
186const string kMimeImageJpeg = "image/jpeg";
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000187
Nick Deakin7dfe0162023-01-19 18:27:09 -0500188const int kGContainerVersion = 1;
189
190// GContainer XMP constants - names for XMP handlers
191const string XMPXmlHandler::gContainerItemName = kConItem;
192
193// RecoveryMap XMP constants - URI and namespace prefix
194const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/";
195const string kRecoveryMapPrefix = "RecoveryMap";
196
197// RecoveryMap XMP constants - element and attribute names
198const string kMapRangeScalingFactor = Name(kRecoveryMapPrefix, "RangeScalingFactor");
199const string kMapTransferFunction = Name(kRecoveryMapPrefix, "TransferFunction");
200const string kMapVersion = Name(kRecoveryMapPrefix, "Version");
201
202const string kMapHdr10Metadata = Name(kRecoveryMapPrefix, "HDR10Metadata");
203const string kMapHdr10MaxFall = Name(kRecoveryMapPrefix, "HDR10MaxFALL");
204const string kMapHdr10MaxCll = Name(kRecoveryMapPrefix, "HDR10MaxCLL");
205
206const string kMapSt2086Metadata = Name(kRecoveryMapPrefix, "ST2086Metadata");
207const string kMapSt2086MaxLum = Name(kRecoveryMapPrefix, "ST2086MaxLuminance");
208const string kMapSt2086MinLum = Name(kRecoveryMapPrefix, "ST2086MinLuminance");
209const string kMapSt2086Primary = Name(kRecoveryMapPrefix, "ST2086Primary");
210const string kMapSt2086Coordinate = Name(kRecoveryMapPrefix, "ST2086Coordinate");
211const string kMapSt2086CoordinateX = Name(kRecoveryMapPrefix, "ST2086CoordinateX");
212const string kMapSt2086CoordinateY = Name(kRecoveryMapPrefix, "ST2086CoordinateY");
213
214// RecoveryMap XMP constants - element and attribute values
215const int kSt2086PrimaryRed = 0;
216const int kSt2086PrimaryGreen = 1;
217const int kSt2086PrimaryBlue = 2;
218const int kSt2086PrimaryWhite = 3;
219
220// RecoveryMap XMP constants - names for XMP handlers
221const string XMPXmlHandler::rangeScalingFactorAttrName = kMapRangeScalingFactor;
222const string XMPXmlHandler::transferFunctionAttrName = kMapTransferFunction;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000223
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000224bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000225 string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
226
227 if (xmp_size < nameSpace.size()+2) {
228 // Data too short
229 return false;
230 }
231
232 if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
233 // Not correct namespace
234 return false;
235 }
236
237 // Position the pointers to the start of XMP XML portion
238 xmp_data += nameSpace.size()+1;
239 xmp_size -= nameSpace.size()+1;
240 XMPXmlHandler handler;
241
242 // We need to remove tail data until the closing tag. Otherwise parser will throw an error.
243 while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) {
244 xmp_size--;
245 }
246
247 string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
248 MessageHandler msg_handler;
249 unique_ptr<XmlRule> rule(new XmlElementRule);
250 XmlReader reader(&handler, &msg_handler);
251 reader.StartParse(std::move(rule));
252 reader.Parse(str);
253 reader.FinishParse();
254 if (reader.HasErrors()) {
255 // Parse error
256 return false;
257 }
258
259 if (!handler.getRangeScalingFactor(&metadata->rangeScalingFactor)) {
260 return false;
261 }
262
263 if (!handler.getTransferFunction(&metadata->transferFunction)) {
264 return false;
265 }
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000266 return true;
267}
268
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000269string generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
Nick Deakin7dfe0162023-01-19 18:27:09 -0500270 const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
271 const vector<string> kLiItem({string("rdf:li"), kConItem});
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000272
273 std::stringstream ss;
274 photos_editing_formats::image_io::XmlWriter writer(ss);
275 writer.StartWritingElement("x:xmpmeta");
276 writer.WriteXmlns("x", "adobe:ns:meta/");
277 writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
278 writer.StartWritingElement("rdf:RDF");
279 writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
280 writer.StartWritingElement("rdf:Description");
281 writer.WriteXmlns(kContainerPrefix, kContainerUri);
Nick Deakin7dfe0162023-01-19 18:27:09 -0500282 writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri);
283 writer.WriteElementAndContent(kConVersion, kGContainerVersion);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000284 writer.StartWritingElements(kConDirSeq);
285 size_t item_depth = writer.StartWritingElements(kLiItem);
Nick Deakin7dfe0162023-01-19 18:27:09 -0500286 writer.WriteAttributeNameAndValue(kConItemSemantic, kSemanticPrimary);
287 writer.WriteAttributeNameAndValue(kConItemMime, kMimeImageJpeg);
288 writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
289 writer.WriteAttributeNameAndValue(kMapRangeScalingFactor, metadata.rangeScalingFactor);
290 writer.WriteAttributeNameAndValue(kMapTransferFunction, metadata.transferFunction);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000291 if (metadata.transferFunction == JPEGR_TF_PQ) {
Nick Deakin7dfe0162023-01-19 18:27:09 -0500292 writer.StartWritingElement(kMapHdr10Metadata);
293 writer.WriteAttributeNameAndValue(kMapHdr10MaxFall, metadata.hdr10Metadata.maxFALL);
294 writer.WriteAttributeNameAndValue(kMapHdr10MaxCll, metadata.hdr10Metadata.maxCLL);
295 writer.StartWritingElement(kMapSt2086Metadata);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000296 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500297 kMapSt2086MaxLum, metadata.hdr10Metadata.st2086Metadata.maxLuminance);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000298 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500299 kMapSt2086MinLum, metadata.hdr10Metadata.st2086Metadata.minLuminance);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000300
301 // red
Nick Deakin7dfe0162023-01-19 18:27:09 -0500302 writer.StartWritingElement(kMapSt2086Coordinate);
303 writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryRed);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000304 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500305 kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.redPrimary.x);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000306 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500307 kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.redPrimary.y);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000308 writer.FinishWritingElement();
309
310 // green
Nick Deakin7dfe0162023-01-19 18:27:09 -0500311 writer.StartWritingElement(kMapSt2086Coordinate);
312 writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryGreen);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000313 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500314 kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.greenPrimary.x);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000315 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500316 kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.greenPrimary.y);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000317 writer.FinishWritingElement();
318
319 // blue
Nick Deakin7dfe0162023-01-19 18:27:09 -0500320 writer.StartWritingElement(kMapSt2086Coordinate);
321 writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryBlue);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000322 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500323 kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.bluePrimary.x);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000324 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500325 kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.bluePrimary.y);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000326 writer.FinishWritingElement();
327
328 // white
Nick Deakin7dfe0162023-01-19 18:27:09 -0500329 writer.StartWritingElement(kMapSt2086Coordinate);
330 writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryWhite);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000331 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500332 kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.whitePoint.x);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000333 writer.WriteAttributeNameAndValue(
Nick Deakin7dfe0162023-01-19 18:27:09 -0500334 kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.whitePoint.y);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000335 writer.FinishWritingElement();
336 }
337 writer.FinishWritingElementsToDepth(item_depth);
338 writer.StartWritingElements(kLiItem);
Nick Deakin7dfe0162023-01-19 18:27:09 -0500339 writer.WriteAttributeNameAndValue(kConItemSemantic, kSemanticRecoveryMap);
340 writer.WriteAttributeNameAndValue(kConItemMime, kMimeImageJpeg);
341 writer.WriteAttributeNameAndValue(kConItemLength, secondary_image_length);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000342 writer.FinishWriting();
343
344 return ss.str();
345}
346
Dichen Zhang91dfc572023-01-19 11:22:43 -0800347/*
348 * Helper function
349 * Add J R entry to existing exif, or create a new one with J R entry if it's null.
350 */
351status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest) {
352 if (exif == nullptr || exif->data == nullptr) {
353 uint8_t data[PSEUDO_EXIF_PACKAGE_LENGTH] = {
354 0x45, 0x78, 0x69, 0x66, 0x00, 0x00,
355 0x49, 0x49, 0x2A, 0x00,
356 0x08, 0x00, 0x00, 0x00,
357 0x01, 0x00,
358 0x4A, 0x52,
359 0x07, 0x00,
360 0x01, 0x00, 0x00, 0x00,
361 0x00, 0x00, 0x00, 0x00};
362 int pos = 0;
363 Write(dest, data, PSEUDO_EXIF_PACKAGE_LENGTH, pos);
364 return NO_ERROR;
365 }
366
367 int num_entry = 0;
368 uint8_t num_entry_low = 0;
369 uint8_t num_entry_high = 0;
370 bool use_big_endian = false;
371 if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4949) {
372 num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[14];
373 num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[15];
374 } else if (reinterpret_cast<uint16_t*>(exif->data)[3] == 0x4d4d) {
375 use_big_endian = true;
376 num_entry_high = reinterpret_cast<uint8_t*>(exif->data)[14];
377 num_entry_low = reinterpret_cast<uint8_t*>(exif->data)[15];
378 } else {
379 return ERROR_JPEGR_METADATA_ERROR;
380 }
381 num_entry = (num_entry_high << 8) | num_entry_low;
382 num_entry += 1;
383 num_entry_low = num_entry & 0xff;
384 num_entry_high = (num_entry >> 8) & 0xff;
385
386 int pos = 0;
387 Write(dest, (uint8_t*)exif->data, 14, pos);
388
389 if (use_big_endian) {
390 Write(dest, &num_entry_high, 1, pos);
391 Write(dest, &num_entry_low, 1, pos);
392 uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
393 0x4A, 0x52,
394 0x00, 0x07,
395 0x00, 0x00, 0x00, 0x01,
396 0x00, 0x00, 0x00, 0x00};
397 Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
398 } else {
399 Write(dest, &num_entry_low, 1, pos);
400 Write(dest, &num_entry_high, 1, pos);
401 uint8_t data[EXIF_J_R_ENTRY_LENGTH] = {
402 0x4A, 0x52,
403 0x07, 0x00,
404 0x01, 0x00, 0x00, 0x00,
405 0x00, 0x00, 0x00, 0x00};
406 Write(dest, data, EXIF_J_R_ENTRY_LENGTH, pos);
407 }
408
409 Write(dest, (uint8_t*)exif->data + 16, exif->length - 16, pos);
410
Dichen Zhang0daf5f82023-01-19 13:44:23 -0800411 updateExifOffsets(dest,
412 28, // start from the second tag, skip the "JR" tag
413 num_entry - 1,
414 use_big_endian);
415
Dichen Zhang91dfc572023-01-19 11:22:43 -0800416 return NO_ERROR;
417}
418
Dichen Zhang0daf5f82023-01-19 13:44:23 -0800419/*
420 * Helper function
421 * Modify offsets in EXIF in place.
422 */
423void updateExifOffsets(jr_exif_ptr exif, int pos, bool use_big_endian) {
424 int num_entry = readValue(reinterpret_cast<uint8_t*>(exif->data), pos, 2, use_big_endian);
425 updateExifOffsets(exif, pos + 2, num_entry, use_big_endian);
426}
427
428void updateExifOffsets(jr_exif_ptr exif, int pos, int num_entry, bool use_big_endian) {
429 for (int i = 0; i < num_entry; pos += EXIF_J_R_ENTRY_LENGTH, i++) {
430 int tag = readValue(reinterpret_cast<uint8_t*>(exif->data), pos, 2, use_big_endian);
431 bool need_to_update_offset = false;
432 if (tag == 0x8769) {
433 need_to_update_offset = true;
434 int sub_ifd_offset =
435 readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 8, 4, use_big_endian)
436 + 6 // "Exif\0\0";
437 + EXIF_J_R_ENTRY_LENGTH;
438 updateExifOffsets(exif, sub_ifd_offset, use_big_endian);
439 } else {
440 int data_format =
441 readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 2, 2, use_big_endian);
442 int num_of_components =
443 readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 4, 4, use_big_endian);
444 int data_length = findFormatLengthInBytes(data_format) * num_of_components;
445 if (data_length > 4) {
446 need_to_update_offset = true;
447 }
448 }
449
450 if (!need_to_update_offset) {
451 continue;
452 }
453
454 int offset = readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 8, 4, use_big_endian);
455
456 offset += EXIF_J_R_ENTRY_LENGTH;
457
458 if (use_big_endian) {
459 reinterpret_cast<uint8_t*>(exif->data)[pos + 11] = offset & 0xff;
460 reinterpret_cast<uint8_t*>(exif->data)[pos + 10] = (offset >> 8) & 0xff;
461 reinterpret_cast<uint8_t*>(exif->data)[pos + 9] = (offset >> 16) & 0xff;
462 reinterpret_cast<uint8_t*>(exif->data)[pos + 8] = (offset >> 24) & 0xff;
463 } else {
464 reinterpret_cast<uint8_t*>(exif->data)[pos + 8] = offset & 0xff;
465 reinterpret_cast<uint8_t*>(exif->data)[pos + 9] = (offset >> 8) & 0xff;
466 reinterpret_cast<uint8_t*>(exif->data)[pos + 10] = (offset >> 16) & 0xff;
467 reinterpret_cast<uint8_t*>(exif->data)[pos + 11] = (offset >> 24) & 0xff;
468 }
469 }
470}
471
472/*
473 * Read data from the target position and target length in bytes;
474 */
475int readValue(uint8_t* data, int pos, int length, bool use_big_endian) {
476 if (length == 2) {
477 if (use_big_endian) {
478 return (data[pos] << 8) | data[pos + 1];
479 } else {
480 return (data[pos + 1] << 8) | data[pos];
481 }
482 } else if (length == 4) {
483 if (use_big_endian) {
484 return (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
485 } else {
486 return (data[pos + 3] << 24) | (data[pos + 2] << 16) | (data[pos + 1] << 8) | data[pos];
487 }
488 } else {
489 // Not support for now.
490 ALOGE("Error in readValue(): pos=%d, length=%d", pos, length);
491 return -1;
492 }
493}
494
495/*
496 * Helper function
497 * Returns the length of data format in bytes
498 */
499int findFormatLengthInBytes(int data_format) {
500 switch (data_format) {
501 case 1: // unsigned byte
502 case 2: // ascii strings
503 case 6: // signed byte
504 case 7: // undefined
505 return 1;
506
507 case 3: // unsigned short
508 case 8: // signed short
509 return 2;
510
511 case 4: // unsigned long
512 case 9: // signed long
513 case 11: // single float
514 return 4;
515
516 case 5: // unsigned rational
517 case 10: // signed rational
518 case 12: // double float
519 return 8;
520
521 default:
522 // should not hit here
523 ALOGE("Error in findFormatLengthInBytes(): data_format=%d", data_format);
524 return -1;
525 }
526}
527
528} // namespace android::recoverymap