• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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