blob: 0b346bb658e8316aa070a0bf30376c2c0cc54c93 [file] [log] [blame]
- meta: |
state = [ # some non-default values to test with
('strokeStyle', '"#ff0000"'),
('fillStyle', '"#ff0000"'),
('globalAlpha', 0.5),
('lineWidth', 0.5),
('lineCap', '"round"'),
('lineJoin', '"round"'),
('miterLimit', 0.5),
('shadowOffsetX', 5),
('shadowOffsetY', 5),
('shadowBlur', 5),
('shadowColor', '"#ff0000"'),
('globalCompositeOperation', '"copy"'),
]
for key,value in state:
tests.append( {
'name': '2d.state.saverestore.%s' % key,
'desc': 'save()/restore() works for %s' % key,
'testing': [ '2d.state.%s' % key ],
'code':
"""// Test that restore() undoes any modifications
var old = ctx.%(key)s;
ctx.save();
ctx.%(key)s = %(value)s;
ctx.restore();
@assert ctx.%(key)s === old;
// Also test that save() doesn't modify the values
ctx.%(key)s = %(value)s;
old = ctx.%(key)s;
// we're not interested in failures caused by get(set(x)) != x (e.g.
// from rounding), so compare against 'old' instead of against %(value)s
ctx.save();
@assert ctx.%(key)s === old;
ctx.restore();
""" % { 'key':key, 'value':value }
} )
tests.append( {
'name': 'initial.reset.2dstate',
'desc': 'Resetting the canvas state resets 2D state variables',
'testing': [ 'initial.reset' ],
'code':
"""offscreenCanvas.width = 100;
var default_val;
""" + "".join(
"""
default_val = ctx.%(key)s;
ctx.%(key)s = %(value)s;
offscreenCanvas.width = 100;
@assert ctx.%(key)s === default_val;
""" % { 'key':key, 'value':value }
for key,value in state),
} )
- name: 2d.state.saverestore.transformation
desc: save()/restore() affects the current transformation matrix
testing:
- 2d.state.transformation
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.save();
ctx.translate(200, 0);
ctx.restore();
ctx.fillStyle = '#f00';
ctx.fillRect(-200, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.state.saverestore.clip
desc: save()/restore() affects the clipping path
testing:
- 2d.state.clip
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.save();
ctx.rect(0, 0, 1, 1);
ctx.clip();
ctx.restore();
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.state.saverestore.path
desc: save()/restore() does not affect the current path
testing:
- 2d.state.path
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.save();
ctx.rect(0, 0, 100, 50);
ctx.restore();
ctx.fillStyle = '#0f0';
ctx.fill();
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.state.saverestore.bitmap
desc: save()/restore() does not affect the current bitmap
testing:
- 2d.state.bitmap
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.save();
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.restore();
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.state.saverestore.stack
desc: save()/restore() can be nested as a stack
testing:
- 2d.state.save
- 2d.state.restore
code: |
ctx.lineWidth = 1;
ctx.save();
ctx.lineWidth = 2;
ctx.save();
ctx.lineWidth = 3;
@assert ctx.lineWidth === 3;
ctx.restore();
@assert ctx.lineWidth === 2;
ctx.restore();
@assert ctx.lineWidth === 1;
- name: 2d.state.saverestore.stackdepth
desc: save()/restore() stack depth is not unreasonably limited
testing:
- 2d.state.save
- 2d.state.restore
code: |
var limit = 512;
for (var i = 1; i < limit; ++i)
{
ctx.save();
ctx.lineWidth = i;
}
for (var i = limit-1; i > 0; --i)
{
@assert ctx.lineWidth === i;
ctx.restore();
}
- name: 2d.state.saverestore.underflow
desc: restore() with an empty stack has no effect
testing:
- 2d.state.restore.underflow
code: |
for (var i = 0; i < 16; ++i)
ctx.restore();
ctx.lineWidth = 0.5;
ctx.restore();
@assert ctx.lineWidth === 0.5;
- name: 2d.transformation.order
desc: Transformations are applied in the right order
testing:
- 2d.transformation.order
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.scale(2, 1);
ctx.rotate(Math.PI / 2);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, -50, 50, 50);
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.transformation.scale.basic
desc: scale() works
testing:
- 2d.transformation.scale
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.scale(2, 4);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 12.5);
@assert pixel 90,40 == 0,255,0,255;
- name: 2d.transformation.scale.zero
desc: scale() with a scale factor of zero works
testing:
- 2d.transformation.scale
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.save();
ctx.translate(50, 0);
ctx.scale(0, 1);
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.restore();
ctx.save();
ctx.translate(0, 25);
ctx.scale(1, 0);
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.restore();
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.scale.negative
desc: scale() with negative scale factors works
testing:
- 2d.transformation.scale
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.save();
ctx.scale(-1, 1);
ctx.fillStyle = '#0f0';
ctx.fillRect(-50, 0, 50, 50);
ctx.restore();
ctx.save();
ctx.scale(1, -1);
ctx.fillStyle = '#0f0';
ctx.fillRect(50, -50, 50, 50);
ctx.restore();
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.transformation.scale.large
desc: scale() with large scale factors works
notes: Not really that large at all, but it hits the limits in Firefox.
testing:
- 2d.transformation.scale
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.scale(1e5, 1e5);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 1, 1);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.scale.nonfinite
desc: scale() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.scale(<0.1 Infinity -Infinity NaN>, <0.1 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.scale.multiple
desc: Multiple scale()s combine
testing:
- 2d.transformation.scale.multiple
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.scale(Math.sqrt(2), Math.sqrt(2));
ctx.scale(Math.sqrt(2), Math.sqrt(2));
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 25);
@assert pixel 90,40 == 0,255,0,255;
- name: 2d.transformation.rotate.zero
desc: rotate() by 0 does nothing
testing:
- 2d.transformation.rotate
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(0);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.rotate.radians
desc: rotate() uses radians
testing:
- 2d.transformation.rotate.radians
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(Math.PI); // should fail obviously if this is 3.1 degrees
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -50, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.rotate.direction
desc: rotate() is clockwise
testing:
- 2d.transformation.rotate.direction
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(Math.PI / 2);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, -100, 50, 100);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.rotate.wrap
desc: rotate() wraps large positive values correctly
testing:
- 2d.transformation.rotate
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(Math.PI * (1 + 4096)); // == pi (mod 2*pi)
// We need about pi +/- 0.001 in order to get correct-looking results
// 32-bit floats can store pi*4097 with precision 2^-10, so that should
// be safe enough on reasonable implementations
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -50, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,2 == 0,255,0,255;
@assert pixel 98,47 == 0,255,0,255;
- name: 2d.transformation.rotate.wrapnegative
desc: rotate() wraps large negative values correctly
testing:
- 2d.transformation.rotate
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.rotate(-Math.PI * (1 + 4096));
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -50, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,2 == 0,255,0,255;
@assert pixel 98,47 == 0,255,0,255;
- name: 2d.transformation.rotate.nonfinite
desc: rotate() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.rotate(<0.1 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.translate.basic
desc: translate() works
testing:
- 2d.transformation.translate
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -50, 100, 50);
@assert pixel 90,40 == 0,255,0,255;
- name: 2d.transformation.translate.nonfinite
desc: translate() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.translate(<0.1 Infinity -Infinity NaN>, <0.1 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.transform.identity
desc: transform() with the identity matrix does nothing
testing:
- 2d.transformation.transform
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.transform(1,0, 0,1, 0,0);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.transform.skewed
desc: transform() with skewy matrix transforms correctly
testing:
- 2d.transformation.transform
code: |
// Create green with a red square ring inside it
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(20, 10, 60, 30);
ctx.fillStyle = '#0f0';
ctx.fillRect(40, 20, 20, 10);
// Draw a skewed shape to fill that gap, to make sure it is aligned correctly
ctx.transform(1,4, 2,3, 5,6);
// Post-transform coordinates:
// [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
// Hence pre-transform coordinates:
var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
[-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
[-7.4,11.2]];
ctx.beginPath();
ctx.moveTo(pts[0][0], pts[0][1]);
for (var i = 0; i < pts.length; ++i)
ctx.lineTo(pts[i][0], pts[i][1]);
ctx.fill();
@assert pixel 21,11 == 0,255,0,255;
@assert pixel 79,11 == 0,255,0,255;
@assert pixel 21,39 == 0,255,0,255;
@assert pixel 79,39 == 0,255,0,255;
@assert pixel 39,19 == 0,255,0,255;
@assert pixel 61,19 == 0,255,0,255;
@assert pixel 39,31 == 0,255,0,255;
@assert pixel 61,31 == 0,255,0,255;
- name: 2d.transformation.transform.multiply
desc: transform() multiplies the CTM
testing:
- 2d.transformation.transform.multiply
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.transform(1,2, 3,4, 5,6);
ctx.transform(-2,1, 3/2,-1/2, 1,-2);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.transform.nonfinite
desc: transform() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.transform(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.transformation.setTransform.skewed
testing:
- 2d.transformation.setTransform
code: |
// Create green with a red square ring inside it
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(20, 10, 60, 30);
ctx.fillStyle = '#0f0';
ctx.fillRect(40, 20, 20, 10);
// Draw a skewed shape to fill that gap, to make sure it is aligned correctly
ctx.setTransform(1,4, 2,3, 5,6);
// Post-transform coordinates:
// [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]];
// Hence pre-transform coordinates:
var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2],
[-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2],
[-7.4,11.2]];
ctx.beginPath();
ctx.moveTo(pts[0][0], pts[0][1]);
for (var i = 0; i < pts.length; ++i)
ctx.lineTo(pts[i][0], pts[i][1]);
ctx.fill();
@assert pixel 21,11 == 0,255,0,255;
@assert pixel 79,11 == 0,255,0,255;
@assert pixel 21,39 == 0,255,0,255;
@assert pixel 79,39 == 0,255,0,255;
@assert pixel 39,19 == 0,255,0,255;
@assert pixel 61,19 == 0,255,0,255;
@assert pixel 39,31 == 0,255,0,255;
@assert pixel 61,31 == 0,255,0,255;
- name: 2d.transformation.setTransform.multiple
testing:
- 2d.transformation.setTransform.identity
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.setTransform(1/2,0, 0,1/2, 0,0);
ctx.setTransform(2,0, 0,2, 0,0);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 25);
@assert pixel 75,35 == 0,255,0,255;
- name: 2d.transformation.setTransform.nonfinite
desc: setTransform() with Infinity/NaN is ignored
testing:
- 2d.nonfinite
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.translate(100, 10);
@nonfinite ctx.setTransform(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
ctx.fillStyle = '#0f0';
ctx.fillRect(-100, -10, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.composite.globalAlpha.range
testing:
- 2d.composite.globalAlpha.range
code: |
ctx.globalAlpha = 0.5;
var a = ctx.globalAlpha; // might not be exactly 0.5, if it is rounded/quantised, so remember for future comparisons
ctx.globalAlpha = 1.1;
@assert ctx.globalAlpha === a;
ctx.globalAlpha = -0.1;
@assert ctx.globalAlpha === a;
ctx.globalAlpha = 0;
@assert ctx.globalAlpha === 0;
ctx.globalAlpha = 1;
@assert ctx.globalAlpha === 1;
- name: 2d.composite.globalAlpha.invalid
testing:
- 2d.composite.globalAlpha.range
code: |
ctx.globalAlpha = 0.5;
var a = ctx.globalAlpha; // might not be exactly 0.5, if it is rounded/quantised, so remember for future comparisons
ctx.globalAlpha = Infinity;
@assert ctx.globalAlpha === a;
ctx.globalAlpha = -Infinity;
@assert ctx.globalAlpha === a;
ctx.globalAlpha = NaN;
@assert ctx.globalAlpha === a;
- name: 2d.composite.globalAlpha.default
testing:
- 2d.composite.globalAlpha.default
code: |
@assert ctx.globalAlpha === 1.0;
- name: 2d.composite.globalAlpha.fill
testing:
- 2d.composite.globalAlpha.shape
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 ==~ 2,253,0,255;
- name: 2d.composite.globalAlpha.image
testing:
- 2d.composite.globalAlpha.image
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/red.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx.drawImage(response, 0, 0);
@assert pixel 50,25 ==~ 2,253,0,255;
});
- name: 2d.composite.globalAlpha.canvas
testing:
- 2d.composite.globalAlpha.image
code: |
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var ctx2 = offscreenCanvas2.getContext('2d');
ctx2.fillStyle = '#f00';
ctx2.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
ctx.drawImage(offscreenCanvas2, 0, 0);
@assert pixel 50,25 ==~ 2,253,0,255;
- name: 2d.composite.globalAlpha.imagepattern
testing:
- 2d.composite.globalAlpha.image
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/red.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 ==~ 2,253,0,255;
});
- name: 2d.composite.globalAlpha.canvaspattern
testing:
- 2d.composite.globalAlpha.image
code: |
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var ctx2 = offscreenCanvas2.getContext('2d');
ctx2.fillStyle = '#f00';
ctx2.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = ctx.createPattern(offscreenCanvas2, 'no-repeat');
ctx.globalAlpha = 0.01; // avoid any potential alpha=0 optimisations
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 ==~ 2,253,0,255;
- name: 2d.composite.globalAlpha.canvascopy
testing:
- 2d.composite.globalAlpha.image
code: |
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var ctx2 = offscreenCanvas2.getContext('2d');
ctx2.fillStyle = '#0f0';
ctx2.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.globalCompositeOperation = 'copy'
ctx.globalAlpha = 0.51;
ctx.drawImage(offscreenCanvas2, 0, 0);
@assert pixel 50,25 ==~ 0,255,0,130;
- meta: |
# Composite operation tests
# <http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2007-March/010608.html>
ops = [
# name FA FB
('source-over', '1', '1-aA'),
('destination-over', '1-aB', '1'),
('source-in', 'aB', '0'),
('destination-in', '0', 'aA'),
('source-out', '1-aB', '0'),
('destination-out', '0', '1-aA'),
('source-atop', 'aB', '1-aA'),
('destination-atop', '1-aB', 'aA'),
('xor', '1-aB', '1-aA'),
('copy', '1', '0'),
('lighter', '1', '1'),
]
# The ones that change the output when src = (0,0,0,0):
ops_trans = [ 'source-in', 'destination-in', 'source-out', 'destination-atop', 'copy' ];
def calc_output((RA, GA, BA, aA), (RB, GB, BB, aB), FA_code, FB_code):
rA, gA, bA = RA*aA, GA*aA, BA*aA
rB, gB, bB = RB*aB, GB*aB, BB*aB
FA = eval(FA_code)
FB = eval(FB_code)
rO = rA*FA + rB*FB
gO = gA*FA + gB*FB
bO = bA*FA + bB*FB
aO = aA*FA + aB*FB
rO = min(255, rO)
gO = min(255, gO)
bO = min(255, bO)
aO = min(1, aO)
if aO:
RO = rO / aO
GO = gO / aO
BO = bO / aO
else: RO = GO = BO = 0
return (RO, GO, BO, aO)
def to_test((r,g,b,a)):
return '%d,%d,%d,%d' % (round(r), round(g), round(b), round(a*255))
def to_cairo((r,g,b,a)):
return '%f,%f,%f,%f' % (r/255., g/255., b/255., a)
for (name, src, dest) in [
('solid', (255, 255, 0, 1.0), (0, 255, 255, 1.0)),
('transparent', (0, 0, 255, 0.75), (0, 255, 0, 0.5)),
# catches the atop, xor and lighter bugs in Opera 9.10
]:
for op, FA_code, FB_code in ops:
expected = calc_output(src, dest, FA_code, FB_code)
tests.append( {
'name': '2d.composite.%s.%s' % (name, op),
'testing': [ '2d.composite.%s' % op ],
'code': """
ctx.fillStyle = 'rgba%s';
ctx.fillRect(0, 0, 100, 50);
ctx.globalCompositeOperation = '%s';
ctx.fillStyle = 'rgba%s';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 ==~ %s +/- 5;
""" % (dest, op, src, to_test(expected)),
} )
for (name, src, dest) in [ ('image', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]:
for op, FA_code, FB_code in ops:
expected = calc_output(src, dest, FA_code, FB_code)
tests.append( {
'name': '2d.composite.%s.%s' % (name, op),
'testing': [ '2d.composite.%s' % op ],
'images': [ 'yellow75.png' ],
'code': """
ctx.fillStyle = 'rgba%s';
ctx.fillRect(0, 0, 100, 50);
ctx.globalCompositeOperation = '%s';
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/yellow75.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx.drawImage(response, 0, 0);
@assert pixel 50,25 ==~ %s +/- 5;
});
""" % (dest, op, to_test(expected)),
} )
for (name, src, dest) in [ ('canvas', (255, 255, 0, 0.75), (0, 255, 255, 0.5)) ]:
for op, FA_code, FB_code in ops:
expected = calc_output(src, dest, FA_code, FB_code)
tests.append( {
'name': '2d.composite.%s.%s' % (name, op),
'testing': [ '2d.composite.%s' % op ],
'images': [ 'yellow75.png' ],
'code': """
var offscreenCanvas2 = new OffscreenCanvas(offscreenCanvas.width, offscreenCanvas.height);
var ctx2 = offscreenCanvas2.getContext('2d');
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/yellow75.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx2.drawImage(response, 0, 0);
ctx.fillStyle = 'rgba%s';
ctx.fillRect(0, 0, 100, 50);
ctx.globalCompositeOperation = '%s';
ctx.drawImage(offscreenCanvas2, 0, 0);
@assert pixel 50,25 ==~ %s +/- 5;
});
""" % (dest, op, to_test(expected)),
} )
for (name, src, dest) in [ ('uncovered.fill', (0, 0, 255, 0.75), (0, 255, 0, 0.5)) ]:
for op, FA_code, FB_code in ops:
if op not in ops_trans: continue
expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
new_test = {
'name': '2d.composite.%s.%s' % (name, op),
'desc': 'fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
'testing': [ '2d.composite.%s' % op ],
'code': """
ctx.fillStyle = 'rgba%s';
ctx.fillRect(0, 0, 100, 50);
ctx.globalCompositeOperation = '%s';
ctx.fillStyle = 'rgba%s';
ctx.translate(0, 25);
ctx.fillRect(0, 50, 100, 50);
@assert pixel 50,25 ==~ %s +/- 5;
""" % (dest, op, src, to_test(expected0)),
}
if op == 'destination-in':
new_test['timeout'] = 'long'
tests.append(new_test)
for (name, src, dest) in [ ('uncovered.image', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
for op, FA_code, FB_code in ops:
if op not in ops_trans: continue
expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
tests.append( {
'name': '2d.composite.%s.%s' % (name, op),
'desc': 'drawImage() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
'testing': [ '2d.composite.%s' % op ],
'images': [ 'yellow.png' ],
'code': """
ctx.fillStyle = 'rgba%s';
ctx.fillRect(0, 0, 100, 50);
ctx.globalCompositeOperation = '%s';
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/yellow.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx.drawImage(response, 40, 40, 10, 10, 40, 50, 10, 10);
@assert pixel 15,15 ==~ %s +/- 5;
@assert pixel 50,25 ==~ %s +/- 5;
});
""" % (dest, op, to_test(expected0), to_test(expected0)),
} )
for (name, src, dest) in [ ('uncovered.nocontext', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
for op, FA_code, FB_code in ops:
if op not in ops_trans: continue
expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
tests.append( {
'name': '2d.composite.%s.%s' % (name, op),
'desc': 'drawImage() of a canvas with no context draws pixels as (0,0,0,0), and does not leave the pixels unchanged.',
'testing': [ '2d.composite.%s' % op ],
'code': """
ctx.fillStyle = 'rgba%s';
ctx.fillRect(0, 0, 100, 50);
ctx.globalCompositeOperation = '%s';
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
ctx.drawImage(offscreenCanvas2, 0, 0);
@assert pixel 50,25 ==~ %s +/- 5;
""" % (dest, op, to_test(expected0)),
} )
for (name, src, dest) in [ ('uncovered.pattern', (255, 255, 0, 1.0), (0, 255, 255, 0.5)) ]:
for op, FA_code, FB_code in ops:
if op not in ops_trans: continue
expected0 = calc_output((0,0,0,0.0), dest, FA_code, FB_code)
tests.append( {
'name': '2d.composite.%s.%s' % (name, op),
'desc': 'Pattern fill() draws pixels not covered by the source object as (0,0,0,0), and does not leave the pixels unchanged.',
'testing': [ '2d.composite.%s' % op ],
'images': [ 'yellow.png' ],
'code': """
ctx.fillStyle = 'rgba%s';
ctx.fillRect(0, 0, 100, 50);
ctx.globalCompositeOperation = '%s';
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/yellow.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx.fillStyle = ctx.createPattern(response, 'no-repeat');
ctx.fillRect(0, 50, 100, 50);
@assert pixel 50,25 ==~ %s +/- 5;
});
""" % (dest, op, to_test(expected0)),
} )
for op, FA_code, FB_code in ops:
tests.append( {
'name': '2d.composite.clip.%s' % (op),
'desc': 'fill() does not affect pixels outside the clip region.',
'testing': [ '2d.composite.%s' % op ],
'code': """
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.globalCompositeOperation = '%s';
ctx.rect(-20, -20, 10, 10);
ctx.clip();
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 50, 50);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
""" % (op),
} )
- name: 2d.composite.operation.get
testing:
- 2d.composite.operation
code: |
var modes = ['source-atop', 'source-in', 'source-out', 'source-over',
'destination-atop', 'destination-in', 'destination-out', 'destination-over',
'lighter', 'copy', 'xor'];
for (var i = 0; i < modes.length; ++i)
{
ctx.globalCompositeOperation = modes[i];
@assert ctx.globalCompositeOperation === modes[i];
}
- name: 2d.composite.operation.unrecognised
testing:
- 2d.composite.operation.unrecognised
code: |
ctx.globalCompositeOperation = 'xor';
ctx.globalCompositeOperation = 'nonexistent';
@assert ctx.globalCompositeOperation === 'xor';
- name: 2d.composite.operation.darker
testing:
- 2d.composite.operation.unrecognised
code: |
ctx.globalCompositeOperation = 'xor';
ctx.globalCompositeOperation = 'darker';
@assert ctx.globalCompositeOperation === 'xor';
- name: 2d.composite.operation.over
testing:
- 2d.composite.operation.unrecognised
code: |
ctx.globalCompositeOperation = 'xor';
ctx.globalCompositeOperation = 'over';
@assert ctx.globalCompositeOperation === 'xor';
- name: 2d.composite.operation.clear
testing:
- 2d.composite.operation.clear
code: |
ctx.globalCompositeOperation = 'xor';
ctx.globalCompositeOperation = 'clear';
@assert ctx.globalCompositeOperation === 'clear';
- name: 2d.composite.operation.highlight
testing:
- 2d.composite.operation.unrecognised
code: |
ctx.globalCompositeOperation = 'xor';
ctx.globalCompositeOperation = 'highlight';
@assert ctx.globalCompositeOperation === 'xor';
- name: 2d.composite.operation.nullsuffix
testing:
- 2d.composite.operation.exact
code: |
ctx.globalCompositeOperation = 'xor';
ctx.globalCompositeOperation = 'source-over\0';
@assert ctx.globalCompositeOperation === 'xor';
- name: 2d.composite.operation.casesensitive
testing:
- 2d.composite.operation.casesensitive
code: |
ctx.globalCompositeOperation = 'xor';
ctx.globalCompositeOperation = 'Source-over';
@assert ctx.globalCompositeOperation === 'xor';
- name: 2d.composite.operation.default
testing:
- 2d.composite.operation.default
code: |
@assert ctx.globalCompositeOperation === 'source-over';
- meta: |
# Colour parsing tests
# Try most of the CSS3 Color <color> values - http://www.w3.org/TR/css3-color/#colorunits
big_float = '1' + ('0' * 39)
big_double = '1' + ('0' * 310)
for name, string, r,g,b,a, notes in [
('html4', 'limE', 0,255,0,255, ""),
('hex3', '#0f0', 0,255,0,255, ""),
('hex4', '#0f0f', 0,255,0,255, ""),
('hex6', '#00fF00', 0,255,0,255, ""),
('hex8', '#00ff00ff', 0,255,0,255, ""),
('rgb-num', 'rgb(0,255,0)', 0,255,0,255, ""),
('rgb-clamp-1', 'rgb(-1000, 1000, -1000)', 0,255,0,255, 'Assumes colours are clamped to [0,255].'),
('rgb-clamp-2', 'rgb(-200%, 200%, -200%)', 0,255,0,255, 'Assumes colours are clamped to [0,255].'),
('rgb-clamp-3', 'rgb(-2147483649, 4294967298, -18446744073709551619)', 0,255,0,255, 'Assumes colours are clamped to [0,255].'),
('rgb-clamp-4', 'rgb(-'+big_float+', '+big_float+', -'+big_float+')', 0,255,0,255, 'Assumes colours are clamped to [0,255].'),
('rgb-clamp-5', 'rgb(-'+big_double+', '+big_double+', -'+big_double+')', 0,255,0,255, 'Assumes colours are clamped to [0,255].'),
('rgb-percent', 'rgb(0% ,100% ,0%)', 0,255,0,255, 'CSS3 Color says "The integer value 255 corresponds to 100%". (In particular, it is not 254...)'),
('rgb-eof', 'rgb(0, 255, 0', 0,255,0,255, ""), # see CSS2.1 4.2 "Unexpected end of style sheet"
('rgba-solid-1', 'rgba( 0 , 255 , 0 , 1 )', 0,255,0,255, ""),
('rgba-solid-2', 'rgba( 0 , 255 , 0 , 1.0 )', 0,255,0,255, ""),
('rgba-solid-3', 'rgba( 0 , 255 , 0 , +1 )', 0,255,0,255, ""),
('rgba-solid-4', 'rgba( -0 , 255 , +0 , 1 )', 0,255,0,255, ""),
('rgba-num-1', 'rgba( 0 , 255 , 0 , .499 )', 0,255,0,127, ""),
('rgba-num-2', 'rgba( 0 , 255 , 0 , 0.499 )', 0,255,0,127, ""),
('rgba-percent', 'rgba(0%,100%,0%,0.499)', 0,255,0,127, ""), # 0.499*255 rounds to 127, both down and nearest, so it should be safe
('rgba-clamp-1', 'rgba(0, 255, 0, -2)', 0,0,0,0, ""),
('rgba-clamp-2', 'rgba(0, 255, 0, 2)', 0,255,0,255, ""),
('rgba-eof', 'rgba(0, 255, 0, 1', 0,255,0,255, ""),
('transparent-1', 'transparent', 0,0,0,0, ""),
('transparent-2', 'TrAnSpArEnT', 0,0,0,0, ""),
('hsl-1', 'hsl(120, 100%, 50%)', 0,255,0,255, ""),
('hsl-2', 'hsl( -240 , 100% , 50% )', 0,255,0,255, ""),
('hsl-3', 'hsl(360120, 100%, 50%)', 0,255,0,255, ""),
('hsl-4', 'hsl(-360240, 100%, 50%)', 0,255,0,255, ""),
('hsl-5', 'hsl(120.0, 100.0%, 50.0%)', 0,255,0,255, ""),
('hsl-6', 'hsl(+120, +100%, +50%)', 0,255,0,255, ""),
('hsl-clamp-1', 'hsl(120, 200%, 50%)', 0,255,0,255, ""),
('hsl-clamp-2', 'hsl(120, -200%, 49.9%)', 127,127,127,255, ""),
('hsl-clamp-3', 'hsl(120, 100%, 200%)', 255,255,255,255, ""),
('hsl-clamp-4', 'hsl(120, 100%, -200%)', 0,0,0,255, ""),
('hsla-1', 'hsla(120, 100%, 50%, 0.499)', 0,255,0,127, ""),
('hsla-2', 'hsla( 120.0 , 100.0% , 50.0% , 1 )', 0,255,0,255, ""),
('hsla-clamp-1', 'hsla(120, 200%, 50%, 1)', 0,255,0,255, ""),
('hsla-clamp-2', 'hsla(120, -200%, 49.9%, 1)', 127,127,127,255, ""),
('hsla-clamp-3', 'hsla(120, 100%, 200%, 1)', 255,255,255,255, ""),
('hsla-clamp-4', 'hsla(120, 100%, -200%, 1)', 0,0,0,255, ""),
('hsla-clamp-5', 'hsla(120, 100%, 50%, 2)', 0,255,0,255, ""),
('hsla-clamp-6', 'hsla(120, 100%, 0%, -2)', 0,0,0,0, ""),
('svg-1', 'gray', 128,128,128,255, ""),
('svg-2', 'grey', 128,128,128,255, ""),
# css-color-4 rgb() color function
# https://drafts.csswg.org/css-color/#numeric-rgb
('css-color-4-rgb-1', 'rgb(0, 255.0, 0)', 0,255,0,255, ""),
('css-color-4-rgb-2', 'rgb(0, 255, 0, 0.2)', 0,255,0,51, ""),
('css-color-4-rgb-3', 'rgb(0, 255, 0, 20%)', 0,255,0,51, ""),
('css-color-4-rgb-4', 'rgb(0 255 0)', 0,255,0,255, ""),
('css-color-4-rgb-5', 'rgb(0 255 0 / 0.2)', 0,255,0,51, ""),
('css-color-4-rgb-6', 'rgb(0 255 0 / 20%)', 0,255,0,51, ""),
('css-color-4-rgba-1', 'rgba(0, 255.0, 0)', 0,255,0,255, ""),
('css-color-4-rgba-2', 'rgba(0, 255, 0, 0.2)', 0,255,0,51, ""),
('css-color-4-rgba-3', 'rgba(0, 255, 0, 20%)', 0,255,0,51, ""),
('css-color-4-rgba-4', 'rgba(0 255 0)', 0,255,0,255, ""),
('css-color-4-rgba-5', 'rgba(0 255 0 / 0.2)', 0,255,0,51, ""),
('css-color-4-rgba-6', 'rgba(0 255 0 / 20%)', 0,255,0,51, ""),
# css-color-4 hsl() color function
# https://drafts.csswg.org/css-color/#the-hsl-notation
('css-color-4-hsl-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""),
('css-color-4-hsl-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""),
('css-color-4-hsl-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
('css-color-4-hsl-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""),
('css-color-4-hsl-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
('css-color-4-hsl-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""),
('css-color-4-hsl-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""),
('css-color-4-hsl-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""),
('css-color-4-hsl-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""),
('css-color-4-hsla-1', 'hsl(120 100.0% 50.0%)', 0,255,0,255, ""),
('css-color-4-hsla-2', 'hsl(120 100.0% 50.0% / 0.2)', 0,255,0,51, ""),
('css-color-4-hsla-3', 'hsl(120.0, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
('css-color-4-hsla-4', 'hsl(120.0, 100.0%, 50.0%, 20%)', 0,255,0,51, ""),
('css-color-4-hsla-5', 'hsl(120deg, 100.0%, 50.0%, 0.2)', 0,255,0,51, ""),
('css-color-4-hsla-6', 'hsl(120deg, 100.0%, 50.0%)', 0,255,0,255, ""),
('css-color-4-hsla-7', 'hsl(133.33333333grad, 100.0%, 50.0%)', 0,255,0,255, ""),
('css-color-4-hsla-8', 'hsl(2.0943951024rad, 100.0%, 50.0%)', 0,255,0,255, ""),
('css-color-4-hsla-9', 'hsl(0.3333333333turn, 100.0%, 50.0%)', 0,255,0,255, ""),
# currentColor is handled later
]:
# TODO: test by retrieving fillStyle, instead of actually drawing?
# TODO: test strokeStyle, shadowColor in the same way
test = {
'name': '2d.fillStyle.parse.%s' % name,
'testing': [ '2d.colours.parse' ],
'notes': notes,
'code': """
ctx.fillStyle = '#f00';
ctx.fillStyle = '%s';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == %d,%d,%d,%d;
""" % (string, r,g,b,a),
}
tests.append(test)
# Also test that invalid colours are ignored
for name, string in [
('hex1', '#f'),
('hex2', '#f0'),
('hex3', '#g00'),
('hex4', '#fg00'),
('hex5', '#ff000'),
('hex6', '#fg0000'),
('hex7', '#ff0000f'),
('hex8', '#fg0000ff'),
('rgb-1', 'rgb(255.0, 0, 0,)'),
('rgb-2', 'rgb(100%, 0, 0)'),
('rgb-3', 'rgb(255, - 1, 0)'),
('rgba-1', 'rgba(100%, 0, 0, 1)'),
('rgba-2', 'rgba(255, 0, 0, 1. 0)'),
('rgba-3', 'rgba(255, 0, 0, 1.)'),
('rgba-4', 'rgba(255, 0, 0, '),
('rgba-5', 'rgba(255, 0, 0, 1,)'),
('hsl-1', 'hsl(0%, 100%, 50%)'),
('hsl-2', 'hsl(z, 100%, 50%)'),
('hsl-3', 'hsl(0, 0, 50%)'),
('hsl-4', 'hsl(0, 100%, 0)'),
('hsl-5', 'hsl(0, 100.%, 50%)'),
('hsl-6', 'hsl(0, 100%, 50%,)'),
('hsla-1', 'hsla(0%, 100%, 50%, 1)'),
('hsla-2', 'hsla(0, 0, 50%, 1)'),
('hsla-3', 'hsla(0, 0, 50%, 1,)'),
('name-1', 'darkbrown'),
('name-2', 'firebrick1'),
('name-3', 'red blue'),
('name-4', '"red"'),
('name-5', '"red'),
# css-color-4 color function
# comma and comma-less expressions should not mix together.
('css-color-4-rgb-1', 'rgb(255, 0, 0 / 1)'),
('css-color-4-rgb-2', 'rgb(255 0 0, 1)'),
('css-color-4-rgb-3', 'rgb(255, 0 0)'),
('css-color-4-rgba-1', 'rgba(255, 0, 0 / 1)'),
('css-color-4-rgba-2', 'rgba(255 0 0, 1)'),
('css-color-4-rgba-3', 'rgba(255, 0 0)'),
('css-color-4-hsl-1', 'hsl(0, 100%, 50% / 1)'),
('css-color-4-hsl-2', 'hsl(0 100% 50%, 1)'),
('css-color-4-hsl-3', 'hsl(0, 100% 50%)'),
('css-color-4-hsla-1', 'hsla(0, 100%, 50% / 1)'),
('css-color-4-hsla-2', 'hsla(0 100% 50%, 1)'),
('css-color-4-hsla-3', 'hsla(0, 100% 50%)'),
# trailing slash
('css-color-4-rgb-4', 'rgb(0 0 0 /)'),
('css-color-4-rgb-5', 'rgb(0, 0, 0 /)'),
('css-color-4-hsl-4', 'hsl(0 100% 50% /)'),
('css-color-4-hsl-5', 'hsl(0, 100%, 50% /)'),
]:
test = {
'name': '2d.fillStyle.parse.invalid.%s' % name,
'testing': [ '2d.colours.parse' ],
'code': """
ctx.fillStyle = '#0f0';
try { ctx.fillStyle = '%s'; } catch (e) { } // this shouldn't throw, but it shouldn't matter here if it does
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
""" % string,
}
tests.append(test)
# Some can't have positive tests, only negative tests, because we don't know what colour they're meant to be
for name, string in [
('system', 'ThreeDDarkShadow'),
#('flavor', 'flavor'), # removed from latest CSS3 Color drafts
]:
test = {
'name': '2d.fillStyle.parse.%s' % name,
'testing': [ '2d.colours.parse' ],
'code': """
ctx.fillStyle = '#f00';
ctx.fillStyle = '%s';
@assert ctx.fillStyle =~ /^#(?!(FF0000|ff0000|f00)$)/; // test that it's not red
""" % (string,),
}
tests.append(test)
- name: 2d.fillStyle.invalidstring
testing:
- 2d.colours.invalidstring
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillStyle = 'invalid';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.fillStyle.invalidtype
testing:
- 2d.colours.invalidtype
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillStyle = null;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.fillStyle.get.solid
testing:
- 2d.colours.getcolour
- 2d.serializecolour.solid
code: |
ctx.fillStyle = '#fa0';
@assert ctx.fillStyle === '#ffaa00';
- name: 2d.fillStyle.get.semitransparent
testing:
- 2d.colours.getcolour
- 2d.serializecolour.transparent
code: |
ctx.fillStyle = 'rgba(255,255,255,0.45)';
@assert ctx.fillStyle =~ /^rgba\(255, 255, 255, 0\.4\d+\)$/;
- name: 2d.fillStyle.get.transparent
testing:
- 2d.colours.getcolour
- 2d.serializecolour.transparent
code: |
ctx.fillStyle = 'rgba(0,0,0,0)';
@assert ctx.fillStyle === 'rgba(0, 0, 0, 0)';
- name: 2d.fillStyle.default
testing:
- 2d.colours.default
code: |
@assert ctx.fillStyle === '#000000';
- name: 2d.strokeStyle.default
testing:
- 2d.colours.default
code: |
@assert ctx.strokeStyle === '#000000';
- name: 2d.gradient.interpolate.solid
testing:
- 2d.gradient.interpolate.linear
code: |
var g = ctx.createLinearGradient(0, 0, 100, 0);
g.addColorStop(0, '#0f0');
g.addColorStop(1, '#0f0');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.gradient.interpolate.colour
testing:
- 2d.gradient.interpolate.linear
code: |
var g = ctx.createLinearGradient(0, 0, 100, 0);
g.addColorStop(0, '#ff0');
g.addColorStop(1, '#00f');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 25,25 ==~ 191,191,63,255 +/- 3;
@assert pixel 50,25 ==~ 127,127,127,255 +/- 3;
@assert pixel 75,25 ==~ 63,63,191,255 +/- 3;
- name: 2d.gradient.interpolate.alpha
testing:
- 2d.gradient.interpolate.linear
code: |
ctx.fillStyle = '#ff0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createLinearGradient(0, 0, 100, 0);
g.addColorStop(0, 'rgba(0,0,255, 0)');
g.addColorStop(1, 'rgba(0,0,255, 1)');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 25,25 ==~ 191,191,63,255 +/- 3;
@assert pixel 50,25 ==~ 127,127,127,255 +/- 3;
@assert pixel 75,25 ==~ 63,63,191,255 +/- 3;
- name: 2d.gradient.interpolate.colouralpha
testing:
- 2d.gradient.interpolate.alpha
code: |
var g = ctx.createLinearGradient(0, 0, 100, 0);
g.addColorStop(0, 'rgba(255,255,0, 0)');
g.addColorStop(1, 'rgba(0,0,255, 1)');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 25,25 ==~ 190,190,65,65 +/- 3;
@assert pixel 50,25 ==~ 126,126,128,128 +/- 3;
@assert pixel 75,25 ==~ 62,62,192,192 +/- 3;
- name: 2d.gradient.interpolate.outside
testing:
- 2d.gradient.outside.first
- 2d.gradient.outside.last
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createLinearGradient(25, 0, 75, 0);
g.addColorStop(0.4, '#0f0');
g.addColorStop(0.6, '#0f0');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 20,25 ==~ 0,255,0,255;
@assert pixel 50,25 ==~ 0,255,0,255;
@assert pixel 80,25 ==~ 0,255,0,255;
- name: 2d.gradient.interpolate.zerosize.fill
testing:
- 2d.gradient.linear.zerosize
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.rect(0, 0, 100, 50);
ctx.fill();
@assert pixel 40,20 == 0,255,0,255;
- name: 2d.gradient.interpolate.zerosize.stroke
testing:
- 2d.gradient.linear.zerosize
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.strokeStyle = g;
ctx.rect(20, 20, 60, 10);
ctx.stroke();
@assert pixel 19,19 == 0,255,0,255;
@assert pixel 20,19 == 0,255,0,255;
@assert pixel 21,19 == 0,255,0,255;
@assert pixel 19,20 == 0,255,0,255;
@assert pixel 20,20 == 0,255,0,255;
@assert pixel 21,20 == 0,255,0,255;
@assert pixel 19,21 == 0,255,0,255;
@assert pixel 20,21 == 0,255,0,255;
@assert pixel 21,21 == 0,255,0,255;
- name: 2d.gradient.interpolate.zerosize.fillRect
testing:
- 2d.gradient.linear.zerosize
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 40,20 == 0,255,0,255; @moz-todo
- name: 2d.gradient.interpolate.zerosize.strokeRect
testing:
- 2d.gradient.linear.zerosize
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createLinearGradient(50, 25, 50, 25); // zero-length line (undefined direction)
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.strokeStyle = g;
ctx.strokeRect(20, 20, 60, 10);
@assert pixel 19,19 == 0,255,0,255;
@assert pixel 20,19 == 0,255,0,255;
@assert pixel 21,19 == 0,255,0,255;
@assert pixel 19,20 == 0,255,0,255;
@assert pixel 20,20 == 0,255,0,255;
@assert pixel 21,20 == 0,255,0,255;
@assert pixel 19,21 == 0,255,0,255;
@assert pixel 20,21 == 0,255,0,255;
@assert pixel 21,21 == 0,255,0,255;
- name: 2d.gradient.interpolate.vertical
testing:
- 2d.gradient.interpolate.linear
code: |
var g = ctx.createLinearGradient(0, 0, 0, 50);
g.addColorStop(0, '#ff0');
g.addColorStop(1, '#00f');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,12 ==~ 191,191,63,255 +/- 10;
@assert pixel 50,25 ==~ 127,127,127,255 +/- 5;
@assert pixel 50,37 ==~ 63,63,191,255 +/- 10;
- name: 2d.gradient.interpolate.multiple
testing:
- 2d.gradient.interpolate.linear
code: |
offscreenCanvas.width = 200;
var g = ctx.createLinearGradient(0, 0, 200, 0);
g.addColorStop(0, '#ff0');
g.addColorStop(0.5, '#0ff');
g.addColorStop(1, '#f0f');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 200, 50);
@assert pixel 50,25 ==~ 127,255,127,255 +/- 3;
@assert pixel 100,25 ==~ 0,255,255,255 +/- 3;
@assert pixel 150,25 ==~ 127,127,255,255 +/- 3;
- name: 2d.gradient.interpolate.overlap
testing:
- 2d.gradient.interpolate.overlap
code: |
offscreenCanvas.width = 200;
var g = ctx.createLinearGradient(0, 0, 200, 0);
g.addColorStop(0, '#f00');
g.addColorStop(0, '#ff0');
g.addColorStop(0.25, '#00f');
g.addColorStop(0.25, '#0f0');
g.addColorStop(0.25, '#0f0');
g.addColorStop(0.25, '#0f0');
g.addColorStop(0.25, '#ff0');
g.addColorStop(0.5, '#00f');
g.addColorStop(0.5, '#0f0');
g.addColorStop(0.75, '#00f');
g.addColorStop(0.75, '#f00');
g.addColorStop(0.75, '#ff0');
g.addColorStop(0.5, '#0f0');
g.addColorStop(0.5, '#0f0');
g.addColorStop(0.5, '#ff0');
g.addColorStop(1, '#00f');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 200, 50);
@assert pixel 49,25 ==~ 0,0,255,255 +/- 16;
@assert pixel 51,25 ==~ 255,255,0,255 +/- 16;
@assert pixel 99,25 ==~ 0,0,255,255 +/- 16;
@assert pixel 101,25 ==~ 255,255,0,255 +/- 16;
@assert pixel 149,25 ==~ 0,0,255,255 +/- 16;
@assert pixel 151,25 ==~ 255,255,0,255 +/- 16;
- name: 2d.gradient.interpolate.overlap2
testing:
- 2d.gradient.interpolate.overlap
code: |
var g = ctx.createLinearGradient(0, 0, 100, 0);
var ps = [ 0, 1/10, 1/4, 1/3, 1/2, 3/4, 1 ];
for (var p = 0; p < ps.length; ++p)
{
g.addColorStop(ps[p], '#0f0');
for (var i = 0; i < 15; ++i)
g.addColorStop(ps[p], '#f00');
g.addColorStop(ps[p], '#0f0');
}
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 30,25 == 0,255,0,255;
@assert pixel 40,25 == 0,255,0,255;
@assert pixel 60,25 == 0,255,0,255;
@assert pixel 80,25 == 0,255,0,255;
- name: 2d.gradient.empty
testing:
- 2d.gradient.empty
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createLinearGradient(0, 0, 0, 50);
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 ==~ 0,255,0,255;
- name: 2d.gradient.object.update
testing:
- 2d.gradient.update
code: |
var g = ctx.createLinearGradient(-100, 0, 200, 0);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
g.addColorStop(0.1, '#0f0');
g.addColorStop(0.9, '#0f0');
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 ==~ 0,255,0,255;
- name: 2d.gradient.object.compare
testing:
- 2d.gradient.object
code: |
var g1 = ctx.createLinearGradient(0, 0, 100, 0);
var g2 = ctx.createLinearGradient(0, 0, 100, 0);
@assert g1 !== g2;
ctx.fillStyle = g1;
@assert ctx.fillStyle === g1;
- name: 2d.gradient.object.crosscanvas
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var g = offscreenCanvas2.getContext('2d').createLinearGradient(0, 0, 100, 0);
g.addColorStop(0, '#0f0');
g.addColorStop(1, '#0f0');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 ==~ 0,255,0,255;
- name: 2d.gradient.object.invalidoffset
testing:
- 2d.gradient.invalidoffset
code: |
var g = ctx.createLinearGradient(0, 0, 100, 0);
@assert throws INDEX_SIZE_ERR g.addColorStop(-1, '#000');
@assert throws INDEX_SIZE_ERR g.addColorStop(2, '#000');
@assert throws TypeError g.addColorStop(Infinity, '#000');
@assert throws TypeError g.addColorStop(-Infinity, '#000');
@assert throws TypeError g.addColorStop(NaN, '#000');
- name: 2d.gradient.object.invalidcolour
testing:
- 2d.gradient.invalidcolour
code: |
var g = ctx.createLinearGradient(0, 0, 100, 0);
@assert throws SYNTAX_ERR g.addColorStop(0, "");
@assert throws SYNTAX_ERR g.addColorStop(0, 'null');
@assert throws SYNTAX_ERR g.addColorStop(0, 'undefined');
@assert throws SYNTAX_ERR g.addColorStop(0, null);
@assert throws SYNTAX_ERR g.addColorStop(0, undefined);
- name: 2d.gradient.linear.nonfinite
desc: createLinearGradient() throws TypeError if arguments are not finite
testing:
- 2d.gradient.linear.nonfinite
code: |
@nonfinite @assert throws TypeError ctx.createLinearGradient(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>);
- name: 2d.gradient.linear.transform.1
desc: Linear gradient coordinates are relative to the coordinate space at the time of filling
testing:
- 2d.gradient.linear.transform
code: |
var g = ctx.createLinearGradient(0, 0, 200, 0);
g.addColorStop(0, '#f00');
g.addColorStop(0.25, '#0f0');
g.addColorStop(0.75, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.translate(-50, 0);
ctx.fillRect(50, 0, 100, 50);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.gradient.linear.transform.2
desc: Linear gradient coordinates are relative to the coordinate space at the time of filling
testing:
- 2d.gradient.linear.transform
code: |
ctx.translate(100, 0);
var g = ctx.createLinearGradient(0, 0, 200, 0);
g.addColorStop(0, '#f00');
g.addColorStop(0.25, '#0f0');
g.addColorStop(0.75, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.translate(-150, 0);
ctx.fillRect(50, 0, 100, 50);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.gradient.linear.transform.3
desc: Linear gradient transforms do not experience broken caching effects
testing:
- 2d.gradient.linear.transform
code: |
var g = ctx.createLinearGradient(0, 0, 200, 0);
g.addColorStop(0, '#f00');
g.addColorStop(0.25, '#0f0');
g.addColorStop(0.75, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
ctx.translate(-50, 0);
ctx.fillRect(50, 0, 100, 50);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.gradient.radial.negative
desc: createRadialGradient() throws INDEX_SIZE_ERR if either radius is negative
testing:
- 2d.gradient.radial.negative
code: |
@assert throws INDEX_SIZE_ERR ctx.createRadialGradient(0, 0, -0.1, 0, 0, 1);
@assert throws INDEX_SIZE_ERR ctx.createRadialGradient(0, 0, 1, 0, 0, -0.1);
@assert throws INDEX_SIZE_ERR ctx.createRadialGradient(0, 0, -0.1, 0, 0, -0.1);
- name: 2d.gradient.radial.nonfinite
desc: createRadialGradient() throws TypeError if arguments are not finite
testing:
- 2d.gradient.radial.nonfinite
code: |
@nonfinite @assert throws TypeError ctx.createRadialGradient(<0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <0 Infinity -Infinity NaN>, <1 Infinity -Infinity NaN>);
- name: 2d.gradient.radial.inside1
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(50, 25, 100, 50, 25, 200);
g.addColorStop(0, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.inside2
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#0f0');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.inside3
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(50, 25, 200, 50, 25, 100);
g.addColorStop(0, '#f00');
g.addColorStop(0.993, '#f00');
g.addColorStop(1, '#0f0');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.outside1
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(200, 25, 10, 200, 25, 20);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#0f0');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.outside2
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
g.addColorStop(0, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.outside3
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(200, 25, 20, 200, 25, 10);
g.addColorStop(0, '#0f0');
g.addColorStop(0.001, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.touch1
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(150, 25, 50, 200, 25, 100);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255; @moz-todo
@assert pixel 50,1 == 0,255,0,255; @moz-todo
@assert pixel 98,1 == 0,255,0,255; @moz-todo
@assert pixel 1,25 == 0,255,0,255; @moz-todo
@assert pixel 50,25 == 0,255,0,255; @moz-todo
@assert pixel 98,25 == 0,255,0,255; @moz-todo
@assert pixel 1,48 == 0,255,0,255; @moz-todo
@assert pixel 50,48 == 0,255,0,255; @moz-todo
@assert pixel 98,48 == 0,255,0,255; @moz-todo
- name: 2d.gradient.radial.touch2
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
g.addColorStop(0, '#f00');
g.addColorStop(0.01, '#0f0');
g.addColorStop(0.99, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.touch3
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(120, -15, 25, 140, -30, 50);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255; @moz-todo
@assert pixel 50,1 == 0,255,0,255; @moz-todo
@assert pixel 98,1 == 0,255,0,255; @moz-todo
@assert pixel 1,25 == 0,255,0,255; @moz-todo
@assert pixel 50,25 == 0,255,0,255; @moz-todo
@assert pixel 98,25 == 0,255,0,255; @moz-todo
@assert pixel 1,48 == 0,255,0,255; @moz-todo
@assert pixel 50,48 == 0,255,0,255; @moz-todo
@assert pixel 98,48 == 0,255,0,255; @moz-todo
- name: 2d.gradient.radial.equal
testing:
- 2d.gradient.radial.equal
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(50, 25, 20, 50, 25, 20);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255; @moz-todo
@assert pixel 50,1 == 0,255,0,255; @moz-todo
@assert pixel 98,1 == 0,255,0,255; @moz-todo
@assert pixel 1,25 == 0,255,0,255; @moz-todo
@assert pixel 50,25 == 0,255,0,255; @moz-todo
@assert pixel 98,25 == 0,255,0,255; @moz-todo
@assert pixel 1,48 == 0,255,0,255; @moz-todo
@assert pixel 50,48 == 0,255,0,255; @moz-todo
@assert pixel 98,48 == 0,255,0,255; @moz-todo
- name: 2d.gradient.radial.cone.behind
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(120, 25, 10, 211, 25, 100);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255; @moz-todo
@assert pixel 50,1 == 0,255,0,255; @moz-todo
@assert pixel 98,1 == 0,255,0,255; @moz-todo
@assert pixel 1,25 == 0,255,0,255; @moz-todo
@assert pixel 50,25 == 0,255,0,255; @moz-todo
@assert pixel 98,25 == 0,255,0,255; @moz-todo
@assert pixel 1,48 == 0,255,0,255; @moz-todo
@assert pixel 50,48 == 0,255,0,255; @moz-todo
@assert pixel 98,48 == 0,255,0,255; @moz-todo
- name: 2d.gradient.radial.cone.front
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(311, 25, 10, 210, 25, 100);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#0f0');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.cone.bottom
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 101);
g.addColorStop(0, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.cone.top
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(230, 25, 100, 100, 25, 101);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#0f0');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.cone.beside
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(0, 100, 40, 100, 100, 50);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255; @moz-todo
@assert pixel 50,1 == 0,255,0,255; @moz-todo
@assert pixel 98,1 == 0,255,0,255; @moz-todo
@assert pixel 1,25 == 0,255,0,255; @moz-todo
@assert pixel 50,25 == 0,255,0,255; @moz-todo
@assert pixel 98,25 == 0,255,0,255; @moz-todo
@assert pixel 1,48 == 0,255,0,255; @moz-todo
@assert pixel 50,48 == 0,255,0,255; @moz-todo
@assert pixel 98,48 == 0,255,0,255; @moz-todo
- name: 2d.gradient.radial.cone.cylinder
testing:
- 2d.gradient.radial.rendering
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(210, 25, 100, 230, 25, 100);
g.addColorStop(0, '#0f0');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.cone.shape1
testing:
- 2d.gradient.radial.rendering
code: |
var tol = 1; // tolerance to avoid antialiasing artifacts
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.moveTo(30+tol, 40);
ctx.lineTo(110, -20+tol);
ctx.lineTo(110, 100-tol);
ctx.fill();
var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
g.addColorStop(0, '#0f0');
g.addColorStop(1, '#0f0');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.cone.shape2
testing:
- 2d.gradient.radial.rendering
code: |
var tol = 1; // tolerance to avoid antialiasing artifacts
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var g = ctx.createRadialGradient(30+10*5/2, 40, 10*3/2, 30+10*15/4, 40, 10*9/4);
g.addColorStop(0, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.beginPath();
ctx.moveTo(30-tol, 40);
ctx.lineTo(110, -20-tol);
ctx.lineTo(110, 100+tol);
ctx.fill();
@assert pixel 1,1 == 0,255,0,255; @moz-todo
@assert pixel 50,1 == 0,255,0,255; @moz-todo
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255; @moz-todo
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255; @moz-todo
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.gradient.radial.transform.1
desc: Radial gradient coordinates are relative to the coordinate space at the time of filling
testing:
- 2d.gradient.radial.transform
code: |
var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
g.addColorStop(0, '#0f0');
g.addColorStop(0.5, '#0f0');
g.addColorStop(0.51, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.translate(50, 25);
ctx.scale(10, 10);
ctx.fillRect(-5, -2.5, 10, 5);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.gradient.radial.transform.2
desc: Radial gradient coordinates are relative to the coordinate space at the time of filling
testing:
- 2d.gradient.radial.transform
code: |
ctx.translate(100, 0);
var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
g.addColorStop(0, '#0f0');
g.addColorStop(0.5, '#0f0');
g.addColorStop(0.51, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.translate(-50, 25);
ctx.scale(10, 10);
ctx.fillRect(-5, -2.5, 10, 5);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.gradient.radial.transform.3
desc: Radial gradient transforms do not experience broken caching effects
testing:
- 2d.gradient.radial.transform
code: |
var g = ctx.createRadialGradient(0, 0, 0, 0, 0, 11.2);
g.addColorStop(0, '#0f0');
g.addColorStop(0.5, '#0f0');
g.addColorStop(0.51, '#f00');
g.addColorStop(1, '#f00');
ctx.fillStyle = g;
ctx.fillRect(0, 0, 100, 50);
ctx.translate(50, 25);
ctx.scale(10, 10);
ctx.fillRect(-5, -2.5, 10, 5);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.pattern.basic.image
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.basic.canvas
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var ctx2 = offscreenCanvas2.getContext('2d');
ctx2.fillStyle = '#0f0';
ctx2.fillRect(0, 0, 100, 50);
var pattern = ctx.createPattern(offscreenCanvas2, 'no-repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.pattern.basic.zerocanvas
testing:
- 2d.pattern.zerocanvas
code: |
offscreenCanvas.width = 0;
offscreenCanvas.height = 10;
@assert offscreenCanvas.width === 0;
@assert offscreenCanvas.height === 10;
@assert throws INVALID_STATE_ERR ctx.createPattern(offscreenCanvas, 'repeat');
offscreenCanvas.width = 10;
offscreenCanvas.height = 0;
@assert offscreenCanvas.width === 10;
@assert offscreenCanvas.height === 0;
@assert throws INVALID_STATE_ERR ctx.createPattern(offscreenCanvas, 'repeat');
offscreenCanvas.width = 0;
offscreenCanvas.height = 0;
@assert offscreenCanvas.width === 0;
@assert offscreenCanvas.height === 0;
@assert throws INVALID_STATE_ERR ctx.createPattern(offscreenCanvas, 'repeat');
- name: 2d.pattern.basic.nocontext
testing:
- 2d.pattern.painting
code: |
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var pattern = ctx.createPattern(offscreenCanvas2, 'no-repeat');
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.pattern.image.undefined
testing:
- 2d.pattern.IDL
code: |
@assert throws TypeError ctx.createPattern(undefined, 'repeat');
- name: 2d.pattern.image.null
testing:
- 2d.pattern.IDL
code: |
@assert throws TypeError ctx.createPattern(null, 'repeat');
- name: 2d.pattern.image.string
testing:
- 2d.pattern.IDL
code: |
@assert throws TypeError ctx.createPattern('../images/red.png', 'repeat');
- name: 2d.pattern.repeat.empty
testing:
- 2d.pattern.missing
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green-1x1.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, "");
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 200, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.repeat.null
testing:
- 2d.pattern.unrecognised
code: |
@assert ctx.createPattern(offscreenCanvas, null) != null;
- name: 2d.pattern.repeat.undefined
testing:
- 2d.pattern.unrecognised
code: |
@assert throws SYNTAX_ERR ctx.createPattern(offscreenCanvas, undefined);
- name: 2d.pattern.repeat.unrecognised
testing:
- 2d.pattern.unrecognised
code: |
@assert throws SYNTAX_ERR ctx.createPattern(offscreenCanvas, "invalid");
- name: 2d.pattern.repeat.unrecognisednull
testing:
- 2d.pattern.unrecognised
code: |
@assert throws SYNTAX_ERR ctx.createPattern(offscreenCanvas, "null");
- name: 2d.pattern.repeat.case
testing:
- 2d.pattern.exact
code: |
@assert throws SYNTAX_ERR ctx.createPattern(offscreenCanvas, "Repeat");
- name: 2d.pattern.repeat.nullsuffix
testing:
- 2d.pattern.exact
code: |
@assert throws SYNTAX_ERR ctx.createPattern(offscreenCanvas, "repeat\0");
- name: 2d.pattern.modify.canvas1
testing:
- 2d.pattern.modify
code: |
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var ctx2 = offscreenCanvas2.getContext('2d');
ctx2.fillStyle = '#0f0';
ctx2.fillRect(0, 0, 100, 50);
var pattern = ctx.createPattern(offscreenCanvas2, 'no-repeat');
ctx2.fillStyle = '#f00';
ctx2.fillRect(0, 0, 100, 50);
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.pattern.modify.canvas2
testing:
- 2d.pattern.modify
code: |
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var ctx2 = offscreenCanvas2.getContext('2d');
ctx2.fillStyle = '#0f0';
ctx2.fillRect(0, 0, 100, 50);
var pattern = ctx.createPattern(offscreenCanvas2, 'no-repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx2.fillStyle = '#f00';
ctx2.fillRect(0, 0, 100, 50);
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.pattern.crosscanvas
code: |
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var pattern = offscreenCanvas2.getContext('2d').createPattern(response, 'no-repeat');
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
});
- name: 2d.pattern.paint.norepeat.basic
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.norepeat.outside
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/red.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = pattern;
ctx.fillRect(0, -50, 100, 50);
ctx.fillRect(-100, 0, 100, 50);
ctx.fillRect(0, 50, 100, 50);
ctx.fillRect(100, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.norepeat.coord1
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(50, 0, 50, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.translate(50, 0);
ctx.fillRect(-50, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.norepeat.coord2
testing:
- 2d.pattern.painting
code: |
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(50, 0, 50, 50);
ctx.fillStyle = pattern;
ctx.translate(50, 0);
ctx.fillRect(-50, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.norepeat.coord3
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/red.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.translate(50, 25);
ctx.fillRect(-50, -25, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 25);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeat.basic
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green-16x16.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeat.outside
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green-16x16.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.translate(50, 25);
ctx.fillRect(-50, -25, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeat.coord1
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/rgrg-256x256.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.translate(-128, -78);
ctx.fillRect(128, 78, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeat.coord2
testing:
- 2d.pattern.painting
code: |
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/grgr-256x256.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeat.coord3
testing:
- 2d.pattern.painting
code: |
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/rgrg-256x256.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
ctx.translate(-128, -78);
ctx.fillRect(128, 78, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeatx.basic
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 16);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green-16x16.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'repeat-x');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeatx.outside
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/red-16x16.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'repeat-x');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 16);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeatx.coord1
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/red-16x16.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'repeat-x');
ctx.fillStyle = pattern;
ctx.translate(0, 16);
ctx.fillRect(0, -16, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 16);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeaty.basic
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 16, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/green-16x16.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'repeat-y');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeaty.outside
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/red-16x16.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'repeat-y');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 16, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.repeaty.coord1
testing:
- 2d.pattern.painting
images:
- red-16x16.png
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/red-16x16.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'repeat-y');
ctx.fillStyle = pattern;
ctx.translate(48, 0);
ctx.fillRect(-48, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 16, 50);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 50,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 50,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.orientation.image
desc: Image patterns do not get flipped when painted
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/rrgg-256x256.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
var pattern = ctx.createPattern(response, 'no-repeat');
ctx.fillStyle = pattern;
ctx.save();
ctx.translate(0, -103);
ctx.fillRect(0, 103, 100, 50);
ctx.restore();
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 25);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
});
- name: 2d.pattern.paint.orientation.canvas
desc: Canvas patterns do not get flipped when painted
testing:
- 2d.pattern.painting
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
var offscreenCanvas2 = new OffscreenCanvas(100, 50);
var ctx2 = offscreenCanvas2.getContext('2d');
ctx2.fillStyle = '#f00';
ctx2.fillRect(0, 0, 100, 25);
ctx2.fillStyle = '#0f0';
ctx2.fillRect(0, 25, 100, 25);
var pattern = ctx.createPattern(offscreenCanvas2, 'no-repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 25);
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 98,1 == 0,255,0,255;
@assert pixel 1,48 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.shadow.attributes.shadowBlur.initial
testing:
- 2d.shadow.blur.get
- 2d.shadow.blur.initial
code: |
@assert ctx.shadowBlur === 0;
- name: 2d.shadow.attributes.shadowBlur.valid
testing:
- 2d.shadow.blur.get
- 2d.shadow.blur.set
code: |
ctx.shadowBlur = 1;
@assert ctx.shadowBlur === 1;
ctx.shadowBlur = 0.5;
@assert ctx.shadowBlur === 0.5;
ctx.shadowBlur = 1e6;
@assert ctx.shadowBlur === 1e6;
ctx.shadowBlur = 0;
@assert ctx.shadowBlur === 0;
- name: 2d.shadow.attributes.shadowBlur.invalid
testing:
- 2d.shadow.blur.invalid
code: |
ctx.shadowBlur = 1;
ctx.shadowBlur = -2;
@assert ctx.shadowBlur === 1;
ctx.shadowBlur = 1;
ctx.shadowBlur = Infinity;
@assert ctx.shadowBlur === 1;
ctx.shadowBlur = 1;
ctx.shadowBlur = -Infinity;
@assert ctx.shadowBlur === 1;
ctx.shadowBlur = 1;
ctx.shadowBlur = NaN;
@assert ctx.shadowBlur === 1;
- name: 2d.shadow.attributes.shadowOffset.initial
testing:
- 2d.shadow.offset.initial
code: |
@assert ctx.shadowOffsetX === 0;
@assert ctx.shadowOffsetY === 0;
- name: 2d.shadow.attributes.shadowOffset.valid
testing:
- 2d.shadow.offset.get
- 2d.shadow.offset.set
code: |
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 2;
@assert ctx.shadowOffsetX === 1;
@assert ctx.shadowOffsetY === 2;
ctx.shadowOffsetX = 0.5;
ctx.shadowOffsetY = 0.25;
@assert ctx.shadowOffsetX === 0.5;
@assert ctx.shadowOffsetY === 0.25;
ctx.shadowOffsetX = -0.5;
ctx.shadowOffsetY = -0.25;
@assert ctx.shadowOffsetX === -0.5;
@assert ctx.shadowOffsetY === -0.25;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
@assert ctx.shadowOffsetX === 0;
@assert ctx.shadowOffsetY === 0;
ctx.shadowOffsetX = 1e6;
ctx.shadowOffsetY = 1e6;
@assert ctx.shadowOffsetX === 1e6;
@assert ctx.shadowOffsetY === 1e6;
- name: 2d.shadow.attributes.shadowOffset.invalid
testing:
- 2d.shadow.offset.invalid
code: |
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 2;
ctx.shadowOffsetX = Infinity;
ctx.shadowOffsetY = Infinity;
@assert ctx.shadowOffsetX === 1;
@assert ctx.shadowOffsetY === 2;
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 2;
ctx.shadowOffsetX = -Infinity;
ctx.shadowOffsetY = -Infinity;
@assert ctx.shadowOffsetX === 1;
@assert ctx.shadowOffsetY === 2;
ctx.shadowOffsetX = 1;
ctx.shadowOffsetY = 2;
ctx.shadowOffsetX = NaN;
ctx.shadowOffsetY = NaN;
@assert ctx.shadowOffsetX === 1;
@assert ctx.shadowOffsetY === 2;
- name: 2d.shadow.attributes.shadowColor.initial
testing:
- 2d.shadow.color.initial
code: |
@assert ctx.shadowColor === 'rgba(0, 0, 0, 0)';
- name: 2d.shadow.attributes.shadowColor.valid
testing:
- 2d.shadow.color.get
- 2d.shadow.color.set
code: |
ctx.shadowColor = 'lime';
@assert ctx.shadowColor === '#00ff00';
ctx.shadowColor = 'RGBA(0,255, 0,0)';
@assert ctx.shadowColor === 'rgba(0, 255, 0, 0)';
- name: 2d.shadow.attributes.shadowColor.invalid
testing:
- 2d.shadow.color.invalid
code: |
ctx.shadowColor = '#00ff00';
ctx.shadowColor = 'bogus';
@assert ctx.shadowColor === '#00ff00';
ctx.shadowColor = '#00ff00';
ctx.shadowColor = 'red bogus';
@assert ctx.shadowColor === '#00ff00';
ctx.shadowColor = '#00ff00';
ctx.shadowColor = ctx;
@assert ctx.shadowColor === '#00ff00';
ctx.shadowColor = '#00ff00';
ctx.shadowColor = undefined;
@assert ctx.shadowColor === '#00ff00';
- name: 2d.shadow.enable.off.1
desc: Shadows are not drawn when only shadowColor is set
testing:
- 2d.shadow.enable
- 2d.shadow.render
code: |
ctx.shadowColor = '#f00';
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.shadow.enable.off.2
desc: Shadows are not drawn when only shadowColor is set
testing:
- 2d.shadow.enable
- 2d.shadow.render
code: |
ctx.globalCompositeOperation = 'destination-atop';
ctx.shadowColor = '#f00';
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.shadow.enable.blur
desc: Shadows are drawn if shadowBlur is set
testing:
- 2d.shadow.enable
- 2d.shadow.render
code: |
ctx.globalCompositeOperation = 'destination-atop';
ctx.shadowColor = '#0f0';
ctx.shadowBlur = 0.1;
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.shadow.enable.x
desc: Shadows are drawn if shadowOffsetX is set
testing:
- 2d.shadow.enable
- 2d.shadow.render
code: |
ctx.globalCompositeOperation = 'destination-atop';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetX = 0.1;
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.shadow.enable.y
desc: Shadows are drawn if shadowOffsetY is set
testing:
- 2d.shadow.enable
- 2d.shadow.render
code: |
ctx.globalCompositeOperation = 'destination-atop';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetY = 0.1;
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
@assert pixel 50,25 == 0,255,0,255;
- name: 2d.shadow.offset.positiveX
desc: Shadows can be offset with positive x
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetX = 50;
ctx.fillRect(0, 0, 50, 50);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.shadow.offset.negativeX
desc: Shadows can be offset with negative x
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetX = -50;
ctx.fillRect(50, 0, 50, 50);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.shadow.offset.positiveY
desc: Shadows can be offset with positive y
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetY = 25;
ctx.fillRect(0, 0, 100, 25);
@assert pixel 50,12 == 0,255,0,255;
@assert pixel 50,37 == 0,255,0,255;
- name: 2d.shadow.offset.negativeY
desc: Shadows can be offset with negative y
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = '#0f0';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetY = -25;
ctx.fillRect(0, 25, 100, 25);
@assert pixel 50,12 == 0,255,0,255;
@assert pixel 50,37 == 0,255,0,255;
- name: 2d.shadow.outside
desc: Shadows of shapes outside the visible area can be offset onto the visible area
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.shadowColor = '#0f0';
ctx.shadowOffsetX = 100;
ctx.fillRect(-100, 0, 25, 50);
ctx.shadowOffsetX = -100;
ctx.fillRect(175, 0, 25, 50);
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 100;
ctx.fillRect(25, -100, 50, 25);
ctx.shadowOffsetY = -100;
ctx.fillRect(25, 125, 50, 25);
@assert pixel 12,25 == 0,255,0,255;
@assert pixel 87,25 == 0,255,0,255;
@assert pixel 50,12 == 0,255,0,255;
@assert pixel 50,37 == 0,255,0,255;
- name: 2d.shadow.clip.1
desc: Shadows of clipped shapes are still drawn within the clipping region
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(50, 0, 50, 50);
ctx.save();
ctx.beginPath();
ctx.rect(50, 0, 50, 50);
ctx.clip();
ctx.shadowColor = '#0f0';
ctx.shadowOffsetX = 50;
ctx.fillRect(0, 0, 50, 50);
ctx.restore();
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.shadow.clip.2
desc: Shadows are not drawn outside the clipping region
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(50, 0, 50, 50);
ctx.save();
ctx.beginPath();
ctx.rect(0, 0, 50, 50);
ctx.clip();
ctx.shadowColor = '#f00';
ctx.shadowOffsetX = 50;
ctx.fillRect(0, 0, 50, 50);
ctx.restore();
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.shadow.clip.3
desc: Shadows of clipped shapes are still drawn within the clipping region
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(50, 0, 50, 50);
ctx.save();
ctx.beginPath();
ctx.rect(0, 0, 50, 50);
ctx.clip();
ctx.fillStyle = '#f00';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetX = 50;
ctx.fillRect(-50, 0, 50, 50);
ctx.restore();
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
- name: 2d.shadow.stroke.basic
desc: Shadows are drawn for strokes
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.strokeStyle = '#f00';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetY = 50;
ctx.beginPath();
ctx.lineWidth = 50;
ctx.moveTo(0, -25);
ctx.lineTo(100, -25);
ctx.stroke();
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
- name: 2d.shadow.stroke.cap.1
desc: Shadows are not drawn for areas outside stroke caps
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.strokeStyle = '#f00';
ctx.shadowColor = '#f00';
ctx.shadowOffsetY = 50;
ctx.beginPath();
ctx.lineWidth = 50;
ctx.lineCap = 'butt';
ctx.moveTo(-50, -25);
ctx.lineTo(0, -25);
ctx.moveTo(100, -25);
ctx.lineTo(150, -25);
ctx.stroke();
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
- name: 2d.shadow.stroke.cap.2
desc: Shadows are drawn for stroke caps
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.strokeStyle = '#f00';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetY = 50;
ctx.beginPath();
ctx.lineWidth = 50;
ctx.lineCap = 'square';
ctx.moveTo(25, -25);
ctx.lineTo(75, -25);
ctx.stroke();
@assert pixel 1,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,25 == 0,255,0,255;
- name: 2d.shadow.stroke.join.1
desc: Shadows are not drawn for areas outside stroke joins
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.strokeStyle = '#f00';
ctx.shadowColor = '#f00';
ctx.shadowOffsetX = 100;
ctx.lineWidth = 200;
ctx.lineJoin = 'bevel';
ctx.beginPath();
ctx.moveTo(-200, -50);
ctx.lineTo(-150, -50);
ctx.lineTo(-151, -100);
ctx.stroke();
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 48,48 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.shadow.stroke.join.2
desc: Shadows are drawn for stroke joins
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = '#0f0';
ctx.fillRect(50, 0, 50, 50);
ctx.strokeStyle = '#f00';
ctx.shadowColor = '#0f0';
ctx.shadowOffsetX = 100;
ctx.lineWidth = 200;
ctx.lineJoin = 'miter';
ctx.beginPath();
ctx.moveTo(-200, -50);
ctx.lineTo(-150, -50);
ctx.lineTo(-151, -100);
ctx.stroke();
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 48,48 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.shadow.stroke.join.3
desc: Shadows are drawn for stroke joins respecting miter limit
testing:
- 2d.shadow.render
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.strokeStyle = '#f00';
ctx.shadowColor = '#f00';
ctx.shadowOffsetX = 100;
ctx.lineWidth = 200;
ctx.lineJoin = 'miter';
ctx.miterLimit = 0.1;
ctx.beginPath();
ctx.moveTo(-200, -50);
ctx.lineTo(-150, -50);
ctx.lineTo(-151, -100); // (not an exact right angle, to avoid some other bug in Firefox 3)
ctx.stroke();
@assert pixel 1,1 == 0,255,0,255;
@assert pixel 48,48 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 98,48 == 0,255,0,255;
- name: 2d.shadow.image.basic
desc: Shadows are drawn for images
testing:
- 2d.shadow.render
images:
- red.png
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.shadowColor = '#0f0';
ctx.shadowOffsetY = 50;
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/red.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx.drawImage(response, 0, -50);
@assert pixel 50,25 == 0,255,0,255;
});
- name: 2d.shadow.image.transparent.1
desc: Shadows are not drawn for transparent images
testing:
- 2d.shadow.render
images:
- transparent.png
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.shadowColor = '#f00';
ctx.shadowOffsetY = 50;
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/transparent.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx.drawImage(response, 0, -50);
@assert pixel 50,25 == 0,255,0,255;
});
- name: 2d.shadow.image.transparent.2
desc: Shadows are not drawn for transparent parts of images
testing:
- 2d.shadow.render
images:
- redtransparent.png
code: |
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 50, 50);
ctx.fillStyle = '#f00';
ctx.fillRect(50, 0, 50, 50);
ctx.shadowOffsetY = 50;
ctx.shadowColor = '#0f0';
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/redtransparent.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx.drawImage(response, 50, -50);
ctx.shadowColor = '#f00';
ctx.drawImage(response, -50, -50);
@assert pixel 25,25 == 0,255,0,255;
@assert pixel 50,25 == 0,255,0,255;
@assert pixel 75,25 == 0,255,0,255;
});
- name: 2d.shadow.image.alpha
desc: Shadows are drawn correctly for partially-transparent images
testing:
- 2d.shadow.render
images:
- transparent50.png
code: |
ctx.fillStyle = '#f00';
ctx.fillRect(0, 0, 100, 50);
ctx.shadowOffsetY = 50;
ctx.shadowColor = '#00f';
var promise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open("GET", '/images/transparent50.png');
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function() {
resolve(xhr.response);
};
});
promise.then(function(response) {
ctx.drawImage(response, 0, -50);
@assert pixel 50,25 ==~ 127,0,127,255;