blob: 75ff84c0881f59f1ea32f09a306beeeab91fa29a [file] [log] [blame]
- name: 2d.filter.canvasFilterObject
desc: Test CanvasFilter() object
code: |
@assert ctx.filter == 'none';
ctx.filter = 'blur(5px)';
@assert ctx.filter == 'blur(5px)';
ctx.filter = new CanvasFilter({blur: {stdDeviation: 5}});
@assert ctx.filter.toString() == '[object CanvasFilter]';
ctx.filter = new CanvasFilter([{blur: {stdDeviation: 5}}, {blur: {stdDeviation: 10}}]);
@assert ctx.filter.toString() == '[object CanvasFilter]';
var canvas2 = new OffscreenCanvas(100, 50);
var ctx2 = canvas2.getContext('2d');
ctx2.filter = ctx.filter;
@assert ctx.filter.toString() == '[object CanvasFilter]';
ctx.filter = 'blur(5px)';
@assert ctx.filter == 'blur(5px)';
ctx.filter = 'none';
@assert ctx.filter == 'none';
ctx.filter = new CanvasFilter({blur: {stdDeviation: 5}});
ctx.filter = "this string is not a filter and should do nothing";
@assert ctx.filter.toString() == '[object CanvasFilter]';
t.done();
- name: 2d.filter.canvasFilterObject.blur.exceptions
desc: Test exceptions on CanvasFilter() blur.object
code: |
@assert throws TypeError ctx.filter = new CanvasFilter({blur: null});
@assert throws TypeError ctx.filter = new CanvasFilter({blur: {}});
@assert throws TypeError ctx.filter = new CanvasFilter({blur: {stdDevation: null}});
@assert throws TypeError ctx.filter = new CanvasFilter({blur: {stdDeviation: "foo"}});
t.done();
- name: 2d.filter.canvasFilterObject.colorMatrix
desc: Test the functionality of ColorMatrix filters in CanvasFilter objects
code: |
@assert throws TypeError new CanvasFilter({colorMatrix: {values: undefined}});
@assert throws TypeError new CanvasFilter({colorMatrix: {values: "foo"}});
@assert throws TypeError new CanvasFilter({colorMatrix: {values: null}});
@assert throws TypeError new CanvasFilter({colorMatrix: {values: [1, 2, 3]}});
@assert throws TypeError new CanvasFilter({colorMatrix: {values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, "a"]}});
@assert throws TypeError new CanvasFilter({colorMatrix: {values: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, Infinity]}});
ctx.fillStyle = "#f00";
ctx.filter = new CanvasFilter({colorMatrix: {type: "hueRotate", values: 0}});
ctx.fillRect(0, 0, 100, 50);
@assert pixel 10,10 ==~ 255,0,0,255;
ctx.filter = new CanvasFilter({colorMatrix: {type: "hueRotate", values: 90}});
ctx.fillRect(0, 0, 100, 50);
@assert pixel 10,10 ==~ 0,91,0,255;
ctx.filter = new CanvasFilter({colorMatrix: {type: "hueRotate", values: 180}});
ctx.fillRect(0, 0, 100, 50);
@assert pixel 10,10 ==~ 0,109,109,255;
ctx.filter = new CanvasFilter({colorMatrix: {type: "hueRotate", values: 270}});
ctx.fillRect(0, 0, 100, 50);
@assert pixel 10,10 ==~ 109,18,255,255;
ctx.filter = new CanvasFilter({colorMatrix: {type: "saturate", values: 0.5}});
ctx.fillRect(0, 0, 100, 50);
@assert pixel 10,10 ==~ 155,27,27,255;
ctx.clearRect(0, 0, 100, 50);
ctx.filter = new CanvasFilter({colorMatrix: {type: "luminanceToAlpha"}});
ctx.fillRect(0, 0, 100, 50);
@assert pixel 10,10 ==~ 0,0,0,54;
ctx.filter = new CanvasFilter({colorMatrix: {values: [
0, 0, 0, 0, 0,
1, 1, 1, 1, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0
]}});
ctx.fillRect(0, 0, 50, 25);
ctx.fillStyle = "#0f0";
ctx.fillRect(50, 0, 50, 25);
ctx.fillStyle = "#00f";
ctx.fillRect(0, 25, 50, 25);
ctx.fillStyle = "#fff";
ctx.fillRect(50, 25, 50, 25);
@assert pixel 10,10 ==~ 0,255,0,255;
@assert pixel 60,10 ==~ 0,255,0,255;
@assert pixel 10,30 ==~ 0,255,0,255;
@assert pixel 60,30 ==~ 0,255,0,255;
t.done();
- name: 2d.filter.canvasFilterObject.convolveMatrix.exceptions
desc: Test exceptions on CanvasFilter() convolveMatrix
code: |
@assert throws TypeError new CanvasFilter({convolveMatrix: {}});
@assert throws TypeError new CanvasFilter({convolveMatrix: null});
@assert throws TypeError new CanvasFilter({convolveMatrix: {divisor: 2}});
@assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: null}});
@assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: 1}});
@assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: [[1, 0], [0]]}});
@assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: [[1, "a"], [0]]}});
@assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: [[1, 0], 0]}});
@assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: [[1, 0], [0, Infinity]]}});
@assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: []}});
// This should not throw an error
ctx.filter = new CanvasFilter({convolveMatrix: {kernelMatrix: [[]]}});
t.done();
- name: 2d.filter.canvasFilterObject.componentTransfer.linear
desc: Test pixels on CanvasFilter() componentTransfer with linear type
code: |
// From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
function getColor(inputColor, slopes, intercepts) {
return [
Math.max(0, Math.min(1, inputColor[0]/255 * slopes[0] + intercepts[0])) * 255,
Math.max(0, Math.min(1, inputColor[1]/255 * slopes[1] + intercepts[1])) * 255,
Math.max(0, Math.min(1, inputColor[2]/255 * slopes[2] + intercepts[2])) * 255,
];
}
const slopes = [0.5, 1.2, -0.2];
const intercepts = [0.25, 0, 0.5];
ctx.filter = new CanvasFilter({componentTransfer: {
funcR: {type: "linear", slope: slopes[0], intercept: intercepts[0]},
funcG: {type: "linear", slope: slopes[1], intercept: intercepts[1]},
funcB: {type: "linear", slope: slopes[2], intercept: intercepts[2]},
}});
const inputColors = [
[255, 255, 255],
[0, 0, 0],
[127, 0, 34],
[252, 186, 3],
[50, 68, 87],
];
for (const color of inputColors) {
let outputColor = getColor(color, slopes, intercepts);
ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
ctx.fillRect(0, 0, 10, 10);
_assertPixelApprox(offscreenCanvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, "5,5", `${outputColor[0]},${outputColor[1]},${outputColor[2]}`, 2);
}
t.done();
- name: 2d.filter.canvasFilterObject.componentTransfer.identity
desc: Test pixels on CanvasFilter() componentTransfer with identity type
code: |
ctx.filter = new CanvasFilter({componentTransfer: {
funcR: {type: "identity"},
funcG: {type: "identity"},
funcB: {type: "identity"},
}});
const inputColors = [
[255, 255, 255],
[0, 0, 0],
[127, 0, 34],
[252, 186, 3],
[50, 68, 87],
];
for (const color of inputColors) {
ctx.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, 1)`,
ctx.fillRect(0, 0, 10, 10);
_assertPixel(offscreenCanvas, 5, 5, color[0],color[1],color[2],255, "5,5", `${color[0]},${color[1]},${color[2]}`);
}
t.done();
- name: 2d.filter.canvasFilterObject.componentTransfer.gamma
desc: Test pixels on CanvasFilter() componentTransfer with gamma type
code: |
// From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
function getColor(inputColor, amplitude, exponent, offset) {
return [
Math.max(0, Math.min(1, Math.pow(inputColor[0]/255, exponent[0]) * amplitude[0] + offset[0])) * 255,
Math.max(0, Math.min(1, Math.pow(inputColor[1]/255, exponent[1]) * amplitude[1] + offset[1])) * 255,
Math.max(0, Math.min(1, Math.pow(inputColor[2]/255, exponent[2]) * amplitude[2] + offset[2])) * 255,
];
}
const amplitudes = [2, 1.1, 0.5];
const exponents = [5, 3, 1];
const offsets = [0.25, 0, 0.5];
ctx.filter = new CanvasFilter({componentTransfer: {
funcR: {type: "gamma", amplitude: amplitudes[0], exponent: exponents[0], offset: offsets[0]},
funcG: {type: "gamma", amplitude: amplitudes[1], exponent: exponents[1], offset: offsets[1]},
funcB: {type: "gamma", amplitude: amplitudes[2], exponent: exponents[2], offset: offsets[2]},
}});
const inputColors = [
[255, 255, 255],
[0, 0, 0],
[127, 0, 34],
[252, 186, 3],
[50, 68, 87],
];
for (const color of inputColors) {
let outputColor = getColor(color, amplitudes, exponents, offsets);
ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
ctx.fillRect(0, 0, 10, 10);
_assertPixelApprox(offscreenCanvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, "5,5", `${outputColor[0]},${outputColor[1]},${outputColor[2]}`, 2);
}
t.done();
- name: 2d.filter.canvasFilterObject.componentTransfer.table
desc: Test pixels on CanvasFilter() componentTransfer with table type
code: |
// From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
function getTransformedValue(C, V) {
// Get the right interval
const n = V.length - 1;
const k = C == 1 ? n - 1 : Math.floor(C * n);
return V[k] + (C - k/n) * n * (V[k + 1] - V[k]);
}
function getColor(inputColor, tableValues) {
const result = [0, 0, 0];
for (const i in inputColor) {
const C = inputColor[i]/255;
const Cprime = getTransformedValue(C, tableValues[i]);
result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
}
return result;
}
tableValuesR = [0, 0, 1, 1];
tableValuesG = [2, 0, 0.5, 3];
tableValuesB = [1, -1, 5, 0];
ctx.filter = new CanvasFilter({componentTransfer: {
funcR: {type: "table", tableValues: tableValuesR},
funcG: {type: "table", tableValues: tableValuesG},
funcB: {type: "table", tableValues: tableValuesB},
}});
const inputColors = [
[255, 255, 255],
[0, 0, 0],
[127, 0, 34],
[252, 186, 3],
[50, 68, 87],
];
for (const color of inputColors) {
let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
ctx.fillRect(0, 0, 10, 10);
_assertPixelApprox(offscreenCanvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, "5,5", `${outputColor[0]},${outputColor[1]},${outputColor[2]}`, 2);
}
t.done()
- name: 2d.filter.canvasFilterObject.componentTransfer.discrete
desc: Test pixels on CanvasFilter() componentTransfer with discrete type
code: |
// From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement
function getTransformedValue(C, V) {
// Get the right interval
const n = V.length - 1;
const k = C == 1 ? n - 1 : Math.floor(C * n);
return V[k];
}
function getColor(inputColor, tableValues) {
const result = [0, 0, 0];
for (const i in inputColor) {
const C = inputColor[i]/255;
const Cprime = getTransformedValue(C, tableValues[i]);
result[i] = Math.max(0, Math.min(1, Cprime)) * 255;
}
return result;
}
tableValuesR = [0, 0, 1, 1];
tableValuesG = [2, 0, 0.5, 3];
tableValuesB = [1, -1, 5, 0];
ctx.filter = new CanvasFilter({componentTransfer: {
funcR: {type: "discrete", tableValues: tableValuesR},
funcG: {type: "discrete", tableValues: tableValuesG},
funcB: {type: "discrete", tableValues: tableValuesB},
}});
const inputColors = [
[255, 255, 255],
[0, 0, 0],
[127, 0, 34],
[252, 186, 3],
[50, 68, 87],
];
for (const color of inputColors) {
let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]);
ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
ctx.fillRect(0, 0, 10, 10);
_assertPixelApprox(offscreenCanvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, "5,5", `${outputColor[0]},${outputColor[1]},${outputColor[2]}`, 2);
}
t.done();