AGTM: Remove JSON prototype
Change GetSerializedAgtmItutT35 to only parse the ITU-T T.35 header, and
nothing else (not the version inside ST 2094-50, etc).
Update tests to reflect this.
Remove the JSON-based prototype AGTM implementation, and instead hook up
the skhdr::Agtm based implementation.
Bug: 395659818
Change-Id: I3a864f11f8d928fbe7e68dbbc4bfcca8086eaad2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7256035
Commit-Queue: ccameron chromium <ccameron@chromium.org>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1558413}
diff --git a/cc/paint/tone_map_util.cc b/cc/paint/tone_map_util.cc
index 0cbeeb45..7d30e9c 100644
--- a/cc/paint/tone_map_util.cc
+++ b/cc/paint/tone_map_util.cc
@@ -5,6 +5,7 @@
#include "cc/paint/tone_map_util.h"
#include <cmath>
+#include <memory>
#include <string_view>
#include <utility>
@@ -15,7 +16,8 @@
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/effects/SkColorMatrix.h"
#include "third_party/skia/include/effects/SkRuntimeEffect.h"
-#include "ui/gfx/hdr_metadata_agtm.h"
+#include "third_party/skia/include/private/SkHdrMetadata.h"
+#include "ui/gfx/hdr_metadata.h"
namespace cc {
@@ -81,107 +83,6 @@
SkNamedTransferFn::kLinear, SkNamedGamut::kRec2020));
}
-// AGTM tone mapping shader.
-static constexpr char kAgtmToneMapSKSL[] =
- "uniform half altr_i_weight;\n"
- "uniform half altr_j_weight;\n"
- "uniform half4 altr_i_mix_rgbx;\n"
- "uniform half4 altr_j_mix_rgbx;\n"
- "uniform half4 altr_i_mix_Mmcx;\n"
- "uniform half4 altr_j_mix_Mmcx;\n"
- "uniform shader altr_i_curve;\n"
- "uniform shader altr_j_curve;\n"
- "uniform half gain_min;\n"
- "uniform half gain_span;\n"
- "uniform half gain_application_offset;\n"
- "uniform half curve_scale;\n"
- "\n"
- "half3 EvalComponentMixing(half3 color, bool j) {\n"
- " half4 rgbx = j ? altr_j_mix_rgbx : altr_i_mix_rgbx;\n"
- " half4 Mmcx = j ? altr_j_mix_Mmcx : altr_i_mix_Mmcx;\n"
- " half M = max(max(color.r, color.g), color.b);\n"
- " half m = min(min(color.r, color.g), color.b);\n"
- " half common = dot(rgbx.rgb, color) +\n"
- " Mmcx[0] * M +\n"
- " Mmcx[1] * m;\n"
- " return Mmcx[2] * color + half3(common);\n"
- "}\n"
- "half EvalGain(half color, bool j) {\n"
- " half2 tc = half2(curve_scale * color + 0.5, 0.5);\n"
- " half y = j ? altr_j_curve.eval(tc).a : altr_i_curve.eval(tc).a;\n"
- " return gain_min + gain_span * y;\n"
- "}\n"
- "half4 main(half4 color) {\n"
- " half3 G = half3(0.0);\n"
- " if (altr_i_weight > 0.0) {\n"
- " half3 mixed = EvalComponentMixing(color.rgb, false);\n"
- " G += altr_i_weight * half3(EvalGain(mixed.r, false),\n"
- " EvalGain(mixed.g, false),\n"
- " EvalGain(mixed.b, false));\n"
- " }\n"
- " if (altr_j_weight > 0.0) {\n"
- " half3 mixed = EvalComponentMixing(color.rgb, true);\n"
- " G += altr_j_weight * half3(EvalGain(mixed.r, true),\n"
- " EvalGain(mixed.g, true),\n"
- " EvalGain(mixed.b, true));\n"
- " }\n"
- " color.rgb *= exp2(G);\n"
- " return color;\n"
- "}\n";
-
-sk_sp<SkColorFilter> GetAgtmFilter(const gfx::HdrMetadataAgtmParsed& params,
- float H_target) {
- auto result = SkRuntimeEffect::MakeForColorFilter(
- SkString(kAgtmToneMapSKSL, sizeof(kAgtmToneMapSKSL) - 1),
- /*options=*/{});
- CHECK(result.effect) << result.errorText.c_str();
-
- SkRuntimeShaderBuilder builder(result.effect);
- builder.uniform("gain_min") = params.gain_min;
- builder.uniform("gain_span") = params.gain_span;
- builder.uniform("gain_application_offset") = params.gain_application_offset;
-
- // Set the alternate representation weightings and parameters.
- size_t altr_i = gfx::HdrMetadataAgtmParsed::kBaselineIndex;
- size_t altr_j = gfx::HdrMetadataAgtmParsed::kBaselineIndex;
- float altr_i_weight = 0.f;
- float altr_j_weight = 0.f;
- params.ComputeAlternateWeights(H_target, altr_i, altr_i_weight, altr_j,
- altr_j_weight);
- builder.uniform("altr_i_weight") = altr_i_weight;
- if (altr_i == gfx::HdrMetadataAgtmParsed::kBaselineIndex) {
- DCHECK_EQ(altr_i_weight, 0.f);
- DCHECK_EQ(altr_j, gfx::HdrMetadataAgtmParsed::kBaselineIndex);
- builder.child("altr_i_curve") = nullptr;
- builder.uniform("altr_i_mix_rgbx") = SkColor4f();
- builder.uniform("altr_i_mix_Mmcx") = SkColor4f();
- } else {
- const auto& altr = params.alternates[altr_i];
- builder.child("altr_i_curve") =
- altr.curve->makeRawShader(SkSamplingOptions(SkFilterMode::kLinear));
- builder.uniform("altr_i_mix_rgbx") = altr.mix_rgbx;
- builder.uniform("altr_i_mix_Mmcx") = altr.mix_Mmcx;
- builder.uniform("curve_scale") =
- params.baseline_max_component / (altr.curve->width() - 1.f);
- }
- builder.uniform("altr_j_weight") = altr_j_weight;
- if (altr_j == gfx::HdrMetadataAgtmParsed::kBaselineIndex) {
- DCHECK_EQ(altr_j_weight, 0.f);
- builder.child("altr_j_curve") = nullptr;
- builder.uniform("altr_j_mix_rgbx") = SkColor4f();
- builder.uniform("altr_j_mix_Mmcx") = SkColor4f();
- } else {
- const auto& altr = params.alternates[altr_j];
- builder.child("altr_j_curve") =
- altr.curve->makeRawShader(SkSamplingOptions(SkFilterMode::kLinear));
- builder.uniform("altr_j_mix_rgbx") = altr.mix_rgbx;
- builder.uniform("altr_j_mix_Mmcx") = altr.mix_Mmcx;
- }
- auto filter = builder.makeColorFilter();
- CHECK(filter);
- return filter->makeWithWorkingColorSpace(params.gain_application_color_space);
-}
-
} // namespace
bool ToneMapUtil::UseGlobalToneMapFilter(const SkImage* image,
@@ -220,8 +121,11 @@
skcms_TransferFunction trfn;
image->colorSpace()->transferFn(&trfn);
- gfx::HdrMetadataAgtmParsed agtm;
- const bool agtm_parsed = agtm.Parse(metadata.getSerializedAgtm());
+ // Parse AGTM, only if the feature is enabled.
+ std::unique_ptr<skhdr::Agtm> agtm_parsed;
+ if (gfx::HdrMetadataAgtm::IsEnabled()) {
+ agtm_parsed = skhdr::Agtm::Make(metadata.getSerializedAgtm());
+ }
// The remainder of the function will construct `filter` to perform all
// transformations (scaling, OOTF, and tone mapping).
@@ -232,7 +136,7 @@
auto compute_reference_white_luminance = [&]() {
// AGTM metadata gets priority.
if (agtm_parsed) {
- return agtm.hdr_reference_white;
+ return agtm_parsed->getHdrReferenceWhite();
}
// Then NDWL.
if (metadata.ndwl.has_value() && metadata.ndwl->nits > 0.f) {
@@ -257,7 +161,7 @@
// Apply tone mapping.
if (agtm_parsed) {
- auto tone_map_filter = GetAgtmFilter(agtm, target_hdr_headroom);
+ auto tone_map_filter = agtm_parsed->makeColorFilter(target_hdr_headroom);
filter = SkColorFilters::Compose(tone_map_filter, std::move(filter));
} else {
const float content_max_luminance =
diff --git a/media/base/agtm.cc b/media/base/agtm.cc
index 90a1d396..23a3eb0 100644
--- a/media/base/agtm.cc
+++ b/media/base/agtm.cc
@@ -12,19 +12,19 @@
sk_sp<const SkData> GetSerializedAgtmItutT35(
uint8_t t35_country_code,
base::span<const uint8_t> t35_payload) {
- // The minimum header size needed for valid Agtm metadata.
- static constexpr size_t kItuT35HeaderSize = 6;
+ // The size of the ITU-T T.35 header.
+ static constexpr size_t kItuT35HeaderSize = 4;
+ // The minimum body size needed for valid Agtm metadata.
+ static constexpr size_t kAgtmMinSize = 2;
static constexpr uint8_t kItuT35USCountryCode = 0xB5;
// Defined in SMPTE ST 2094-50: Annex D and Annex C (C.2.1).
const bool is_agtm = t35_country_code == kItuT35USCountryCode &&
- t35_payload.size() >= kItuT35HeaderSize &&
+ t35_payload.size() >= kItuT35HeaderSize + kAgtmMinSize &&
// itu_t_t35_us_terminal_provider_code u(16)
t35_payload[0] == 0x00 && t35_payload[1] == 0x90 &&
// itu_t_t35_smpte_terminal_provider_oriented_code u(16)
- t35_payload[2] == 0x00 && t35_payload[3] == 0x01 &&
- // application_version u(8)
- t35_payload[4] == 0x01;
+ t35_payload[2] == 0x00 && t35_payload[3] == 0x01;
if (!is_agtm) {
return nullptr;
}
diff --git a/media/base/agtm_unittest.cc b/media/base/agtm_unittest.cc
index bef9a3c..f5d1f797 100644
--- a/media/base/agtm_unittest.cc
+++ b/media/base/agtm_unittest.cc
@@ -15,8 +15,8 @@
0x00, 0x01, 0x02, 0x03};
const auto agtm = GetSerializedAgtmItutT35(t35_country_code, data);
ASSERT_TRUE(agtm);
- EXPECT_EQ(agtm->size(), 3u);
- const std::vector<uint8_t> expected = {0x01, 0x02, 0x03};
+ EXPECT_EQ(agtm->size(), 5u);
+ const std::vector<uint8_t> expected = {0x01, 0x00, 0x01, 0x02, 0x03};
EXPECT_TRUE(SkData::Equals(
agtm.get(),
SkData::MakeWithCopy(expected.data(), expected.size()).get()));
diff --git a/media/filters/dav1d_video_decoder_unittest.cc b/media/filters/dav1d_video_decoder_unittest.cc
index 83fb350..4b6645a1 100644
--- a/media/filters/dav1d_video_decoder_unittest.cc
+++ b/media/filters/dav1d_video_decoder_unittest.cc
@@ -277,7 +277,7 @@
const auto& frame = output_frames_.front();
ASSERT_TRUE(frame->hdr_metadata().getSerializedAgtm());
- EXPECT_EQ(frame->hdr_metadata().getSerializedAgtm()->size(), 99u);
+ EXPECT_EQ(frame->hdr_metadata().getSerializedAgtm()->size(), 101u);
}
// Decode |i_frame_buffer_| and then a frame with a larger width and verify
diff --git a/media/filters/vpx_video_decoder_unittest.cc b/media/filters/vpx_video_decoder_unittest.cc
index b72b640..c7f6d13 100644
--- a/media/filters/vpx_video_decoder_unittest.cc
+++ b/media/filters/vpx_video_decoder_unittest.cc
@@ -391,7 +391,7 @@
const auto& frame = output_frames_.front();
ASSERT_TRUE(frame->hdr_metadata().getSerializedAgtm());
- EXPECT_EQ(frame->hdr_metadata().getSerializedAgtm()->size(), 533u);
+ EXPECT_EQ(frame->hdr_metadata().getSerializedAgtm()->size(), 535u);
Destroy();
}
@@ -421,7 +421,7 @@
const auto& frame = output_frames_.front();
ASSERT_TRUE(frame->hdr_metadata().getSerializedAgtm());
- EXPECT_EQ(frame->hdr_metadata().getSerializedAgtm()->size(), 533u);
+ EXPECT_EQ(frame->hdr_metadata().getSerializedAgtm()->size(), 535u);
Destroy();
}
diff --git a/media/gpu/av1_decoder_unittest.cc b/media/gpu/av1_decoder_unittest.cc
index c20f2141..c76e6cd 100644
--- a/media/gpu/av1_decoder_unittest.cc
+++ b/media/gpu/av1_decoder_unittest.cc
@@ -1046,7 +1046,7 @@
EXPECT_EQ(results, expected);
const gfx::HDRMetadata hdr_metadata = decoder_->GetHDRMetadata();
ASSERT_TRUE(hdr_metadata.getSerializedAgtm());
- EXPECT_EQ(hdr_metadata.getSerializedAgtm()->size(), 99u);
+ EXPECT_EQ(hdr_metadata.getSerializedAgtm()->size(), 101u);
}
// TODO(hiroh): Add more tests: reference frame tracking, render size change,
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index 252dedd..f369aa98 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -429,8 +429,6 @@
"display_color_spaces.h",
"hdr_metadata.cc",
"hdr_metadata.h",
- "hdr_metadata_agtm.cc",
- "hdr_metadata_agtm.h",
"hdr_static_metadata.cc",
"hdr_static_metadata.h",
"icc_profile.cc",
diff --git a/ui/gfx/hdr_metadata.cc b/ui/gfx/hdr_metadata.cc
index d2b30ea9..db5ceb3 100644
--- a/ui/gfx/hdr_metadata.cc
+++ b/ui/gfx/hdr_metadata.cc
@@ -11,6 +11,7 @@
#include "skia/ext/skcolorspace_primaries.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkData.h"
+#include "ui/gfx/color_space.h"
#include "ui/gfx/skia_span_util.h"
#include "ui/gfx/switches.h"
@@ -33,7 +34,7 @@
size_cmp != std::weak_ordering::equivalent) {
return size_cmp;
}
- return gfx::SkDataToSpan(a) <=> SkDataToSpan(b);
+ return SkDataToSpan(a) <=> SkDataToSpan(b);
}
} // namespace
@@ -121,7 +122,7 @@
HDRMetadata::~HDRMetadata() = default;
// static
-float HDRMetadata::GetContentMaxLuminance(const gfx::HDRMetadata& metadata) {
+float HDRMetadata::GetContentMaxLuminance(const HDRMetadata& metadata) {
if (metadata.cta_861_3.has_value() &&
metadata.cta_861_3->max_content_light_level > 0.f) {
return metadata.cta_861_3->max_content_light_level;
@@ -134,8 +135,34 @@
}
// static
+float HDRMetadata::GetWaylandReferenceLuminance(
+ const ColorSpace& color_space,
+ const HDRMetadata& hdr_metadata) {
+ if (HdrMetadataAgtm::IsEnabled()) {
+ if (auto agtm_parsed =
+ skhdr::Agtm::Make(hdr_metadata.getSerializedAgtm())) {
+ return agtm_parsed->getHdrReferenceWhite();
+ }
+ }
+
+ if (hdr_metadata.ndwl.has_value() && hdr_metadata.ndwl->nits > 0.f) {
+ return hdr_metadata.ndwl->nits;
+ }
+
+ if (color_space.GetTransferID() == ColorSpace::TransferID::PQ ||
+ color_space.GetTransferID() == ColorSpace::TransferID::HLG) {
+ auto sk_color_space = color_space.ToSkColorSpace();
+ skcms_TransferFunction transfer_fn;
+ sk_color_space->transferFn(&transfer_fn);
+ return transfer_fn.a;
+ }
+
+ return ColorSpace::kDefaultSDRWhiteLevel;
+}
+
+// static
HDRMetadata HDRMetadata::PopulateUnspecifiedWithDefaults(
- const gfx::HDRMetadata& hdr_metadata) {
+ const HDRMetadata& hdr_metadata) {
constexpr HdrMetadataSmpteSt2086 kDefaults2086(SkNamedPrimaries::kRec2020,
1000.f, 0.f);
diff --git a/ui/gfx/hdr_metadata.h b/ui/gfx/hdr_metadata.h
index 936d887..78afe3f 100644
--- a/ui/gfx/hdr_metadata.h
+++ b/ui/gfx/hdr_metadata.h
@@ -19,6 +19,8 @@
namespace gfx {
+class ColorSpace;
+
// Content light level info (CLLI) metadata from CTA 861.3.
struct COLOR_SPACE_EXPORT HdrMetadataCta861_3 {
constexpr HdrMetadataCta861_3() = default;
@@ -161,7 +163,11 @@
// - return the CTA 861.3 max content light level metadata, if present
// - return the SMPTE ST 2086 luminance max metadata, if present
// - otherwise return 1,000 nits
- static float GetContentMaxLuminance(const gfx::HDRMetadata& metadata);
+ static float GetContentMaxLuminance(const HDRMetadata& metadata);
+
+ // Compute the reference luminance for use with Wayland color management.
+ static float GetWaylandReferenceLuminance(const ColorSpace& color_space,
+ const HDRMetadata& hdr_metadata);
// Return a copy of `hdr_metadata` with its `smpte_st_2086` fully
// populated. Any unspecified values are set to default values (in particular,
@@ -170,7 +176,7 @@
// `max_frame_average_light_level` values are not changed (they may stay
// zero).
static HDRMetadata PopulateUnspecifiedWithDefaults(
- const gfx::HDRMetadata& hdr_metadata);
+ const HDRMetadata& hdr_metadata);
std::string ToString() const;
diff --git a/ui/gfx/hdr_metadata_agtm.cc b/ui/gfx/hdr_metadata_agtm.cc
deleted file mode 100644
index e3719c38..0000000
--- a/ui/gfx/hdr_metadata_agtm.cc
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/gfx/hdr_metadata_agtm.h"
-
-#include "base/json/json_reader.h"
-#include "base/logging.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkData.h"
-#include "ui/gfx/hdr_metadata.h"
-
-namespace gfx {
-
-namespace {
-
-bool ReadFloat(const base::DictValue* dict, const char* key, float& value) {
- if (!dict) {
- return false;
- }
- if (auto v = dict->FindDouble(key)) {
- value = v.value();
- return true;
- }
- return false;
-}
-
-// Read a piecewise cubic into a sampled SkImage
-bool ReadPiecewiseCubic(const base::DictValue* dict, sk_sp<SkImage>& curve) {
- if (!dict) {
- DVLOG(1) << "Piecewise cubic is missing";
- return false;
- }
- // The parameters for the piecewise cubic segment.
- float m_min = 0.f, m_span = 0.f;
- float x0 = -1.f, y0 = 0.f, m0 = 0.f;
- float x1 = -1.f, y1 = 0.f, m1 = 0.f;
-
- // The output SkBitmap.
- const size_t kSamples = 1024;
- SkBitmap bm;
- if (!bm.tryAllocPixels(SkImageInfo::Make(kSamples, 1, kA16_unorm_SkColorType,
- kUnpremul_SkAlphaType))) {
- DVLOG(1) << "Failed to allocate pixels for gain curve";
- return false;
- }
-
- // The sample x value that is being written into `bm`.
- size_t xi = 0;
-
- // Function to evalue the current piecewise cubic segment.
- auto eval_piecewise_cubic = [&](float x) {
- // If x0 >= x1, then hold the y0 value. This can happen for repeated control
- // points, or initializing values beyond the control points.
- if (x0 >= x1) {
- return y0;
- }
- const float m0p = (m_min + m_span * m0) * (x1 - x0);
- const float m1p = (m_min + m_span * m1) * (x1 - x0);
- const float c3 = (2.0 * y0 + m0p - 2.0 * y1 + m1p);
- const float c2 = (-3.0 * y0 + 3.0 * y1 - 2.0 * m0p - m1p);
- const float c1 = m0p;
- const float c0 = y0;
- const float t = (x - x0) / (x1 - x0);
- return c0 + t * (c1 + t * (c2 + t * c3));
- };
-
- // Function to increment `xi` until it is past `x1`, writing values into `bm`
- // along the way.
- auto write_samples_through_x1 = [&]() {
- while (xi < kSamples && xi / (kSamples - 1.f) < x1) {
- const float y = eval_piecewise_cubic(xi / (kSamples - 1.f));
- *bm.pixmap().writable_addr16(xi, 0) =
- std::round(65535.f * std::clamp(y, 0.f, 1.f));
- xi += 1;
- }
- };
-
- // Read the parameters and control points, writing to `bm` along the way.
- if (!ReadFloat(dict, "m_min", m_min) && !ReadFloat(dict, "m_span", m_span)) {
- DVLOG(1) << "Missing slope minimum or span";
- return false;
- }
- const auto* cp_list = dict->FindList("control_points");
- if (!cp_list || cp_list->size() < 1 || cp_list->size() > 64) {
- DVLOG(1) << "Control point list missing or incorrect size";
- return false;
- }
- bool is_first_control_point = true;
- for (const auto& control_point : *cp_list) {
- x0 = x1;
- y0 = y1;
- m0 = m1;
- const auto* control_point_dict = control_point.GetIfDict();
- if (!ReadFloat(control_point_dict, "x", x1) ||
- !ReadFloat(control_point_dict, "y", y1) ||
- !ReadFloat(control_point_dict, "m", m1)) {
- DVLOG(1) << "Control point missing x, y, or m value";
- return false;
- }
- // Repeat the first control point, so that the piecewise cubic segment
- // function will evaluate to a constant.
- if (is_first_control_point) {
- is_first_control_point = false;
- x0 = x1;
- y0 = y1;
- m0 = m1;
- }
- // Samples must be sorted in increasing order.
- if (x0 > x1) {
- DVLOG(1) << "Sample x values not sorted";
- return false;
- }
- // The function must have C0 continuity.
- if (x0 == x1 && y0 != y1) {
- DVLOG(1) << "Function not continuous";
- return false;
- }
- // Write and increment xi until we need to read past x1.
- write_samples_through_x1();
- }
-
- // Write all samples past the last control point. Repeat the last control
- // point, so that the piecewise cubic segment function will evaluate to a
- // constant.
- x0 = x1;
- y0 = y1;
- m0 = m1;
- write_samples_through_x1();
-
- curve = SkImages::RasterFromPixmapCopy(bm.pixmap());
- return true;
-}
-
-bool ReadAgtmAlternate(const base::DictValue* dict,
- HdrMetadataAgtmParsed::Alternate& altr) {
- if (!ReadFloat(dict, "hdr_headroom", altr.hdr_headroom)) {
- DVLOG(1) << "Alternate representation missing HDR headroom";
- return false;
- }
- if (const auto* v = dict->FindDict("component_mix_params")) {
- if (!ReadFloat(v, "red", altr.mix_rgbx[0]) &&
- !ReadFloat(v, "green", altr.mix_rgbx[1]) &&
- !ReadFloat(v, "blue", altr.mix_rgbx[2]) &&
- !ReadFloat(v, "max", altr.mix_Mmcx[0]) &&
- !ReadFloat(v, "min", altr.mix_Mmcx[1]) &&
- !ReadFloat(v, "component", altr.mix_Mmcx[2])) {
- DVLOG(1) << "Alternate missing component mix params";
- return false;
- }
- } else {
- DVLOG(1) << "Alternate missing component mix dictionary";
- return false;
- }
- if (!ReadPiecewiseCubic(dict->FindDict("piecewise_cubic"), altr.curve)) {
- DVLOG(1) << "Failed to piecewise cubic";
- return false;
- }
- return true;
-}
-
-bool ReadAgtmRoot(const base::Value& value, HdrMetadataAgtmParsed& params) {
- const auto* dict = value.GetIfDict();
- if (!dict) {
- DVLOG(1) << "Agtm root is not dictionary";
- return false;
- }
- if (!ReadFloat(dict, "hdr_reference_white", params.hdr_reference_white) ||
- !ReadFloat(dict, "baseline_hdr_headroom", params.baseline_hdr_headroom) ||
- !ReadFloat(dict, "baseline_max_component",
- params.baseline_max_component) ||
- !ReadFloat(dict, "gain_min", params.gain_min) ||
- !ReadFloat(dict, "gain_span", params.gain_span) ||
- !ReadFloat(dict, "gain_application_offset",
- params.gain_application_offset)) {
- DVLOG(1) << "Required values are absent";
- return false;
- }
- if (auto v = dict->FindInt("gain_application_space_primaries")) {
- params.gain_application_color_space =
- SkColorSpace::MakeCICP(static_cast<SkNamedPrimaries::CicpId>(v.value()),
- SkNamedTransferFn::CicpId::kLinear);
- }
- if (!params.gain_application_color_space) {
- DVLOG(1) << "Invalid or absent gain application space primaries";
- return false;
- }
- const auto* altr_list = dict->FindList("alternates");
- if (altr_list) {
- if (altr_list->size() > 4) {
- DVLOG(1) << "Too many alternates";
- return false;
- }
- for (const auto& altr_value : *altr_list) {
- HdrMetadataAgtmParsed::Alternate altr;
- if (!ReadAgtmAlternate(altr_value.GetIfDict(), altr)) {
- DVLOG(1) << "Failed to read alternate parameters";
- return false;
- }
- params.alternates.push_back(altr);
- }
- }
- return true;
-}
-
-} // namespace
-
-HdrMetadataAgtmParsed::HdrMetadataAgtmParsed() = default;
-HdrMetadataAgtmParsed::~HdrMetadataAgtmParsed() = default;
-
-bool HdrMetadataAgtmParsed::Parse(const SkData* payload) {
- if (!HdrMetadataAgtm::IsEnabled()) {
- return false;
- }
- if (!payload) {
- DVLOG(1) << "Empty AGTM payload";
- return false;
- }
- auto value = base::JSONReader::Read(
- std::string_view(reinterpret_cast<const char*>(payload->data()),
- payload->size()),
- base::JSON_PARSE_CHROMIUM_EXTENSIONS);
- if (!value) {
- DVLOG(1) << "Failed to parse AGTM metadata JSON";
- return false;
- }
- if (!ReadAgtmRoot(value.value(), *this)) {
- return false;
- }
- return true;
-}
-
-void HdrMetadataAgtmParsed::ComputeAlternateWeights(float H_target,
- size_t& i,
- float& w_i,
- size_t& j,
- float& w_j) const {
- const float H_base = baseline_hdr_headroom;
-
- // Let H_i and H_j be the HDR headrooms of the ith and jth representation.
- // Set them to the same value to indicate that the ith representation should
- // be used in full.
- i = j = kBaselineIndex;
- float H_i = H_base;
- float H_j = H_base;
-
- // Special-case the absence of any alternate representations.
- if (alternates.empty()) {
- w_i = w_j = 0.f;
- return;
- }
-
- for (i = 0; i < alternates.size(); ++i) {
- j = kBaselineIndex;
- H_i = alternates[i].hdr_headroom;
- H_j = H_base;
-
- // Mix of i = 0 and potentially baseline
- if (H_target <= H_i) {
- DCHECK_EQ(i, 0u);
- j = kBaselineIndex;
- if (H_base <= H_i) {
- H_j = H_base;
- } else {
- H_j = H_i;
- }
- break;
- }
-
- // Mix of i = N-1 and potentially baseline.
- if (i == alternates.size() - 1) {
- DCHECK_GT(H_target, H_i);
- j = kBaselineIndex;
- if (H_base >= H_i) {
- H_j = H_base;
- } else {
- H_j = H_i;
- }
- break;
- }
-
- // Consider the interval between alternate representations i and i+1 only if
- // H_target is in that interval.
- j = i + 1;
- H_j = alternates[j].hdr_headroom;
- if (H_i <= H_target && H_target <= H_j) {
- // If it's the case that i < target < base < i+1, then we will only mix
- // i and base.
- if (H_i <= H_target && H_target <= H_base && H_base <= H_j) {
- j = kBaselineIndex;
- H_j = H_base;
- }
-
- // If it's the case that i < base < target < i+1, then we will only mix
- // i+1 and base.
- if (H_i <= H_base && H_base <= H_target && H_target <= H_j) {
- i = j;
- H_i = H_j;
- j = kBaselineIndex;
- H_j = H_base;
- break;
- }
-
- // Otherwise, it's the case that i < target < i+1 and base isn't in that
- // interval.
- DCHECK(H_i <= H_target && H_target <= H_j);
- break;
- }
- }
-
- // Compute the weights for the two representations.
- if (H_j == H_i) {
- w_i = 1.f;
- } else {
- w_i = std::min(std::max((H_target - H_j) / (H_i - H_j), 0.f), 1.f);
- }
- w_j = 1.f - w_i;
-
- // Zero out baseline weights.
- if (i == kBaselineIndex) {
- w_i = 0.f;
- }
- if (j == kBaselineIndex) {
- w_j = 0.f;
- }
-}
-
-HdrMetadataAgtmParsed::Alternate::Alternate() = default;
-HdrMetadataAgtmParsed::Alternate::Alternate(const Alternate&) = default;
-HdrMetadataAgtmParsed::Alternate::Alternate(Alternate&&) = default;
-HdrMetadataAgtmParsed::Alternate& HdrMetadataAgtmParsed::Alternate::operator=(
- const Alternate&) = default;
-HdrMetadataAgtmParsed::Alternate& HdrMetadataAgtmParsed::Alternate::operator=(
- Alternate&&) = default;
-HdrMetadataAgtmParsed::Alternate::~Alternate() = default;
-
-} // namespace gfx
diff --git a/ui/gfx/hdr_metadata_agtm.h b/ui/gfx/hdr_metadata_agtm.h
deleted file mode 100644
index 4631f453..0000000
--- a/ui/gfx/hdr_metadata_agtm.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2025 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_GFX_HDR_METADATA_AGTM_H_
-#define UI_GFX_HDR_METADATA_AGTM_H_
-
-#include <vector>
-
-#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkColorSpace.h"
-#include "third_party/skia/include/core/SkImage.h"
-#include "ui/gfx/color_space_export.h"
-
-namespace gfx {
-
-struct COLOR_SPACE_EXPORT HdrMetadataAgtmParsed {
- HdrMetadataAgtmParsed();
- HdrMetadataAgtmParsed(const HdrMetadataAgtmParsed&) = delete;
- HdrMetadataAgtmParsed& operator=(const HdrMetadataAgtmParsed&) = delete;
- ~HdrMetadataAgtmParsed();
-
- bool Parse(const SkData* data);
-
- // Compute the alternate indices and their weights for tone mapping targeting
- // H_target. If w_i==0, then i may be kBaselineIndex, indicating that it does
- // not refer to a valid alternate representation. Likewise for w_j. If i is
- // kBaselineIndex then j is also kBaselineIndex.
- static constexpr size_t kBaselineIndex = -1;
- void ComputeAlternateWeights(float H_target,
- size_t& i,
- float& w_i,
- size_t& j,
- float& w_j) const;
-
- // Luminance of HDR reference white in nits.
- float hdr_reference_white = 0.f;
-
- // Baseline representation HDR headroom.
- float baseline_hdr_headroom = 0.f;
-
- // The maximum component of the baseline image in the gain application space,
- // used to convert from gain application space to [0,1] for evaluation.
- float baseline_max_component = 0.f;
-
- // Gain parameters to convert from [0,1] to real values.
- float gain_min = 0.f;
- float gain_span = 0.f;
-
- // Gain application parameters.
- float gain_application_offset = 0.f;
- sk_sp<SkColorSpace> gain_application_color_space;
-
- // Alternate representations' headrooms and gain curves.
- struct Alternate {
- Alternate();
- Alternate(const Alternate&);
- Alternate(Alternate&&);
- Alternate& operator=(const Alternate&);
- Alternate& operator=(Alternate&&);
- ~Alternate();
-
- // HDR headroom for the alternate representation.
- float hdr_headroom;
-
- // Component mixing function and gain curve parameters.
- SkColor4f mix_rgbx{0.f, 0.f, 0.f, 0.f};
- SkColor4f mix_Mmcx{1.f, 0.f, 0.f, 0.f};
- sk_sp<SkImage> curve;
- };
- std::vector<Alternate> alternates;
-};
-
-} // namespace gfx
-
-#endif // UI_GFX_HDR_METADATA_AGTM_H_
diff --git a/ui/ozone/platform/wayland/host/wayland_wp_color_manager.cc b/ui/ozone/platform/wayland/host/wayland_wp_color_manager.cc
index a8e232d..c47b9b6 100644
--- a/ui/ozone/platform/wayland/host/wayland_wp_color_manager.cc
+++ b/ui/ozone/platform/wayland/host/wayland_wp_color_manager.cc
@@ -8,7 +8,6 @@
#include "base/feature_list.h"
#include "base/logging.h"
-#include "ui/gfx/hdr_metadata_agtm.h"
#include "ui/ozone/platform/wayland/host/wayland_connection.h"
#include "ui/ozone/platform/wayland/host/wayland_output_manager.h"
@@ -101,28 +100,6 @@
}
}
-float GetReferenceLuminance(const gfx::ColorSpace& color_space,
- const gfx::HDRMetadata& hdr_metadata) {
- gfx::HdrMetadataAgtmParsed agtm;
- if (agtm.Parse(hdr_metadata.getSerializedAgtm())) {
- return agtm.hdr_reference_white;
- }
-
- if (hdr_metadata.ndwl.has_value() && hdr_metadata.ndwl->nits > 0.f) {
- return hdr_metadata.ndwl->nits;
- }
-
- if (color_space.GetTransferID() == gfx::ColorSpace::TransferID::PQ ||
- color_space.GetTransferID() == gfx::ColorSpace::TransferID::HLG) {
- auto sk_color_space = color_space.ToSkColorSpace();
- skcms_TransferFunction transfer_fn;
- sk_color_space->transferFn(&transfer_fn);
- return transfer_fn.a;
- }
-
- return gfx::ColorSpace::kDefaultSDRWhiteLevel;
-}
-
} // namespace
// static
@@ -323,7 +300,8 @@
}
}
- const float ref_luma = GetReferenceLuminance(color_space, hdr_metadata);
+ const float ref_luma = gfx::HDRMetadata::GetWaylandReferenceLuminance(
+ color_space, hdr_metadata);
if (IsSupportedFeature(WP_COLOR_MANAGER_V1_FEATURE_SET_LUMINANCES)) {
wp_image_description_creator_params_v1_set_luminances(
creator, 0, gfx::HDRMetadata::GetContentMaxLuminance(hdr_metadata),