jpegr library: add multi-picture format support

test: jpegr_test
bug: b/264715926
Change-Id: I1bd299ddc0435e54f7c8554b92b3cd6eb2f6eb2b
(cherry picked from commit 283c64b190e74e63064524f6eb598654cbe98f4c)
diff --git a/libs/jpegrecoverymap/jpegrutils.cpp b/libs/jpegrecoverymap/jpegrutils.cpp
index 49526c8..38b78ad 100644
--- a/libs/jpegrecoverymap/jpegrutils.cpp
+++ b/libs/jpegrecoverymap/jpegrutils.cpp
@@ -15,18 +15,19 @@
  */
 
 #include <jpegrecoverymap/jpegrutils.h>
+#include <utils/Log.h>
 #include <image_io/xml/xml_reader.h>
 #include <image_io/xml/xml_writer.h>
 #include <image_io/base/message_handler.h>
 #include <image_io/xml/xml_element_rules.h>
 #include <image_io/xml/xml_handler.h>
 #include <image_io/xml/xml_rule.h>
+#include <cmath>
 
 using namespace photos_editing_formats::image_io;
 using namespace std;
 
 namespace android::jpegrecoverymap {
-
 /*
  * Helper function used for generating XMP metadata.
  *
@@ -34,12 +35,62 @@
  * @param suffix The suffix part of the name.
  * @return A name of the form "prefix:suffix".
  */
-string Name(const string &prefix, const string &suffix) {
+static inline string Name(const string &prefix, const string &suffix) {
   std::stringstream ss;
   ss << prefix << ":" << suffix;
   return ss.str();
 }
 
+DataStruct::DataStruct(int s) {
+    data = malloc(s);
+    length = s;
+    memset(data, 0, s);
+    writePos = 0;
+}
+
+DataStruct::~DataStruct() {
+    if (data != nullptr) {
+        free(data);
+    }
+}
+
+void* DataStruct::getData() {
+    return data;
+}
+
+int DataStruct::getLength() {
+    return length;
+}
+
+int DataStruct::getBytesWritten() {
+    return writePos;
+}
+
+bool DataStruct::write8(uint8_t value) {
+    uint8_t v = value;
+    return write(&v, 1);
+}
+
+bool DataStruct::write16(uint16_t value) {
+    uint16_t v = value;
+    return write(&v, 2);
+}
+bool DataStruct::write32(uint32_t value) {
+    uint32_t v = value;
+    return write(&v, 4);
+}
+
+bool DataStruct::write(const void* src, int size) {
+    if (writePos + size > length) {
+        ALOGE("Writing out of boundary: write position: %d, size: %d, capacity: %d",
+                writePos, size, length);
+        return false;
+    }
+    memcpy((uint8_t*) data + writePos, src, size);
+    writePos += size;
+    return true;
+}
+
 /*
  * Helper function used for writing data to destination.
  */
@@ -58,7 +109,7 @@
 public:
 
     XMPXmlHandler() : XmlHandler() {
-        gContainerItemState = NotStrarted;
+        state = NotStrarted;
     }
 
     enum ParseState {
@@ -70,11 +121,11 @@
     virtual DataMatchResult StartElement(const XmlTokenContext& context) {
         string val;
         if (context.BuildTokenValue(&val)) {
-            if (!val.compare(gContainerItemName)) {
-                gContainerItemState = Started;
+            if (!val.compare(containerName)) {
+                state = Started;
             } else {
-                if (gContainerItemState != Done) {
-                    gContainerItemState = NotStrarted;
+                if (state != Done) {
+                    state = NotStrarted;
                 }
             }
         }
@@ -82,8 +133,8 @@
     }
 
     virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
-        if (gContainerItemState == Started) {
-            gContainerItemState = Done;
+        if (state == Started) {
+            state = Done;
             lastAttributeName = "";
         }
         return context.GetResult();
@@ -91,7 +142,7 @@
 
     virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
         string val;
-        if (gContainerItemState == Started) {
+        if (state == Started) {
             if (context.BuildTokenValue(&val)) {
                 if (!val.compare(maxContentBoostAttrName)) {
                     lastAttributeName = maxContentBoostAttrName;
@@ -107,7 +158,7 @@
 
     virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
         string val;
-        if (gContainerItemState == Started) {
+        if (state == Started) {
             if (context.BuildTokenValue(&val, true)) {
                 if (!lastAttributeName.compare(maxContentBoostAttrName)) {
                     maxContentBoostStr = val;
@@ -120,11 +171,11 @@
     }
 
     bool getMaxContentBoost(float* max_content_boost) {
-        if (gContainerItemState == Done) {
+        if (state == Done) {
             stringstream ss(maxContentBoostStr);
             float val;
             if (ss >> val) {
-                *max_content_boost = val;
+                *max_content_boost = exp2(val);
                 return true;
             } else {
                 return false;
@@ -135,11 +186,11 @@
     }
 
     bool getMinContentBoost(float* min_content_boost) {
-        if (gContainerItemState == Done) {
+        if (state == Done) {
             stringstream ss(minContentBoostStr);
             float val;
             if (ss >> val) {
-                *min_content_boost = val;
+                *min_content_boost = exp2(val);
                 return true;
             } else {
                 return false;
@@ -150,13 +201,13 @@
     }
 
 private:
-    static const string gContainerItemName;
+    static const string containerName;
     static const string maxContentBoostAttrName;
     string              maxContentBoostStr;
     static const string minContentBoostAttrName;
     string              minContentBoostStr;
     string              lastAttributeName;
-    ParseState          gContainerItemState;
+    ParseState          state;
 };
 
 // GContainer XMP constants - URI and namespace prefix
@@ -168,8 +219,7 @@
 const string kConItem                 = Name(kContainerPrefix, "Item");
 
 // GContainer XMP constants - names for XMP handlers
-const string XMPXmlHandler::gContainerItemName = kConItem;
-
+const string XMPXmlHandler::containerName = "rdf:Description";
 // Item XMP constants - URI and namespace prefix
 const string kItemUri        = "http://ns.google.com/photos/1.0/container/item/";
 const string kItemPrefix     = "Item";
@@ -185,17 +235,23 @@
 const string kMimeImageJpeg       = "image/jpeg";
 
 // RecoveryMap XMP constants - URI and namespace prefix
-const string kRecoveryMapUri      = "http://ns.google.com/photos/1.0/recoverymap/";
-const string kRecoveryMapPrefix   = "RecoveryMap";
+const string kRecoveryMapUri      = "http://ns.adobe.com/hdr-gain-map/1.0/";
+const string kRecoveryMapPrefix   = "hdrgm";
 
 // RecoveryMap XMP constants - element and attribute names
-const string kMapMaxContentBoost  = Name(kRecoveryMapPrefix, "MaxContentBoost");
-const string kMapMinContentBoost  = Name(kRecoveryMapPrefix, "MinContentBoost");
 const string kMapVersion          = Name(kRecoveryMapPrefix, "Version");
+const string kMapGainMapMin       = Name(kRecoveryMapPrefix, "GainMapMin");
+const string kMapGainMapMax       = Name(kRecoveryMapPrefix, "GainMapMax");
+const string kMapGamma            = Name(kRecoveryMapPrefix, "Gamma");
+const string kMapOffsetSdr        = Name(kRecoveryMapPrefix, "OffsetSDR");
+const string kMapOffsetHdr        = Name(kRecoveryMapPrefix, "OffsetHDR");
+const string kMapHDRCapacityMin   = Name(kRecoveryMapPrefix, "HDRCapacityMin");
+const string kMapHDRCapacityMax   = Name(kRecoveryMapPrefix, "HDRCapacityMax");
+const string kMapBaseRendition    = Name(kRecoveryMapPrefix, "BaseRendition");
 
 // RecoveryMap XMP constants - names for XMP handlers
-const string XMPXmlHandler::maxContentBoostAttrName = kMapMaxContentBoost;
-const string XMPXmlHandler::minContentBoostAttrName = kMapMinContentBoost;
+const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
+const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
 
 bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
     string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -243,7 +299,7 @@
     return true;
 }
 
-string generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
+string generateXmpForPrimaryImage(int secondary_image_length) {
   const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
   const vector<string> kLiItem({string("rdf:li"), kConItem});
 
@@ -257,7 +313,6 @@
   writer.StartWritingElement("rdf:Description");
   writer.WriteXmlns(kContainerPrefix, kContainerUri);
   writer.WriteXmlns(kItemPrefix, kItemUri);
-  writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri);
   writer.StartWritingElements(kConDirSeq);
   size_t item_depth = writer.StartWritingElements(kLiItem);
   writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
@@ -267,9 +322,33 @@
   writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticRecoveryMap);
   writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
   writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
+  writer.FinishWriting();
+
+  return ss.str();
+}
+
+string generateXmpForSecondaryImage(jpegr_metadata& metadata) {
+  const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
+  const vector<string> kLiItem({string("rdf:li"), kConItem});
+
+  std::stringstream ss;
+  photos_editing_formats::image_io::XmlWriter writer(ss);
+  writer.StartWritingElement("x:xmpmeta");
+  writer.WriteXmlns("x", "adobe:ns:meta/");
+  writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
+  writer.StartWritingElement("rdf:RDF");
+  writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+  writer.StartWritingElement("rdf:Description");
+  writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri);
   writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
-  writer.WriteAttributeNameAndValue(kMapMaxContentBoost, metadata.maxContentBoost);
-  writer.WriteAttributeNameAndValue(kMapMinContentBoost, metadata.minContentBoost);
+  writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost));
+  writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost));
+  writer.WriteAttributeNameAndValue(kMapGamma, "1");
+  writer.WriteAttributeNameAndValue(kMapOffsetSdr, "0");
+  writer.WriteAttributeNameAndValue(kMapOffsetHdr, "0");
+  writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, "0");
+  writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, "2.3");
+  writer.WriteAttributeNameAndValue(kMapBaseRendition, "SDR");
   writer.FinishWriting();
 
   return ss.str();