1- name: 2d.transformation.order 2 desc: Transformations are applied in the right order 3 testing: 4 - 2d.transformation.order 5 code: | 6 ctx.fillStyle = '#f00'; 7 ctx.fillRect(0, 0, 100, 50); 8 9 ctx.scale(2, 1); 10 ctx.rotate(Math.PI / 2); 11 ctx.fillStyle = '#0f0'; 12 ctx.fillRect(0, -50, 50, 50); 13 @assert pixel 75,25 == 0,255,0,255; 14 expected: green 15 16 17- name: 2d.transformation.scale.basic 18 desc: scale() works 19 testing: 20 - 2d.transformation.scale 21 code: | 22 ctx.fillStyle = '#f00'; 23 ctx.fillRect(0, 0, 100, 50); 24 25 ctx.scale(2, 4); 26 ctx.fillStyle = '#0f0'; 27 ctx.fillRect(0, 0, 50, 12.5); 28 @assert pixel 90,40 == 0,255,0,255; 29 expected: green 30 31- name: 2d.transformation.scale.zero 32 desc: scale() with a scale factor of zero works 33 testing: 34 - 2d.transformation.scale 35 code: | 36 ctx.fillStyle = '#0f0'; 37 ctx.fillRect(0, 0, 100, 50); 38 39 ctx.save(); 40 ctx.translate(50, 0); 41 ctx.scale(0, 1); 42 ctx.fillStyle = '#f00'; 43 ctx.fillRect(0, 0, 100, 50); 44 ctx.restore(); 45 46 ctx.save(); 47 ctx.translate(0, 25); 48 ctx.scale(1, 0); 49 ctx.fillStyle = '#f00'; 50 ctx.fillRect(0, 0, 100, 50); 51 ctx.restore(); 52 53 canvas.toDataURL(); 54 55 @assert pixel 50,25 == 0,255,0,255; 56 expected: green 57 58- name: 2d.transformation.scale.negative 59 desc: scale() with negative scale factors works 60 testing: 61 - 2d.transformation.scale 62 code: | 63 ctx.fillStyle = '#f00'; 64 ctx.fillRect(0, 0, 100, 50); 65 66 ctx.save(); 67 ctx.scale(-1, 1); 68 ctx.fillStyle = '#0f0'; 69 ctx.fillRect(-50, 0, 50, 50); 70 ctx.restore(); 71 72 ctx.save(); 73 ctx.scale(1, -1); 74 ctx.fillStyle = '#0f0'; 75 ctx.fillRect(50, -50, 50, 50); 76 ctx.restore(); 77 @assert pixel 25,25 == 0,255,0,255; 78 @assert pixel 75,25 == 0,255,0,255; 79 expected: green 80 81- name: 2d.transformation.scale.large 82 desc: scale() with large scale factors works 83 notes: Not really that large at all, but it hits the limits in Firefox. 84 testing: 85 - 2d.transformation.scale 86 code: | 87 ctx.fillStyle = '#f00'; 88 ctx.fillRect(0, 0, 100, 50); 89 90 ctx.scale(1e5, 1e5); 91 ctx.fillStyle = '#0f0'; 92 ctx.fillRect(0, 0, 1, 1); 93 @assert pixel 50,25 == 0,255,0,255; 94 expected: green 95 96- name: 2d.transformation.scale.nonfinite 97 desc: scale() with Infinity/NaN is ignored 98 testing: 99 - 2d.nonfinite 100 code: | 101 ctx.fillStyle = '#f00'; 102 ctx.fillRect(0, 0, 100, 50); 103 104 ctx.translate(100, 10); 105 @nonfinite ctx.scale(<0.1 Infinity -Infinity NaN>, <0.1 Infinity -Infinity NaN>); 106 107 ctx.fillStyle = '#0f0'; 108 ctx.fillRect(-100, -10, 100, 50); 109 110 @assert pixel 50,25 == 0,255,0,255; 111 expected: green 112 113- name: 2d.transformation.scale.multiple 114 desc: Multiple scale()s combine 115 testing: 116 - 2d.transformation.scale.multiple 117 code: | 118 ctx.fillStyle = '#f00'; 119 ctx.fillRect(0, 0, 100, 50); 120 121 ctx.scale(Math.sqrt(2), Math.sqrt(2)); 122 ctx.scale(Math.sqrt(2), Math.sqrt(2)); 123 ctx.fillStyle = '#0f0'; 124 ctx.fillRect(0, 0, 50, 25); 125 @assert pixel 90,40 == 0,255,0,255; 126 expected: green 127 128 129- name: 2d.transformation.rotate.zero 130 desc: rotate() by 0 does nothing 131 testing: 132 - 2d.transformation.rotate 133 code: | 134 ctx.fillStyle = '#f00'; 135 ctx.fillRect(0, 0, 100, 50); 136 137 ctx.rotate(0); 138 ctx.fillStyle = '#0f0'; 139 ctx.fillRect(0, 0, 100, 50); 140 @assert pixel 50,25 == 0,255,0,255; 141 expected: green 142 143- name: 2d.transformation.rotate.radians 144 desc: rotate() uses radians 145 testing: 146 - 2d.transformation.rotate.radians 147 code: | 148 ctx.fillStyle = '#f00'; 149 ctx.fillRect(0, 0, 100, 50); 150 151 ctx.rotate(Math.PI); // should fail obviously if this is 3.1 degrees 152 ctx.fillStyle = '#0f0'; 153 ctx.fillRect(-100, -50, 100, 50); 154 @assert pixel 50,25 == 0,255,0,255; 155 expected: green 156 157- name: 2d.transformation.rotate.direction 158 desc: rotate() is clockwise 159 testing: 160 - 2d.transformation.rotate.direction 161 code: | 162 ctx.fillStyle = '#f00'; 163 ctx.fillRect(0, 0, 100, 50); 164 165 ctx.rotate(Math.PI / 2); 166 ctx.fillStyle = '#0f0'; 167 ctx.fillRect(0, -100, 50, 100); 168 @assert pixel 50,25 == 0,255,0,255; 169 expected: green 170 171- name: 2d.transformation.rotate.wrap 172 desc: rotate() wraps large positive values correctly 173 testing: 174 - 2d.transformation.rotate 175 code: | 176 ctx.fillStyle = '#f00'; 177 ctx.fillRect(0, 0, 100, 50); 178 179 ctx.rotate(Math.PI * (1 + 4096)); // == pi (mod 2*pi) 180 // We need about pi +/- 0.001 in order to get correct-looking results 181 // 32-bit floats can store pi*4097 with precision 2^-10, so that should 182 // be safe enough on reasonable implementations 183 ctx.fillStyle = '#0f0'; 184 ctx.fillRect(-100, -50, 100, 50); 185 @assert pixel 50,25 == 0,255,0,255; 186 @assert pixel 98,2 == 0,255,0,255; 187 @assert pixel 98,47 == 0,255,0,255; 188 expected: green 189 190- name: 2d.transformation.rotate.wrapnegative 191 desc: rotate() wraps large negative values correctly 192 testing: 193 - 2d.transformation.rotate 194 code: | 195 ctx.fillStyle = '#f00'; 196 ctx.fillRect(0, 0, 100, 50); 197 198 ctx.rotate(-Math.PI * (1 + 4096)); 199 ctx.fillStyle = '#0f0'; 200 ctx.fillRect(-100, -50, 100, 50); 201 @assert pixel 50,25 == 0,255,0,255; 202 @assert pixel 98,2 == 0,255,0,255; 203 @assert pixel 98,47 == 0,255,0,255; 204 expected: green 205 206- name: 2d.transformation.rotate.nonfinite 207 desc: rotate() with Infinity/NaN is ignored 208 testing: 209 - 2d.nonfinite 210 code: | 211 ctx.fillStyle = '#f00'; 212 ctx.fillRect(0, 0, 100, 50); 213 214 ctx.translate(100, 10); 215 @nonfinite ctx.rotate(<0.1 Infinity -Infinity NaN>); 216 217 ctx.fillStyle = '#0f0'; 218 ctx.fillRect(-100, -10, 100, 50); 219 220 @assert pixel 50,25 == 0,255,0,255; 221 expected: green 222 223- name: 2d.transformation.translate.basic 224 desc: translate() works 225 testing: 226 - 2d.transformation.translate 227 code: | 228 ctx.fillStyle = '#f00'; 229 ctx.fillRect(0, 0, 100, 50); 230 231 ctx.translate(100, 50); 232 ctx.fillStyle = '#0f0'; 233 ctx.fillRect(-100, -50, 100, 50); 234 @assert pixel 90,40 == 0,255,0,255; 235 expected: green 236 237- name: 2d.transformation.translate.nonfinite 238 desc: translate() with Infinity/NaN is ignored 239 testing: 240 - 2d.nonfinite 241 code: | 242 ctx.fillStyle = '#f00'; 243 ctx.fillRect(0, 0, 100, 50); 244 245 ctx.translate(100, 10); 246 @nonfinite ctx.translate(<0.1 Infinity -Infinity NaN>, <0.1 Infinity -Infinity NaN>); 247 248 ctx.fillStyle = '#0f0'; 249 ctx.fillRect(-100, -10, 100, 50); 250 251 @assert pixel 50,25 == 0,255,0,255; 252 expected: green 253 254 255- name: 2d.transformation.transform.identity 256 desc: transform() with the identity matrix does nothing 257 testing: 258 - 2d.transformation.transform 259 code: | 260 ctx.fillStyle = '#f00'; 261 ctx.fillRect(0, 0, 100, 50); 262 263 ctx.transform(1,0, 0,1, 0,0); 264 ctx.fillStyle = '#0f0'; 265 ctx.fillRect(0, 0, 100, 50); 266 @assert pixel 50,25 == 0,255,0,255; 267 expected: green 268 269- name: 2d.transformation.transform.skewed 270 desc: transform() with skewy matrix transforms correctly 271 testing: 272 - 2d.transformation.transform 273 code: | 274 // Create green with a red square ring inside it 275 ctx.fillStyle = '#0f0'; 276 ctx.fillRect(0, 0, 100, 50); 277 ctx.fillStyle = '#f00'; 278 ctx.fillRect(20, 10, 60, 30); 279 ctx.fillStyle = '#0f0'; 280 ctx.fillRect(40, 20, 20, 10); 281 282 // Draw a skewed shape to fill that gap, to make sure it is aligned correctly 283 ctx.transform(1,4, 2,3, 5,6); 284 // Post-transform coordinates: 285 // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]]; 286 // Hence pre-transform coordinates: 287 var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2], 288 [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2], 289 [-7.4,11.2]]; 290 ctx.beginPath(); 291 ctx.moveTo(pts[0][0], pts[0][1]); 292 for (var i = 0; i < pts.length; ++i) 293 ctx.lineTo(pts[i][0], pts[i][1]); 294 ctx.fill(); 295 @assert pixel 21,11 == 0,255,0,255; 296 @assert pixel 79,11 == 0,255,0,255; 297 @assert pixel 21,39 == 0,255,0,255; 298 @assert pixel 79,39 == 0,255,0,255; 299 @assert pixel 39,19 == 0,255,0,255; 300 @assert pixel 61,19 == 0,255,0,255; 301 @assert pixel 39,31 == 0,255,0,255; 302 @assert pixel 61,31 == 0,255,0,255; 303 expected: green 304 305- name: 2d.transformation.transform.multiply 306 desc: transform() multiplies the CTM 307 testing: 308 - 2d.transformation.transform.multiply 309 code: | 310 ctx.fillStyle = '#f00'; 311 ctx.fillRect(0, 0, 100, 50); 312 313 ctx.transform(1,2, 3,4, 5,6); 314 ctx.transform(-2,1, 3/2,-1/2, 1,-2); 315 ctx.fillStyle = '#0f0'; 316 ctx.fillRect(0, 0, 100, 50); 317 @assert pixel 50,25 == 0,255,0,255; 318 expected: green 319 320- name: 2d.transformation.transform.nonfinite 321 desc: transform() with Infinity/NaN is ignored 322 testing: 323 - 2d.nonfinite 324 code: | 325 ctx.fillStyle = '#f00'; 326 ctx.fillRect(0, 0, 100, 50); 327 328 ctx.translate(100, 10); 329 @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>); 330 331 ctx.fillStyle = '#0f0'; 332 ctx.fillRect(-100, -10, 100, 50); 333 334 @assert pixel 50,25 == 0,255,0,255; 335 expected: green 336 337- name: 2d.transformation.setTransform.skewed 338 testing: 339 - 2d.transformation.setTransform 340 code: | 341 // Create green with a red square ring inside it 342 ctx.fillStyle = '#0f0'; 343 ctx.fillRect(0, 0, 100, 50); 344 ctx.fillStyle = '#f00'; 345 ctx.fillRect(20, 10, 60, 30); 346 ctx.fillStyle = '#0f0'; 347 ctx.fillRect(40, 20, 20, 10); 348 349 // Draw a skewed shape to fill that gap, to make sure it is aligned correctly 350 ctx.setTransform(1,4, 2,3, 5,6); 351 // Post-transform coordinates: 352 // [[20,10],[80,10],[80,40],[20,40],[20,10],[40,20],[40,30],[60,30],[60,20],[40,20],[20,10]]; 353 // Hence pre-transform coordinates: 354 var pts=[[-7.4,11.2],[-43.4,59.2],[-31.4,53.2],[4.6,5.2],[-7.4,11.2], 355 [-15.4,25.2],[-11.4,23.2],[-23.4,39.2],[-27.4,41.2],[-15.4,25.2], 356 [-7.4,11.2]]; 357 ctx.beginPath(); 358 ctx.moveTo(pts[0][0], pts[0][1]); 359 for (var i = 0; i < pts.length; ++i) 360 ctx.lineTo(pts[i][0], pts[i][1]); 361 ctx.fill(); 362 @assert pixel 21,11 == 0,255,0,255; 363 @assert pixel 79,11 == 0,255,0,255; 364 @assert pixel 21,39 == 0,255,0,255; 365 @assert pixel 79,39 == 0,255,0,255; 366 @assert pixel 39,19 == 0,255,0,255; 367 @assert pixel 61,19 == 0,255,0,255; 368 @assert pixel 39,31 == 0,255,0,255; 369 @assert pixel 61,31 == 0,255,0,255; 370 expected: green 371 372- name: 2d.transformation.setTransform.multiple 373 testing: 374 - 2d.transformation.setTransform.identity 375 code: | 376 ctx.fillStyle = '#f00'; 377 ctx.fillRect(0, 0, 100, 50); 378 379 ctx.setTransform(1/2,0, 0,1/2, 0,0); 380 ctx.setTransform(); 381 ctx.setTransform(2,0, 0,2, 0,0); 382 ctx.fillStyle = '#0f0'; 383 ctx.fillRect(0, 0, 50, 25); 384 @assert pixel 75,35 == 0,255,0,255; 385 expected: green 386 387- name: 2d.transformation.setTransform.nonfinite 388 desc: setTransform() with Infinity/NaN is ignored 389 testing: 390 - 2d.nonfinite 391 code: | 392 ctx.fillStyle = '#f00'; 393 ctx.fillRect(0, 0, 100, 50); 394 395 ctx.translate(100, 10); 396 @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>); 397 398 ctx.fillStyle = '#0f0'; 399 ctx.fillRect(-100, -10, 100, 50); 400 401 @assert pixel 50,25 == 0,255,0,255; 402 expected: green 403 404- name: 2d.transformation.setTransform.3d 405 desc: setTransform() with 4x4 matrix keeps all parameters 406 testing: 407 - 2d.transformation.setTransform.3d 408 code: | 409 const transform = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]); 410 ctx.setTransform(transform); 411 const canvasTransform = ctx.getTransform(); 412 @assert transform.toLocaleString() == canvasTransform.toLocaleString(); 413 414- name: 2d.transformation.transform.3d 415 desc: transform() with 4x4 matrix concatenates properly 416 testing: 417 - 2d.transformation.transform.3d 418 code: | 419 const transform = new DOMMatrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]); 420 ctx.transform(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); 421 let canvasTransform = ctx.getTransform(); 422 @assert transform.toLocaleString() == canvasTransform.toLocaleString(); 423 ctx.transform(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); 424 canvasTransform = ctx.getTransform(); 425 transform.multiplySelf(transform); 426 @assert transform.toLocaleString() == canvasTransform.toLocaleString(); 427 428- name: 2d.transformation.translate.3d 429 desc: translate() function with three arguments modifies the underlying matrix appropriately 430 testing: 431 - 2d.transformation.translate.3d 432 code: | 433 const dx = 2; 434 const dy = 3; 435 const dz = 4; 436 ctx.translate(dx, dy, dz); 437 let canvasTransform = ctx.getTransform(); 438 @assert canvasTransform.m41 = dx; 439 @assert canvasTransform.m42 = dy; 440 @assert canvasTransform.m43 = dz; 441 ctx.translate(dx, dy, dz); 442 canvasTransform = ctx.getTransform(); 443 @assert canvasTransform.m41 = 2 * dx; 444 @assert canvasTransform.m42 = 2 * dy; 445 @assert canvasTransform.m43 = 2 * dz; 446 447- name: 2d.transformation.scale.3d 448 desc: scale() function with three arguments modifies the underlying matrix appropriately 449 testing: 450 - 2d.transformation.scale.3d 451 code: | 452 const sx = 2; 453 const sy = 3; 454 const sz = 4; 455 ctx.scale(sx, sy, sz); 456 let canvasTransform = ctx.getTransform(); 457 @assert canvasTransform.m11 = sx; 458 @assert canvasTransform.m22 = sy; 459 @assert canvasTransform.m33 = sz; 460 ctx.scale(sx, sy, sz); 461 canvasTransform = ctx.getTransform(); 462 @assert canvasTransform.m11 = 2 * sx; 463 @assert canvasTransform.m22 = 2 * sy; 464 @assert canvasTransform.m33 = 2 * sz; 465 466- name: 2d.transformation.rotate3d.x 467 desc: rotate3d() around the x axis results in the correct transformation matrix 468 testing: 469 - 2d.transformation.rotate3d.x 470 code: | 471 // angles are in radians, test something that is not a multiple of pi 472 const angle = 2; 473 const domMatrix = new DOMMatrix(); 474 ctx.rotate3d(angle, 0, 0); 475 domMatrix.rotateAxisAngleSelf(1, 0, 0, rad2deg(angle)); 476 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()) 477 ctx.rotate3d(angle, 0, 0); 478 domMatrix.rotateAxisAngleSelf(1, 0, 0, rad2deg(angle)); 479 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()) 480 481- name: 2d.transformation.rotate3d.y 482 desc: rotate3d() around the y axis results in the correct transformation matrix 483 testing: 484 - 2d.transformation.rotate3d.y 485 code: | 486 // angles are in radians, test something that is not a multiple of pi 487 const angle = 2; 488 const domMatrix = new DOMMatrix(); 489 ctx.rotate3d(0, angle, 0); 490 domMatrix.rotateAxisAngleSelf(0, 1, 0, rad2deg(angle)); 491 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()) 492 ctx.rotate3d(0, angle, 0); 493 domMatrix.rotateAxisAngleSelf(0, 1, 0, rad2deg(angle)); 494 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()) 495 496- name: 2d.transformation.rotate3d.z 497 desc: rotate3d() around the z axis results in the correct transformation matrix 498 testing: 499 - 2d.transformation.rotate3d.z 500 code: | 501 // angles are in radians, test something that is not a multiple of pi 502 const angle = 2; 503 const domMatrix = new DOMMatrix(); 504 ctx.rotate3d(0, 0, angle); 505 domMatrix.rotateAxisAngleSelf(0, 0, 1, rad2deg(angle)); 506 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()) 507 ctx.rotate3d(0, 0, angle); 508 domMatrix.rotateAxisAngleSelf(0, 0, 1, rad2deg(angle)); 509 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()) 510 511- name: 2d.transformation.rotate3d 512 desc: rotate3d() results in the correct transformation matrix 513 testing: 514 - 2d.transformation.rotate3d 515 code: | 516 // angles are in radians, test something that is not a multiple of pi 517 const angleX = 2; 518 const angleY = 3; 519 const angleZ = 4; 520 const domMatrix = new DOMMatrix(); 521 ctx.rotate3d(angleX, angleY, angleZ); 522 domMatrix.rotateSelf(rad2deg(angleX), rad2deg(angleY), rad2deg(angleZ)); 523 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()); 524 ctx.rotate3d(angleX, angleY, angleZ); 525 domMatrix.rotateSelf(rad2deg(angleX), rad2deg(angleY), rad2deg(angleZ)); 526 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()); 527 528- name: 2d.transformation.rotateAxis 529 desc: rotateAxis() results in the correct transformation matrix 530 testing: 531 - 2d.transformation.rotateAxis 532 code: | 533 // angles are in radians, test something that is not a multiple of pi 534 const angle = 2; 535 const axis = {x: 1, y: 2, z: 3} 536 const domMatrix = new DOMMatrix(); 537 ctx.rotateAxis(axis.x, axis.y, axis.z, angle); 538 domMatrix.rotateAxisAngleSelf(axis.x, axis.y, axis.z, rad2deg(angle)); 539 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()); 540 ctx.rotateAxis(axis.x, axis.y, axis.z, angle); 541 domMatrix.rotateAxisAngleSelf(axis.x, axis.y, axis.z, rad2deg(angle)); 542 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()); 543 544- name: 2d.transformation.perspective 545 desc: perspective() results in the correct transformation matrix 546 testing: 547 - 2d.transformation.perspective 548 code: | 549 const length = 100; 550 ctx.perspective(length); 551 const domMatrix = new DOMMatrix(); 552 domMatrix.m34 = -1/length; 553 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()); 554 ctx.rotateAxis(1, 2, 3, 4); 555 domMatrix.rotateAxisAngleSelf(1, 2, 3, rad2deg(4)); 556 _assertMatricesApproxEqual(domMatrix, ctx.getTransform()); 557 558- name: 2d.transformation.combined.3d.transforms 559 desc: perspective and rotate3d work together 560 testing: 561 - 2d.transformation.3d.transforms 562 code: | 563 ctx.fillStyle = '#0f0'; 564 ctx.fillRect(0, 0, 100, 50); 565 566 // Create a perspective transform in combiation with fillRect to draw a red 567 // trapezoid then draw a smaller green trapezoid inside it. 568 ctx.translate(50, 5); 569 ctx.perspective(100); 570 ctx.rotate3d(Math.PI/4, 0, 0); 571 ctx.fillStyle = "#f00"; 572 ctx.fillRect(-30, 0, 60, 40); 573 ctx.fillStyle = "#0f0"; 574 ctx.fillRect(-15, 10, 30, 20); 575 576 // These are the expected points of those two trapezoids from putting 577 // the corners of the filled rectangles through the perspective transform. 578 const bigTrapezoid = [[81, 5], [93, 45], [7, 45], [19, 5]]; 579 const smallTrapezoid = [[32, 31], [68, 31], [65, 13], [35, 13]]; 580 581 // Now filling a shape with green at those exact points with an identity 582 // transform should result in a purely green image. 583 ctx.resetTransform(); 584 ctx.beginPath(); 585 ctx.moveTo(bigTrapezoid[3][0], bigTrapezoid[3][1]); 586 for (const point of bigTrapezoid) ctx.lineTo(point[0], point[1]) 587 ctx.lineTo(smallTrapezoid[3][0], smallTrapezoid[3][1]); 588 for (const point of smallTrapezoid) ctx.lineTo(point[0], point[1]) 589 ctx.fill(); 590 @assert pixel 21,11 == 0,255,0,255; 591 @assert pixel 79,11 == 0,255,0,255; 592 @assert pixel 21,39 == 0,255,0,255; 593 @assert pixel 79,39 == 0,255,0,255; 594 @assert pixel 39,19 == 0,255,0,255; 595 @assert pixel 61,19 == 0,255,0,255; 596 @assert pixel 39,31 == 0,255,0,255; 597 @assert pixel 61,31 == 0,255,0,255; 598 expected: green 599