summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/canvas/tools
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/html/canvas/tools')
-rw-r--r--testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py213
-rw-r--r--testing/web-platform/tests/html/canvas/tools/name2dir-offscreen.yaml2
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml8
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml706
-rw-r--r--testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml204
5 files changed, 854 insertions, 279 deletions
diff --git a/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py b/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py
index cf141f2f07..d7042810be 100644
--- a/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py
+++ b/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py
@@ -28,7 +28,7 @@
#
# * Test the tests, add new ones to Git, remove deleted ones from Git, etc.
-from typing import Any, List, Mapping, Optional, Set, Tuple
+from typing import Any, DefaultDict, List, Mapping, Optional, Set, Tuple
import re
import collections
@@ -184,6 +184,10 @@ def _remove_extra_newlines(text: str) -> str:
return text
def _expand_test_code(code: str) -> str:
+ code = re.sub(r' @moz-todo', '', code)
+
+ code = re.sub(r'@moz-UniversalBrowserRead;', '', code)
+
code = _remove_extra_newlines(code)
# Unroll expressions with a cross-product-style parameter expansion.
@@ -202,11 +206,13 @@ def _expand_test_code(code: str) -> str:
code = re.sub(r'@assert pixel (\d+,\d+) ==~ (\d+,\d+,\d+,\d+) \+/- (\d+);',
r'_assertPixelApprox(canvas, \1, \2, \3);', code)
- code = re.sub(r'@assert throws (\S+_ERR) (.*);',
- r'assert_throws_dom("\1", function() { \2; });', code)
+ code = re.sub(r'@assert throws (\S+_ERR) (.*?);$',
+ r'assert_throws_dom("\1", function() { \2; });', code,
+ flags=re.MULTILINE | re.DOTALL)
- code = re.sub(r'@assert throws (\S+Error) (.*);',
- r'assert_throws_js(\1, function() { \2; });', code)
+ code = re.sub(r'@assert throws (\S+Error) (.*?);$',
+ r'assert_throws_js(\1, function() { \2; });', code,
+ flags=re.MULTILINE | re.DOTALL)
code = re.sub(
r'@assert (.*) === (.*);', lambda m: '_assertSame(%s, %s, "%s", "%s");'
@@ -226,10 +232,6 @@ def _expand_test_code(code: str) -> str:
r'@assert (.*);', lambda m: '_assert(%s, "%s");' % (m.group(
1), _escapeJS(m.group(1))), code)
- code = re.sub(r' @moz-todo', '', code)
-
- code = re.sub(r'@moz-UniversalBrowserRead;', '', code)
-
assert ('@' not in code)
return code
@@ -376,45 +378,50 @@ def _write_testharness_test(jinja_env: jinja2.Environment,
'utf-8')
+def _generate_expected_image(expected: str, name: str, sub_dir: str,
+ enabled_canvas_types: Set[CanvasType],
+ html_canvas_cfg: TestConfig,
+ offscreen_canvas_cfg: TestConfig) -> str:
+ """Creates a reference image using Cairo and returns the file location."""
+ if expected == 'green':
+ return '/images/green-100x50.png'
+ if expected == 'clear':
+ return '/images/clear-100x50.png'
+ if ';' in expected:
+ print('Found semicolon in %s' % name)
+ expected = re.sub(
+ r'^size (\d+) (\d+)',
+ r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)'
+ r'\ncr = cairo.Context(surface)', expected)
+
+ if CanvasType.HTML_CANVAS in enabled_canvas_types:
+ expected_canvas = (
+ expected + "\nsurface.write_to_png('%s.png')\n" %
+ os.path.join(html_canvas_cfg.image_out_dir, sub_dir, name))
+ eval(compile(expected_canvas, '<test %s>' % name, 'exec'), {},
+ {'cairo': cairo})
+
+ if {CanvasType.OFFSCREEN_CANVAS, CanvasType.WORKER} & enabled_canvas_types:
+ expected_offscreen = (
+ expected + "\nsurface.write_to_png('%s.png')\n" %
+ os.path.join(offscreen_canvas_cfg.image_out_dir, sub_dir, name))
+ eval(compile(expected_offscreen, '<test %s>' % name, 'exec'), {},
+ {'cairo': cairo})
+
+ return '%s.png' % name
+
+
def _generate_test(test: Mapping[str, Any], jinja_env: jinja2.Environment,
- sub_dir: str, enabled_tests: Set[CanvasType],
+ name_to_sub_dir: Mapping[str, str],
+ used_tests: DefaultDict[str, Set[CanvasType]],
html_canvas_cfg: TestConfig,
offscreen_canvas_cfg: TestConfig) -> None:
_validate_test(test)
name = test['name']
- expected_img = None
- if 'expected' in test and test['expected'] is not None:
- expected = test['expected']
- if expected == 'green':
- expected_img = '/images/green-100x50.png'
- elif expected == 'clear':
- expected_img = '/images/clear-100x50.png'
- else:
- if ';' in expected:
- print('Found semicolon in %s' % name)
- expected = re.sub(
- r'^size (\d+) (\d+)',
- r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)'
- r'\ncr = cairo.Context(surface)', expected)
-
- if CanvasType.HTML_CANVAS in enabled_tests:
- expected_canvas = (
- expected + "\nsurface.write_to_png('%s.png')\n" %
- os.path.join(html_canvas_cfg.image_out_dir, sub_dir, name))
- eval(compile(expected_canvas, '<test %s>' % name, 'exec'), {},
- {'cairo': cairo})
-
- if {CanvasType.OFFSCREEN_CANVAS, CanvasType.WORKER} & enabled_tests:
- expected_offscreen = (
- expected +
- "\nsurface.write_to_png('%s.png')\n" % os.path.join(
- offscreen_canvas_cfg.image_out_dir, sub_dir, name))
- eval(compile(expected_offscreen, '<test %s>' % name, 'exec'),
- {}, {'cairo': cairo})
-
- expected_img = '%s.png' % name
+ sub_dir = _get_test_sub_dir(name, name_to_sub_dir)
+ enabled_canvas_types = _get_enabled_canvas_types(test)
# Defaults:
params = {
@@ -423,11 +430,29 @@ def _generate_test(test: Mapping[str, Any], jinja_env: jinja2.Environment,
}
params.update(test)
+
+ # Render parameters used in the test name.
+ name = jinja_env.from_string(name).render(params)
+ print('\r(%s)' % name, ' ' * 32, '\t')
+
+ expected_img = None
+ if 'expected' in test and test['expected'] is not None:
+ expected_img = _generate_expected_image(test['expected'], name,
+ sub_dir, enabled_canvas_types,
+ html_canvas_cfg,
+ offscreen_canvas_cfg)
+
params.update({
'code': _expand_test_code(test['code']),
'expected_img': expected_img
})
+ already_tested = used_tests[name].intersection(enabled_canvas_types)
+ if already_tested:
+ raise InvalidTestDefinitionError(
+ f'Test {name} is defined twice for types {already_tested}')
+ used_tests[name].update(enabled_canvas_types)
+
canvas_path = os.path.join(html_canvas_cfg.out_dir, sub_dir, name)
offscreen_path = os.path.join(offscreen_canvas_cfg.out_dir, sub_dir, name)
if 'manual' in test:
@@ -435,11 +460,55 @@ def _generate_test(test: Mapping[str, Any], jinja_env: jinja2.Environment,
offscreen_path += '-manual'
if 'reference' in test or 'html_reference' in test:
- _write_reference_test(jinja_env, params, enabled_tests,
+ _write_reference_test(jinja_env, params, enabled_canvas_types,
canvas_path, offscreen_path)
else:
- _write_testharness_test(jinja_env, params, enabled_tests, canvas_path,
- offscreen_path)
+ _write_testharness_test(jinja_env, params, enabled_canvas_types,
+ canvas_path, offscreen_path)
+
+
+def _recursive_expand_variant_matrix(test_list: List[Mapping[str, Any]],
+ variant_matrix: List[Mapping[str, Any]],
+ current_selection: List[Tuple[str, Any]],
+ original_test: Mapping[str, Any]):
+ if len(current_selection) == len(variant_matrix):
+ # Selection for each variant is done, so add a new test to test_list.
+ test = original_test.copy()
+ variant_name_list = []
+ should_append_variant_names = original_test.get(
+ 'append_variants_to_name', True)
+ for variant_name, variant_params in current_selection:
+ variant_name_list.append(variant_name)
+ # Append variant name. Variant names starting with '_' are
+ # not appended, which is useful to create variants with the same
+ # name in different folders (element vs. offscreen).
+ if (should_append_variant_names
+ and not variant_name.startswith('_')):
+ test['name'] += '.' + variant_name
+ test.update(variant_params)
+ # Expose variant names as a list so they can be used from the yaml
+ # files, which helps with better naming of tests.
+ test.update({'variant_names': variant_name_list})
+ test_list.append(test)
+ else:
+ # Continue the recursion with each possible selection for the current
+ # variant.
+ variant = variant_matrix[len(current_selection)]
+ for variant_options in variant.items():
+ current_selection.append(variant_options)
+ _recursive_expand_variant_matrix(test_list, variant_matrix,
+ current_selection, original_test)
+ current_selection.pop()
+
+
+def _expand_variant_matrix(
+ variant_matrix: List[Mapping[str, Any]],
+ original_test: Mapping[str, Any]) -> List[Mapping[str, Any]]:
+ current_selection = []
+ matrix_tests = []
+ _recursive_expand_variant_matrix(matrix_tests, variant_matrix,
+ current_selection, original_test)
+ return matrix_tests
def genTestUtils_union(NAME2DIRFILE: str) -> None:
@@ -495,41 +564,25 @@ def genTestUtils_union(NAME2DIRFILE: str) -> None:
pass # Ignore if it already exists,
used_tests = collections.defaultdict(set)
- for original_test in tests:
- variants = original_test.get('variants', {'': dict()})
- for variant_name, variant_params in variants.items():
- test = original_test.copy()
- if variant_name or variant_params:
- # Append variant name. Variant names starting with '_' are
- # not appended, which is useful to create variants with the same
- # name in different folders (element vs. offscreen).
- if not variant_name.startswith('_'):
- test['name'] += '.' + variant_name
- test.update(variant_params)
-
- name = test['name']
- print('\r(%s)' % name, ' ' * 32, '\t')
-
- enabled_canvas_types = _get_enabled_canvas_types(test)
-
- already_tested = used_tests[name].intersection(
- enabled_canvas_types)
- if already_tested:
- raise InvalidTestDefinitionError(
- f'Test {name} is defined twice for types {already_tested}')
- used_tests[name].update(enabled_canvas_types)
-
- sub_dir = _get_test_sub_dir(name, name_to_sub_dir)
- _generate_test(
- test,
- jinja_env,
- sub_dir,
- enabled_canvas_types,
- html_canvas_cfg=TestConfig(
- out_dir=CANVASOUTPUTDIR,
- image_out_dir=CANVASIMAGEOUTPUTDIR),
- offscreen_canvas_cfg=TestConfig(
- out_dir=OFFSCREENCANVASOUTPUTDIR,
- image_out_dir=OFFSCREENCANVASIMAGEOUTPUTDIR))
+ for test in tests:
+ if 'variant_matrix' in test:
+ variants = _expand_variant_matrix(test['variant_matrix'], test)
+ elif 'variants' in test:
+ variant_matrix = [test['variants']]
+ variants = _expand_variant_matrix(variant_matrix, test)
+ else:
+ variants = [test]
+
+ for variant in variants:
+ _generate_test(variant,
+ jinja_env,
+ name_to_sub_dir,
+ used_tests,
+ html_canvas_cfg=TestConfig(
+ out_dir=CANVASOUTPUTDIR,
+ image_out_dir=CANVASIMAGEOUTPUTDIR),
+ offscreen_canvas_cfg=TestConfig(
+ out_dir=OFFSCREENCANVASOUTPUTDIR,
+ image_out_dir=OFFSCREENCANVASIMAGEOUTPUTDIR))
print()
diff --git a/testing/web-platform/tests/html/canvas/tools/name2dir-offscreen.yaml b/testing/web-platform/tests/html/canvas/tools/name2dir-offscreen.yaml
index c52acb793b..6e4b3f42b2 100644
--- a/testing/web-platform/tests/html/canvas/tools/name2dir-offscreen.yaml
+++ b/testing/web-platform/tests/html/canvas/tools/name2dir-offscreen.yaml
@@ -19,4 +19,4 @@
2d.missingargs: "conformance-requirements"
2d.voidreturn: "conformance-requirements"
2d.canvas.host: "canvas-host"
-2d.canvas.context: "canvas-context" \ No newline at end of file
+2d.canvas.context: "canvas-context"
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml
index 93c556288d..09e9e00186 100644
--- a/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml
@@ -638,3 +638,11 @@
@nonfinite ctx.drawImage(<bitmap>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <100 Infinity -Infinity NaN>, <50 Infinity -Infinity NaN>);
@assert pixel 50,25 == 0,255,0,255;
expected: green
+
+- name: 2d.drawImage.detachedcanvas
+ desc: drawImage with detached OffscreenCanvas as the source should throw exception
+ canvasType: ['HTMLCanvas']
+ code: |
+ var canvas2 = new OffscreenCanvas(80, 80);
+ (new MessageChannel()).port1.postMessage(canvas2, [canvas2]);
+ @assert throws INVALID_STATE_ERR ctx.drawImage(canvas2, 0, 0);
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml
index 01c83a33e2..f327b9fe94 100644
--- a/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml
@@ -89,51 +89,102 @@
ctx.filter = 'this string is not a filter and should do nothing';
@assert ctx.filter.toString() == '[object CanvasFilter]';
-- name: 2d.filter.canvasFilterObject.blur.exceptions.tentative
- desc: Test exceptions on CanvasFilter() blur.object
+- name: 2d.filter.{{ variant_names[0] }}.blur.exceptions{{ tentative }}
+ desc: Test exceptions on gaussianBlur filter
code: |
- @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur'});
- @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: undefined});
- @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: 'foo'});
- @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: [1,2,3]});
- @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: NaN});
- @assert throws TypeError ctx.filter = new CanvasFilter({name: 'gaussianBlur', stdDeviation: {}});
-
-- name: 2d.filter.canvasFilterObject.colorMatrix.tentative
- desc: Test the functionality of ColorMatrix filters in CanvasFilter objects
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'gaussianBlur'}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'gaussianBlur', stdDeviation: undefined}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'gaussianBlur', stdDeviation: 'foo'}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'gaussianBlur', stdDeviation: [1,2,3]}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'gaussianBlur', stdDeviation: NaN}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'gaussianBlur', stdDeviation: {}}") }};
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter:
+ param})
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(
+ param)
+ tentative: .tentative
+
+- name: 2d.filter.{{ variant_names[0] }}.colorMatrix{{ tentative }}
+ desc: Test the functionality of ColorMatrix filters
code: |
- @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: undefined});
- @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: 'foo'});
- @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: null});
- @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3]});
- @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 'a']});
- @assert throws TypeError new CanvasFilter({name: 'colorMatrix', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, Infinity]});
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', values: undefined}") }};
+
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', values: 'foo'}") }};
+
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', values: null}") }};
+
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', values: [1, 2, 3]}") }};
+
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix',
+ values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 'a']}") }};
+
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix',
+ values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, Infinity]}") }};
+
ctx.fillStyle = '#f00';
- ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 0});
+ {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', type: 'hueRotate', values: 0}") }};
ctx.fillRect(0, 0, 100, 50);
+ {{ close_layer -}}
@assert pixel 10,10 ==~ 255,0,0,255;
- ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 90});
+
+ {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', type: 'hueRotate', values: 90}") }};
ctx.fillRect(0, 0, 100, 50);
+ {{ close_layer -}}
@assert pixel 10,10 ==~ 0,91,0,255;
- ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 180});
+
+ {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', type: 'hueRotate', values: 180}") }};
ctx.fillRect(0, 0, 100, 50);
+ {{ close_layer -}}
@assert pixel 10,10 ==~ 0,109,109,255;
- ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'hueRotate', values: 270});
+
+ {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', type: 'hueRotate', values: 270}") }};
ctx.fillRect(0, 0, 100, 50);
+ {{ close_layer -}}
@assert pixel 10,10 ==~ 109,18,255,255;
- ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'saturate', values: 0.5});
+
+ {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', type: 'saturate', values: 0.5}") }};
ctx.fillRect(0, 0, 100, 50);
+ {{ close_layer -}}
@assert pixel 10,10 ==~ 155,27,27,255;
+
ctx.clearRect(0, 0, 100, 50);
- ctx.filter = new CanvasFilter({name: 'colorMatrix', type: 'luminanceToAlpha'});
+ {{ filter_declaration | replace("param",
+ "{name: 'colorMatrix', type: 'luminanceToAlpha'}") }};
ctx.fillRect(0, 0, 100, 50);
+ {{ close_layer -}}
@assert pixel 10,10 ==~ 0,0,0,54;
- ctx.filter = new CanvasFilter({name: 'colorMatrix', values: [
- 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 0,
- 0, 0, 0, 0, 0,
- 0, 0, 0, 1, 0
- ]});
+
+ {{ filter_declaration | replace("param", "{name: 'colorMatrix', values: [
+ 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0
+ ]}") }};
ctx.fillRect(0, 0, 50, 25);
ctx.fillStyle = '#0f0';
ctx.fillRect(50, 0, 50, 25);
@@ -141,51 +192,120 @@
ctx.fillRect(0, 25, 50, 25);
ctx.fillStyle = '#fff';
ctx.fillRect(50, 25, 50, 25);
+ {{ close_layer -}}
@assert pixel 10,10 ==~ 0,255,0,255;
@assert pixel 60,10 ==~ 0,255,0,255;
@assert pixel 10,30 ==~ 0,255,0,255;
@assert pixel 60,30 ==~ 0,255,0,255;
-
-- name: 2d.filter.canvasFilterObject.convolveMatrix.exceptions.tentative
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter:
+ param})
+ close_layer: |
+ ctx.endLayer();
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(
+ param)
+ tentative: .tentative
+
+- name: 2d.filter.{{ variant_names[0] }}.convolveMatrix.exceptions{{ tentative }}
desc: Test exceptions on CanvasFilter() convolveMatrix
code: |
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix'});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', divisor: 2});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: null});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: 1});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0]]});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 'a'], [0]]});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], 0]});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 0], [0, Infinity]]});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: []});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1]});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [1, 2, 3, 4]});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], []]});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1, 2], []]});
- @assert throws TypeError new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[], [1, 2]]});
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix'}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', divisor: 2}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: null}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: 1}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [[1, 0], [0]]}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [[1, 'a'], [0]]}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [[1, 0], 0]}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [[1, 0], [0, Infinity]]}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: []}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [1]}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [1, 2, 3, 4]}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [[], []]}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [[1, 2], []]}") }};
+ @assert throws TypeError {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [[], [1, 2]]}") }};
// This should not throw an error
- ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[]]});
- ctx.filter = new CanvasFilter({name: 'convolveMatrix', kernelMatrix: [[1]]});
-
-- name: 2d.filter.canvasFilterObject.componentTransfer.linear.tentative
+ {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [[]]}") }};
+ {{ close_layer -}}
+ {{ filter_declaration | replace("param",
+ "{name: 'convolveMatrix', kernelMatrix: [[1]]}") }};
+ {{ close_layer -}}
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter:
+ param})
+ close_layer: |
+ ctx.endLayer();
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(
+ param)
+ tentative: .tentative
+
+- name: >-
+ 2d.filter.{{ variant_names[0] }}.componentTransfer.linear{{ tentative }}
desc: Test pixels on CanvasFilter() componentTransfer with linear type
+ size: [100, 100]
+ fuzzy: maxDifference=0-2; totalPixels=0-500
code: |
+ const slopes = [0.5, 1.2, -0.2];
+ const intercepts = [0.25, 0, 0.5];
+ {{ filter_declaration | replace("param", "{name: 'componentTransfer',
+ funcR: {type: 'linear', slope: slopes[0], intercept: intercepts[0]},
+ funcG: {type: 'linear', slope: slopes[1], intercept: intercepts[1]},
+ funcB: {type: 'linear', slope: slopes[2], intercept: intercepts[2]},
+ }") }};
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ const color = inputColors[i];
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
+ }
+ {{ close_layer }}
+ reference: |
// From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
function getColor(inputColor, slopes, intercepts) {
return [
- Math.max(0, Math.min(1, inputColor[0]/255 * slopes[0] + intercepts[0])) * 255,
- Math.max(0, Math.min(1, inputColor[1]/255 * slopes[1] + intercepts[1])) * 255,
- Math.max(0, Math.min(1, inputColor[2]/255 * slopes[2] + intercepts[2])) * 255,
+ Math.max(0, Math.min(1,
+ inputColor[0]/255 * slopes[0] + intercepts[0])) * 255,
+ Math.max(0, Math.min(1,
+ inputColor[1]/255 * slopes[1] + intercepts[1])) * 255,
+ Math.max(0, Math.min(1,
+ inputColor[2]/255 * slopes[2] + intercepts[2])) * 255,
];
}
const slopes = [0.5, 1.2, -0.2];
const intercepts = [0.25, 0, 0.5];
- ctx.filter = new CanvasFilter({name: 'componentTransfer',
- funcR: {type: 'linear', slope: slopes[0], intercept: intercepts[0]},
- funcG: {type: 'linear', slope: slopes[1], intercept: intercepts[1]},
- funcB: {type: 'linear', slope: slopes[2], intercept: intercepts[2]},
- });
const inputColors = [
[255, 255, 255],
@@ -195,21 +315,35 @@
[50, 68, 87],
];
- for (const color of inputColors) {
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ const color = inputColors[i];
let outputColor = getColor(color, slopes, intercepts);
- ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
- ctx.fillRect(0, 0, 10, 10);
- _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ ctx.fillStyle = `rgb(${outputColor[0]}, ${outputColor[1]},
+ ${outputColor[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
}
-
-- name: 2d.filter.canvasFilterObject.componentTransfer.identity.tentative
+ {{ close_layer }}
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter: param})
+ close_layer: ctx.endLayer();
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(param)
+ tentative: .tentative
+
+- name: >-
+ 2d.filter.{{ variant_names[0] }}.componentTransfer.identity{{ tentative }}
desc: Test pixels on CanvasFilter() componentTransfer with identity type
+ size: [100, 100]
code: |
- ctx.filter = new CanvasFilter({name: 'componentTransfer',
+ {{ filter_declaration | replace("param", "{name: 'componentTransfer',
funcR: {type: 'identity'},
funcG: {type: 'identity'},
funcB: {type: 'identity'},
- });
+ }") }};
const inputColors = [
[255, 255, 255],
@@ -219,32 +353,86 @@
[50, 68, 87],
];
- for (const color of inputColors) {
- ctx.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, 1)`,
- ctx.fillRect(0, 0, 10, 10);
- _assertPixel(canvas, 5, 5, color[0],color[1],color[2],255);
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ const color = inputColors[i];
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
}
+ {{ close_layer }}
+ reference: |
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
-- name: 2d.filter.canvasFilterObject.componentTransfer.gamma.tentative
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ let outputColor = inputColors[i];
+ ctx.fillStyle = `rgb(${outputColor[0]}, ${outputColor[1]},
+ ${outputColor[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
+ }
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter: param})
+ close_layer: ctx.endLayer();
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(param)
+ tentative: .tentative
+
+- name: >-
+ 2d.filter.{{ variant_names[0] }}.componentTransfer.gamma{{ tentative }}
desc: Test pixels on CanvasFilter() componentTransfer with gamma type
+ size: [100, 100]
+ fuzzy: maxDifference=0-2; totalPixels=0-500
code: |
+ const amplitudes = [2, 1.1, 0.5];
+ const exponents = [5, 3, 1];
+ const offsets = [0.25, 0, 0.5];
+ {{ filter_declaration | replace("param", "{name: 'componentTransfer',
+ funcR: {type: 'gamma', amplitude: amplitudes[0],
+ exponent: exponents[0], offset: offsets[0]},
+ funcG: {type: 'gamma', amplitude: amplitudes[1],
+ exponent: exponents[1], offset: offsets[1]},
+ funcB: {type: 'gamma', amplitude: amplitudes[2],
+ exponent: exponents[2], offset: offsets[2]},
+ }") }};
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ const color = inputColors[i];
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
+ }
+ {{ close_layer }}
+ reference: |
// From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
function getColor(inputColor, amplitude, exponent, offset) {
return [
- Math.max(0, Math.min(1, Math.pow(inputColor[0]/255, exponent[0]) * amplitude[0] + offset[0])) * 255,
- Math.max(0, Math.min(1, Math.pow(inputColor[1]/255, exponent[1]) * amplitude[1] + offset[1])) * 255,
- Math.max(0, Math.min(1, Math.pow(inputColor[2]/255, exponent[2]) * amplitude[2] + offset[2])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[0]/255,
+ exponent[0]) * amplitude[0] + offset[0])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[1]/255,
+ exponent[1]) * amplitude[1] + offset[1])) * 255,
+ Math.max(0, Math.min(1, Math.pow(inputColor[2]/255,
+ exponent[2]) * amplitude[2] + offset[2])) * 255,
];
}
const amplitudes = [2, 1.1, 0.5];
const exponents = [5, 3, 1];
const offsets = [0.25, 0, 0.5];
- ctx.filter = new CanvasFilter({name: 'componentTransfer',
- funcR: {type: 'gamma', amplitude: amplitudes[0], exponent: exponents[0], offset: offsets[0]},
- funcG: {type: 'gamma', amplitude: amplitudes[1], exponent: exponents[1], offset: offsets[1]},
- funcB: {type: 'gamma', amplitude: amplitudes[2], exponent: exponents[2], offset: offsets[2]},
- });
const inputColors = [
[255, 255, 255],
@@ -254,16 +442,54 @@
[50, 68, 87],
];
- for (const color of inputColors) {
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ const color = inputColors[i];
let outputColor = getColor(color, amplitudes, exponents, offsets);
- ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
- ctx.fillRect(0, 0, 10, 10);
- _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ ctx.fillStyle = `rgb(${outputColor[0]}, ${outputColor[1]},
+ ${outputColor[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
}
-
-- name: 2d.filter.canvasFilterObject.componentTransfer.table.tentative
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter: param})
+ close_layer: ctx.endLayer();
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(param)
+ tentative: .tentative
+
+- name: >-
+ 2d.filter.{{ variant_names[0] }}.componentTransfer.table{{ tentative }}
desc: Test pixels on CanvasFilter() componentTransfer with table type
+ size: [100, 100]
+ fuzzy: maxDifference=0-2; totalPixels=0-500
code: |
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ {{ filter_declaration | replace("param", "{name: 'componentTransfer',
+ funcR: {type: 'table', tableValues: tableValuesR},
+ funcG: {type: 'table', tableValues: tableValuesG},
+ funcB: {type: 'table', tableValues: tableValuesB},
+ }") }};
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ const color = inputColors[i];
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
+ }
+ {{ close_layer }}
+ reference: |
// From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
function getTransformedValue(C, V) {
// Get the right interval
@@ -285,11 +511,6 @@
tableValuesR = [0, 0, 1, 1];
tableValuesG = [2, 0, 0.5, 3];
tableValuesB = [1, -1, 5, 0];
- ctx.filter = new CanvasFilter({name: 'componentTransfer',
- funcR: {type: 'table', tableValues: tableValuesR},
- funcG: {type: 'table', tableValues: tableValuesG},
- funcB: {type: 'table', tableValues: tableValuesB},
- });
const inputColors = [
[255, 255, 255],
@@ -299,16 +520,55 @@
[50, 68, 87],
];
- for (const color of inputColors) {
- let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
- ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
- ctx.fillRect(0, 0, 10, 10);
- _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ const color = inputColors[i];
+ let outputColor = getColor(
+ color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${outputColor[0]}, ${outputColor[1]},
+ ${outputColor[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
}
-
-- name: 2d.filter.canvasFilterObject.componentTransfer.discrete.tentative
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter: param})
+ close_layer: ctx.endLayer();
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(param)
+ tentative: .tentative
+
+- name: >-
+ 2d.filter.{{ variant_names[0] }}.componentTransfer.discrete{{ tentative }}
desc: Test pixels on CanvasFilter() componentTransfer with discrete type
+ size: [100, 100]
+ fuzzy: maxDifference=0-2; totalPixels=0-500
code: |
+ tableValuesR = [0, 0, 1, 1];
+ tableValuesG = [2, 0, 0.5, 3];
+ tableValuesB = [1, -1, 5, 0];
+ {{ filter_declaration | replace("param", "{name: 'componentTransfer',
+ funcR: {type: 'discrete', tableValues: tableValuesR},
+ funcG: {type: 'discrete', tableValues: tableValuesG},
+ funcB: {type: 'discrete', tableValues: tableValuesB},
+ }") }};
+
+ const inputColors = [
+ [255, 255, 255],
+ [0, 0, 0],
+ [127, 0, 34],
+ [252, 186, 3],
+ [50, 68, 87],
+ ];
+
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ const color = inputColors[i];
+ ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
+ }
+ {{ close_layer }}
+ reference: |
// From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
function getTransformedValue(C, V) {
// Get the right interval
@@ -330,11 +590,6 @@
tableValuesR = [0, 0, 1, 1];
tableValuesG = [2, 0, 0.5, 3];
tableValuesB = [1, -1, 5, 0];
- ctx.filter = new CanvasFilter({name: 'componentTransfer',
- funcR: {type: 'discrete', tableValues: tableValuesR},
- funcG: {type: 'discrete', tableValues: tableValuesG},
- funcB: {type: 'discrete', tableValues: tableValuesB},
- });
const inputColors = [
[255, 255, 255],
@@ -343,24 +598,38 @@
[252, 186, 3],
[50, 68, 87],
];
-
- for (const color of inputColors) {
- let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
- ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
- ctx.fillRect(0, 0, 10, 10);
- _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, 2);
+ for (let i = 0 ; i < inputColors.length ; ++i) {
+ const color = inputColors[i];
+ let outputColor = getColor(
+ color, [tableValuesR, tableValuesG, tableValuesB]);
+ ctx.fillStyle = `rgb(${outputColor[0]}, ${outputColor[1]},
+ ${outputColor[2]})`;
+ ctx.fillRect(i * 10, i * 10, 10, 10);
}
-
-- name: 2d.filter.canvasFilterObject.gaussianBlur.tentative
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter: param})
+ close_layer: ctx.endLayer();
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(param)
+ tentative: .tentative
+
+- name: >-
+ 2d.filter.{{ variant_names[0] }}.gaussianBlur.{{ variant_names[1] }}{{
+ tentative }}
desc: Test CanvasFilter() with gaussianBlur.
size: [100, 100]
code: |
ctx.fillStyle = 'teal';
- ctx.filter = new CanvasFilter({
+ {{ filter_declaration | replace("param", "{
name: 'gaussianBlur',
stdDeviation: [{{ blur_x }}, {{blur_y}}],
- });
+ }") }}
ctx.fillRect(25, 25, 50, 50);
+ {{ close_layer }}
html_reference: |
<svg xmlns="http://www.w3.org/2000/svg"
width="{{ size[0] }}" height="{{ size[1] }}"
@@ -371,24 +640,33 @@
<rect x="25" y="25" width="50" height="50"
fill="teal" filter="url(#blur)" />
</svg>
- variants:
- x-only:
- blur_x: 4
- blur_y: 0
- mostly-x:
- blur_x: 4
- blur_y: 1
- isotropic:
- blur_x: 4
- blur_y: 4
- mostly-y:
- blur_x: 1
- blur_y: 4
- y-only:
- blur_x: 0
- blur_y: 4
-
-- name: 2d.filter.canvasFilterObject.dropShadow.tentative
+ append_variants_to_name: false
+ variant_matrix:
+ - layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter: param});
+ close_layer: ctx.endLayer();
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(param);
+ tentative: .tentative
+ - x-only:
+ blur_x: 4
+ blur_y: 0
+ mostly-x:
+ blur_x: 4
+ blur_y: 1
+ isotropic:
+ blur_x: 4
+ blur_y: 4
+ mostly-y:
+ blur_x: 1
+ blur_y: 4
+ y-only:
+ blur_x: 0
+ blur_y: 4
+
+- name: 2d.filter.{{ variant_names[0] }}.dropShadow{{ tentative }}
desc: Test CanvasFilter() dropShadow object.
size: [520, 420]
code: |
@@ -401,90 +679,91 @@
ctx.fillStyle = 'crimson';
// Parameter defaults.
- ctx.filter = new CanvasFilter({name: 'dropShadow'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow'}") }}
ctx.fillRect(10, 10, 80, 80);
+ {{ close_layer -}}
// All parameters specified.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
- floodColor: 'purple', floodOpacity: 0.7});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple', floodOpacity: 0.7}") }}
ctx.fillRect(110, 10, 80, 80);
+ {{ close_layer -}}
// Named color.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
- floodColor: 'purple'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'purple'}") }}
ctx.fillRect(10, 110, 80, 80);
+ {{ close_layer -}}
// System color.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
- floodColor: 'LinkText'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'LinkText'}") }}
ctx.fillRect(110, 110, 80, 80);
+ {{ close_layer -}}
// Numerical color.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
- floodColor: 'rgba(20, 50, 130, 1)'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 1)'}") }}
ctx.fillRect(210, 110, 80, 80);
+ {{ close_layer -}}
// Transparent floodColor.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
- floodColor: 'rgba(20, 50, 130, 0.7)'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)'}") }}
ctx.fillRect(310, 110, 80, 80);
+ {{ close_layer -}}
// Transparent floodColor and floodOpacity.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
- floodColor: 'rgba(20, 50, 130, 0.7)', floodOpacity: 0.7});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 3,
+ floodColor: 'rgba(20, 50, 130, 0.7)', floodOpacity: 0.7}") }}
ctx.fillRect(410, 110, 80, 80);
+ {{ close_layer -}}
// No blur.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 0,
- floodColor: 'purple'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 0,
+ floodColor: 'purple'}") }}
ctx.fillRect(10, 210, 80, 80);
+ {{ close_layer -}}
// Single float blur.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
- floodColor: 'purple'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: 5,
+ floodColor: 'purple'}") }}
ctx.fillRect(110, 210, 80, 80);
+ {{ close_layer -}}
// Single negative float blur.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: -5,
- floodColor: 'purple'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: -5,
+ floodColor: 'purple'}") }}
ctx.fillRect(210, 210, 80, 80);
+ {{ close_layer -}}
// Two floats (X&Y) blur.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [3, 5],
- floodColor: 'purple'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [3, 5],
+ floodColor: 'purple'}") }}
ctx.fillRect(310, 210, 80, 80);
+ {{ close_layer -}}
// Two negative floats (X&Y) blur.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [-3, -5],
- floodColor: 'purple'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: 9, dy: 12, stdDeviation: [-3, -5],
+ floodColor: 'purple'}") }}
ctx.fillRect(410, 210, 80, 80);
+ {{ close_layer -}}
// Degenerate parameter values.
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: [-5], dy: [], stdDeviation: null,
- floodColor: 'purple', floodOpacity: [2]});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: [-5], dy: [], stdDeviation: null,
+ floodColor: 'purple', floodOpacity: [2]}") }}
ctx.fillRect(10, 310, 80, 80);
+ {{ close_layer -}}
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: null, dy: '5', stdDeviation: [[-5], ['3']],
- floodColor: 'purple', floodOpacity: '0.8'});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: null, dy: '5', stdDeviation: [[-5], ['3']],
+ floodColor: 'purple', floodOpacity: '0.8'}") }}
ctx.fillRect(110, 310, 80, 80);
+ {{ close_layer -}}
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: true, dy: ['10'], stdDeviation: false,
- floodColor: 'purple', floodOpacity: ['0.4']});
+ {{ filter_declaration | replace("param", "{name: 'dropShadow', dx: true, dy: ['10'], stdDeviation: false,
+ floodColor: 'purple', floodOpacity: ['0.4']}") }}
ctx.fillRect(210, 310, 80, 80);
+ {{ close_layer -}}
html_reference: |
<svg xmlns="http://www.w3.org/2000/svg"
width={{ size[0] }} height={{ size[1] }}
@@ -537,41 +816,70 @@
<rect x=210 y=310 width=80 height=80 fill="crimson"
filter="drop-shadow(1px 10px 0px rgba(128, 0, 128, 0.4))"/>
</svg>
-
-- name: 2d.filter.canvasFilterObject.dropShadow.exceptions.tentative
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter: param});
+ close_layer: |
+ ctx.endLayer();
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(param);
+ tentative: .tentative
+
+- name: 2d.filter.{{ variant_names[0] }}.dropShadow.exceptions{{ tentative }}
desc: Test exceptions on CanvasFilter() dropShadow object
code: |
- @unroll @assert new CanvasFilter({\-
+ // Should not throw an error.
+ @unroll {{ filter_declaration | replace("param", "{\-
name: 'dropShadow', \-
<dx | dy | floodOpacity>: \-
- <10 | -1 | 0.5 | null | true | false | [] | [20] | '30'>});
- @unroll @assert new CanvasFilter({\-
+ <10 | -1 | 0.5 | null | true | false | [] | [20] | '30'>}") }};
+ @unroll {{ filter_declaration | replace("param", "{\-
+ name: 'dropShadow', \-
+ <dx | dy | floodOpacity>: \-
+ <10 | -1 | 0.5 | null | true | false | [] | [20] | '30'>}") }};
+ @unroll {{ filter_declaration | replace("param", "{\-
name: 'dropShadow', \-
<stdDeviation>: \-
<10 | -1 | 0.5 | null | true | false | [] | [20] | '30' | \-
[10, -1] | [0.5, null] | [true, false] | [[], [20]] | \-
- ['30', ['40']]>});
- @unroll @assert new CanvasFilter({\-
+ ['30', ['40']]>}") }};
+ @unroll {{ filter_declaration | replace("param", "{\-
name: 'dropShadow', \-
<floodColor>: \-
- <'red' | 'canvas' | 'rgba(4, -3, 0.5, 1)' | '#aabbccdd' | '#abcd'>});
+ <'red' | 'canvas' | 'rgba(4, -3, 0.5, 1)' | '#aabbccdd' |
+ '#abcd'>}") }};
- @unroll @assert throws TypeError new CanvasFilter({\-
- name: 'dropShadow', \-
+ // Should throw a TypeError.
+ @unroll @assert throws TypeError {{ filter_declaration | replace("param", \-
+ "{name: 'dropShadow', \-
<dx | dy | floodOpacity>: \-
- <NaN | Infinity | -Infinity | undefined | 'test' | {} | [1, 2]>});
- @unroll @assert throws TypeError new CanvasFilter({\-
- name: 'dropShadow', \-
+ <NaN | Infinity | -Infinity | undefined | 'test' | {} | [1, 2]>}") }};
+ @unroll @assert throws TypeError {{ filter_declaration | replace("param", \-
+ "{name: 'dropShadow', \-
<stdDeviation>: \-
<NaN | Infinity | -Infinity | undefined | 'test' | {} | [1, 2, 3] | \-
[1, NaN] | [1, Infinity] | [1, -Infinity] | [1, undefined] | \-
- [1, 'test'] | [1, {}] | [1, [2, 3]]>});
- @unroll @assert throws TypeError new CanvasFilter({\-
- name: 'dropShadow', \-
+ [1, 'test'] | [1, {}] | [1, [2, 3]]>}") }};
+ @unroll @assert throws TypeError {{ filter_declaration | replace("param", \-
+ "{name: 'dropShadow', \-
<floodColor>: \-
- <'test' | 'rgba(NaN, 3, 2, 1)' | 10 | undefined | null | NaN>});
-
-- name: 2d.filter.canvasFilterObject.turbulence.inputTypes.tentative
+ <'test' | 'rgba(NaN, 3, 2, 1)' | 10 | undefined | null | NaN>}") }};
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter:
+ param}); ctx.endLayer()
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(
+ param)
+ tentative: .tentative
+
+- name: 2d.filter.{{ variant_names[0] }}.turbulence.inputTypes{{ tentative }}
desc: Test exceptions on CanvasFilter() turbulence object
code: |
const errorTestCases = [
@@ -671,10 +979,22 @@
for (testCase of errorTestCases) {
const filterOptions = {...{name: 'turbulence'}, ...testCase};
- @assert throws TypeError new CanvasFilter(filterOptions);
+ @assert throws TypeError {{ filter_declaration |
+ replace("param", "filterOptions") }};
}
for (testCase of workingTestCases) {
const filterOptions = {...{name: 'turbulence'}, ...testCase};
- @assert new CanvasFilter(filterOptions) != null;
+ {{ filter_declaration | replace("param", "filterOptions") }};
+ {{- close_layer }}
}
+ append_variants_to_name: false
+ variants:
+ layers:
+ filter_declaration: |-
+ ctx.beginLayer({filter: param})
+ close_layer: "\n ctx.endLayer();"
+ canvasFilterObject:
+ filter_declaration: |-
+ ctx.filter = new CanvasFilter(param)
+ tentative: .tentative
diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml
index a44cb2ea2c..437a70c3f7 100644
--- a/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml
+++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml
@@ -44,7 +44,7 @@
variants: &global-state-variants
no-global-states:
render_states: // No global states.
- alpha:
+ alpha: &global-state-alpha
render_states: ctx.globalAlpha = 0.6;
blending:
render_states: ctx.globalCompositeOperation = 'multiply';
@@ -56,15 +56,15 @@
ctx.shadowOffsetY = 10;
ctx.shadowColor = 'rgba(255, 165, 0, 0.5)';
ctx.shadowBlur = 3;
- alpha.blending:
+ alpha.blending: &global-state-alpha-blending
render_states: |-
ctx.globalAlpha = 0.6;
ctx.globalCompositeOperation = 'multiply';
- alpha.composite:
+ alpha.composite: &global-state-alpha-composite
render_states: |-
ctx.globalAlpha = 0.6;
ctx.globalCompositeOperation = 'source-in';
- alpha.shadow:
+ alpha.shadow: &global-state-alpha-shadow
render_states: |-
ctx.globalAlpha = 0.5;
ctx.shadowOffsetX = -10;
@@ -167,7 +167,20 @@
ctx.drawImage(img, 0, 0);
};
img.src = 'data:image/svg+xml;base64,' + btoa(svg);
- variants: *global-state-variants
+ variants:
+ <<: *global-state-variants
+ alpha:
+ <<: *global-state-alpha
+ fuzzy: maxDifference=0-2; totalPixels=0-6766
+ alpha.blending:
+ <<: *global-state-alpha-blending
+ fuzzy: maxDifference=0-1; totalPixels=0-2453
+ alpha.composite:
+ <<: *global-state-alpha-composite
+ fuzzy: maxDifference=0-1; totalPixels=0-5204
+ alpha.shadow:
+ <<: *global-state-alpha-shadow
+ fuzzy: maxDifference=0-2; totalPixels=0-6311
- name: 2d.layer.global-filter
desc: Tests that layers ignore the global context filter.
@@ -334,6 +347,144 @@
ctx.drawImage(canvas2, 0, 0);
+- name: 2d.layer.ctm.filter
+ desc: Checks that parent transforms affect layer filters.
+ size: [200, 200]
+ code: |
+ // Transforms inside the layer should not apply to the layer's filter.
+ ctx.beginLayer({filter: 'drop-shadow(5px 5px 0px grey)'});
+ ctx.translate(30, 90);
+ ctx.scale(2, 2);
+ ctx.rotate(Math.PI / 2);
+ ctx.fillRect(-30, -5, 60, 10);
+ ctx.endLayer();
+
+ // Transforms in the layer's parent should apply to the layer's filter.
+ ctx.translate(80, 90);
+ ctx.scale(2, 2);
+ ctx.rotate(Math.PI / 2);
+ ctx.beginLayer({filter: 'drop-shadow(5px 5px 0px grey)'});
+ ctx.fillRect(-30, -5, 60, 10);
+ ctx.endLayer();
+ html_reference: |
+ <svg xmlns="http://www.w3.org/2000/svg"
+ width="{{ size[0] }}" height="{{ size[1] }}"
+ color-interpolation-filters="sRGB">
+ <filter id="filter" x="-100%" y="-100%" width="300%" height="300%">
+ <feDropShadow dx="5" dy="5" stdDeviation="0" flood-color="grey" />
+ </filter>
+
+ <g filter="url(#filter)">
+ <g transform="translate(30, 90) scale(2) rotate(90)">
+ <rect x="-30" y="-5" width=60 height=10></rect>
+ </g>
+ </g>
+
+ <g transform="translate(80, 90) scale(2) rotate(90)">
+ <g filter="url(#filter)">
+ <rect x="-30" y="-5" width=60 height=10></rect>
+ </g>
+ </g>
+ </svg>
+
+- name: 2d.layer.ctm.shadow-in-transformed-layer
+ desc: Check shadows inside of a transformed layer.
+ size: [200, 200]
+ code: |
+ ctx.translate(80, 90);
+ ctx.scale(2, 2);
+ ctx.rotate(Math.PI / 2);
+
+ ctx.beginLayer();
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'grey';
+ ctx.fillRect(-30, -5, 60, 10);
+
+ const canvas2 = new OffscreenCanvas(100, 100);
+ const ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(0, 0, 40, 10);
+ ctx.drawImage(canvas2, -30, -30);
+
+ ctx.endLayer();
+ reference: |
+ ctx.translate(80, 90);
+ ctx.scale(2, 2);
+ ctx.rotate(Math.PI / 2);
+
+ ctx.shadowOffsetX = 10;
+ ctx.shadowOffsetY = 10;
+ ctx.shadowColor = 'grey';
+ ctx.fillRect(-30, -5, 60, 10);
+
+ const canvas2 = new OffscreenCanvas(100, 100);
+ const ctx2 = canvas2.getContext('2d');
+ ctx2.fillStyle = 'blue';
+ ctx2.fillRect(0, 0, 40, 10);
+ ctx.drawImage(canvas2, -30, -30);
+
+- name: 2d.layer.ctm.getTransform
+ desc: Tests getTransform inside layers.
+ code: |
+ ctx.translate(10, 20);
+ ctx.beginLayer();
+ ctx.scale(2, 3);
+ const m = ctx.getTransform();
+ assert_array_equals([m.a, m.b, m.c, m.d, m.e, m.f], [2, 0, 0, 3, 10, 20]);
+ ctx.endLayer();
+
+- name: 2d.layer.ctm.setTransform
+ desc: Tests setTransform inside layers.
+ code: |
+ ctx.translate(80, 0);
+
+ ctx.beginLayer();
+ ctx.rotate(2);
+ ctx.beginLayer();
+ ctx.scale(5, 6);
+ ctx.setTransform(4, 0, 0, 2, 20, 10);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 10, 10);
+ ctx.endLayer();
+ ctx.endLayer();
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 20, 20);
+ reference: |
+ ctx.translate(80, 0);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 20, 20);
+
+ ctx.setTransform(4, 0, 0, 2, 20, 10);
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 10, 10);
+
+- name: 2d.layer.ctm.resetTransform
+ desc: Tests resetTransform inside layers.
+ code: |
+ ctx.translate(40, 0);
+
+ ctx.beginLayer();
+ ctx.rotate(2);
+ ctx.beginLayer();
+ ctx.scale(5, 6);
+ ctx.resetTransform();
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 20, 20);
+ ctx.endLayer();
+ ctx.endLayer();
+
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 20, 20);
+ reference: |
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(0, 0, 20, 20);
+
+ ctx.translate(40, 0);
+ ctx.fillStyle = 'green';
+ ctx.fillRect(0, 0, 20, 20);
+
- name: 2d.layer.clip-inside
desc: Check clipping set inside the layer
size: [100, 100]
@@ -627,6 +778,49 @@
ctx.fillStyle = 'blue';
ctx.fillRect(10, 10, 80, 50);
+- name: 2d.layer.drawImage
+ size: [200, 200]
+ desc: >-
+ Checks that drawImage writes the image to the layer and not the parent
+ directly.
+ code: |
+ ctx.fillStyle = 'skyblue';
+ ctx.fillRect(0, 0, 100, 100);
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -10, dy: -10,
+ stdDeviation: 0, floodColor: 'navy'}});
+
+ ctx.fillStyle = 'maroon';
+ ctx.fillRect(20, 20, 50, 50);
+
+ ctx.globalCompositeOperation = 'xor';
+
+ // The image should xor only with the layer content, not the parents'.
+ const canvas_image = new OffscreenCanvas(200,200);
+ const ctx_image = canvas_image.getContext("2d");
+ ctx_image.fillStyle = 'pink';
+ ctx_image.fillRect(40, 40, 50, 50);
+ ctx.drawImage(canvas_image, 0, 0);
+
+ ctx.endLayer();
+ reference: |
+ ctx.fillStyle = 'skyblue';
+ ctx.fillRect(0, 0, 100, 100);
+
+ ctx.beginLayer({filter: {name: 'dropShadow', dx: -10, dy: -10,
+ stdDeviation: 0, floodColor: 'navy'}});
+
+ ctx.fillStyle = 'maroon';
+ ctx.fillRect(20, 20, 50, 50);
+
+ ctx.globalCompositeOperation = 'xor';
+
+ // Should xor only with the layer content, not the parents'.
+ ctx.fillStyle = 'pink';
+ ctx.fillRect(40, 40, 50, 50);
+
+ ctx.endLayer();
+
- name: 2d.layer.valid-calls
desc: No exception raised on {{ variant_desc }}.
variants: