blob: cde0ceb520a1b3c6ddd0b508fd89f5572a8516a8 [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
Dichen Zhang761b52d2023-02-10 22:38:38 +000017#include <jpegrecoverymap/jpegrutils.h>
Nick Deakin31f03e32023-03-31 16:00:13 -040018
19#include <algorithm>
20#include <cmath>
21
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000022#include <image_io/xml/xml_reader.h>
Dichen Zhangdc8452b2022-11-23 17:17:56 +000023#include <image_io/xml/xml_writer.h>
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000024#include <image_io/base/message_handler.h>
25#include <image_io/xml/xml_element_rules.h>
26#include <image_io/xml/xml_handler.h>
27#include <image_io/xml/xml_rule.h>
Nick Deakin31f03e32023-03-31 16:00:13 -040028#include <utils/Log.h>
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000029
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000030using namespace photos_editing_formats::image_io;
31using namespace std;
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +000032
Dichen Zhangb2ed8302023-02-10 23:05:04 +000033namespace android::jpegrecoverymap {
Dichen Zhangdc8452b2022-11-23 17:17:56 +000034/*
35 * Helper function used for generating XMP metadata.
36 *
37 * @param prefix The prefix part of the name.
38 * @param suffix The suffix part of the name.
39 * @return A name of the form "prefix:suffix".
40 */
Dichen Zhang61ede362023-02-22 18:50:13 +000041static inline string Name(const string &prefix, const string &suffix) {
Dichen Zhangdc8452b2022-11-23 17:17:56 +000042 std::stringstream ss;
43 ss << prefix << ":" << suffix;
44 return ss.str();
45}
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +000046
Dichen Zhang61ede362023-02-22 18:50:13 +000047DataStruct::DataStruct(int s) {
48 data = malloc(s);
49 length = s;
50 memset(data, 0, s);
51 writePos = 0;
52}
53
54DataStruct::~DataStruct() {
55 if (data != nullptr) {
56 free(data);
57 }
58}
59
60void* DataStruct::getData() {
61 return data;
62}
63
64int DataStruct::getLength() {
65 return length;
66}
67
68int DataStruct::getBytesWritten() {
69 return writePos;
70}
71
72bool DataStruct::write8(uint8_t value) {
73 uint8_t v = value;
74 return write(&v, 1);
75}
76
77bool DataStruct::write16(uint16_t value) {
78 uint16_t v = value;
79 return write(&v, 2);
80}
81bool DataStruct::write32(uint32_t value) {
82 uint32_t v = value;
83 return write(&v, 4);
84}
85
86bool DataStruct::write(const void* src, int size) {
87 if (writePos + size > length) {
88 ALOGE("Writing out of boundary: write position: %d, size: %d, capacity: %d",
89 writePos, size, length);
90 return false;
91 }
92 memcpy((uint8_t*) data + writePos, src, size);
93 writePos += size;
94 return true;
95}
96
Dichen Zhang91dfc572023-01-19 11:22:43 -080097/*
98 * Helper function used for writing data to destination.
99 */
100status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) {
101 if (position + length > destination->maxLength) {
102 return ERROR_JPEGR_BUFFER_TOO_SMALL;
103 }
104
105 memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
106 position += length;
107 return NO_ERROR;
108}
109
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000110// Extremely simple XML Handler - just searches for interesting elements
111class XMPXmlHandler : public XmlHandler {
112public:
113
114 XMPXmlHandler() : XmlHandler() {
Dichen Zhang61ede362023-02-22 18:50:13 +0000115 state = NotStrarted;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000116 }
117
118 enum ParseState {
119 NotStrarted,
120 Started,
121 Done
122 };
123
124 virtual DataMatchResult StartElement(const XmlTokenContext& context) {
125 string val;
126 if (context.BuildTokenValue(&val)) {
Dichen Zhang61ede362023-02-22 18:50:13 +0000127 if (!val.compare(containerName)) {
128 state = Started;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000129 } else {
Dichen Zhang61ede362023-02-22 18:50:13 +0000130 if (state != Done) {
131 state = NotStrarted;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000132 }
133 }
134 }
135 return context.GetResult();
136 }
137
138 virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
Dichen Zhang61ede362023-02-22 18:50:13 +0000139 if (state == Started) {
140 state = Done;
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000141 lastAttributeName = "";
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000142 }
143 return context.GetResult();
144 }
145
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000146 virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000147 string val;
Dichen Zhang61ede362023-02-22 18:50:13 +0000148 if (state == Started) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000149 if (context.BuildTokenValue(&val)) {
Nick Deakin01759062023-02-02 18:21:43 -0500150 if (!val.compare(maxContentBoostAttrName)) {
151 lastAttributeName = maxContentBoostAttrName;
Nick Deakin3f89a3e2023-02-14 20:35:42 -0500152 } else if (!val.compare(minContentBoostAttrName)) {
153 lastAttributeName = minContentBoostAttrName;
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000154 } else {
155 lastAttributeName = "";
156 }
157 }
158 }
159 return context.GetResult();
160 }
161
162 virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
163 string val;
Dichen Zhang61ede362023-02-22 18:50:13 +0000164 if (state == Started) {
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000165 if (context.BuildTokenValue(&val, true)) {
Nick Deakin01759062023-02-02 18:21:43 -0500166 if (!lastAttributeName.compare(maxContentBoostAttrName)) {
167 maxContentBoostStr = val;
Nick Deakin3f89a3e2023-02-14 20:35:42 -0500168 } else if (!lastAttributeName.compare(minContentBoostAttrName)) {
169 minContentBoostStr = val;
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000170 }
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000171 }
172 }
173 return context.GetResult();
174 }
175
Nick Deakin01759062023-02-02 18:21:43 -0500176 bool getMaxContentBoost(float* max_content_boost) {
Dichen Zhang61ede362023-02-22 18:50:13 +0000177 if (state == Done) {
Nick Deakin01759062023-02-02 18:21:43 -0500178 stringstream ss(maxContentBoostStr);
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000179 float val;
180 if (ss >> val) {
Dichen Zhang61ede362023-02-22 18:50:13 +0000181 *max_content_boost = exp2(val);
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000182 return true;
183 } else {
184 return false;
185 }
186 } else {
187 return false;
188 }
189 }
190
Nick Deakin3f89a3e2023-02-14 20:35:42 -0500191 bool getMinContentBoost(float* min_content_boost) {
Dichen Zhang61ede362023-02-22 18:50:13 +0000192 if (state == Done) {
Nick Deakin3f89a3e2023-02-14 20:35:42 -0500193 stringstream ss(minContentBoostStr);
194 float val;
195 if (ss >> val) {
Dichen Zhang61ede362023-02-22 18:50:13 +0000196 *min_content_boost = exp2(val);
Nick Deakin3f89a3e2023-02-14 20:35:42 -0500197 return true;
198 } else {
199 return false;
200 }
201 } else {
202 return false;
203 }
204 }
205
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000206private:
Dichen Zhang61ede362023-02-22 18:50:13 +0000207 static const string containerName;
Nick Deakin01759062023-02-02 18:21:43 -0500208 static const string maxContentBoostAttrName;
209 string maxContentBoostStr;
Nick Deakin3f89a3e2023-02-14 20:35:42 -0500210 static const string minContentBoostAttrName;
211 string minContentBoostStr;
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000212 string lastAttributeName;
Dichen Zhang61ede362023-02-22 18:50:13 +0000213 ParseState state;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000214};
215
Nick Deakin7dfe0162023-01-19 18:27:09 -0500216// GContainer XMP constants - URI and namespace prefix
217const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
Nick Deakin01759062023-02-02 18:21:43 -0500218const string kContainerPrefix = "Container";
Fyodor Kyslovad58a342022-12-12 02:10:35 +0000219
Nick Deakin7dfe0162023-01-19 18:27:09 -0500220// GContainer XMP constants - element and attribute names
221const string kConDirectory = Name(kContainerPrefix, "Directory");
222const string kConItem = Name(kContainerPrefix, "Item");
Nick Deakin7dfe0162023-01-19 18:27:09 -0500223
224// GContainer XMP constants - names for XMP handlers
Dichen Zhang61ede362023-02-22 18:50:13 +0000225const string XMPXmlHandler::containerName = "rdf:Description";
Nick Deakin01759062023-02-02 18:21:43 -0500226// Item XMP constants - URI and namespace prefix
227const string kItemUri = "http://ns.google.com/photos/1.0/container/item/";
228const string kItemPrefix = "Item";
229
230// Item XMP constants - element and attribute names
231const string kItemLength = Name(kItemPrefix, "Length");
232const string kItemMime = Name(kItemPrefix, "Mime");
233const string kItemSemantic = Name(kItemPrefix, "Semantic");
234
235// Item XMP constants - element and attribute values
Nick Deakin31f03e32023-03-31 16:00:13 -0400236const string kSemanticPrimary = "Primary";
237const string kSemanticGainMap = "GainMap";
238const string kMimeImageJpeg = "image/jpeg";
Nick Deakin01759062023-02-02 18:21:43 -0500239
Nick Deakin31f03e32023-03-31 16:00:13 -0400240// GainMap XMP constants - URI and namespace prefix
241const string kGainMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/";
242const string kGainMapPrefix = "hdrgm";
Nick Deakin7dfe0162023-01-19 18:27:09 -0500243
Nick Deakin31f03e32023-03-31 16:00:13 -0400244// GainMap XMP constants - element and attribute names
245const string kMapVersion = Name(kGainMapPrefix, "Version");
246const string kMapGainMapMin = Name(kGainMapPrefix, "GainMapMin");
247const string kMapGainMapMax = Name(kGainMapPrefix, "GainMapMax");
248const string kMapGamma = Name(kGainMapPrefix, "Gamma");
249const string kMapOffsetSdr = Name(kGainMapPrefix, "OffsetSDR");
250const string kMapOffsetHdr = Name(kGainMapPrefix, "OffsetHDR");
251const string kMapHDRCapacityMin = Name(kGainMapPrefix, "HDRCapacityMin");
252const string kMapHDRCapacityMax = Name(kGainMapPrefix, "HDRCapacityMax");
253const string kMapBaseRenditionIsHDR = Name(kGainMapPrefix, "BaseRenditionIsHDR");
Nick Deakin7dfe0162023-01-19 18:27:09 -0500254
Nick Deakin31f03e32023-03-31 16:00:13 -0400255// GainMap XMP constants - names for XMP handlers
Dichen Zhang61ede362023-02-22 18:50:13 +0000256const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
257const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000258
Dichen Zhange286f1c2023-03-14 00:22:00 +0000259bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata_struct* metadata) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000260 string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
261
262 if (xmp_size < nameSpace.size()+2) {
263 // Data too short
264 return false;
265 }
266
267 if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
268 // Not correct namespace
269 return false;
270 }
271
272 // Position the pointers to the start of XMP XML portion
273 xmp_data += nameSpace.size()+1;
274 xmp_size -= nameSpace.size()+1;
275 XMPXmlHandler handler;
276
277 // We need to remove tail data until the closing tag. Otherwise parser will throw an error.
278 while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) {
279 xmp_size--;
280 }
281
282 string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
283 MessageHandler msg_handler;
284 unique_ptr<XmlRule> rule(new XmlElementRule);
285 XmlReader reader(&handler, &msg_handler);
286 reader.StartParse(std::move(rule));
287 reader.Parse(str);
288 reader.FinishParse();
289 if (reader.HasErrors()) {
290 // Parse error
291 return false;
292 }
293
Nick Deakin01759062023-02-02 18:21:43 -0500294 if (!handler.getMaxContentBoost(&metadata->maxContentBoost)) {
Fyodor Kyslov6c87e7b2022-11-23 03:12:14 +0000295 return false;
296 }
297
Nick Deakin3f89a3e2023-02-14 20:35:42 -0500298 if (!handler.getMinContentBoost(&metadata->minContentBoost)) {
299 return false;
300 }
301
Fyodor Kyslov1dcc4422022-11-16 01:40:53 +0000302 return true;
303}
304
Dichen Zhang61ede362023-02-22 18:50:13 +0000305string generateXmpForPrimaryImage(int secondary_image_length) {
Nick Deakin7dfe0162023-01-19 18:27:09 -0500306 const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
307 const vector<string> kLiItem({string("rdf:li"), kConItem});
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000308
309 std::stringstream ss;
310 photos_editing_formats::image_io::XmlWriter writer(ss);
311 writer.StartWritingElement("x:xmpmeta");
312 writer.WriteXmlns("x", "adobe:ns:meta/");
313 writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
314 writer.StartWritingElement("rdf:RDF");
315 writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
316 writer.StartWritingElement("rdf:Description");
317 writer.WriteXmlns(kContainerPrefix, kContainerUri);
Nick Deakin01759062023-02-02 18:21:43 -0500318 writer.WriteXmlns(kItemPrefix, kItemUri);
Nick Deakin31f03e32023-03-31 16:00:13 -0400319
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000320 writer.StartWritingElements(kConDirSeq);
Nick Deakin31f03e32023-03-31 16:00:13 -0400321
322 size_t item_depth = writer.StartWritingElement("rdf:li");
323 writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
324 writer.StartWritingElement(kConItem);
Nick Deakin01759062023-02-02 18:21:43 -0500325 writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
326 writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000327 writer.FinishWritingElementsToDepth(item_depth);
Nick Deakin31f03e32023-03-31 16:00:13 -0400328
329 writer.StartWritingElement("rdf:li");
330 writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
331 writer.StartWritingElement(kConItem);
332 writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticGainMap);
Nick Deakin01759062023-02-02 18:21:43 -0500333 writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
334 writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
Nick Deakin31f03e32023-03-31 16:00:13 -0400335
Dichen Zhang61ede362023-02-22 18:50:13 +0000336 writer.FinishWriting();
337
338 return ss.str();
339}
340
Dichen Zhange286f1c2023-03-14 00:22:00 +0000341string generateXmpForSecondaryImage(jpegr_metadata_struct& metadata) {
Dichen Zhang61ede362023-02-22 18:50:13 +0000342 const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
Dichen Zhang61ede362023-02-22 18:50:13 +0000343
344 std::stringstream ss;
345 photos_editing_formats::image_io::XmlWriter writer(ss);
346 writer.StartWritingElement("x:xmpmeta");
347 writer.WriteXmlns("x", "adobe:ns:meta/");
348 writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
349 writer.StartWritingElement("rdf:RDF");
350 writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
351 writer.StartWritingElement("rdf:Description");
Nick Deakin31f03e32023-03-31 16:00:13 -0400352 writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
Nick Deakin3f89a3e2023-02-14 20:35:42 -0500353 writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
Dichen Zhang61ede362023-02-22 18:50:13 +0000354 writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost));
355 writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost));
356 writer.WriteAttributeNameAndValue(kMapGamma, "1");
357 writer.WriteAttributeNameAndValue(kMapOffsetSdr, "0");
358 writer.WriteAttributeNameAndValue(kMapOffsetHdr, "0");
Nick Deakin31f03e32023-03-31 16:00:13 -0400359 writer.WriteAttributeNameAndValue(
360 kMapHDRCapacityMin, std::max(log2(metadata.minContentBoost), 0.0f));
361 writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.maxContentBoost));
362 writer.WriteAttributeNameAndValue(kMapBaseRenditionIsHDR, "False");
Dichen Zhangdc8452b2022-11-23 17:17:56 +0000363 writer.FinishWriting();
364
365 return ss.str();
366}
367
Dichen Zhangb2ed8302023-02-10 23:05:04 +0000368} // namespace android::jpegrecoverymap