commit 65214284cef1071e384bcf2be19b39f40f4ccd2a parent a32458797c54cd1f472d7b7c50a815df532025bf Author: Christopher Cameron <ccameron@chromium.org> Date: Fri, 3 Oct 2025 09:30:32 +0000 Bug 1991147 [wpt PR 55104] - SoftwareImageDecodeCache: Plumb HDR metadata, add WPT tests, a=testonly Automatic update from web-platform-tests SoftwareImageDecodeCache: Plumb HDR metadata, add WPT tests Add WPT tests to verify that tone mapping respects this metadata. * The actual tone mapping algorithm isn't tested, just some endpoints (because the algorithm still isn't standardized). * If the content maximum luminance is less than the target luminance, then no tone mapping is done. * If the content maximum luminance is greater than the target target luminance, then pixel values greater or equal to the content maximum luminance are tone mapped to the target luminance or higher. These tests failed for cc::SoftwareImageDecodeCache because it did not plumb the HDR metadata through correctly. Fix this by propagating: * PaintImage::GetHDRMetadata to CacheEntry * CacheEntry::hdr_metadata() to DecodedDrawImage * DecodedDrawImage::hdr_metadata() to ToneMapUtil::AddGlobalToneMapFilterToPaint at paint time Bug: 428575083 Change-Id: Ib08f4475edfa3cdbfd5d873e02255ee69f607453 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6988087 Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org> Commit-Queue: ccameron chromium <ccameron@chromium.org> Cr-Commit-Position: refs/heads/main@{#1521388} -- wpt-commits: b623f3327dcc9c21bddb314890bf2725df928bc1 wpt-pr: 55104 Diffstat:
5 files changed, 134 insertions(+), 0 deletions(-)
diff --git a/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/clli-mdcv-png.html b/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/clli-mdcv-png.html @@ -0,0 +1,134 @@ +<!DOCTYPE HTML> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +// Render serveral equivalent ISO 21496-1 gainmap images with various values for +// globalHdrHeadroom. +// The test images used by this test have an SDR representation (at headroom 0) +// with pixel values in sRGB of: +// #C08040, #8040C0, #40C080, #FFFFFF +// They have an HDR representation (at headroom 1) with pixel values in sRGB of +// #40C080, #C08040, #8040C0 color(srgb 0x17F/0xFF, 0x17F/0xFF, 0x17F/0xFF) +// The test images vary in: +// * Some have SDR as the base image (encoded as sRGB) and some have HDR as the +// base image (encoded as Rec2100 PQ or Rec2100 HLG). +// * Some use sRGB as the gain application primaries, others use Rec2020 +// The tests that use a Rec2100 PQ or HLG base are allowed a higher amount of +// error because of the brutal quantization error in those spaces. +const tests = [ + { + filename:'pq-clli_none-mdcv_none.png', + desc:'No CLLI or MDCV', + max_linear_value:1000/203, + }, + { + filename:'pq-clli_100-mdcv_p3_5000.png', + desc:'CLLI 100, MDCV 5000', + max_linear_value:203/203, + }, + { + filename:'pq-clli_500-mdcv_none.png', + desc:'CLLI 500', + max_linear_value:500/203, + }, + { + filename:'pq-clli_none-mdcv_rec2020_5000.png', + desc:'MDCV 5000', + max_linear_value:5000/203, + }, +]; +for (const test of tests) { + promise_test(async () => { + filename = test.filename; + const canvas = new OffscreenCanvas(40, 40); + const ctx = canvas.getContext('2d', {colorType:'float16'}); + if (ctx.globalHDRHeadroom === undefined) { + return; + } + + const pixelLinearValues = [ + 100/203, + 500/203, + 1000/203, + 5000/203, + ]; + const globalHDRHeadroomValues = [ + 0, // log2(1) + 1.3045, // log2(500/203) + 4.6224, // log2(5000/203) + Infinity, + ]; + const globalHDRHeadroomLinear = [ + 203/203, + 500/203, + 5000/203, + Infinity, + ]; + + // Draw the image at the specified headrooms. + const image = new Image; + image.src = 'resources/' + filename; + await new Promise(resolve => image.onload = resolve); + for (let hdrHeadroomIndex = 0; hdrHeadroomIndex < 4; ++hdrHeadroomIndex) { + ctx.globalHDRHeadroom = globalHDRHeadroomValues[hdrHeadroomIndex]; + ctx.drawImage(image, 0, 10*hdrHeadroomIndex); + } + + // Read back a pixel in each solid color region. + for (let hdrHeadroomIndex = 0; hdrHeadroomIndex < 4; ++hdrHeadroomIndex) { + for (let pixelIndex = 0; pixelIndex < 4; ++pixelIndex) { + const name = 'globalHDRHeadroom: ' + + globalHDRHeadroomValues[hdrHeadroomIndex] + + ', hdrHeadroomIndex:' + + hdrHeadroomIndex; + const data = ctx.getImageData( + 5 + 10*pixelIndex, 5 + 10*hdrHeadroomIndex, 1, 1, + {colorSpace:'rec2100-linear', pixelFormat:'rgba-float16'}); + if (test.max_linear_value <= globalHDRHeadroomLinear[hdrHeadroomIndex]) { + // If the test image's (max nits)/203 is <= exp2(globalHDRHeadroom), + // then no tone mapping is performed. + if (pixelLinearValues[pixelIndex] <= + globalHDRHeadroomLinear[hdrHeadroomIndex]) { + // Values below the target will be unchanged. + const kEps = 0.15; + assert_approx_equals( + data.data[0], pixelLinearValues[pixelIndex], kEps, + 'No tone mapping'); + } else { + // Values above the target may be clamped. + const kEps = 0.05; + assert_greater_than( + data.data[0], globalHDRHeadroomLinear[hdrHeadroomIndex] - kEps, + 'Tone map to or above target max'); + } + } else { + // If the test image doesn't fit in targeted headroom, then tone + // mapping will happen. + if (pixelLinearValues[pixelIndex] == test.max_linear_value) { + // Pixels equal to the content maximum will map to the target + // maximum. + const kEps = 0.05; + assert_approx_equals( + data.data[0], globalHDRHeadroomLinear[hdrHeadroomIndex], kEps, + 'Tone map to target max'); + } else if (pixelLinearValues[pixelIndex] > test.max_linear_value) { + // Pixels greater than content maximum will map to be greater or + // equal to the target maximum. + const kEps = 0.05; + assert_greater_than( + data.data[0], globalHDRHeadroomLinear[hdrHeadroomIndex] - kEps, + 'Tone map to or above target max'); + } else { + // Pixels less than the content maximum will map to a value less + // than the target maximum. + const kEps = 0.05; + assert_less_than( + data.data[0], globalHDRHeadroomLinear[hdrHeadroomIndex], + 'Tone map below target max'); + } + } + } + } + }, test.desc); +} +</script> diff --git a/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/resources/pq-clli_100-mdcv_p3_5000.png b/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/resources/pq-clli_100-mdcv_p3_5000.png Binary files differ. diff --git a/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/resources/pq-clli_500-mdcv_none.png b/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/resources/pq-clli_500-mdcv_none.png Binary files differ. diff --git a/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/resources/pq-clli_none-mdcv_none.png b/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/resources/pq-clli_none-mdcv_none.png Binary files differ. diff --git a/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/resources/pq-clli_none-mdcv_rec2020_5000.png b/testing/web-platform/tests/html/canvas/element/global-hdr-headroom/resources/pq-clli_none-mdcv_rec2020_5000.png Binary files differ.