diff --git a/src/image/p5.Image.js b/src/image/p5.Image.js index b9188c4b44..8ab3175afc 100644 --- a/src/image/p5.Image.js +++ b/src/image/p5.Image.js @@ -1111,7 +1111,8 @@ p5.Image = class { * * `img.mask()` uses another p5.Image object's * alpha channel as the alpha channel for this image. Masks are cumulative - * and can't be removed once applied. + * and can't be removed once applied. If the mask has a different + * pixel density from this image, the mask will be scaled. * * @method mask * @param {p5.Image} srcImage source image. @@ -1149,21 +1150,22 @@ p5.Image = class { } const currBlend = this.drawingContext.globalCompositeOperation; - let scaleFactor = 1; + let imgScaleFactor = this._pixelDensity; + let maskScaleFactor = 1; if (p5Image instanceof p5.Renderer) { - scaleFactor = p5Image._pInst._pixelDensity; + maskScaleFactor = p5Image._pInst._pixelDensity; } const copyArgs = [ p5Image, 0, 0, - scaleFactor * p5Image.width, - scaleFactor * p5Image.height, + maskScaleFactor * p5Image.width, + maskScaleFactor * p5Image.height, 0, 0, - this.width, - this.height + imgScaleFactor * this.width, + imgScaleFactor * this.height ]; this.drawingContext.globalCompositeOperation = 'destination-in'; @@ -1178,8 +1180,8 @@ p5.Image = class { this.gifProperties.frames[i].image = this.drawingContext.getImageData( 0, 0, - this.width, - this.height + imgScaleFactor * this.width, + imgScaleFactor * this.height ); } this.drawingContext.putImageData( diff --git a/test/unit/image/p5.Image.js b/test/unit/image/p5.Image.js index 0e44d84a29..6db53702d2 100644 --- a/test/unit/image/p5.Image.js +++ b/test/unit/image/p5.Image.js @@ -51,8 +51,44 @@ suite('p5.Image', function() { }); suite('p5.Image.prototype.mask', function() { - test('it should mask the image', function() { + for (const density of [1, 2]) { + test(`it should mask the image at pixel density ${density}`, function() { + let img = myp5.createImage(10, 10); + img.pixelDensity(density); + img.loadPixels(); + for (let i = 0; i < img.height; i++) { + for (let j = 0; j < img.width; j++) { + let alpha = i < 5 ? 255 : 0; + img.set(i, j, myp5.color(0, 0, 0, alpha)); + } + } + img.updatePixels(); + + let mask = myp5.createImage(10, 10); + mask.pixelDensity(density); + mask.loadPixels(); + for (let i = 0; i < mask.width; i++) { + for (let j = 0; j < mask.height; j++) { + let alpha = j < 5 ? 255 : 0; + mask.set(i, j, myp5.color(0, 0, 0, alpha)); + } + } + mask.updatePixels(); + + img.mask(mask); + img.loadPixels(); + for (let i = 0; i < img.width; i++) { + for (let j = 0; j < img.height; j++) { + let alpha = i < 5 && j < 5 ? 255 : 0; + assert.strictEqual(img.get(i, j)[3], alpha); + } + } + }); + } + + test('it should mask images of different density', function() { let img = myp5.createImage(10, 10); + img.pixelDensity(1); img.loadPixels(); for (let i = 0; i < img.height; i++) { for (let j = 0; j < img.width; j++) { @@ -62,15 +98,16 @@ suite('p5.Image', function() { } img.updatePixels(); - let mask = myp5.createImage(10, 10); + let mask = myp5.createImage(20, 20); mask.loadPixels(); for (let i = 0; i < mask.width; i++) { for (let j = 0; j < mask.height; j++) { - let alpha = j < 5 ? 255 : 0; + let alpha = j < 10 ? 255 : 0; mask.set(i, j, myp5.color(0, 0, 0, alpha)); } } mask.updatePixels(); + mask.pixelDensity(2); img.mask(mask); img.loadPixels(); @@ -82,6 +119,25 @@ suite('p5.Image', function() { } }); + test('it should mask images from createGraphics', function() { + myp5.createCanvas(10,10); + myp5.pixelDensity(2); + let img = myp5.createGraphics(10,10); + img.rect(0,0,10,10); + img.background(0); + let mask = createGraphics(10,10); + mask.rect(0,0,5,5); + let masked = img.get(); + masked.mask( mask.get() ); + + for (let i = 0; i < masked.width; i++) { + for (let j = 0; j < masked.height; j++) { + let alpha = i < 5 && j < 5 ? 255 : 0; + assert.strictEqual(masked.get(i, j)[3], alpha); + } + } + }); + test('it should mask the animated gif image', function() { const imagePath = 'unit/assets/nyan_cat.gif'; return new Promise(function(resolve, reject) {