Repository: processing/p5.js (JavaScript)

Issue:
Masking behaviour change between 1.4.1 and latest
### Most appropriate sub-area of p5.js?

- [ ] Accessibility
- [ ] Color
- [X] Core/Environment/Rendering
- [ ] Data
- [ ] DOM
- [ ] Events
- [ ] Image
- [ ] IO
- [ ] Math
- [ ] Typography
- [ ] Utilities
- [ ] WebGL
- [ ] Build process
- [ ] Unit testing
- [ ] Internationalization
- [ ] Friendly errors
- [ ] Other (specify if possible)

### p5.js version

1.9.0

### Web browser and version

chrome

### Operating system

irrelevant/webEditor

### Steps to reproduce this

### Steps:
1. old version working with 1.4.1: https://jsfiddle.net/8b253Ly1/
2. new version no longer working , just silently failing: https://editor.p5js.org/jacopom/sketches/7SjQBqBMy
3. same code, different lib version linked

### Snippet:

```js

var img;
var masked;

var mask;

function setup() {
    createCanvas(400, 400);

		// creates an invisible graphic buffer
		// that contains a rectangle
    img = createGraphics(200, 200);
    img.rect(0, 0, 100, 100);

		// creates an invisible graphic buffer
		// that contains a circle
    mask = createGraphics(200, 200);
    mask.ellipse(100, 100, 100, 100);

		// we apply the circle in 'mask' as a clipping mask
		// to the rectangle image contained in 'img'
		// we save the result in a third buffer: 'masked'
    ( masked = img.get() ).mask( mask.get() );
}

function draw() {
    background(130,0,10);

		// print the buffer after the clipping
    image(masked, 0, 0);
}

```

This is something I used to teach in class, now it's broken and doesn't even give an error, what changed? how do I fix it?


Discussion: For now, setting `pixelDensity(1)` after creating your canvas seems to make it work again for me? I wonder if it broke after this https://github.com/processing/p5.js/pull/6447 and there's something we need to update to get `graphics.get()` images to mask correctly
Thank you for the swift response, just as a curiosity, is that the correct way to go about it or am I encountering this because I stepped outside of the most beaten path?
If there's a more mainstream/better supported way to create masks I wouldn't mind switching to that instead and abandoning this unsupported and kind of elaborate way of doing it.
Thank you.
That method should still work, so it's worth fixing!

But to clip to a path, we now have `beginClip()` and `endClip()` which might be a bit easier: https://p5js.org/reference/#/p5/beginClip
While I personally love that solution I'm currently using the previous example as a mini-demo to learn how to play with shapes ( both adding and subtracting shapes )

Anything involving push and pop requires me to increase my students' load of technical information of the inner workings of P5 and I generally try to avoid it until much later on. 

This example allows me to keep the complexity somewhat contained to the objects we create and therefore allows the students to maintain a sense of control and not get overwhelmed.

Thank you again for pointing out that solution but if the goal is teaching neophytes I feel like the "old" way offers a more direct and intuitive approach and I agree that it should be restored/maintained. 
Hey, I'm quite sure that either the `get()` function is not returning the density correctly or the `mask` function itself is bugged. 

Run this code (not created by me): https://editor.p5js.org/allison.parrish/sketches/kOHWUvQR1 on a display that has a display density not equal to 1, and you will see that the masked shape appears to be offset from the cursor. When you include `pixelDensity(1)`, the code works properly and the masked shape appears right on the cursor.

I looked into the `mask` function in `p5.Image` and it seems that the function is taking into account the pixel density of the image in the parameter, but not the pixel density of the image itself and that might be causing the bug. Is it okay if I take a stab at trying to fix this bug?
Thanks @Papershine, that would be great! Let me know if you have any questions! (also hello from someone else who went to UBC!)



Fix (patch):
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 <a href="#/p5.Image">p5.Image</a> 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(