1- name: 2d.filter.value 2 desc: test if ctx.filter works correctly 3 code: | 4 @assert ctx.filter == 'none'; 5 ctx.filter = 'blur(5px)'; 6 @assert ctx.filter == 'blur(5px)'; 7 ctx.save(); 8 ctx.filter = 'none'; 9 @assert ctx.filter == 'none'; 10 ctx.restore(); 11 @assert ctx.filter == 'blur(5px)'; 12 13 ctx.filter = 'blur(10)'; 14 @assert ctx.filter == 'blur(5px)'; 15 ctx.filter = 'blur 10px'; 16 @assert ctx.filter == 'blur(5px)'; 17 18 ctx.filter = 'inherit'; 19 @assert ctx.filter == 'blur(5px)'; 20 ctx.filter = 'initial'; 21 @assert ctx.filter == 'blur(5px)'; 22 ctx.filter = 'unset'; 23 @assert ctx.filter == 'blur(5px)'; 24 25 ctx.filter = ''; 26 @assert ctx.filter == 'blur(5px)'; 27 ctx.filter = null; 28 @assert ctx.filter == 'blur(5px)'; 29 ctx.filter = undefined; 30 @assert ctx.filter == 'blur(5px)'; 31 32 ctx.filter = 'blur( 5px)'; 33 assert_equals(ctx.filter, 'blur( 5px)'); 34 35- name: 2d.filter.canvasFilterObject 36 desc: Test CanvasFilter() object 37 code: | 38 @assert ctx.filter == 'none'; 39 ctx.filter = 'blur(5px)'; 40 @assert ctx.filter == 'blur(5px)'; 41 ctx.filter = new CanvasFilter({blur: {stdDeviation: 5}}); 42 @assert ctx.filter.toString() == '[object CanvasFilter]'; 43 ctx.filter = new CanvasFilter([{blur: {stdDeviation: 5}}, {blur: {stdDeviation: 10}}]); 44 @assert ctx.filter.toString() == '[object CanvasFilter]'; 45 var canvas2 = document.createElement('canvas'); 46 var ctx2 = canvas2.getContext('2d'); 47 ctx2.filter = ctx.filter; 48 @assert ctx.filter.toString() == '[object CanvasFilter]'; 49 ctx.filter = 'blur(5px)'; 50 @assert ctx.filter == 'blur(5px)'; 51 ctx.filter = 'none'; 52 @assert ctx.filter == 'none'; 53 ctx.filter = new CanvasFilter({blur: {stdDeviation: 5}}); 54 ctx.filter = "this string is not a filter and should do nothing"; 55 @assert ctx.filter.toString() == '[object CanvasFilter]'; 56 57- name: 2d.filter.canvasFilterObject.blur.exceptions 58 desc: Test exceptions on CanvasFilter() blur.object 59 code: | 60 @assert throws TypeError ctx.filter = new CanvasFilter({blur: null}); 61 @assert throws TypeError ctx.filter = new CanvasFilter({blur: {}}); 62 @assert throws TypeError ctx.filter = new CanvasFilter({blur: {stdDevation: null}}); 63 @assert throws TypeError ctx.filter = new CanvasFilter({blur: {stdDeviation: "foo"}}); 64 65- name: 2d.filter.canvasFilterObject.colorMatrix 66 desc: Test the functionality of ColorMatrix filters in CanvasFilter objects 67 code: | 68 @assert throws TypeError new CanvasFilter({colorMatrix: {values: undefined}}); 69 @assert throws TypeError new CanvasFilter({colorMatrix: {values: "foo"}}); 70 @assert throws TypeError new CanvasFilter({colorMatrix: {values: null}}); 71 @assert throws TypeError new CanvasFilter({colorMatrix: {values: [1, 2, 3]}}); 72 @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"]}}); 73 @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]}}); 74 ctx.fillStyle = "#f00"; 75 ctx.filter = new CanvasFilter({colorMatrix: {type: "hueRotate", values: 0}}); 76 ctx.fillRect(0, 0, 100, 50); 77 @assert pixel 10,10 ==~ 255,0,0,255; 78 ctx.filter = new CanvasFilter({colorMatrix: {type: "hueRotate", values: 90}}); 79 ctx.fillRect(0, 0, 100, 50); 80 @assert pixel 10,10 ==~ 0,91,0,255; 81 ctx.filter = new CanvasFilter({colorMatrix: {type: "hueRotate", values: 180}}); 82 ctx.fillRect(0, 0, 100, 50); 83 @assert pixel 10,10 ==~ 0,109,109,255; 84 ctx.filter = new CanvasFilter({colorMatrix: {type: "hueRotate", values: 270}}); 85 ctx.fillRect(0, 0, 100, 50); 86 @assert pixel 10,10 ==~ 109,18,255,255; 87 ctx.filter = new CanvasFilter({colorMatrix: {type: "saturate", values: 0.5}}); 88 ctx.fillRect(0, 0, 100, 50); 89 @assert pixel 10,10 ==~ 155,27,27,255; 90 ctx.clearRect(0, 0, 100, 50); 91 ctx.filter = new CanvasFilter({colorMatrix: {type: "luminanceToAlpha"}}); 92 ctx.fillRect(0, 0, 100, 50); 93 @assert pixel 10,10 ==~ 0,0,0,54; 94 ctx.filter = new CanvasFilter({colorMatrix: {values: [ 95 0, 0, 0, 0, 0, 96 1, 1, 1, 1, 0, 97 0, 0, 0, 0, 0, 98 0, 0, 0, 1, 0 99 ]}}); 100 ctx.fillRect(0, 0, 50, 25); 101 ctx.fillStyle = "#0f0"; 102 ctx.fillRect(50, 0, 50, 25); 103 ctx.fillStyle = "#00f"; 104 ctx.fillRect(0, 25, 50, 25); 105 ctx.fillStyle = "#fff"; 106 ctx.fillRect(50, 25, 50, 25); 107 @assert pixel 10,10 ==~ 0,255,0,255; 108 @assert pixel 60,10 ==~ 0,255,0,255; 109 @assert pixel 10,30 ==~ 0,255,0,255; 110 @assert pixel 60,30 ==~ 0,255,0,255; 111 expected: green 112 113- name: 2d.filter.canvasFilterObject.convolveMatrix.exceptions 114 desc: Test exceptions on CanvasFilter() convolveMatrix 115 code: | 116 @assert throws TypeError new CanvasFilter({convolveMatrix: {}}); 117 @assert throws TypeError new CanvasFilter({convolveMatrix: null}); 118 @assert throws TypeError new CanvasFilter({convolveMatrix: {divisor: 2}}); 119 @assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: null}}); 120 @assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: 1}}); 121 @assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: [[1, 0], [0]]}}); 122 @assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: [[1, "a"], [0]]}}); 123 @assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: [[1, 0], 0]}}); 124 @assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: [[1, 0], [0, Infinity]]}}); 125 @assert throws TypeError new CanvasFilter({convolveMatrix: {kernelMatrix: []}}); 126 // This should not throw an error 127 ctx.filter = new CanvasFilter({convolveMatrix: {kernelMatrix: [[]]}}); 128 129- name: 2d.filter.canvasFilterObject.componentTransfer.linear 130 desc: Test pixels on CanvasFilter() componentTransfer with linear type 131 code: | 132 // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement 133 function getColor(inputColor, slopes, intercepts) { 134 return [ 135 Math.max(0, Math.min(1, inputColor[0]/255 * slopes[0] + intercepts[0])) * 255, 136 Math.max(0, Math.min(1, inputColor[1]/255 * slopes[1] + intercepts[1])) * 255, 137 Math.max(0, Math.min(1, inputColor[2]/255 * slopes[2] + intercepts[2])) * 255, 138 ]; 139 } 140 141 const slopes = [0.5, 1.2, -0.2]; 142 const intercepts = [0.25, 0, 0.5]; 143 ctx.filter = new CanvasFilter({componentTransfer: { 144 funcR: {type: "linear", slope: slopes[0], intercept: intercepts[0]}, 145 funcG: {type: "linear", slope: slopes[1], intercept: intercepts[1]}, 146 funcB: {type: "linear", slope: slopes[2], intercept: intercepts[2]}, 147 }}); 148 149 const inputColors = [ 150 [255, 255, 255], 151 [0, 0, 0], 152 [127, 0, 34], 153 [252, 186, 3], 154 [50, 68, 87], 155 ]; 156 157 for (const color of inputColors) { 158 let outputColor = getColor(color, slopes, intercepts); 159 ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`; 160 ctx.fillRect(0, 0, 10, 10); 161 _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, "5,5", `${outputColor[0]},${outputColor[1]},${outputColor[2]}`, 2); 162 } 163 164- name: 2d.filter.canvasFilterObject.componentTransfer.identity 165 desc: Test pixels on CanvasFilter() componentTransfer with identity type 166 code: | 167 ctx.filter = new CanvasFilter({componentTransfer: { 168 funcR: {type: "identity"}, 169 funcG: {type: "identity"}, 170 funcB: {type: "identity"}, 171 }}); 172 173 const inputColors = [ 174 [255, 255, 255], 175 [0, 0, 0], 176 [127, 0, 34], 177 [252, 186, 3], 178 [50, 68, 87], 179 ]; 180 181 for (const color of inputColors) { 182 ctx.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, 1)`, 183 ctx.fillRect(0, 0, 10, 10); 184 _assertPixel(canvas, 5, 5, color[0],color[1],color[2],255, "5,5", `${color[0]},${color[1]},${color[2]}`); 185 } 186 187- name: 2d.filter.canvasFilterObject.componentTransfer.gamma 188 desc: Test pixels on CanvasFilter() componentTransfer with gamma type 189 code: | 190 // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement 191 function getColor(inputColor, amplitude, exponent, offset) { 192 return [ 193 Math.max(0, Math.min(1, Math.pow(inputColor[0]/255, exponent[0]) * amplitude[0] + offset[0])) * 255, 194 Math.max(0, Math.min(1, Math.pow(inputColor[1]/255, exponent[1]) * amplitude[1] + offset[1])) * 255, 195 Math.max(0, Math.min(1, Math.pow(inputColor[2]/255, exponent[2]) * amplitude[2] + offset[2])) * 255, 196 ]; 197 } 198 199 const amplitudes = [2, 1.1, 0.5]; 200 const exponents = [5, 3, 1]; 201 const offsets = [0.25, 0, 0.5]; 202 ctx.filter = new CanvasFilter({componentTransfer: { 203 funcR: {type: "gamma", amplitude: amplitudes[0], exponent: exponents[0], offset: offsets[0]}, 204 funcG: {type: "gamma", amplitude: amplitudes[1], exponent: exponents[1], offset: offsets[1]}, 205 funcB: {type: "gamma", amplitude: amplitudes[2], exponent: exponents[2], offset: offsets[2]}, 206 }}); 207 208 const inputColors = [ 209 [255, 255, 255], 210 [0, 0, 0], 211 [127, 0, 34], 212 [252, 186, 3], 213 [50, 68, 87], 214 ]; 215 216 for (const color of inputColors) { 217 let outputColor = getColor(color, amplitudes, exponents, offsets); 218 ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`; 219 ctx.fillRect(0, 0, 10, 10); 220 _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, "5,5", `${outputColor[0]},${outputColor[1]},${outputColor[2]}`, 2); 221 } 222 223- name: 2d.filter.canvasFilterObject.componentTransfer.table 224 desc: Test pixels on CanvasFilter() componentTransfer with table type 225 code: | 226 // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement 227 function getTransformedValue(C, V) { 228 // Get the right interval 229 const n = V.length - 1; 230 const k = C == 1 ? n - 1 : Math.floor(C * n); 231 return V[k] + (C - k/n) * n * (V[k + 1] - V[k]); 232 } 233 234 function getColor(inputColor, tableValues) { 235 const result = [0, 0, 0]; 236 for (const i in inputColor) { 237 const C = inputColor[i]/255; 238 const Cprime = getTransformedValue(C, tableValues[i]); 239 result[i] = Math.max(0, Math.min(1, Cprime)) * 255; 240 } 241 return result; 242 } 243 244 tableValuesR = [0, 0, 1, 1]; 245 tableValuesG = [2, 0, 0.5, 3]; 246 tableValuesB = [1, -1, 5, 0]; 247 ctx.filter = new CanvasFilter({componentTransfer: { 248 funcR: {type: "table", tableValues: tableValuesR}, 249 funcG: {type: "table", tableValues: tableValuesG}, 250 funcB: {type: "table", tableValues: tableValuesB}, 251 }}); 252 253 const inputColors = [ 254 [255, 255, 255], 255 [0, 0, 0], 256 [127, 0, 34], 257 [252, 186, 3], 258 [50, 68, 87], 259 ]; 260 261 for (const color of inputColors) { 262 let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]); 263 ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`; 264 ctx.fillRect(0, 0, 10, 10); 265 _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, "5,5", `${outputColor[0]},${outputColor[1]},${outputColor[2]}`, 2); 266 } 267 268- name: 2d.filter.canvasFilterObject.componentTransfer.discrete 269 desc: Test pixels on CanvasFilter() componentTransfer with discrete type 270 code: | 271 // From https://www.w3.org/TR/SVG11/filters.html#feComponentTransferElement 272 function getTransformedValue(C, V) { 273 // Get the right interval 274 const n = V.length; 275 const k = C == 1 ? n - 1 : Math.floor(C * n); 276 return V[k]; 277 } 278 279 function getColor(inputColor, tableValues) { 280 const result = [0, 0, 0]; 281 for (const i in inputColor) { 282 const C = inputColor[i]/255; 283 const Cprime = getTransformedValue(C, tableValues[i]); 284 result[i] = Math.max(0, Math.min(1, Cprime)) * 255; 285 } 286 return result; 287 } 288 289 tableValuesR = [0, 0, 1, 1]; 290 tableValuesG = [2, 0, 0.5, 3]; 291 tableValuesB = [1, -1, 5, 0]; 292 ctx.filter = new CanvasFilter({componentTransfer: { 293 funcR: {type: "discrete", tableValues: tableValuesR}, 294 funcG: {type: "discrete", tableValues: tableValuesG}, 295 funcB: {type: "discrete", tableValues: tableValuesB}, 296 }}); 297 298 const inputColors = [ 299 [255, 255, 255], 300 [0, 0, 0], 301 [127, 0, 34], 302 [252, 186, 3], 303 [50, 68, 87], 304 ]; 305 306 for (const color of inputColors) { 307 let outputColor = getColor(color, [tableValuesR, tableValuesG, tableValuesB]); 308 ctx.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`; 309 ctx.fillRect(0, 0, 10, 10); 310 _assertPixelApprox(canvas, 5, 5, outputColor[0],outputColor[1],outputColor[2],255, "5,5", `${outputColor[0]},${outputColor[1]},${outputColor[2]}`, 2); 311 } 312