chromium/third_party/blink/web_tests/fast/canvas/color-space/transferFromImageBitmap.html

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

// This test examines the color managed ImageBitmapRenderingContext.
//  - We first draw on 2D canvas with different color space and alpha
//    properties.
//  - Next, the canvas is used as a source image to create an ImageBitmap.
//    Different colorSpaceConversion values are tested when creating the
//    intermediate ImageBitmap object.
//  - Next, ImageBitmap is transferred to an ImageBitmapRenderingContext.
//  - Since we cannot read back pixels from ImageBitmapRenderingContext, we
//    draw it to another 2D canvas and read back the pixels.
//  - Finally, we compare the pixels read from the first 2D canvas with those
//    read from the final 2D canvas. We expect them to match with some
//    tolerance.

function testPixels(actualPixels, expectedPixels, testScenario)
{
    var tolerance = 0;

    // When using a linear gamma image bitmap as the intermediate image object
    // for sRGB source canvas, {sRGB-> wide gamut -> sRGB} color conversion in
    // Skia introduces some variance in color values. Hence, in this case we
    // set the tolerance to 8 (6 is the max error observed on Linux for the used
    // pixels). For a thorough investigation, please see:
    // https://fiddle.skia.org/c/584fbc0b778a32a6cc2ea3ecbf2c6e8e
    if (testScenario.colorSpace == "srgb" &&
        testScenario.pixelFormat == "uint8" &&
        ['rec2020', 'display-p3'].indexOf(
            testScenario.colorSpaceConversion) > -1)
        tolerance = 8;

    // Otherwise, if the source color space is wide gamut, we set the tolerance
    // to 0.01.
    else if (testScenario.pixelFormat == "float16")
        tolerance = 0.01;

    assert_array_approx_equals(actualPixels, expectedPixels, tolerance);
}

function generateFillStyle(red, green, blue, alpha) {
    return "rgba(" + red + "," + green + "," + blue + "," + alpha + ")";
}

function generateExpectedResult(testScenario, canvas)
{
    var ctx = canvas.getContext('2d', {colorSpace: testScenario.colorSpace,
                                       pixelFormat: testScenario.pixelFormat});
    ctx.fillStyle = generateFillStyle(155, 27, 27, testScenario.alpha);
    ctx.fillRect(0, 0, 1, 1);
    ctx.fillStyle = generateFillStyle(27, 155, 27, testScenario.alpha);
    ctx.fillRect(1, 0, 1, 1);
    ctx.fillStyle = generateFillStyle(27, 27, 155, testScenario.alpha);
    ctx.fillRect(0, 1, 1, 1);
    ctx.fillStyle = generateFillStyle(27, 27, 27, testScenario.alpha);
    ctx.fillRect(1, 1, 1, 1);
    return ctx.getImageData(0, 0, 2, 2, testScenario.colorSetting).data;
}

function generateTestName(testScenario) {
    var str = "Testing ImageBitmapRenderingContext:" +
              " Source color space: " + testScenario.colorSpace +
              ", pixel format: " + testScenario.pixelFormat +
              ", alpha: " + testScenario.alpha +
              ", intermediate color space: " +
              testScenario.colorSpaceConversion;
    return str;
}

function runTransferFromImageBitmapTest(testScenario) {
    var canvas = document.createElement("canvas");
    canvas.width = 2;
    canvas.height = 2;
    var expectedPixels = generateExpectedResult(testScenario, canvas);

    promise_test(function() {
        var options = {colorSpaceConversion: testScenario.colorSpaceConversion};
        var promise = createImageBitmap(canvas, options);
        return Promise.all([promise]).then(([imagebitmap]) => {
            var dstCanvas = document.createElement("canvas");
            dstCanvas.width = 2;
            dstCanvas.height = 2;
            var dstCtx = dstCanvas.getContext('bitmaprenderer');
            dstCtx.transferFromImageBitmap(imagebitmap);

            // ImageBitmapRenderingContext does not have ImageData, so we draw
            // it on another canvas and read back the data.
            var finalCanvas = document.createElement("canvas");
            finalCanvas.width = 2;
            finalCanvas.height = 2;
            var ctx = finalCanvas.getContext('2d',
                {colorSpace: testScenario.colorSpace,
                 pixelFormat: testScenario.pixelFormat});
            ctx.drawImage(dstCanvas, 0, 0);
            var actualPixels = ctx.getImageData(0, 0, 2, 2,
                testScenario.colorSetting).data;
            testPixels(actualPixels, expectedPixels, testScenario);
        });
       }, generateTestName(testScenario));
}


function runAllTests() {
    var colorSpaces = [
        {colorSpace: 'srgb', pixelFormat: 'uint8'},
        {colorSpace: 'srgb', pixelFormat: 'float16'},
        {colorSpace: 'rec2020', pixelFormat: 'float16'},
        {colorSpace: 'display-p3', pixelFormat: 'float16'}
        ];
    var alphaValues = [0.5, 1];
    var colorSpaceConversions = [
        'none', 'default'];
    var colorSettings = [
        {colorSpace: 'srgb', storageFormat: 'uint8'},
        {colorSpace: 'srgb', storageFormat: 'float32'},
        {colorSpace: 'rec2020', storageFormat: 'float32'},
        {colorSpace: 'display-p3', storageFormat: 'float32'}
        ];

    var testScenarios = [];
    for (var i = 0; i < colorSpaces.length; i++)
        for (var j = 0; j < alphaValues.length; j++)
            for (var k = 0; k < colorSpaceConversions.length; k++) {
                var testScenario = {};
                testScenario.colorSpace = colorSpaces[i].colorSpace;
                testScenario.pixelFormat = colorSpaces[i].pixelFormat;
                testScenario.alpha = alphaValues[j];
                testScenario.colorSpaceConversion = colorSpaceConversions[k];
                testScenario.colorSetting = colorSettings[i];
                testScenarios.push(testScenario);
            }

    for (var i = 0; i < testScenarios.length; i++)
        runTransferFromImageBitmapTest(testScenarios[i]);
}

runAllTests();

</script>