Most appropriate sub-area of p5.js?
p5.js version
2.x (main branch)
Web browser and version
All browsers (Chromium / Firefox / Safari)
Operating system
macOS / All
Steps to reproduce this
Steps:
- Create a
p5.Image (e.g. const img = createImage(100, 100)).
- Set
img.pixelDensity(2) — img.width becomes 50.
- Call
img.pixelDensity(2) again — img.width becomes 25 instead of remaining 50.
- Call
img.pixelDensity(1) — img.width remains 25 instead of restoring to 100.
- Call
img.resize(50, 50) on an image with pixelDensity(2) — img.canvas.width is set to 50 instead of 100 (50 * 2), causing img.get(30, 30) to return [0, 0, 0, 0] as out of bounds, and img.set(x, y) to silently drop writes.
Snippet:
function setup() {
createCanvas(200, 200);
// --- Bug 1: pixelDensity setter is not idempotent & cannot be reset ---
const img1 = createImage(100, 100);
img1.pixelDensity(2);
console.log('After pixelDensity(2):', img1.width); // 50 (correct)
img1.pixelDensity(2);
console.log('After 2nd pixelDensity(2):', img1.width); // 25 (BUG: expected 50, halved again)
img1.pixelDensity(1);
console.log('After resetting pixelDensity(1):', img1.width); // 25 (BUG: expected 100, not restored)
// --- Bug 2: resize() desynchronizes canvas backing store at density > 1 ---
const img2 = createImage(100, 100);
img2.pixelDensity(2);
img2.resize(50, 50);
console.log('Logical width:', img2.width); // 50
console.log('Backing canvas.width:', img2.canvas.width); // 50 (BUG: expected 100)
// Because canvas.width is 50, get() checks x * 2 >= canvas.width.
// Coordinate (30, 30) is inside the 50x50 logical image, but 30 * 2 = 60 >= 50.
const col = img2.get(30, 30);
console.log('img2.get(30, 30):', col); // [0, 0, 0, 0] (BUG: treated as out of bounds)
}
Expected behavior:
img.pixelDensity(density) should be idempotent: calling img.pixelDensity(2) repeatedly should preserve logical dimensions at canvas.width / density (50), and calling img.pixelDensity(1) should restore logical dimensions to canvas.width / 1 (100).
img.resize(width, height) should maintain high-DPI resolution by resizing the backing canvas to Math.floor(width * this._pixelDensity) and Math.floor(height * this._pixelDensity). Pixel operations (get(), set(), loadPixels()) should work accurately across the full logical dimensions of the resized image.
Actual behavior:
- In
src/image/p5.Image.js:
// Adjust canvas dimensions based on pixel density
this.width /= density;
this.height /= density;
this.width and this.height are mutated via relative division (/= density). Each call compounds the division, and resetting to 1 divides by 1 without restoring the original dimensions.
- In
src/image/p5.Image.js:
// Resize the original canvas, which will clear its contents
this.canvas.width = this.width = width;
this.canvas.height = this.height = height;
resize() sets this.canvas.width and this.canvas.height directly to the logical target size width and height, discarding physical pixel density and desynchronizing the backing canvas from this._pixelDensity. As a result, subsequent get(x, y) calls fail bounds checking for any coordinates where x * pd >= canvas.width, returning transparent black [0, 0, 0, 0], and set(x, y) silently drops pixel writes.
Most appropriate sub-area of p5.js?
p5.js version
2.x (main branch)
Web browser and version
All browsers (Chromium / Firefox / Safari)
Operating system
macOS / All
Steps to reproduce this
Steps:
p5.Image(e.g.const img = createImage(100, 100)).img.pixelDensity(2)—img.widthbecomes50.img.pixelDensity(2)again —img.widthbecomes25instead of remaining50.img.pixelDensity(1)—img.widthremains25instead of restoring to100.img.resize(50, 50)on an image withpixelDensity(2)—img.canvas.widthis set to50instead of100(50 * 2), causingimg.get(30, 30)to return[0, 0, 0, 0]as out of bounds, andimg.set(x, y)to silently drop writes.Snippet:
Expected behavior:
img.pixelDensity(density)should be idempotent: callingimg.pixelDensity(2)repeatedly should preserve logical dimensions atcanvas.width / density(50), and callingimg.pixelDensity(1)should restore logical dimensions tocanvas.width / 1(100).img.resize(width, height)should maintain high-DPI resolution by resizing the backing canvas toMath.floor(width * this._pixelDensity)andMath.floor(height * this._pixelDensity). Pixel operations (get(),set(),loadPixels()) should work accurately across the full logical dimensions of the resized image.Actual behavior:
src/image/p5.Image.js:this.widthandthis.heightare mutated via relative division (/= density). Each call compounds the division, and resetting to1divides by1without restoring the original dimensions.src/image/p5.Image.js:resize()setsthis.canvas.widthandthis.canvas.heightdirectly to the logical target sizewidthandheight, discarding physical pixel density and desynchronizing the backing canvas fromthis._pixelDensity. As a result, subsequentget(x, y)calls fail bounds checking for any coordinates wherex * pd >= canvas.width, returning transparent black[0, 0, 0, 0], andset(x, y)silently drops pixel writes.