Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0175906b authored by Nick Deakin's avatar Nick Deakin
Browse files

jpegrecoverymap: Update XMP to match spec.

Remove TransferFunction and PQ metadata from XMP, since they are no
longer needed. Update RangeScalingFactor to MaxContentBoost. Also update
GContainer prefix to Container and correct Item to be its own prefix, in
order to properly conform to GContainer.

In order to still provide a decode flow, default to HLG.

Bug: 264715926
Test: tests pass
Change-Id: I6a94a74666381637c4a7ad301de05cf562c53265
parent 62be1e9c
Loading
Loading
Loading
Loading
+8 −42
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@

namespace android::recoverymap {

// Color gamuts for image data
typedef enum {
  JPEGR_COLORGAMUT_UNSPECIFIED,
  JPEGR_COLORGAMUT_BT709,
@@ -28,7 +29,7 @@ typedef enum {
  JPEGR_COLORGAMUT_BT2100,
} jpegr_color_gamut;

// Transfer functions as defined for XMP metadata
// Transfer functions for image data
typedef enum {
  JPEGR_TF_UNSPECIFIED = -1,
  JPEGR_TF_LINEAR = 0,
@@ -82,45 +83,11 @@ struct jpegr_exif_struct {
    int length;
};

struct chromaticity_coord {
  float x;
  float y;
};


struct st2086_metadata {
  // xy chromaticity coordinate of the red primary of the mastering display
  chromaticity_coord redPrimary;
  // xy chromaticity coordinate of the green primary of the mastering display
  chromaticity_coord greenPrimary;
  // xy chromaticity coordinate of the blue primary of the mastering display
  chromaticity_coord bluePrimary;
  // xy chromaticity coordinate of the white point of the mastering display
  chromaticity_coord whitePoint;
  // Maximum luminance in nits of the mastering display
  uint32_t maxLuminance;
  // Minimum luminance in nits of the mastering display
  float minLuminance;
};

struct hdr10_metadata {
  // Mastering display color volume
  st2086_metadata st2086Metadata;
  // Max frame average light level in nits
  float maxFALL;
  // Max content light level in nits
  float maxCLL;
};

struct jpegr_metadata {
  // JPEG/R version
  uint32_t version;
  // Range scaling factor for the map
  float rangeScalingFactor;
  // The transfer function for decoding the HDR representation of the image
  jpegr_transfer_function transferFunction;
  // HDR10 metadata, only applicable for transferFunction of JPEGR_TF_PQ
  hdr10_metadata hdr10Metadata;
  // Max Content Boost for the map
  float maxContentBoost;
};

typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
@@ -270,14 +237,14 @@ private:
     *
     * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
     * @param uncompressed_p010_image uncompressed HDR image in P010 color format
     * @param hdr_tf transfer function of the HDR image
     * @param dest recovery map; caller responsible for memory of data
     * @param metadata metadata provides the transfer function for the HDR
     *                 image; range_scaling_factor and hdr10 FALL and CLL will
     *                 be updated.
     * @param metadata max_content_boost is filled in
     * @return NO_ERROR if calculation succeeds, error code if error occurs.
     */
    status_t generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                                 jr_uncompressed_ptr uncompressed_p010_image,
                                 jpegr_transfer_function hdr_tf,
                                 jr_metadata_ptr metadata,
                                 jr_uncompressed_ptr dest);

@@ -285,8 +252,7 @@ private:
     * This method is called in the decoding pipeline. It will take the uncompressed (decoded)
     * 8-bit yuv image, the uncompressed (decoded) recovery map, and extracted JPEG/R metadata as
     * input, and calculate the 10-bit recovered image. The recovered output image is the same
     * color gamut as the SDR image, with the transfer function specified in the JPEG/R metadata,
     * and is in RGBA1010102 data format.
     * color gamut as the SDR image, with HLG transfer function, and is in RGBA1010102 data format.
     *
     * @param uncompressed_yuv_420_image uncompressed SDR image in YUV_420 color format
     * @param uncompressed_recovery_map uncompressed recovery map
+14 −19
Original line number Diff line number Diff line
@@ -55,7 +55,7 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta
 *
 * below is an example of the XMP metadata that this function generates where
 * secondary_image_length = 1000
 * range_scaling_factor = 1.25
 * max_content_boost = 8.0
 *
 * <x:xmpmeta
 *   xmlns:x="adobe:ns:meta/"
@@ -63,31 +63,26 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta
 *   <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:Container="http://ns.google.com/photos/1.0/container/"
 *       xmlns:Item="http://ns.google.com/photos/1.0/container/item/"
 *       xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/">
 *       <GContainer:Version>1</GContainer:Version>
 *       <GContainer:Directory>
 *       <Container:Directory>
 *         <rdf:Seq>
 *           <rdf:li>
 *             <GContainer:Item
 *               GContainer:ItemSemantic="Primary"
 *               GContainer:ItemMime="image/jpeg"
 *               RecoveryMap:Version=”1”
 *               RecoveryMap:RangeScalingFactor=”1.25”
 *               RecoveryMap:TransferFunction=”2”/>
 *               <RecoveryMap:HDR10Metadata
 *                 // some attributes
 *                 // some elements
 *               </RecoveryMap:HDR10Metadata>
 *             <Container:Item
 *              Item:Semantic="Primary"
 *              Item:Mime="image/jpeg"
 *              RecoveryMap:Version="1"
 *              RecoveryMap:MaxContentBoost="8.0"/>
 *           </rdf:li>
 *           <rdf:li>
 *             <GContainer:Item
 *               GContainer:ItemSemantic="RecoveryMap"
 *               GContainer:ItemMime="image/jpeg"
 *               GContainer:ItemLength="1000"/>
 *             <Container:Item
 *               Item:Semantic="RecoveryMap"
 *               Item:Mime="image/jpeg"
 *               Item:Length="1000"/>
 *           </rdf:li>
 *         </rdf:Seq>
 *       </GContainer:Directory>
 *       </Container:Directory>
 *     </rdf:Description>
 *   </rdf:RDF>
 * </x:xmpmeta>
+15 −63
Original line number Diff line number Diff line
@@ -72,16 +72,6 @@ static const size_t kJpegBlock = 8;
// JPEG compress quality (0 ~ 100) for recovery map
static const int kMapCompressQuality = 85;

// TODO: fill in st2086 metadata
static const st2086_metadata kSt2086Metadata = {
  {0.0f, 0.0f},
  {0.0f, 0.0f},
  {0.0f, 0.0f},
  {0.0f, 0.0f},
  0,
  1.0f,
};

#define CONFIG_MULTITHREAD 1
int GetCPUCoreCount() {
  int cpuCoreCount = 1;
@@ -133,10 +123,6 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,

  jpegr_metadata metadata;
  metadata.version = kJpegrVersion;
  metadata.transferFunction = hdr_tf;
  if (hdr_tf == JPEGR_TF_PQ) {
    metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
  }

  jpegr_uncompressed_struct uncompressed_yuv_420_image;
  unique_ptr<uint8_t[]> uncompressed_yuv_420_image_data = make_unique<uint8_t[]>(
@@ -146,7 +132,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,

  jpegr_uncompressed_struct map;
  JPEGR_CHECK(generateRecoveryMap(
      &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
      &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
  std::unique_ptr<uint8_t[]> map_data;
  map_data.reset(reinterpret_cast<uint8_t*>(map.data));

@@ -207,14 +193,10 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,

  jpegr_metadata metadata;
  metadata.version = kJpegrVersion;
  metadata.transferFunction = hdr_tf;
  if (hdr_tf == JPEGR_TF_PQ) {
    metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
  }

  jpegr_uncompressed_struct map;
  JPEGR_CHECK(generateRecoveryMap(
      uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
      uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
  std::unique_ptr<uint8_t[]> map_data;
  map_data.reset(reinterpret_cast<uint8_t*>(map.data));

@@ -271,14 +253,10 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,

  jpegr_metadata metadata;
  metadata.version = kJpegrVersion;
  metadata.transferFunction = hdr_tf;
  if (hdr_tf == JPEGR_TF_PQ) {
    metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
  }

  jpegr_uncompressed_struct map;
  JPEGR_CHECK(generateRecoveryMap(
      uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
      uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
  std::unique_ptr<uint8_t[]> map_data;
  map_data.reset(reinterpret_cast<uint8_t*>(map.data));

@@ -328,14 +306,10 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,

  jpegr_metadata metadata;
  metadata.version = kJpegrVersion;
  metadata.transferFunction = hdr_tf;
  if (hdr_tf == JPEGR_TF_PQ) {
    metadata.hdr10Metadata.st2086Metadata = kSt2086Metadata;
  }

  jpegr_uncompressed_struct map;
  JPEGR_CHECK(generateRecoveryMap(
      &uncompressed_yuv_420_image, uncompressed_p010_image, &metadata, &map));
      &uncompressed_yuv_420_image, uncompressed_p010_image, hdr_tf, &metadata, &map));
  std::unique_ptr<uint8_t[]> map_data;
  map_data.reset(reinterpret_cast<uint8_t*>(map.data));

@@ -437,7 +411,6 @@ status_t RecoveryMap::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recov
    return ERROR_JPEGR_INVALID_NULL_PTR;
  }

  // TODO: should we have ICC data for the map?
  JpegEncoder jpeg_encoder;
  if (!jpeg_encoder.compressImage(uncompressed_recovery_map->data,
                                  uncompressed_recovery_map->width,
@@ -518,6 +491,7 @@ void JobQueue::reset() {

status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
                                          jr_uncompressed_ptr uncompressed_p010_image,
                                          jpegr_transfer_function hdr_tf,
                                          jr_metadata_ptr metadata,
                                          jr_uncompressed_ptr dest) {
  if (uncompressed_yuv_420_image == nullptr
@@ -554,7 +528,7 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4

  ColorTransformFn hdrInvOetf = nullptr;
  float hdr_white_nits = 0.0f;
  switch (metadata->transferFunction) {
  switch (hdr_tf) {
    case JPEGR_TF_LINEAR:
      hdrInvOetf = identityConversion;
      break;
@@ -658,7 +632,7 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4

          size_t pixel_idx = x + y * dest->width;
          reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
              encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->rangeScalingFactor);
              encodeRecovery(sdr_y_nits, hdr_y_nits, metadata->maxContentBoost);
        }
      }
    }
@@ -681,11 +655,7 @@ status_t RecoveryMap::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_4
  workers.clear();
  hdr_y_nits_avg /= image_width * image_height;

  metadata->rangeScalingFactor = hdr_y_nits_max / kSdrWhiteNits;
  if (metadata->transferFunction == JPEGR_TF_PQ) {
    metadata->hdr10Metadata.maxFALL = hdr_y_nits_avg;
    metadata->hdr10Metadata.maxCLL = hdr_y_nits_max;
  }
  metadata->maxContentBoost = hdr_y_nits_max / kSdrWhiteNits;

  // generate map
  jobQueue.reset();
@@ -721,39 +691,21 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_
  dest->width = uncompressed_yuv_420_image->width;
  dest->height = uncompressed_yuv_420_image->height;
  ShepardsIDW idwTable(kMapDimensionScaleFactor);
  RecoveryLUT recoveryLUT(metadata->rangeScalingFactor);
  RecoveryLUT recoveryLUT(metadata->maxContentBoost);

  JobQueue jobQueue;
  std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map,
                                       metadata, dest, &jobQueue, &idwTable,
                                       &recoveryLUT]() -> void {
    const float hdr_ratio = metadata->rangeScalingFactor;
    const float hdr_ratio = metadata->maxContentBoost;
    size_t width = uncompressed_yuv_420_image->width;
    size_t height = uncompressed_yuv_420_image->height;

    ColorTransformFn hdrOetf = nullptr;
    switch (metadata->transferFunction) {
      case JPEGR_TF_LINEAR:
        hdrOetf = identityConversion;
        break;
      case JPEGR_TF_HLG:
#if USE_HLG_OETF_LUT
        hdrOetf = hlgOetfLUT;
#else
        hdrOetf = hlgOetf;
#endif
        break;
      case JPEGR_TF_PQ:
#if USE_PQ_OETF_LUT
        hdrOetf = pqOetfLUT;
    ColorTransformFn hdrOetf = hlgOetfLUT;
#else
        hdrOetf = pqOetf;
    ColorTransformFn hdrOetf = hlgOetf;
#endif
        break;
      default:
        // Should be impossible to hit after input validation.
        hdrOetf = identityConversion;
    }

    size_t rowStart, rowEnd;
    while (jobQueue.dequeueJob(rowStart, rowEnd)) {
@@ -783,7 +735,7 @@ status_t RecoveryMap::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_
#else
          Color rgb_hdr = applyRecovery(rgb_sdr, recovery, hdr_ratio);
#endif
          Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->rangeScalingFactor);
          Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost);
          uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr);

          size_t pixel_idx = x + y * width;
+34 −123
Original line number Diff line number Diff line
@@ -93,10 +93,8 @@ public:
        string val;
        if (gContainerItemState == Started) {
            if (context.BuildTokenValue(&val)) {
                if (!val.compare(rangeScalingFactorAttrName)) {
                    lastAttributeName = rangeScalingFactorAttrName;
                } else if (!val.compare(transferFunctionAttrName)) {
                    lastAttributeName = transferFunctionAttrName;
                if (!val.compare(maxContentBoostAttrName)) {
                    lastAttributeName = maxContentBoostAttrName;
                } else {
                    lastAttributeName = "";
                }
@@ -109,22 +107,20 @@ public:
        string val;
        if (gContainerItemState == Started) {
            if (context.BuildTokenValue(&val, true)) {
                if (!lastAttributeName.compare(rangeScalingFactorAttrName)) {
                    rangeScalingFactorStr = val;
                } else if (!lastAttributeName.compare(transferFunctionAttrName)) {
                    transferFunctionStr = val;
                if (!lastAttributeName.compare(maxContentBoostAttrName)) {
                    maxContentBoostStr = val;
                }
            }
        }
        return context.GetResult();
    }

    bool getRangeScalingFactor(float* scaling_factor) {
    bool getMaxContentBoost(float* max_content_boost) {
        if (gContainerItemState == Done) {
            stringstream ss(rangeScalingFactorStr);
            stringstream ss(maxContentBoostStr);
            float val;
            if (ss >> val) {
                *scaling_factor = val;
                *max_content_boost = val;
                return true;
            } else {
                return false;
@@ -134,84 +130,49 @@ public:
        }
    }

    bool getTransferFunction(jpegr_transfer_function* transfer_function) {
        if (gContainerItemState == Done) {
            stringstream ss(transferFunctionStr);
            int val;
            if (ss >> val) {
                *transfer_function = static_cast<jpegr_transfer_function>(val);
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
        return true;
    }

private:
    static const string gContainerItemName;
    static const string rangeScalingFactorAttrName;
    static const string transferFunctionAttrName;
    string              rangeScalingFactorStr;
    string              transferFunctionStr;
    static const string maxContentBoostAttrName;
    string              maxContentBoostStr;
    string              lastAttributeName;
    ParseState          gContainerItemState;
};

// GContainer XMP constants - URI and namespace prefix
const string kContainerUri        = "http://ns.google.com/photos/1.0/container/";
const string kContainerPrefix     = "GContainer";
const string kContainerPrefix     = "Container";

// GContainer XMP constants - element and attribute names
const string kConDirectory            = Name(kContainerPrefix, "Directory");
const string kConItem                 = Name(kContainerPrefix, "Item");
const string kConItemLength           = Name(kContainerPrefix, "ItemLength");
const string kConItemMime             = Name(kContainerPrefix, "ItemMime");
const string kConItemSemantic         = Name(kContainerPrefix, "ItemSemantic");
const string kConVersion              = Name(kContainerPrefix, "Version");

// GContainer XMP constants - element and attribute values
// GContainer XMP constants - names for XMP handlers
const string XMPXmlHandler::gContainerItemName = kConItem;

// Item XMP constants - URI and namespace prefix
const string kItemUri        = "http://ns.google.com/photos/1.0/container/item/";
const string kItemPrefix     = "Item";

// Item XMP constants - element and attribute names
const string kItemLength           = Name(kItemPrefix, "Length");
const string kItemMime             = Name(kItemPrefix, "Mime");
const string kItemSemantic         = Name(kItemPrefix, "Semantic");

// Item XMP constants - element and attribute values
const string kSemanticPrimary     = "Primary";
const string kSemanticRecoveryMap = "RecoveryMap";
const string kMimeImageJpeg       = "image/jpeg";

const int kGContainerVersion      = 1;

// GContainer XMP constants - names for XMP handlers
const string XMPXmlHandler::gContainerItemName = kConItem;

// RecoveryMap XMP constants - URI and namespace prefix
const string kRecoveryMapUri      = "http://ns.google.com/photos/1.0/recoverymap/";
const string kRecoveryMapPrefix   = "RecoveryMap";

// RecoveryMap XMP constants - element and attribute names
const string kMapRangeScalingFactor = Name(kRecoveryMapPrefix, "RangeScalingFactor");
const string kMapTransferFunction   = Name(kRecoveryMapPrefix, "TransferFunction");
const string kMapMaxContentBoost  = Name(kRecoveryMapPrefix, "MaxContentBoost");
const string kMapVersion          = Name(kRecoveryMapPrefix, "Version");

const string kMapHdr10Metadata      = Name(kRecoveryMapPrefix, "HDR10Metadata");
const string kMapHdr10MaxFall       = Name(kRecoveryMapPrefix, "HDR10MaxFALL");
const string kMapHdr10MaxCll        = Name(kRecoveryMapPrefix, "HDR10MaxCLL");

const string kMapSt2086Metadata     = Name(kRecoveryMapPrefix, "ST2086Metadata");
const string kMapSt2086MaxLum       = Name(kRecoveryMapPrefix, "ST2086MaxLuminance");
const string kMapSt2086MinLum       = Name(kRecoveryMapPrefix, "ST2086MinLuminance");
const string kMapSt2086Primary      = Name(kRecoveryMapPrefix, "ST2086Primary");
const string kMapSt2086Coordinate   = Name(kRecoveryMapPrefix, "ST2086Coordinate");
const string kMapSt2086CoordinateX  = Name(kRecoveryMapPrefix, "ST2086CoordinateX");
const string kMapSt2086CoordinateY  = Name(kRecoveryMapPrefix, "ST2086CoordinateY");

// RecoveryMap XMP constants - element and attribute values
const int kSt2086PrimaryRed       = 0;
const int kSt2086PrimaryGreen     = 1;
const int kSt2086PrimaryBlue      = 2;
const int kSt2086PrimaryWhite     = 3;

// RecoveryMap XMP constants - names for XMP handlers
const string XMPXmlHandler::rangeScalingFactorAttrName = kMapRangeScalingFactor;
const string XMPXmlHandler::transferFunctionAttrName = kMapTransferFunction;
const string XMPXmlHandler::maxContentBoostAttrName = kMapMaxContentBoost;

bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) {
    string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
@@ -248,13 +209,10 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta
        return false;
    }

    if (!handler.getRangeScalingFactor(&metadata->rangeScalingFactor)) {
    if (!handler.getMaxContentBoost(&metadata->maxContentBoost)) {
        return false;
    }

    if (!handler.getTransferFunction(&metadata->transferFunction)) {
        return false;
    }
    return true;
}

@@ -271,66 +229,19 @@ string generateXmp(int secondary_image_length, jpegr_metadata& metadata) {
  writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
  writer.StartWritingElement("rdf:Description");
  writer.WriteXmlns(kContainerPrefix, kContainerUri);
  writer.WriteXmlns(kItemPrefix, kItemUri);
  writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri);
  writer.WriteElementAndContent(kConVersion, kGContainerVersion);
  writer.StartWritingElements(kConDirSeq);
  size_t item_depth = writer.StartWritingElements(kLiItem);
  writer.WriteAttributeNameAndValue(kConItemSemantic, kSemanticPrimary);
  writer.WriteAttributeNameAndValue(kConItemMime, kMimeImageJpeg);
  writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
  writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
  writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
  writer.WriteAttributeNameAndValue(kMapRangeScalingFactor, metadata.rangeScalingFactor);
  writer.WriteAttributeNameAndValue(kMapTransferFunction, metadata.transferFunction);
  if (metadata.transferFunction == JPEGR_TF_PQ) {
    writer.StartWritingElement(kMapHdr10Metadata);
    writer.WriteAttributeNameAndValue(kMapHdr10MaxFall, metadata.hdr10Metadata.maxFALL);
    writer.WriteAttributeNameAndValue(kMapHdr10MaxCll, metadata.hdr10Metadata.maxCLL);
    writer.StartWritingElement(kMapSt2086Metadata);
    writer.WriteAttributeNameAndValue(
        kMapSt2086MaxLum, metadata.hdr10Metadata.st2086Metadata.maxLuminance);
    writer.WriteAttributeNameAndValue(
        kMapSt2086MinLum, metadata.hdr10Metadata.st2086Metadata.minLuminance);

    // red
    writer.StartWritingElement(kMapSt2086Coordinate);
    writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryRed);
    writer.WriteAttributeNameAndValue(
        kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.redPrimary.x);
    writer.WriteAttributeNameAndValue(
        kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.redPrimary.y);
    writer.FinishWritingElement();

    // green
    writer.StartWritingElement(kMapSt2086Coordinate);
    writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryGreen);
    writer.WriteAttributeNameAndValue(
        kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.greenPrimary.x);
    writer.WriteAttributeNameAndValue(
        kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.greenPrimary.y);
    writer.FinishWritingElement();

    // blue
    writer.StartWritingElement(kMapSt2086Coordinate);
    writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryBlue);
    writer.WriteAttributeNameAndValue(
        kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.bluePrimary.x);
    writer.WriteAttributeNameAndValue(
        kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.bluePrimary.y);
    writer.FinishWritingElement();

    // white
    writer.StartWritingElement(kMapSt2086Coordinate);
    writer.WriteAttributeNameAndValue(kMapSt2086Primary, kSt2086PrimaryWhite);
    writer.WriteAttributeNameAndValue(
        kMapSt2086CoordinateX, metadata.hdr10Metadata.st2086Metadata.whitePoint.x);
    writer.WriteAttributeNameAndValue(
        kMapSt2086CoordinateY, metadata.hdr10Metadata.st2086Metadata.whitePoint.y);
    writer.FinishWritingElement();
  }
  writer.WriteAttributeNameAndValue(kMapMaxContentBoost, metadata.maxContentBoost);
  writer.FinishWritingElementsToDepth(item_depth);
  writer.StartWritingElements(kLiItem);
  writer.WriteAttributeNameAndValue(kConItemSemantic, kSemanticRecoveryMap);
  writer.WriteAttributeNameAndValue(kConItemMime, kMimeImageJpeg);
  writer.WriteAttributeNameAndValue(kConItemLength, secondary_image_length);
  writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticRecoveryMap);
  writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
  writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
  writer.FinishWriting();

  return ss.str();
+2 −4
Original line number Diff line number Diff line
@@ -103,8 +103,7 @@ TEST_F(RecoveryMapTest, build) {

TEST_F(RecoveryMapTest, writeXmpThenRead) {
  jpegr_metadata metadata_expected;
  metadata_expected.transferFunction = JPEGR_TF_HLG;
  metadata_expected.rangeScalingFactor = 1.25;
  metadata_expected.maxContentBoost = 1.25;
  int length_expected = 1000;
  const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
  const int nameSpaceLength = nameSpace.size() + 1;  // need to count the null terminator
@@ -120,8 +119,7 @@ TEST_F(RecoveryMapTest, writeXmpThenRead) {

  jpegr_metadata metadata_read;
  EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
  ASSERT_EQ(metadata_expected.transferFunction, metadata_read.transferFunction);
  ASSERT_EQ(metadata_expected.rangeScalingFactor, metadata_read.rangeScalingFactor);
  ASSERT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
}

/* Test Encode API-0 and decode */
Loading