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