chromium/third_party/blink/web_tests/fast/canvas/color-space/canvas-draw-high-bit-depth-images.html

<!DOCTYPE HTML>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script>

// This test is mostly a conformance test which ensures the new 16 bit PNG
// decoding capability of Blink PNGImageDecoder conforms to the regular 8 bit
// PNG decoding code path. Since we cannot read the decoded image pixels
// directly in JS, proper unit tests are added to CanvasRenderingContext2DTest
// to verify correct color transformation while drawing.
// In this layout test reference pixels are generated by drawing 8 bit sRGB on
// wide gamut canvas, and test pixels are generated by drawing 16 bit PNG in
// different color spaces on the wide gamut canvas. e-sRGB is intentionally
// omitted since it results in < 0, > 1 color component values and does not
// match the source pixels. e-sRGB 16 bit PNGs are still covered in unit tests.

var testImagesPath = "../../../images/resources/png-16bit/";

// Source of pixel comparison error in these tests:
// - color conversion from different color profiles (sRGB, Adobe RGB, Display P3,
//   ProPhoto and Rec 2020) to target canvas color space (sRGB, e-sRGB, linear RGB,
//   linear P3, linear Rec 2020).
// - e-sRGB source image is not tested here since the color profile attached to
//   png image by Photoshop is different from what is expected by Skia. Hence,
//   the pixel results do not match. Still, e-sRGB sources are thoroughly tested
//   in unit tests.
// - comparing the result of drawing 8 bit and 16 bit PNGs with each other.
var defaultColorConversionTolerance = 8;
var wideGamutColorConversionTolerance = 0.05;

function runTest(testScenario) {
    var _8bitImageSrc = testImagesPath + testScenario._8bitImagePath;
    var _16bitImageSrc = testImagesPath + testScenario._16bitImagePath;
    var tolerance = defaultColorConversionTolerance;
    if (testScenario.canvasColorParams.pixelFormat == 'float16')
        tolerance = wideGamutColorConversionTolerance;

    var _8bitImage = new Image();
    var _16bitImage = new Image();
    var t_image = async_test(testScenarioToString(testScenario));
    _8bitImage.onload = t_image.step_func(function() {
        _16bitImage.onload = function() {
            var refCanvas = document.createElement("canvas");
            refCanvas.width = refCanvas.height = 40000;
            var refCtx = refCanvas.getContext(
                '2d', testScenario.canvasColorParams);
            refCtx.drawImage(_8bitImage, 0, 0);
            var refImageData = refCtx.getImageData(0, 0, 2, 2);
            var refPixels = refImageData.data;

            var testCanvas = document.createElement("canvas");
            testCanvas.width = testCanvas.height = 40000;
            var testCtx = testCanvas.getContext(
                '2d', testScenario.canvasColorParams);
            testCtx.drawImage(_16bitImage, 0, 0);
            var testImageData = testCtx.getImageData(0, 0, 2, 2);
            var testPixels = testImageData.data;

            assert_array_approx_equals(refPixels, testPixels, tolerance);

            t_image.done();
            };
        _16bitImage.src = _16bitImageSrc;
    });
    _8bitImage.src = _8bitImageSrc;
}

function runAllTests() {
    var pngColorSpaces = [
        "_sRGB",
        "_AdobeRGB",
        "_DisplayP3",
        "_ProPhoto",
        "_Rec2020",
    ];
    var pngTransparencyStatus = [
        "_opaque",
        "_transparent",
    ];
    var pngInterlaceStatus = [
        "",  // non-interlaced
        "_interlaced",
    ];


    var _8bitPngPrefix = "2x2_8bit";
    var _16bitPngPrefix = "2x2_16bit";

    var canvasColorParams = [
        // When canvas is not half float backed, 16 bit PNGs will be decoded as
        // 8 bits PNGs (libpng strips 16 bits to 8 bits).
        {colorSpace: 'srgb', pixelFormat: 'uint8'},

        // For half float backed canvases, 16 bit PNGs are decoded as 16 bits,
        // and then color converted to the canvas color space and color types.
        // We expect the new 16 bit decoding capability to be compatible with
        // the regular 8 bits decoding code path.
        {colorSpace: 'srgb', pixelFormat: 'float16'},
        {colorSpace: 'rec2020', pixelFormat: 'float16'},
        {colorSpace: 'display-p3', pixelFormat: 'float16'}
    ];

    var testScenarioSet = [];
    var id = 1;
    for (var i = 0; i < canvasColorParams.length; i++) {
        for (var j = 0; j < pngColorSpaces.length; j++) {
            for (var k = 0; k < pngTransparencyStatus.length; k++) {
                for (var m = 0; m < pngInterlaceStatus.length; m++) {
                    var testScenario = {};
                    testScenario.canvasColorParams = canvasColorParams[i];
                    testScenario._8bitImagePath = _8bitPngPrefix +
                        pngColorSpaces[j] + pngTransparencyStatus[k] + ".png";
                    testScenario._16bitImagePath = _16bitPngPrefix +
                        pngInterlaceStatus[m] + pngColorSpaces[j] +
                        pngTransparencyStatus[k] + ".png";
                    testScenarioSet.push(testScenario);
                }
            }
        }
    }

    for (var i = 0; i < testScenarioSet.length; i++)
        runTest(testScenarioSet[i]);
}

function testScenarioToString(testScenario) {
    var str = "Canvas color params: " +
        testScenario.canvasColorParams.colorSpace + ", " +
        testScenario.canvasColorParams.pixelFormat + ". Testing " +
        testScenario._8bitImagePath + " vs " + testScenario._16bitImagePath;
    return str;
}

runAllTests();

</script>