diff --git a/lib/include/ultrahdr/gainmapmetadata.h b/lib/include/ultrahdr/gainmapmetadata.h index 5ba62004..af38f215 100644 --- a/lib/include/ultrahdr/gainmapmetadata.h +++ b/lib/include/ultrahdr/gainmapmetadata.h @@ -23,6 +23,8 @@ #include namespace ultrahdr { +constexpr uint8_t kIsMultiChannelMask = (1u << 7); +constexpr uint8_t kUseBaseColorSpaceMask = (1u << 6); // Gain map metadata, for tone mapping between SDR and HDR. // This is the fraction version of {@code uhdr_gainmap_metadata_ext_t}. diff --git a/lib/src/gainmapmetadata.cpp b/lib/src/gainmapmetadata.cpp index 5eda4882..c99a2e0e 100644 --- a/lib/src/gainmapmetadata.cpp +++ b/lib/src/gainmapmetadata.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#include +#include + #include "ultrahdr/gainmapmath.h" #include "ultrahdr/gainmapmetadata.h" @@ -21,6 +24,11 @@ namespace ultrahdr { void streamWriteU8(std::vector &data, uint8_t value) { data.push_back(value); } +void streamWriteU16(std::vector &data, uint16_t value) { + data.push_back((value >> 8) & 0xff); + data.push_back(value & 0xff); +} + void streamWriteU32(std::vector &data, uint32_t value) { data.push_back((value >> 24) & 0xff); data.push_back((value >> 16) & 0xff); @@ -42,6 +50,21 @@ uhdr_error_info_t streamReadU8(const std::vector &data, uint8_t &value, return g_no_error; } +uhdr_error_info_t streamReadU16(const std::vector &data, uint16_t &value, size_t &pos) { + if (pos + 1 >= data.size()) { + uhdr_error_info_t status; + status.error_code = UHDR_CODEC_MEM_ERROR; + status.has_detail = 1; + snprintf(status.detail, sizeof status.detail, + "attempting to read 2 bytes from position %d when the buffer size is %d", (int)pos, + (int)data.size()); + return status; + } + value = (data[pos] << 8 | data[pos + 1]); + pos += 2; + return g_no_error; +} + uhdr_error_info_t streamReadU32(const std::vector &data, uint32_t &value, size_t &pos) { if (pos + 3 >= data.size()) { uhdr_error_info_t status; @@ -68,8 +91,9 @@ uhdr_error_info_t uhdr_gainmap_metadata_frac::encodeGainmapMetadata( return status; } - const uint8_t version = 0; - streamWriteU8(out_data, version); + const uint16_t min_version = 0, writer_version = 0; + streamWriteU16(out_data, min_version); + streamWriteU16(out_data, writer_version); uint8_t flags = 0u; // Always write three channels for now for simplicity. @@ -100,10 +124,10 @@ uhdr_error_info_t uhdr_gainmap_metadata_frac::encodeGainmapMetadata( const uint8_t channelCount = allChannelsIdentical ? 1u : 3u; if (channelCount == 3) { - flags |= 1; + flags |= kIsMultiChannelMask; } if (in_metadata->useBaseColorSpace) { - flags |= 2; + flags |= kUseBaseColorSpaceMask; } if (in_metadata->backwardDirection) { flags |= 4; @@ -171,20 +195,22 @@ uhdr_error_info_t uhdr_gainmap_metadata_frac::decodeGainmapMetadata( } size_t pos = 0; - uint8_t version = 0xff; - UHDR_ERR_CHECK(streamReadU8(in_data, version, pos)) - if (version != 0) { + uint16_t min_version = 0xffff; + uint16_t writer_version = 0xffff; + UHDR_ERR_CHECK(streamReadU16(in_data, min_version, pos)) + if (min_version != 0) { uhdr_error_info_t status; status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE; status.has_detail = 1; - snprintf(status.detail, sizeof status.detail, "received unexpected version %d, expected 0", - version); + snprintf(status.detail, sizeof status.detail, "received unexpected minimum version %d, expected 0", + min_version); return status; } + UHDR_ERR_CHECK(streamReadU16(in_data, writer_version, pos)) uint8_t flags = 0xff; UHDR_ERR_CHECK(streamReadU8(in_data, flags, pos)) - uint8_t channelCount = (flags & 1) * 2 + 1; + uint8_t channelCount = (flags & kIsMultiChannelMask) * 2 + 1; if (!(channelCount == 1 || channelCount == 3)) { uhdr_error_info_t status; status.error_code = UHDR_CODEC_UNSUPPORTED_FEATURE; @@ -193,7 +219,7 @@ uhdr_error_info_t uhdr_gainmap_metadata_frac::decodeGainmapMetadata( "received unexpected channel count %d, expects one of {1, 3}", channelCount); return status; } - out_metadata->useBaseColorSpace = (flags & 2) != 0; + out_metadata->useBaseColorSpace = (flags & kUseBaseColorSpaceMask) != 0; out_metadata->backwardDirection = (flags & 4) != 0; const bool useCommonDenominator = (flags & 8) != 0; @@ -283,16 +309,18 @@ uhdr_error_info_t uhdr_gainmap_metadata_frac::gainmapMetadataFractionToFloat( UHDR_CHECK_NON_ZERO(from->baseOffsetD[i], "baseOffset denominator"); UHDR_CHECK_NON_ZERO(from->alternateOffsetD[i], "alternateOffset denominator"); } + to->version = kJpegrVersion; - to->max_content_boost = (float)from->gainMapMaxN[0] / from->gainMapMaxD[0]; - to->min_content_boost = (float)from->gainMapMinN[0] / from->gainMapMinD[0]; + to->max_content_boost = exp2((float)from->gainMapMaxN[0] / from->gainMapMaxD[0]); + to->min_content_boost = exp2((float)from->gainMapMinN[0] / from->gainMapMinD[0]); + to->gamma = (float)from->gainMapGammaN[0] / from->gainMapGammaD[0]; // BaseRenditionIsHDR is false to->offset_sdr = (float)from->baseOffsetN[0] / from->baseOffsetD[0]; to->offset_hdr = (float)from->alternateOffsetN[0] / from->alternateOffsetD[0]; - to->hdr_capacity_max = (float)from->alternateHdrHeadroomN / from->alternateHdrHeadroomD; - to->hdr_capacity_min = (float)from->baseHdrHeadroomN / from->baseHdrHeadroomD; + to->hdr_capacity_max = exp2((float)from->alternateHdrHeadroomN / from->alternateHdrHeadroomD); + to->hdr_capacity_min = exp2((float)from->baseHdrHeadroomN / from->baseHdrHeadroomD); return g_no_error; } @@ -322,12 +350,12 @@ uhdr_error_info_t uhdr_gainmap_metadata_frac::gainmapMetadataFloatToFraction( return status; \ } - CONVERT_FLT_TO_UNSIGNED_FRACTION(from->max_content_boost, &to->gainMapMaxN[0], + CONVERT_FLT_TO_UNSIGNED_FRACTION(log2(from->max_content_boost), &to->gainMapMaxN[0], &to->gainMapMaxD[0]) to->gainMapMaxN[2] = to->gainMapMaxN[1] = to->gainMapMaxN[0]; to->gainMapMaxD[2] = to->gainMapMaxD[1] = to->gainMapMaxD[0]; - CONVERT_FLT_TO_UNSIGNED_FRACTION(from->min_content_boost, &to->gainMapMinN[0], + CONVERT_FLT_TO_UNSIGNED_FRACTION(log2(from->min_content_boost), &to->gainMapMinN[0], &to->gainMapMinD[0]); to->gainMapMinN[2] = to->gainMapMinN[1] = to->gainMapMinN[0]; to->gainMapMinD[2] = to->gainMapMinD[1] = to->gainMapMinD[0]; @@ -345,10 +373,10 @@ uhdr_error_info_t uhdr_gainmap_metadata_frac::gainmapMetadataFloatToFraction( to->alternateOffsetN[2] = to->alternateOffsetN[1] = to->alternateOffsetN[0]; to->alternateOffsetD[2] = to->alternateOffsetD[1] = to->alternateOffsetD[0]; - CONVERT_FLT_TO_UNSIGNED_FRACTION(from->hdr_capacity_min, &to->baseHdrHeadroomN, + CONVERT_FLT_TO_UNSIGNED_FRACTION(log2(from->hdr_capacity_min), &to->baseHdrHeadroomN, &to->baseHdrHeadroomD); - CONVERT_FLT_TO_UNSIGNED_FRACTION(from->hdr_capacity_max, &to->alternateHdrHeadroomN, + CONVERT_FLT_TO_UNSIGNED_FRACTION(log2(from->hdr_capacity_max), &to->alternateHdrHeadroomN, &to->alternateHdrHeadroomD); return g_no_error; diff --git a/tests/gainmapmetadata_test.cpp b/tests/gainmapmetadata_test.cpp index 88e9a7c1..9b080cc0 100644 --- a/tests/gainmapmetadata_test.cpp +++ b/tests/gainmapmetadata_test.cpp @@ -65,12 +65,12 @@ TEST_F(GainMapMetadataTest, encodeMetadataThenDecode) { uhdr_gainmap_metadata_frac::gainmapMetadataFractionToFloat(&decodedMetadata, &decodedUHdrMetadata); - EXPECT_EQ(expected.max_content_boost, decodedUHdrMetadata.max_content_boost); - EXPECT_EQ(expected.min_content_boost, decodedUHdrMetadata.min_content_boost); - EXPECT_EQ(expected.gamma, decodedUHdrMetadata.gamma); - EXPECT_EQ(expected.offset_sdr, decodedUHdrMetadata.offset_sdr); - EXPECT_EQ(expected.offset_hdr, decodedUHdrMetadata.offset_hdr); - EXPECT_EQ(expected.hdr_capacity_min, decodedUHdrMetadata.hdr_capacity_min); - EXPECT_EQ(expected.hdr_capacity_max, decodedUHdrMetadata.hdr_capacity_max); + EXPECT_FLOAT_EQ(expected.max_content_boost, decodedUHdrMetadata.max_content_boost); + EXPECT_FLOAT_EQ(expected.min_content_boost, decodedUHdrMetadata.min_content_boost); + EXPECT_FLOAT_EQ(expected.gamma, decodedUHdrMetadata.gamma); + EXPECT_FLOAT_EQ(expected.offset_sdr, decodedUHdrMetadata.offset_sdr); + EXPECT_FLOAT_EQ(expected.offset_hdr, decodedUHdrMetadata.offset_hdr); + EXPECT_FLOAT_EQ(expected.hdr_capacity_min, decodedUHdrMetadata.hdr_capacity_min); + EXPECT_FLOAT_EQ(expected.hdr_capacity_max, decodedUHdrMetadata.hdr_capacity_max); } } // namespace ultrahdr