Loading libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +3 −43 Original line number Original line Diff line number Diff line Loading @@ -30,8 +30,9 @@ typedef enum { // Transfer functions as defined for XMP metadata // Transfer functions as defined for XMP metadata typedef enum { typedef enum { JPEGR_TF_HLG = 0, JPEGR_TF_LINEAR = 0, JPEGR_TF_PQ = 1, JPEGR_TF_HLG = 1, JPEGR_TF_PQ = 2, } jpegr_transfer_function; } jpegr_transfer_function; struct jpegr_info_struct { struct jpegr_info_struct { Loading Loading @@ -343,47 +344,6 @@ private: jr_metadata_ptr metadata, jr_metadata_ptr metadata, jr_compressed_ptr dest); jr_compressed_ptr dest); /* * This method generates XMP metadata. * * below is an example of the XMP metadata that this function generates where * secondary_image_length = 1000 * range_scaling_factor = 1.25 * * <x:xmpmeta * xmlns:x="adobe:ns:meta/" * x:xmptk="Adobe XMP Core 5.1.2"> * <rdf:RDF * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> * <rdf:Description * xmlns:GContainer="http://ns.google.com/photos/1.0/container/"> * <GContainer:Version>1</GContainer:Version> * <GContainer:RangeScalingFactor>1.25</GContainer:RangeScalingFactor> * <GContainer:Directory> * <rdf:Seq> * <rdf:li> * <GContainer:Item * Item:Semantic="Primary" * Item:Mime="image/jpeg"/> * </rdf:li> * <rdf:li> * <GContainer:Item * Item:Semantic="RecoveryMap" * Item:Mime="image/jpeg" * Item:Length="1000"/> * </rdf:li> * </rdf:Seq> * </GContainer:Directory> * </rdf:Description> * </rdf:RDF> * </x:xmpmeta> * * @param secondary_image_length length of secondary image * @param metadata JPEG/R metadata to encode as XMP * @return XMP metadata in type of string */ std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata); /* /* * This method will tone map a HDR image to an SDR image. * This method will tone map a HDR image to an SDR image. * * Loading libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h +49 −1 Original line number Original line Diff line number Diff line Loading @@ -17,10 +17,11 @@ #ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #include <sstream> #include <stdint.h> #include <stdint.h> #include <string> #include <cstdio> #include <cstdio> namespace android::recoverymap { namespace android::recoverymap { struct jpegr_metadata; struct jpegr_metadata; Loading @@ -35,6 +36,53 @@ struct jpegr_metadata; */ */ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata); bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata); /* * This method generates XMP metadata. * * below is an example of the XMP metadata that this function generates where * secondary_image_length = 1000 * range_scaling_factor = 1.25 * * <x:xmpmeta * xmlns:x="adobe:ns:meta/" * x:xmptk="Adobe XMP Core 5.1.2"> * <rdf:RDF * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> * <rdf:Description * xmlns:GContainer="http://ns.google.com/photos/1.0/container/" * xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/"> * <GContainer:Version>1</GContainer:Version> * <GContainer:Directory> * <rdf:Seq> * <rdf:li> * <GContainer:Item * Item:Semantic="Primary" * Item:Mime="image/jpeg" * RecoveryMap:Version=”1” * RecoveryMap:RangeScalingFactor=”1.25” * RecoveryMap:TransferFunction=”2”/> * <RecoveryMap:HDR10Metadata * // some attributes * // some elements * </RecoveryMap:HDR10Metadata> * </rdf:li> * <rdf:li> * <GContainer:Item * Item:Semantic="RecoveryMap" * Item:Mime="image/jpeg" * Item:Length="1000"/> * </rdf:li> * </rdf:Seq> * </GContainer:Directory> * </rdf:Description> * </rdf:RDF> * </x:xmpmeta> * * @param secondary_image_length length of secondary image * @param metadata JPEG/R metadata to encode as XMP * @return XMP metadata in type of string */ std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata); } } #endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H No newline at end of file libs/jpegrecoverymap/recoverymap.cpp +6 −65 Original line number Original line Diff line number Diff line Loading @@ -21,7 +21,6 @@ #include <jpegrecoverymap/recoverymaputils.h> #include <jpegrecoverymap/recoverymaputils.h> #include <image_io/jpeg/jpeg_marker.h> #include <image_io/jpeg/jpeg_marker.h> #include <image_io/xml/xml_writer.h> #include <image_io/jpeg/jpeg_info.h> #include <image_io/jpeg/jpeg_info.h> #include <image_io/jpeg/jpeg_scanner.h> #include <image_io/jpeg/jpeg_scanner.h> #include <image_io/jpeg/jpeg_info_builder.h> #include <image_io/jpeg/jpeg_info_builder.h> Loading Loading @@ -64,19 +63,6 @@ static const st2086_metadata kSt2086Metadata = { 1.0f, 1.0f, }; }; /* * Helper function used for generating XMP metadata. * * @param prefix The prefix part of the name. * @param suffix The suffix part of the name. * @return A name of the form "prefix:suffix". */ string Name(const string &prefix, const string &suffix) { std::stringstream ss; ss << prefix << ":" << suffix; return ss.str(); } /* /* * Helper function used for writing data to destination. * Helper function used for writing data to destination. * * Loading Loading @@ -447,6 +433,9 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 ColorTransformFn hdrInvOetf = nullptr; ColorTransformFn hdrInvOetf = nullptr; float hdr_white_nits = 0.0f; float hdr_white_nits = 0.0f; switch (metadata->transferFunction) { switch (metadata->transferFunction) { case JPEGR_TF_LINEAR: hdrInvOetf = identityConversion; break; case JPEGR_TF_HLG: case JPEGR_TF_HLG: hdrInvOetf = hlgInvOetf; hdrInvOetf = hlgInvOetf; hdr_white_nits = kHlgMaxNits; hdr_white_nits = kHlgMaxNits; Loading Loading @@ -544,6 +533,9 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ ColorTransformFn hdrOetf = nullptr; ColorTransformFn hdrOetf = nullptr; switch (metadata->transferFunction) { switch (metadata->transferFunction) { case JPEGR_TF_LINEAR: hdrOetf = identityConversion; break; case JPEGR_TF_HLG: case JPEGR_TF_HLG: hdrOetf = hlgOetf; hdrOetf = hlgOetf; break; break; Loading Loading @@ -680,57 +672,6 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, return NO_ERROR; return NO_ERROR; } } string RecoveryMap::generateXmp(int secondary_image_length, jpegr_metadata& metadata) { const string kContainerPrefix = "GContainer"; const string kContainerUri = "http://ns.google.com/photos/1.0/container/"; const string kItemPrefix = "Item"; const string kRecoveryMap = "RecoveryMap"; const string kDirectory = "Directory"; const string kImageJpeg = "image/jpeg"; const string kItem = "Item"; const string kLength = "Length"; const string kMime = "Mime"; const string kPrimary = "Primary"; const string kSemantic = "Semantic"; const string kVersion = "Version"; const string kConDir = Name(kContainerPrefix, kDirectory); const string kContainerItem = Name(kContainerPrefix, kItem); const string kItemLength = Name(kItemPrefix, kLength); const string kItemMime = Name(kItemPrefix, kMime); const string kItemSemantic = Name(kItemPrefix, kSemantic); const vector<string> kConDirSeq({kConDir, string("rdf:Seq")}); const vector<string> kLiItem({string("rdf:li"), kContainerItem}); 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(kContainerPrefix, kContainerUri); writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), metadata.version); writer.WriteElementAndContent(Name(kContainerPrefix, "rangeScalingFactor"), metadata.rangeScalingFactor); // TODO: determine structure for hdr10 metadata // TODO: write rest of metadata writer.StartWritingElements(kConDirSeq); size_t item_depth = writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary); writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); writer.FinishWritingElementsToDepth(item_depth); writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap); writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length); writer.FinishWriting(); return ss.str(); } status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image, status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr dest) { jr_uncompressed_ptr dest) { if (uncompressed_p010_image == nullptr || dest == nullptr) { if (uncompressed_p010_image == nullptr || dest == nullptr) { Loading libs/jpegrecoverymap/recoverymaputils.cpp +135 −3 Original line number Original line Diff line number Diff line Loading @@ -17,19 +17,29 @@ #include <jpegrecoverymap/recoverymaputils.h> #include <jpegrecoverymap/recoverymaputils.h> #include <jpegrecoverymap/recoverymap.h> #include <jpegrecoverymap/recoverymap.h> #include <image_io/xml/xml_reader.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/base/message_handler.h> #include <image_io/xml/xml_element_rules.h> #include <image_io/xml/xml_element_rules.h> #include <image_io/xml/xml_handler.h> #include <image_io/xml/xml_handler.h> #include <image_io/xml/xml_rule.h> #include <image_io/xml/xml_rule.h> #include <string> #include <sstream> using namespace photos_editing_formats::image_io; using namespace photos_editing_formats::image_io; using namespace std; using namespace std; namespace android::recoverymap { namespace android::recoverymap { /* * Helper function used for generating XMP metadata. * * @param prefix The prefix part of the name. * @param suffix The suffix part of the name. * @return A name of the form "prefix:suffix". */ string Name(const string &prefix, const string &suffix) { std::stringstream ss; ss << prefix << ":" << suffix; return ss.str(); } // Extremely simple XML Handler - just searches for interesting elements // Extremely simple XML Handler - just searches for interesting elements class XMPXmlHandler : public XmlHandler { class XMPXmlHandler : public XmlHandler { Loading Loading @@ -104,6 +114,36 @@ private: const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor"; const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor"; const string kContainerPrefix = "GContainer"; const string kContainerUri = "http://ns.google.com/photos/1.0/container/"; const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/"; const string kItemPrefix = "Item"; const string kRecoveryMap = "RecoveryMap"; const string kDirectory = "Directory"; const string kImageJpeg = "image/jpeg"; const string kItem = "Item"; const string kLength = "Length"; const string kMime = "Mime"; const string kPrimary = "Primary"; const string kSemantic = "Semantic"; const string kVersion = "Version"; const string kHdr10Metadata = "HDR10Metadata"; const string kSt2086Metadata = "ST2086Metadata"; const string kSt2086Coordinate = "ST2086Coordinate"; const string kSt2086CoordinateX = "ST2086CoordinateX"; const string kSt2086CoordinateY = "ST2086CoordinateY"; const string kSt2086Primary = "ST2086Primary"; const int kSt2086PrimaryRed = 0; const int kSt2086PrimaryGreen = 1; const int kSt2086PrimaryBlue = 2; const int kSt2086PrimaryWhite = 3; const int kGContainerVersion = 1; const string kConDir = Name(kContainerPrefix, kDirectory); const string kContainerItem = Name(kContainerPrefix, kItem); const string kItemLength = Name(kItemPrefix, kLength); const string kItemMime = Name(kItemPrefix, kMime); const string kItemSemantic = Name(kItemPrefix, kSemantic); bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) { bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) { string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; Loading Loading @@ -150,4 +190,96 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta return true; return true; } } string generateXmp(int secondary_image_length, jpegr_metadata& metadata) { const vector<string> kConDirSeq({kConDir, string("rdf:Seq")}); const vector<string> kLiItem({string("rdf:li"), kContainerItem}); 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(kContainerPrefix, kContainerUri); writer.WriteXmlns(kRecoveryMap, kRecoveryMapUri); writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), kGContainerVersion); writer.StartWritingElements(kConDirSeq); size_t item_depth = writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary); writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kVersion), metadata.version); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "RangeScalingFactor"), metadata.rangeScalingFactor); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "TransferFunction"), metadata.transferFunction); if (metadata.transferFunction == JPEGR_TF_PQ) { writer.StartWritingElement(Name(kRecoveryMap, kHdr10Metadata)); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "HDR10MaxFALL"), metadata.hdr10Metadata.maxFALL); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "HDR10MaxCLL"), metadata.hdr10Metadata.maxCLL); writer.StartWritingElement(Name(kRecoveryMap, kSt2086Metadata)); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "ST2086MaxLuminance"), metadata.hdr10Metadata.st2086Metadata.maxLuminance); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "ST2086MinLuminance"), metadata.hdr10Metadata.st2086Metadata.minLuminance); // red writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate)); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryRed); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateX), metadata.hdr10Metadata.st2086Metadata.redPrimary.x); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateY), metadata.hdr10Metadata.st2086Metadata.redPrimary.y); writer.FinishWritingElement(); // green writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate)); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryGreen); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateX), metadata.hdr10Metadata.st2086Metadata.greenPrimary.x); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateY), metadata.hdr10Metadata.st2086Metadata.greenPrimary.y); writer.FinishWritingElement(); // blue writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate)); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryBlue); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateX), metadata.hdr10Metadata.st2086Metadata.bluePrimary.x); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateY), metadata.hdr10Metadata.st2086Metadata.bluePrimary.y); writer.FinishWritingElement(); // white writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate)); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryWhite); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateX), metadata.hdr10Metadata.st2086Metadata.whitePoint.x); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateY), metadata.hdr10Metadata.st2086Metadata.whitePoint.y); writer.FinishWritingElement(); } writer.FinishWritingElementsToDepth(item_depth); writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap); writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length); writer.FinishWriting(); return ss.str(); } } // namespace android::recoverymap } // namespace android::recoverymap No newline at end of file libs/jpegrecoverymap/tests/recoverymap_test.cpp +14 −0 Original line number Original line Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ */ #include <jpegrecoverymap/recoverymap.h> #include <jpegrecoverymap/recoverymap.h> #include <jpegrecoverymap/recoverymaputils.h> #include <fcntl.h> #include <fcntl.h> #include <fstream> #include <fstream> #include <gtest/gtest.h> #include <gtest/gtest.h> Loading Loading @@ -94,6 +95,19 @@ TEST_F(RecoveryMapTest, build) { recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false); recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false); } } TEST_F(RecoveryMapTest, writeXmpThenRead) { jpegr_metadata metadata_expected; metadata_expected.transferFunction = JPEGR_TF_HLG; metadata_expected.rangeScalingFactor = 1.25; int length_expected = 1000; std::string xmp = generateXmp(1000, metadata_expected); jpegr_metadata metadata_read; EXPECT_TRUE(getMetadataFromXMP(reinterpret_cast<uint8_t*>(xmp[0]), xmp.size(), &metadata_read)); ASSERT_EQ(metadata_expected.transferFunction, metadata_read.transferFunction); ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor); } TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) { TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) { int ret; int ret; Loading Loading
libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +3 −43 Original line number Original line Diff line number Diff line Loading @@ -30,8 +30,9 @@ typedef enum { // Transfer functions as defined for XMP metadata // Transfer functions as defined for XMP metadata typedef enum { typedef enum { JPEGR_TF_HLG = 0, JPEGR_TF_LINEAR = 0, JPEGR_TF_PQ = 1, JPEGR_TF_HLG = 1, JPEGR_TF_PQ = 2, } jpegr_transfer_function; } jpegr_transfer_function; struct jpegr_info_struct { struct jpegr_info_struct { Loading Loading @@ -343,47 +344,6 @@ private: jr_metadata_ptr metadata, jr_metadata_ptr metadata, jr_compressed_ptr dest); jr_compressed_ptr dest); /* * This method generates XMP metadata. * * below is an example of the XMP metadata that this function generates where * secondary_image_length = 1000 * range_scaling_factor = 1.25 * * <x:xmpmeta * xmlns:x="adobe:ns:meta/" * x:xmptk="Adobe XMP Core 5.1.2"> * <rdf:RDF * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> * <rdf:Description * xmlns:GContainer="http://ns.google.com/photos/1.0/container/"> * <GContainer:Version>1</GContainer:Version> * <GContainer:RangeScalingFactor>1.25</GContainer:RangeScalingFactor> * <GContainer:Directory> * <rdf:Seq> * <rdf:li> * <GContainer:Item * Item:Semantic="Primary" * Item:Mime="image/jpeg"/> * </rdf:li> * <rdf:li> * <GContainer:Item * Item:Semantic="RecoveryMap" * Item:Mime="image/jpeg" * Item:Length="1000"/> * </rdf:li> * </rdf:Seq> * </GContainer:Directory> * </rdf:Description> * </rdf:RDF> * </x:xmpmeta> * * @param secondary_image_length length of secondary image * @param metadata JPEG/R metadata to encode as XMP * @return XMP metadata in type of string */ std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata); /* /* * This method will tone map a HDR image to an SDR image. * This method will tone map a HDR image to an SDR image. * * Loading
libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h +49 −1 Original line number Original line Diff line number Diff line Loading @@ -17,10 +17,11 @@ #ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #include <sstream> #include <stdint.h> #include <stdint.h> #include <string> #include <cstdio> #include <cstdio> namespace android::recoverymap { namespace android::recoverymap { struct jpegr_metadata; struct jpegr_metadata; Loading @@ -35,6 +36,53 @@ struct jpegr_metadata; */ */ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata); bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata); /* * This method generates XMP metadata. * * below is an example of the XMP metadata that this function generates where * secondary_image_length = 1000 * range_scaling_factor = 1.25 * * <x:xmpmeta * xmlns:x="adobe:ns:meta/" * x:xmptk="Adobe XMP Core 5.1.2"> * <rdf:RDF * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> * <rdf:Description * xmlns:GContainer="http://ns.google.com/photos/1.0/container/" * xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/"> * <GContainer:Version>1</GContainer:Version> * <GContainer:Directory> * <rdf:Seq> * <rdf:li> * <GContainer:Item * Item:Semantic="Primary" * Item:Mime="image/jpeg" * RecoveryMap:Version=”1” * RecoveryMap:RangeScalingFactor=”1.25” * RecoveryMap:TransferFunction=”2”/> * <RecoveryMap:HDR10Metadata * // some attributes * // some elements * </RecoveryMap:HDR10Metadata> * </rdf:li> * <rdf:li> * <GContainer:Item * Item:Semantic="RecoveryMap" * Item:Mime="image/jpeg" * Item:Length="1000"/> * </rdf:li> * </rdf:Seq> * </GContainer:Directory> * </rdf:Description> * </rdf:RDF> * </x:xmpmeta> * * @param secondary_image_length length of secondary image * @param metadata JPEG/R metadata to encode as XMP * @return XMP metadata in type of string */ std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata); } } #endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H No newline at end of file
libs/jpegrecoverymap/recoverymap.cpp +6 −65 Original line number Original line Diff line number Diff line Loading @@ -21,7 +21,6 @@ #include <jpegrecoverymap/recoverymaputils.h> #include <jpegrecoverymap/recoverymaputils.h> #include <image_io/jpeg/jpeg_marker.h> #include <image_io/jpeg/jpeg_marker.h> #include <image_io/xml/xml_writer.h> #include <image_io/jpeg/jpeg_info.h> #include <image_io/jpeg/jpeg_info.h> #include <image_io/jpeg/jpeg_scanner.h> #include <image_io/jpeg/jpeg_scanner.h> #include <image_io/jpeg/jpeg_info_builder.h> #include <image_io/jpeg/jpeg_info_builder.h> Loading Loading @@ -64,19 +63,6 @@ static const st2086_metadata kSt2086Metadata = { 1.0f, 1.0f, }; }; /* * Helper function used for generating XMP metadata. * * @param prefix The prefix part of the name. * @param suffix The suffix part of the name. * @return A name of the form "prefix:suffix". */ string Name(const string &prefix, const string &suffix) { std::stringstream ss; ss << prefix << ":" << suffix; return ss.str(); } /* /* * Helper function used for writing data to destination. * Helper function used for writing data to destination. * * Loading Loading @@ -447,6 +433,9 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 ColorTransformFn hdrInvOetf = nullptr; ColorTransformFn hdrInvOetf = nullptr; float hdr_white_nits = 0.0f; float hdr_white_nits = 0.0f; switch (metadata->transferFunction) { switch (metadata->transferFunction) { case JPEGR_TF_LINEAR: hdrInvOetf = identityConversion; break; case JPEGR_TF_HLG: case JPEGR_TF_HLG: hdrInvOetf = hlgInvOetf; hdrInvOetf = hlgInvOetf; hdr_white_nits = kHlgMaxNits; hdr_white_nits = kHlgMaxNits; Loading Loading @@ -544,6 +533,9 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ ColorTransformFn hdrOetf = nullptr; ColorTransformFn hdrOetf = nullptr; switch (metadata->transferFunction) { switch (metadata->transferFunction) { case JPEGR_TF_LINEAR: hdrOetf = identityConversion; break; case JPEGR_TF_HLG: case JPEGR_TF_HLG: hdrOetf = hlgOetf; hdrOetf = hlgOetf; break; break; Loading Loading @@ -680,57 +672,6 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, return NO_ERROR; return NO_ERROR; } } string RecoveryMap::generateXmp(int secondary_image_length, jpegr_metadata& metadata) { const string kContainerPrefix = "GContainer"; const string kContainerUri = "http://ns.google.com/photos/1.0/container/"; const string kItemPrefix = "Item"; const string kRecoveryMap = "RecoveryMap"; const string kDirectory = "Directory"; const string kImageJpeg = "image/jpeg"; const string kItem = "Item"; const string kLength = "Length"; const string kMime = "Mime"; const string kPrimary = "Primary"; const string kSemantic = "Semantic"; const string kVersion = "Version"; const string kConDir = Name(kContainerPrefix, kDirectory); const string kContainerItem = Name(kContainerPrefix, kItem); const string kItemLength = Name(kItemPrefix, kLength); const string kItemMime = Name(kItemPrefix, kMime); const string kItemSemantic = Name(kItemPrefix, kSemantic); const vector<string> kConDirSeq({kConDir, string("rdf:Seq")}); const vector<string> kLiItem({string("rdf:li"), kContainerItem}); 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(kContainerPrefix, kContainerUri); writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), metadata.version); writer.WriteElementAndContent(Name(kContainerPrefix, "rangeScalingFactor"), metadata.rangeScalingFactor); // TODO: determine structure for hdr10 metadata // TODO: write rest of metadata writer.StartWritingElements(kConDirSeq); size_t item_depth = writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary); writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); writer.FinishWritingElementsToDepth(item_depth); writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap); writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length); writer.FinishWriting(); return ss.str(); } status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image, status_t RecoveryMap::toneMap(jr_uncompressed_ptr uncompressed_p010_image, jr_uncompressed_ptr dest) { jr_uncompressed_ptr dest) { if (uncompressed_p010_image == nullptr || dest == nullptr) { if (uncompressed_p010_image == nullptr || dest == nullptr) { Loading
libs/jpegrecoverymap/recoverymaputils.cpp +135 −3 Original line number Original line Diff line number Diff line Loading @@ -17,19 +17,29 @@ #include <jpegrecoverymap/recoverymaputils.h> #include <jpegrecoverymap/recoverymaputils.h> #include <jpegrecoverymap/recoverymap.h> #include <jpegrecoverymap/recoverymap.h> #include <image_io/xml/xml_reader.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/base/message_handler.h> #include <image_io/xml/xml_element_rules.h> #include <image_io/xml/xml_element_rules.h> #include <image_io/xml/xml_handler.h> #include <image_io/xml/xml_handler.h> #include <image_io/xml/xml_rule.h> #include <image_io/xml/xml_rule.h> #include <string> #include <sstream> using namespace photos_editing_formats::image_io; using namespace photos_editing_formats::image_io; using namespace std; using namespace std; namespace android::recoverymap { namespace android::recoverymap { /* * Helper function used for generating XMP metadata. * * @param prefix The prefix part of the name. * @param suffix The suffix part of the name. * @return A name of the form "prefix:suffix". */ string Name(const string &prefix, const string &suffix) { std::stringstream ss; ss << prefix << ":" << suffix; return ss.str(); } // Extremely simple XML Handler - just searches for interesting elements // Extremely simple XML Handler - just searches for interesting elements class XMPXmlHandler : public XmlHandler { class XMPXmlHandler : public XmlHandler { Loading Loading @@ -104,6 +114,36 @@ private: const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor"; const string XMPXmlHandler::rangeScalingFactorName = "GContainer:rangeScalingFactor"; const string kContainerPrefix = "GContainer"; const string kContainerUri = "http://ns.google.com/photos/1.0/container/"; const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/"; const string kItemPrefix = "Item"; const string kRecoveryMap = "RecoveryMap"; const string kDirectory = "Directory"; const string kImageJpeg = "image/jpeg"; const string kItem = "Item"; const string kLength = "Length"; const string kMime = "Mime"; const string kPrimary = "Primary"; const string kSemantic = "Semantic"; const string kVersion = "Version"; const string kHdr10Metadata = "HDR10Metadata"; const string kSt2086Metadata = "ST2086Metadata"; const string kSt2086Coordinate = "ST2086Coordinate"; const string kSt2086CoordinateX = "ST2086CoordinateX"; const string kSt2086CoordinateY = "ST2086CoordinateY"; const string kSt2086Primary = "ST2086Primary"; const int kSt2086PrimaryRed = 0; const int kSt2086PrimaryGreen = 1; const int kSt2086PrimaryBlue = 2; const int kSt2086PrimaryWhite = 3; const int kGContainerVersion = 1; const string kConDir = Name(kContainerPrefix, kDirectory); const string kContainerItem = Name(kContainerPrefix, kItem); const string kItemLength = Name(kItemPrefix, kLength); const string kItemMime = Name(kItemPrefix, kMime); const string kItemSemantic = Name(kItemPrefix, kSemantic); bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) { bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) { string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; Loading Loading @@ -150,4 +190,96 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta return true; return true; } } string generateXmp(int secondary_image_length, jpegr_metadata& metadata) { const vector<string> kConDirSeq({kConDir, string("rdf:Seq")}); const vector<string> kLiItem({string("rdf:li"), kContainerItem}); 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(kContainerPrefix, kContainerUri); writer.WriteXmlns(kRecoveryMap, kRecoveryMapUri); writer.WriteElementAndContent(Name(kContainerPrefix, kVersion), kGContainerVersion); writer.StartWritingElements(kConDirSeq); size_t item_depth = writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kPrimary); writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kVersion), metadata.version); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "RangeScalingFactor"), metadata.rangeScalingFactor); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "TransferFunction"), metadata.transferFunction); if (metadata.transferFunction == JPEGR_TF_PQ) { writer.StartWritingElement(Name(kRecoveryMap, kHdr10Metadata)); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "HDR10MaxFALL"), metadata.hdr10Metadata.maxFALL); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "HDR10MaxCLL"), metadata.hdr10Metadata.maxCLL); writer.StartWritingElement(Name(kRecoveryMap, kSt2086Metadata)); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "ST2086MaxLuminance"), metadata.hdr10Metadata.st2086Metadata.maxLuminance); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, "ST2086MinLuminance"), metadata.hdr10Metadata.st2086Metadata.minLuminance); // red writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate)); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryRed); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateX), metadata.hdr10Metadata.st2086Metadata.redPrimary.x); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateY), metadata.hdr10Metadata.st2086Metadata.redPrimary.y); writer.FinishWritingElement(); // green writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate)); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryGreen); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateX), metadata.hdr10Metadata.st2086Metadata.greenPrimary.x); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateY), metadata.hdr10Metadata.st2086Metadata.greenPrimary.y); writer.FinishWritingElement(); // blue writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate)); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryBlue); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateX), metadata.hdr10Metadata.st2086Metadata.bluePrimary.x); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateY), metadata.hdr10Metadata.st2086Metadata.bluePrimary.y); writer.FinishWritingElement(); // white writer.StartWritingElement(Name(kRecoveryMap, kSt2086Coordinate)); writer.WriteAttributeNameAndValue(Name(kRecoveryMap, kSt2086Primary), kSt2086PrimaryWhite); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateX), metadata.hdr10Metadata.st2086Metadata.whitePoint.x); writer.WriteAttributeNameAndValue( Name(kRecoveryMap, kSt2086CoordinateY), metadata.hdr10Metadata.st2086Metadata.whitePoint.y); writer.FinishWritingElement(); } writer.FinishWritingElementsToDepth(item_depth); writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kRecoveryMap); writer.WriteAttributeNameAndValue(kItemMime, kImageJpeg); writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length); writer.FinishWriting(); return ss.str(); } } // namespace android::recoverymap } // namespace android::recoverymap No newline at end of file
libs/jpegrecoverymap/tests/recoverymap_test.cpp +14 −0 Original line number Original line Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ */ #include <jpegrecoverymap/recoverymap.h> #include <jpegrecoverymap/recoverymap.h> #include <jpegrecoverymap/recoverymaputils.h> #include <fcntl.h> #include <fcntl.h> #include <fstream> #include <fstream> #include <gtest/gtest.h> #include <gtest/gtest.h> Loading Loading @@ -94,6 +95,19 @@ TEST_F(RecoveryMapTest, build) { recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false); recovery_map.decodeJPEGR(nullptr, nullptr, nullptr, false); } } TEST_F(RecoveryMapTest, writeXmpThenRead) { jpegr_metadata metadata_expected; metadata_expected.transferFunction = JPEGR_TF_HLG; metadata_expected.rangeScalingFactor = 1.25; int length_expected = 1000; std::string xmp = generateXmp(1000, metadata_expected); jpegr_metadata metadata_read; EXPECT_TRUE(getMetadataFromXMP(reinterpret_cast<uint8_t*>(xmp[0]), xmp.size(), &metadata_read)); ASSERT_EQ(metadata_expected.transferFunction, metadata_read.transferFunction); ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor); } TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) { TEST_F(RecoveryMapTest, encodeFromP010ThenDecode) { int ret; int ret; Loading