diff options
Diffstat (limited to 'testing/web-platform/tests/css/compositing/canvas-composite-modes.html')
-rw-r--r-- | testing/web-platform/tests/css/compositing/canvas-composite-modes.html | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/compositing/canvas-composite-modes.html b/testing/web-platform/tests/css/compositing/canvas-composite-modes.html new file mode 100644 index 0000000000..c1aae52ce6 --- /dev/null +++ b/testing/web-platform/tests/css/compositing/canvas-composite-modes.html @@ -0,0 +1,157 @@ +<!DOCTYPE HTML> +<meta charset="utf-8"> +<title>Test of <composite-mode> values in canvas globalCompositeOperation</title> +<link rel="help" href="https://html.spec.whatwg.org/multipage/C#compositing"> +<link rel="help" href="https://drafts.fxtf.org/compositing/#canvascompositingandblending"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<canvas id="canvas" width="2" height="2"></canvas> + +<script> + +// Test a small set of sRGB color and alpha values in the 0-255 range. +const VALUES = [ 0, 47, 193, 255 ]; + +const COMPOSITE_OPERATORS = { + // Define a "color" function accepting source and destination colors + // and alphas, and an "alpha" function accepting source and + // destination alphas. + "clear": { + "color": (sa, sc, da, dc) => 0, + "alpha": (sa, da) => 0 + }, + "copy": { + "color": (sa, sc, da, dc) => sa * sc, + "alpha": (sa, da) => sa + }, + "destination": { + // TODO(dbaron): The spec says this should work, but none of + // Chromium, Gecko, or WebKit appear to implement it. + "color": (sa, sc, da, dc) => da * dc, + "alpha": (sa, da) => da + }, + "source-over": { + "color": (sa, sc, da, dc) => sa * sc + da * dc * (1 - sa), + "alpha": (sa, da) => sa + da * (1 - sa) + }, + "destination-over": { + "color": (sa, sc, da, dc) => sa * sc * (1 - da) + da * dc, + "alpha": (sa, da) => sa * (1 - da) + da + }, + "source-in": { + "color": (sa, sc, da, dc) => sa * sc * da, + "alpha": (sa, da) => sa * da + }, + "destination-in": { + "color": (sa, sc, da, dc) => da * dc * sa, + "alpha": (sa, da) => da * sa + }, + "source-out": { + "color": (sa, sc, da, dc) => sa * sc * (1 - da), + "alpha": (sa, da) => sa * (1 - da) + }, + "destination-out": { + "color": (sa, sc, da, dc) => da * dc * (1 - sa), + "alpha": (sa, da) => da * (1 - sa) + }, + "source-atop": { + "color": (sa, sc, da, dc) => sa * sc * da + da * dc * (1 - sa), + "alpha": (sa, da) => sa * da + da * (1 - sa) + }, + "destination-atop": { + "color": (sa, sc, da, dc) => sa * sc * (1 - da) + da * dc * sa, + "alpha": (sa, da) => sa * (1 - da) + da * sa + }, + "xor": { + "color": (sa, sc, da, dc) => sa * sc * (1 - da) + da * dc * (1 - sa), + "alpha": (sa, da) => sa * (1 - da) + da * (1 - sa) + }, + "lighter": { + // TODO(https://github.com/w3c/fxtf-drafts/issues/446): All engines + // actually implement 'lighter' using the formula for 'plus-lighter' + // given below; we should update the spec to match! + "color": (sa, sc, da, dc) => sa * sc + da * dc, + "alpha": (sa, da) => sa + da + }, + "plus-darker": { + // TODO(https://github.com/w3c/fxtf-drafts/issues/447): This formula + // is almost certainly wrong. It doesn't make sense, and the one + // engine that implements this value (WebKit) does something very + // different. + "color": (sa, sc, da, dc) => Math.max(0, 1 - sa * sc + 1 - da * dc), + "alpha": (sa, da) => Math.max(0, 1 - sa + 1 - da) + }, + "plus-lighter": { + "color": (sa, sc, da, dc) => Math.min(1, sa * sc + da * dc), + "alpha": (sa, da) => Math.min(1, sa + da) + } +}; + +let canvas = document.getElementById("canvas"); +let cx = canvas.getContext("2d", { willReadFrequently: true }); + +function roundup_255th(n) { + return Math.ceil(n * 255) / 255; +} + +function rounddown_255th(n) { + return Math.floor(n * 255) / 255; +} + +for (let op in COMPOSITE_OPERATORS) { + test(function() { + cx.save(); + this.add_cleanup(() => { cx.restore(); }); + for (let sc of VALUES) { + for (let sa of VALUES) { + for (let dc of VALUES) { + for (let da of VALUES) { + let desc = `g=${sc} a=${sa} ${op} g=${dc} a=${da}`; + cx.restore(); + cx.save(); + cx.clearRect(0, 0, 2, 2); + cx.fillStyle = `rgb(0, ${dc}, 0)`; + cx.globalAlpha = da / 255; + cx.fillRect(0, 0, 2, 2); + cx.globalCompositeOperation = op; + assert_equals(cx.globalCompositeOperation, op, "composite operation"); + cx.fillStyle = `rgb(0, ${sc}, 0)`; + cx.globalAlpha = sa / 255; + cx.fillRect(0, 0, 2, 2); + let imageData = cx.getImageData(0, 0, 1, 1); + assert_equals(imageData.data.length, 4, "length of ImageData"); + assert_equals(imageData.data[0], 0, `red: ${desc}`); + assert_equals(imageData.data[2], 0, `blue: ${desc}`); + let expected_color = COMPOSITE_OPERATORS[op].color(sa/255, sc/255, da/255, dc/255); + let expected_alpha = COMPOSITE_OPERATORS[op].alpha(sa/255, da/255); + let allowed_color_error; + // undo the premultiplication: + if (expected_alpha == 0) { + assert_equals(expected_color, 0, `premultiplication zero check: ${desc}`); + allowed_color_error = 0; + } else { + // We want to allow for the error in the color expectation + // to increase when the expected alpha is small, because + // we want to allow for roughly the same amount of error + // in the (smaller) *premultiplied* value. + let expected_min_color = rounddown_255th(expected_color) / roundup_255th(expected_alpha); + let expected_max_color = roundup_255th(expected_color) / rounddown_255th(expected_alpha); + // Set the expectation to the midpoint of the error range + // rather than the actual accurate expectation. + expected_color = (expected_max_color + expected_min_color) / 2; + allowed_color_error = (expected_max_color - expected_min_color) / 2; + } + expected_color *= 255; + expected_alpha *= 255; + allowed_color_error *= 255; + allowed_color_error += 3.5; + assert_approx_equals(imageData.data[1], expected_color, allowed_color_error, `green: ${desc}`); + assert_approx_equals(imageData.data[3], expected_alpha, 1.01, `alpha: ${desc}`); + } + } + } + } + }, `globalCompositeOperation ${op}`); +} +</script> |