1 /*
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 <algorithm>
18 #include <cmath>
19
20 #include "ultrahdr/ultrahdrcommon.h"
21 #include "ultrahdr/jpegr.h"
22 #include "ultrahdr/jpegrutils.h"
23
24 #include "image_io/xml/xml_reader.h"
25 #include "image_io/xml/xml_writer.h"
26 #include "image_io/base/message_handler.h"
27 #include "image_io/xml/xml_element_rules.h"
28 #include "image_io/xml/xml_handler.h"
29 #include "image_io/xml/xml_rule.h"
30
31 using namespace photos_editing_formats::image_io;
32 using namespace std;
33
34 namespace ultrahdr {
35 /*
36 * Helper function used for generating XMP metadata.
37 *
38 * @param prefix The prefix part of the name.
39 * @param suffix The suffix part of the name.
40 * @return A name of the form "prefix:suffix".
41 */
Name(const string & prefix,const string & suffix)42 static inline string Name(const string& prefix, const string& suffix) {
43 std::stringstream ss;
44 ss << prefix << ":" << suffix;
45 return ss.str();
46 }
47
DataStruct(int s)48 DataStruct::DataStruct(int s) {
49 data = malloc(s);
50 length = s;
51 memset(data, 0, s);
52 writePos = 0;
53 }
54
~DataStruct()55 DataStruct::~DataStruct() {
56 if (data != nullptr) {
57 free(data);
58 }
59 }
60
getData()61 void* DataStruct::getData() { return data; }
62
getLength()63 int DataStruct::getLength() { return length; }
64
getBytesWritten()65 int DataStruct::getBytesWritten() { return writePos; }
66
write8(uint8_t value)67 bool DataStruct::write8(uint8_t value) {
68 uint8_t v = value;
69 return write(&v, 1);
70 }
71
write16(uint16_t value)72 bool DataStruct::write16(uint16_t value) {
73 uint16_t v = value;
74 return write(&v, 2);
75 }
write32(uint32_t value)76 bool DataStruct::write32(uint32_t value) {
77 uint32_t v = value;
78 return write(&v, 4);
79 }
80
write(const void * src,int size)81 bool DataStruct::write(const void* src, int size) {
82 if (writePos + size > length) {
83 ALOGE("Writing out of boundary: write position: %d, size: %d, capacity: %d", writePos, size,
84 length);
85 return false;
86 }
87 memcpy((uint8_t*)data + writePos, src, size);
88 writePos += size;
89 return true;
90 }
91
92 /*
93 * Helper function used for writing data to destination.
94 */
Write(jr_compressed_ptr destination,const void * source,int length,int & position)95 status_t Write(jr_compressed_ptr destination, const void* source, int length, int& position) {
96 if (position + length > destination->maxLength) {
97 return ERROR_JPEGR_BUFFER_TOO_SMALL;
98 }
99
100 memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
101 position += length;
102 return JPEGR_NO_ERROR;
103 }
104
105 // Extremely simple XML Handler - just searches for interesting elements
106 class XMPXmlHandler : public XmlHandler {
107 public:
XMPXmlHandler()108 XMPXmlHandler() : XmlHandler() {
109 state = NotStrarted;
110 versionFound = false;
111 minContentBoostFound = false;
112 maxContentBoostFound = false;
113 gammaFound = false;
114 offsetSdrFound = false;
115 offsetHdrFound = false;
116 hdrCapacityMinFound = false;
117 hdrCapacityMaxFound = false;
118 baseRenditionIsHdrFound = false;
119 }
120
121 enum ParseState { NotStrarted, Started, Done };
122
StartElement(const XmlTokenContext & context)123 virtual DataMatchResult StartElement(const XmlTokenContext& context) {
124 string val;
125 if (context.BuildTokenValue(&val)) {
126 if (!val.compare(containerName)) {
127 state = Started;
128 } else {
129 if (state != Done) {
130 state = NotStrarted;
131 }
132 }
133 }
134 return context.GetResult();
135 }
136
FinishElement(const XmlTokenContext & context)137 virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
138 if (state == Started) {
139 state = Done;
140 lastAttributeName = "";
141 }
142 return context.GetResult();
143 }
144
AttributeName(const XmlTokenContext & context)145 virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
146 string val;
147 if (state == Started) {
148 if (context.BuildTokenValue(&val)) {
149 if (!val.compare(versionAttrName)) {
150 lastAttributeName = versionAttrName;
151 } else if (!val.compare(maxContentBoostAttrName)) {
152 lastAttributeName = maxContentBoostAttrName;
153 } else if (!val.compare(minContentBoostAttrName)) {
154 lastAttributeName = minContentBoostAttrName;
155 } else if (!val.compare(gammaAttrName)) {
156 lastAttributeName = gammaAttrName;
157 } else if (!val.compare(offsetSdrAttrName)) {
158 lastAttributeName = offsetSdrAttrName;
159 } else if (!val.compare(offsetHdrAttrName)) {
160 lastAttributeName = offsetHdrAttrName;
161 } else if (!val.compare(hdrCapacityMinAttrName)) {
162 lastAttributeName = hdrCapacityMinAttrName;
163 } else if (!val.compare(hdrCapacityMaxAttrName)) {
164 lastAttributeName = hdrCapacityMaxAttrName;
165 } else if (!val.compare(baseRenditionIsHdrAttrName)) {
166 lastAttributeName = baseRenditionIsHdrAttrName;
167 } else {
168 lastAttributeName = "";
169 }
170 }
171 }
172 return context.GetResult();
173 }
174
AttributeValue(const XmlTokenContext & context)175 virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
176 string val;
177 if (state == Started) {
178 if (context.BuildTokenValue(&val, true)) {
179 if (!lastAttributeName.compare(versionAttrName)) {
180 versionStr = val;
181 versionFound = true;
182 } else if (!lastAttributeName.compare(maxContentBoostAttrName)) {
183 maxContentBoostStr = val;
184 maxContentBoostFound = true;
185 } else if (!lastAttributeName.compare(minContentBoostAttrName)) {
186 minContentBoostStr = val;
187 minContentBoostFound = true;
188 } else if (!lastAttributeName.compare(gammaAttrName)) {
189 gammaStr = val;
190 gammaFound = true;
191 } else if (!lastAttributeName.compare(offsetSdrAttrName)) {
192 offsetSdrStr = val;
193 offsetSdrFound = true;
194 } else if (!lastAttributeName.compare(offsetHdrAttrName)) {
195 offsetHdrStr = val;
196 offsetHdrFound = true;
197 } else if (!lastAttributeName.compare(hdrCapacityMinAttrName)) {
198 hdrCapacityMinStr = val;
199 hdrCapacityMinFound = true;
200 } else if (!lastAttributeName.compare(hdrCapacityMaxAttrName)) {
201 hdrCapacityMaxStr = val;
202 hdrCapacityMaxFound = true;
203 } else if (!lastAttributeName.compare(baseRenditionIsHdrAttrName)) {
204 baseRenditionIsHdrStr = val;
205 baseRenditionIsHdrFound = true;
206 }
207 }
208 }
209 return context.GetResult();
210 }
211
getVersion(string * version,bool * present)212 bool getVersion(string* version, bool* present) {
213 if (state == Done) {
214 *version = versionStr;
215 *present = versionFound;
216 return true;
217 } else {
218 return false;
219 }
220 }
221
getMaxContentBoost(float * max_content_boost,bool * present)222 bool getMaxContentBoost(float* max_content_boost, bool* present) {
223 if (state == Done) {
224 *present = maxContentBoostFound;
225 stringstream ss(maxContentBoostStr);
226 float val;
227 if (ss >> val) {
228 *max_content_boost = exp2(val);
229 return true;
230 } else {
231 return false;
232 }
233 } else {
234 return false;
235 }
236 }
237
getMinContentBoost(float * min_content_boost,bool * present)238 bool getMinContentBoost(float* min_content_boost, bool* present) {
239 if (state == Done) {
240 *present = minContentBoostFound;
241 stringstream ss(minContentBoostStr);
242 float val;
243 if (ss >> val) {
244 *min_content_boost = exp2(val);
245 return true;
246 } else {
247 return false;
248 }
249 } else {
250 return false;
251 }
252 }
253
getGamma(float * gamma,bool * present)254 bool getGamma(float* gamma, bool* present) {
255 if (state == Done) {
256 *present = gammaFound;
257 stringstream ss(gammaStr);
258 float val;
259 if (ss >> val) {
260 *gamma = val;
261 return true;
262 } else {
263 return false;
264 }
265 } else {
266 return false;
267 }
268 }
269
getOffsetSdr(float * offset_sdr,bool * present)270 bool getOffsetSdr(float* offset_sdr, bool* present) {
271 if (state == Done) {
272 *present = offsetSdrFound;
273 stringstream ss(offsetSdrStr);
274 float val;
275 if (ss >> val) {
276 *offset_sdr = val;
277 return true;
278 } else {
279 return false;
280 }
281 } else {
282 return false;
283 }
284 }
285
getOffsetHdr(float * offset_hdr,bool * present)286 bool getOffsetHdr(float* offset_hdr, bool* present) {
287 if (state == Done) {
288 *present = offsetHdrFound;
289 stringstream ss(offsetHdrStr);
290 float val;
291 if (ss >> val) {
292 *offset_hdr = val;
293 return true;
294 } else {
295 return false;
296 }
297 } else {
298 return false;
299 }
300 }
301
getHdrCapacityMin(float * hdr_capacity_min,bool * present)302 bool getHdrCapacityMin(float* hdr_capacity_min, bool* present) {
303 if (state == Done) {
304 *present = hdrCapacityMinFound;
305 stringstream ss(hdrCapacityMinStr);
306 float val;
307 if (ss >> val) {
308 *hdr_capacity_min = exp2(val);
309 return true;
310 } else {
311 return false;
312 }
313 } else {
314 return false;
315 }
316 }
317
getHdrCapacityMax(float * hdr_capacity_max,bool * present)318 bool getHdrCapacityMax(float* hdr_capacity_max, bool* present) {
319 if (state == Done) {
320 *present = hdrCapacityMaxFound;
321 stringstream ss(hdrCapacityMaxStr);
322 float val;
323 if (ss >> val) {
324 *hdr_capacity_max = exp2(val);
325 return true;
326 } else {
327 return false;
328 }
329 } else {
330 return false;
331 }
332 }
333
getBaseRenditionIsHdr(bool * base_rendition_is_hdr,bool * present)334 bool getBaseRenditionIsHdr(bool* base_rendition_is_hdr, bool* present) {
335 if (state == Done) {
336 *present = baseRenditionIsHdrFound;
337 if (!baseRenditionIsHdrStr.compare("False")) {
338 *base_rendition_is_hdr = false;
339 return true;
340 } else if (!baseRenditionIsHdrStr.compare("True")) {
341 *base_rendition_is_hdr = true;
342 return true;
343 } else {
344 return false;
345 }
346 } else {
347 return false;
348 }
349 }
350
351 private:
352 static const string containerName;
353
354 static const string versionAttrName;
355 string versionStr;
356 bool versionFound;
357 static const string maxContentBoostAttrName;
358 string maxContentBoostStr;
359 bool maxContentBoostFound;
360 static const string minContentBoostAttrName;
361 string minContentBoostStr;
362 bool minContentBoostFound;
363 static const string gammaAttrName;
364 string gammaStr;
365 bool gammaFound;
366 static const string offsetSdrAttrName;
367 string offsetSdrStr;
368 bool offsetSdrFound;
369 static const string offsetHdrAttrName;
370 string offsetHdrStr;
371 bool offsetHdrFound;
372 static const string hdrCapacityMinAttrName;
373 string hdrCapacityMinStr;
374 bool hdrCapacityMinFound;
375 static const string hdrCapacityMaxAttrName;
376 string hdrCapacityMaxStr;
377 bool hdrCapacityMaxFound;
378 static const string baseRenditionIsHdrAttrName;
379 string baseRenditionIsHdrStr;
380 bool baseRenditionIsHdrFound;
381
382 string lastAttributeName;
383 ParseState state;
384 };
385
386 // GContainer XMP constants - URI and namespace prefix
387 const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
388 const string kContainerPrefix = "Container";
389
390 // GContainer XMP constants - element and attribute names
391 const string kConDirectory = Name(kContainerPrefix, "Directory");
392 const string kConItem = Name(kContainerPrefix, "Item");
393
394 // GContainer XMP constants - names for XMP handlers
395 const string XMPXmlHandler::containerName = "rdf:Description";
396 // Item XMP constants - URI and namespace prefix
397 const string kItemUri = "http://ns.google.com/photos/1.0/container/item/";
398 const string kItemPrefix = "Item";
399
400 // Item XMP constants - element and attribute names
401 const string kItemLength = Name(kItemPrefix, "Length");
402 const string kItemMime = Name(kItemPrefix, "Mime");
403 const string kItemSemantic = Name(kItemPrefix, "Semantic");
404
405 // Item XMP constants - element and attribute values
406 const string kSemanticPrimary = "Primary";
407 const string kSemanticGainMap = "GainMap";
408 const string kMimeImageJpeg = "image/jpeg";
409
410 // GainMap XMP constants - URI and namespace prefix
411 const string kGainMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/";
412 const string kGainMapPrefix = "hdrgm";
413
414 // GainMap XMP constants - element and attribute names
415 const string kMapVersion = Name(kGainMapPrefix, "Version");
416 const string kMapGainMapMin = Name(kGainMapPrefix, "GainMapMin");
417 const string kMapGainMapMax = Name(kGainMapPrefix, "GainMapMax");
418 const string kMapGamma = Name(kGainMapPrefix, "Gamma");
419 const string kMapOffsetSdr = Name(kGainMapPrefix, "OffsetSDR");
420 const string kMapOffsetHdr = Name(kGainMapPrefix, "OffsetHDR");
421 const string kMapHDRCapacityMin = Name(kGainMapPrefix, "HDRCapacityMin");
422 const string kMapHDRCapacityMax = Name(kGainMapPrefix, "HDRCapacityMax");
423 const string kMapBaseRenditionIsHDR = Name(kGainMapPrefix, "BaseRenditionIsHDR");
424
425 // GainMap XMP constants - names for XMP handlers
426 const string XMPXmlHandler::versionAttrName = kMapVersion;
427 const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
428 const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
429 const string XMPXmlHandler::gammaAttrName = kMapGamma;
430 const string XMPXmlHandler::offsetSdrAttrName = kMapOffsetSdr;
431 const string XMPXmlHandler::offsetHdrAttrName = kMapOffsetHdr;
432 const string XMPXmlHandler::hdrCapacityMinAttrName = kMapHDRCapacityMin;
433 const string XMPXmlHandler::hdrCapacityMaxAttrName = kMapHDRCapacityMax;
434 const string XMPXmlHandler::baseRenditionIsHdrAttrName = kMapBaseRenditionIsHDR;
435
getMetadataFromXMP(uint8_t * xmp_data,int xmp_size,ultrahdr_metadata_struct * metadata)436 bool getMetadataFromXMP(uint8_t* xmp_data, int xmp_size, ultrahdr_metadata_struct* metadata) {
437 string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
438
439 if (xmp_size < (int)nameSpace.size() + 2) {
440 // Data too short
441 return false;
442 }
443
444 if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
445 // Not correct namespace
446 return false;
447 }
448
449 // Position the pointers to the start of XMP XML portion
450 xmp_data += nameSpace.size() + 1;
451 xmp_size -= nameSpace.size() + 1;
452 XMPXmlHandler handler;
453
454 // xml parser fails to parse packet header, wrapper. remove them before handing the data to
455 // parser. if there is no packet header, do nothing otherwise go to the position of '<' without
456 // '?' after it.
457 int offset = 0;
458 for (int i = 0; i < xmp_size - 1; ++i) {
459 if (xmp_data[i] == '<') {
460 if (xmp_data[i + 1] != '?') {
461 offset = i;
462 break;
463 }
464 }
465 }
466 xmp_data += offset;
467 xmp_size -= offset;
468
469 // If there is no packet wrapper, do nothing other wise go to the position of last '>' without '?'
470 // before it.
471 offset = 0;
472 for (int i = xmp_size - 1; i >= 1; --i) {
473 if (xmp_data[i] == '>') {
474 if (xmp_data[i - 1] != '?') {
475 offset = xmp_size - (i + 1);
476 break;
477 }
478 }
479 }
480 xmp_size -= offset;
481
482 // remove padding
483 while (xmp_data[xmp_size - 1] != '>' && xmp_size > 1) {
484 xmp_size--;
485 }
486
487 string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
488 MessageHandler msg_handler;
489 unique_ptr<XmlRule> rule(new XmlElementRule);
490 XmlReader reader(&handler, &msg_handler);
491 reader.StartParse(std::move(rule));
492 reader.Parse(str);
493 reader.FinishParse();
494 if (reader.HasErrors()) {
495 // Parse error
496 return false;
497 }
498
499 // Apply default values to any not-present fields, except for Version,
500 // maxContentBoost, and hdrCapacityMax, which are required. Return false if
501 // we encounter a present field that couldn't be parsed, since this
502 // indicates it is invalid (eg. string where there should be a float).
503 bool present = false;
504 if (!handler.getVersion(&metadata->version, &present) || !present) {
505 return false;
506 }
507 if (!handler.getMaxContentBoost(&metadata->maxContentBoost, &present) || !present) {
508 return false;
509 }
510 if (!handler.getHdrCapacityMax(&metadata->hdrCapacityMax, &present) || !present) {
511 return false;
512 }
513 if (!handler.getMinContentBoost(&metadata->minContentBoost, &present)) {
514 if (present) return false;
515 metadata->minContentBoost = 1.0f;
516 }
517 if (!handler.getGamma(&metadata->gamma, &present)) {
518 if (present) return false;
519 metadata->gamma = 1.0f;
520 }
521 if (!handler.getOffsetSdr(&metadata->offsetSdr, &present)) {
522 if (present) return false;
523 metadata->offsetSdr = 1.0f / 64.0f;
524 }
525 if (!handler.getOffsetHdr(&metadata->offsetHdr, &present)) {
526 if (present) return false;
527 metadata->offsetHdr = 1.0f / 64.0f;
528 }
529 if (!handler.getHdrCapacityMin(&metadata->hdrCapacityMin, &present)) {
530 if (present) return false;
531 metadata->hdrCapacityMin = 1.0f;
532 }
533
534 bool base_rendition_is_hdr;
535 if (!handler.getBaseRenditionIsHdr(&base_rendition_is_hdr, &present)) {
536 if (present) return false;
537 base_rendition_is_hdr = false;
538 }
539 if (base_rendition_is_hdr) {
540 ALOGE("Base rendition of HDR is not supported!");
541 return false;
542 }
543
544 return true;
545 }
546
generateXmpForPrimaryImage(int secondary_image_length,ultrahdr_metadata_struct & metadata)547 string generateXmpForPrimaryImage(int secondary_image_length, ultrahdr_metadata_struct& metadata) {
548 const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
549 const vector<string> kLiItem({string("rdf:li"), kConItem});
550
551 std::stringstream ss;
552 photos_editing_formats::image_io::XmlWriter writer(ss);
553 writer.StartWritingElement("x:xmpmeta");
554 writer.WriteXmlns("x", "adobe:ns:meta/");
555 writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
556 writer.StartWritingElement("rdf:RDF");
557 writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
558 writer.StartWritingElement("rdf:Description");
559 writer.WriteXmlns(kContainerPrefix, kContainerUri);
560 writer.WriteXmlns(kItemPrefix, kItemUri);
561 writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
562 writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
563
564 writer.StartWritingElements(kConDirSeq);
565
566 size_t item_depth = writer.StartWritingElement("rdf:li");
567 writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
568 writer.StartWritingElement(kConItem);
569 writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
570 writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
571 writer.FinishWritingElementsToDepth(item_depth);
572
573 writer.StartWritingElement("rdf:li");
574 writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
575 writer.StartWritingElement(kConItem);
576 writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticGainMap);
577 writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
578 writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
579
580 writer.FinishWriting();
581
582 return ss.str();
583 }
584
generateXmpForSecondaryImage(ultrahdr_metadata_struct & metadata)585 string generateXmpForSecondaryImage(ultrahdr_metadata_struct& metadata) {
586 const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
587
588 std::stringstream ss;
589 photos_editing_formats::image_io::XmlWriter writer(ss);
590 writer.StartWritingElement("x:xmpmeta");
591 writer.WriteXmlns("x", "adobe:ns:meta/");
592 writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
593 writer.StartWritingElement("rdf:RDF");
594 writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
595 writer.StartWritingElement("rdf:Description");
596 writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
597 writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
598 writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost));
599 writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost));
600 writer.WriteAttributeNameAndValue(kMapGamma, metadata.gamma);
601 writer.WriteAttributeNameAndValue(kMapOffsetSdr, metadata.offsetSdr);
602 writer.WriteAttributeNameAndValue(kMapOffsetHdr, metadata.offsetHdr);
603 writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, log2(metadata.hdrCapacityMin));
604 writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.hdrCapacityMax));
605 writer.WriteAttributeNameAndValue(kMapBaseRenditionIsHDR, "False");
606 writer.FinishWriting();
607
608 return ss.str();
609 }
610
611 } // namespace ultrahdr
612