canvas: Properly bound all image pattern axis by inserting clip (#37668)

Before we only handled no-repeat for rect, this means we rendered
https://sagudev.github.io/briefcase/no-repeat.html incorrectly (like
firefox). Now if one of the axis is bounded (does not repeat) we clip it
and let other axis be unbounded (technically we clip it to end of
canvas).

This is also needed for vello backend.

Testing: Tests in WPT exists and another test is added.

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
Co-authored-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
sagudev 2025-07-11 08:49:09 +02:00 committed by GitHub
parent 464d71ecfc
commit 75c13f1422
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 314 additions and 70 deletions

View file

@ -478477,7 +478477,7 @@
[]
],
"fill-and-stroke-styles.yaml": [
"f2888e648f42bd052fdc3f38c317866c7f153824",
"32402680bf14ee87815aafee0b33af3703c71c3b",
[]
],
"filters.yaml": [
@ -703792,6 +703792,13 @@
{}
]
],
"2d.pattern.paint.norepeat.outside_arc.html": [
"73159d1bbc6e40a1c361e393bd1b0ea33e30478c",
[
null,
{}
]
],
"2d.pattern.paint.orientation.canvas.html": [
"b68e04272cd62933a1f09da984adda4746a48e1a",
[
@ -714584,6 +714591,20 @@
{}
]
],
"2d.pattern.paint.norepeat.outside_arc.html": [
"9052f14e5b501a98c82538504911013e08913bef",
[
null,
{}
]
],
"2d.pattern.paint.norepeat.outside_arc.worker.js": [
"96e27da06b0714ae27d1414908a5b320396fc84a",
[
"html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside_arc.worker.html",
{}
]
],
"2d.pattern.paint.orientation.canvas.html": [
"3fa8a96a4ad8a17559c613c761eb30a34b71445e",
[

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.pattern.paint.norepeat.outside_arc</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.pattern.paint.norepeat.outside_arc</h1>
<p class="desc"></p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="/images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("");
_addTest(function(canvas, ctx) {
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var img = document.getElementById('red-16x16.png');
var pattern = ctx.createPattern(img, 'no-repeat');
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = pattern;
ctx.beginPath();
ctx.arc(0, 0, 50, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 16, 16);
_assertPixel(canvas, 1,1, 0,255,0,255);
_assertPixel(canvas, 20,1, 0,255,0,255);
_assertPixel(canvas, 1,20, 0,255,0,255);
_assertPixel(canvas, 48,48, 0,255,0,255);
});
</script>
<img src="/images/red-16x16.png" id="red-16x16.png" class="resource">

View file

@ -0,0 +1,40 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>OffscreenCanvas test: 2d.pattern.paint.norepeat.outside_arc</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/html/canvas/resources/canvas-tests.js"></script>
<h1>2d.pattern.paint.norepeat.outside_arc</h1>
<p class="desc"></p>
<script>
promise_test(async t => {
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var response = await fetch('/images/red-16x16.png')
var blob = await response.blob();
var img = await createImageBitmap(blob);
var pattern = ctx.createPattern(img, 'no-repeat');
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = pattern;
ctx.beginPath();
ctx.arc(0, 0, 50, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 16, 16);
_assertPixel(canvas, 1,1, 0,255,0,255);
_assertPixel(canvas, 20,1, 0,255,0,255);
_assertPixel(canvas, 1,20, 0,255,0,255);
_assertPixel(canvas, 48,48, 0,255,0,255);
}, "");
</script>

View file

@ -0,0 +1,33 @@
// DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py.
// OffscreenCanvas test in a worker:2d.pattern.paint.norepeat.outside_arc
// Description:
// Note:
importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var response = await fetch('/images/red-16x16.png')
var blob = await response.blob();
var img = await createImageBitmap(blob);
var pattern = ctx.createPattern(img, 'no-repeat');
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = pattern;
ctx.beginPath();
ctx.arc(0, 0, 50, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 16, 16);
_assertPixel(canvas, 1,1, 0,255,0,255);
_assertPixel(canvas, 20,1, 0,255,0,255);
_assertPixel(canvas, 1,20, 0,255,0,255);
_assertPixel(canvas, 48,48, 0,255,0,255);
}, "");
done();

View file

@ -1867,6 +1867,30 @@
expected: green
variants: *load-image-variant-definition
- name: 2d.pattern.paint.norepeat.outside_arc
images:
- red-16x16.png
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
{{ load_image }}
var pattern = ctx.createPattern(img, 'no-repeat');
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = pattern;
ctx.beginPath();
ctx.arc(0, 0, 50, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 16, 16);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 20,1 == 0,255,0,255;
@assert pixel 1,20 == 0,255,0,255;
@assert pixel 48,48 == 0,255,0,255;
expected: green
variants: *load-image-variant-definition
- name: 2d.pattern.paint.norepeat.coord1
images:
- green.png