diff options
| author | 2022-11-23 17:17:56 +0000 | |
|---|---|---|
| committer | 2022-12-12 21:09:18 +0000 | |
| commit | dc8452b8035de7a0573d19fb7c90d3abf685b6b1 (patch) | |
| tree | dd40dde0c0165f2ece30f85e4f033285ff28b6c1 | |
| parent | 42afe5fa137aacc2fc5d9bdf12052dc455b769ca (diff) | |
Update XMP metadata
Bug: b/252835416
Change-Id: Iced2c5bf6b5e2dfd7139c43fad6b0bca0d1b2d1c
Test: build, print generated xmp
| -rw-r--r-- | libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h | 46 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h | 50 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/recoverymap.cpp | 71 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/recoverymaputils.cpp | 138 | ||||
| -rw-r--r-- | libs/jpegrecoverymap/tests/recoverymap_test.cpp | 14 |
5 files changed, 207 insertions, 112 deletions
diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h index 74f9776be6..fca78a0edb 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymap.h @@ -30,8 +30,9 @@ typedef enum { // Transfer functions as defined for XMP metadata typedef enum { - JPEGR_TF_HLG = 0, - JPEGR_TF_PQ = 1, + JPEGR_TF_LINEAR = 0, + JPEGR_TF_HLG = 1, + JPEGR_TF_PQ = 2, } jpegr_transfer_function; struct jpegr_info_struct { @@ -344,47 +345,6 @@ private: 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. * * @param uncompressed_p010_image (input) uncompressed P010 image diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h index e35f2d72cb..e61d0c4cce 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymaputils.h @@ -17,10 +17,11 @@ #ifndef ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H #define ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H +#include <sstream> #include <stdint.h> +#include <string> #include <cstdio> - namespace android::recoverymap { struct jpegr_metadata; @@ -35,6 +36,53 @@ struct jpegr_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
\ No newline at end of file diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp index c9ac92128f..3d713cef80 100644 --- a/libs/jpegrecoverymap/recoverymap.cpp +++ b/libs/jpegrecoverymap/recoverymap.cpp @@ -21,7 +21,6 @@ #include <jpegrecoverymap/recoverymaputils.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_scanner.h> #include <image_io/jpeg/jpeg_info_builder.h> @@ -65,19 +64,6 @@ static const st2086_metadata kSt2086Metadata = { }; /* - * 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. * * @param destination destination of the data to be written. @@ -447,6 +433,9 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4 ColorTransformFn hdrInvOetf = nullptr; float hdr_white_nits = 0.0f; switch (metadata->transferFunction) { + case JPEGR_TF_LINEAR: + hdrInvOetf = identityConversion; + break; case JPEGR_TF_HLG: hdrInvOetf = hlgInvOetf; hdr_white_nits = kHlgMaxNits; @@ -544,6 +533,9 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ ColorTransformFn hdrOetf = nullptr; switch (metadata->transferFunction) { + case JPEGR_TF_LINEAR: + hdrOetf = identityConversion; + break; case JPEGR_TF_HLG: hdrOetf = hlgOetf; break; @@ -680,57 +672,6 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, 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, jr_uncompressed_ptr dest) { if (uncompressed_p010_image == nullptr || dest == nullptr) { diff --git a/libs/jpegrecoverymap/recoverymaputils.cpp b/libs/jpegrecoverymap/recoverymaputils.cpp index fe46cbad91..537f86fdc0 100644 --- a/libs/jpegrecoverymap/recoverymaputils.cpp +++ b/libs/jpegrecoverymap/recoverymaputils.cpp @@ -17,19 +17,29 @@ #include <jpegrecoverymap/recoverymaputils.h> #include <jpegrecoverymap/recoverymap.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 <string> -#include <sstream> - using namespace photos_editing_formats::image_io; using namespace std; 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 class XMPXmlHandler : public XmlHandler { @@ -104,6 +114,36 @@ private: 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) { string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; @@ -150,4 +190,96 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta 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
\ No newline at end of file diff --git a/libs/jpegrecoverymap/tests/recoverymap_test.cpp b/libs/jpegrecoverymap/tests/recoverymap_test.cpp index 0f96723189..6dea27f0a8 100644 --- a/libs/jpegrecoverymap/tests/recoverymap_test.cpp +++ b/libs/jpegrecoverymap/tests/recoverymap_test.cpp @@ -15,6 +15,7 @@ */ #include <jpegrecoverymap/recoverymap.h> +#include <jpegrecoverymap/recoverymaputils.h> #include <fcntl.h> #include <fstream> #include <gtest/gtest.h> @@ -94,6 +95,19 @@ TEST_F(RecoveryMapTest, build) { 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) { int ret; |