@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url'
55import test from 'ava'
66import PNG from '@jimp/png'
77
8- import { createCanvas , loadImage , GlobalFonts , Image , DOMMatrix , DOMPoint } from '../index'
8+ import { createCanvas , loadImage , GlobalFonts , Image , ImageData , DOMMatrix , DOMPoint } from '../index'
99import { snapshotImage } from './image-snapshot'
1010
1111const __dirname = dirname ( fileURLToPath ( import . meta. url ) )
@@ -456,6 +456,99 @@ test('putImageData double free', (t) => {
456456 } )
457457} )
458458
459+ // https://github.com/Brooooooklyn/canvas/issues/1226
460+ test ( 'putImageData with negative dx/dy should draw correctly' , ( t ) => {
461+ const canvas = createCanvas ( 200 , 200 )
462+ const ctx = canvas . getContext ( '2d' )
463+ // Fill canvas with black
464+ ctx . fillStyle = 'black'
465+ ctx . fillRect ( 0 , 0 , 200 , 200 )
466+ // Create a 100x100 white ImageData
467+ const imageData = new ImageData ( 100 , 100 )
468+ imageData . data . fill ( 255 )
469+ // putImageData at (-20, -20): should place an 80x80 white region at (0,0)
470+ ctx . putImageData ( imageData , - 20 , - 20 )
471+ // Pixel at (0,0) should be white (within the drawn region)
472+ const topLeft = ctx . getImageData ( 0 , 0 , 1 , 1 ) . data
473+ t . is ( topLeft [ 0 ] , 255 )
474+ t . is ( topLeft [ 1 ] , 255 )
475+ t . is ( topLeft [ 2 ] , 255 )
476+ t . is ( topLeft [ 3 ] , 255 )
477+ // Pixel at (79,79) should be white (last pixel of the drawn region)
478+ const edge = ctx . getImageData ( 79 , 79 , 1 , 1 ) . data
479+ t . is ( edge [ 0 ] , 255 )
480+ t . is ( edge [ 1 ] , 255 )
481+ t . is ( edge [ 2 ] , 255 )
482+ t . is ( edge [ 3 ] , 255 )
483+ // Pixel at (80,80) should be black (outside the drawn region)
484+ const outside = ctx . getImageData ( 80 , 80 , 1 , 1 ) . data
485+ t . is ( outside [ 0 ] , 0 )
486+ t . is ( outside [ 1 ] , 0 )
487+ t . is ( outside [ 2 ] , 0 )
488+ t . is ( outside [ 3 ] , 255 )
489+ } )
490+
491+ // https://github.com/Brooooooklyn/canvas/issues/1226
492+ test ( 'getImageData with negative x/y should return correct pixels' , ( t ) => {
493+ const canvas = createCanvas ( 100 , 100 )
494+ const ctx = canvas . getContext ( '2d' )
495+ // Fill entire canvas red
496+ ctx . fillStyle = 'red'
497+ ctx . fillRect ( 0 , 0 , 100 , 100 )
498+ // getImageData(-10, -10, 20, 20): top-left 10x10 is outside canvas (transparent black),
499+ // bottom-right 10x10 is canvas pixels (red)
500+ const data = ctx . getImageData ( - 10 , - 10 , 20 , 20 )
501+ t . is ( data . width , 20 )
502+ t . is ( data . height , 20 )
503+ // Pixel at (0,0) in the ImageData corresponds to canvas (-10,-10) — outside, should be transparent black
504+ const outOfBounds = [ data . data [ 0 ] , data . data [ 1 ] , data . data [ 2 ] , data . data [ 3 ] ]
505+ t . deepEqual ( outOfBounds , [ 0 , 0 , 0 , 0 ] )
506+ // Pixel at (5,5) in the ImageData is still outside canvas (-5,-5) — transparent black
507+ const stillOutside = 4 * ( 5 * 20 + 5 )
508+ t . deepEqual ( [ data . data [ stillOutside ] , data . data [ stillOutside + 1 ] , data . data [ stillOutside + 2 ] , data . data [ stillOutside + 3 ] ] , [ 0 , 0 , 0 , 0 ] )
509+ // Pixel at (10,10) in the ImageData corresponds to canvas (0,0) — should be red
510+ const insideOffset = 4 * ( 10 * 20 + 10 )
511+ t . is ( data . data [ insideOffset ] , 255 ) // R
512+ t . is ( data . data [ insideOffset + 1 ] , 0 ) // G
513+ t . is ( data . data [ insideOffset + 2 ] , 0 ) // B
514+ t . is ( data . data [ insideOffset + 3 ] , 255 ) // A
515+ // Pixel at (19,19) in the ImageData corresponds to canvas (9,9) — should be red
516+ const cornerOffset = 4 * ( 19 * 20 + 19 )
517+ t . is ( data . data [ cornerOffset ] , 255 ) // R
518+ t . is ( data . data [ cornerOffset + 1 ] , 0 ) // G
519+ t . is ( data . data [ cornerOffset + 2 ] , 0 ) // B
520+ t . is ( data . data [ cornerOffset + 3 ] , 255 ) // A
521+ } )
522+
523+ test ( 'getImageData with negative width/height should flip the region per spec' , ( t ) => {
524+ const canvas = createCanvas ( 100 , 100 )
525+ const ctx = canvas . getContext ( '2d' )
526+ // Paint a 10x10 green square at (5,5)
527+ ctx . fillStyle = 'green'
528+ ctx . fillRect ( 5 , 5 , 10 , 10 )
529+ // getImageData(15, 15, -10, -10) per spec: sx=15+(-10)=5, sy=15+(-10)=5, sw=10, sh=10
530+ // Should return the 10x10 region starting at (5,5) — the green square
531+ const data = ctx . getImageData ( 15 , 15 , - 10 , - 10 )
532+ t . is ( data . width , 10 )
533+ t . is ( data . height , 10 )
534+ t . is ( data . data . length , 10 * 10 * 4 )
535+ // Center pixel (5,5) in the ImageData should be green
536+ const centerOffset = 4 * ( 5 * 10 + 5 )
537+ t . is ( data . data [ centerOffset ] , 0 ) // R
538+ t . is ( data . data [ centerOffset + 1 ] , 128 ) // G (green is 0,128,0)
539+ t . is ( data . data [ centerOffset + 2 ] , 0 ) // B
540+ t . is ( data . data [ centerOffset + 3 ] , 255 ) // A
541+ } )
542+
543+ test ( 'createImageData with negative dimensions should use absolute values' , ( t ) => {
544+ const canvas = createCanvas ( 100 , 100 )
545+ const ctx = canvas . getContext ( '2d' )
546+ const imageData = ctx . createImageData ( - 50 , - 30 )
547+ t . is ( imageData . width , 50 )
548+ t . is ( imageData . height , 30 )
549+ t . is ( imageData . data . length , 50 * 30 * 4 )
550+ } )
551+
459552// https://github.com/Brooooooklyn/canvas/issues/987
460553test ( 'draw-canvas-on-canvas' , async ( t ) => {
461554 const backCanvas = createCanvas ( 1920 , 1080 )
0 commit comments