Skip to content

fix(image): account for destination pixel density in p5.Image copy and blend - #9168

Open
Pcmhacker-piro wants to merge 4 commits into
processing:mainfrom
Pcmhacker-piro:fix/copy-blend-high-dpi
Open

Pcmhacker-piro wants to merge 4 commits into
processing:mainfrom
Pcmhacker-piro:fix/copy-blend-high-dpi

Conversation

@Pcmhacker-piro

@Pcmhacker-piro Pcmhacker-piro commented Sep 11, 2026

Copy link
Copy Markdown

Resolves #9169

Overview

Fixes a high-DPI scaling bug where calling copy() and blend() on a p5.Image with pixelDensity > 1 (e.g. on Retina or high-DPI displays) failed to scale the destination coordinates (dx, dy, dw, dh) to the physical dimensions of the backing canvas.

Cause of the Bug

In p5.Image.prototype._copyHelper:

  • Source coordinates were scaled by s = srcImage.canvas.width / srcImage.width.
  • However, dstImage.drawingContext is a 2D canvas context without an automatic DPI scale transform.
  • drawImage() was called with unscaled logical destination coordinates dx, dy, dw, dh.
  • On a high-DPI target (e.g. pixelDensity = 2), the copied image covered only 1/4th of the intended destination area in the top-left corner, leaving the rest untouched.
  • Because p5.Image.prototype.blend() internally delegates to copy(), it was similarly affected.

Changes:

  • In src/image/p5.Image.js, computed destination pixel density scale factor const d = dstImage.canvas.width / dstImage.width.
  • Passed d * dx, d * dy, d * dw, d * dh into dstImage.drawingContext.drawImage().
  • Added unit tests in test/unit/image/p5.Image.js for p5.Image.prototype.copy and p5.Image.prototype.blend verifying correct high-DPI destination rendering.

PR Checklist

  • npm run lint passes
  • [Inline reference] is included / updated (N/A)
  • [Unit tests] are included / updated

@Pcmhacker-piro

Copy link
Copy Markdown
Author

Hi @ksen0, could you please take a look at this PR when you have a moment? It fixes a high-DPI scaling issue where p5.Image.prototype.copy and blend did not scale destination coordinates by pixelDensity, causing copied content to be truncated to 1/4th of the intended area on Retina displays. Unit tests and linter are both passing. Thank you!

@p5-bot

p5-bot Bot commented Sep 14, 2026

Copy link
Copy Markdown

Continuous Release

CDN link

Published Packages

Commit hash: 0e7e74a

Previous deployments

9d2ffa0


This is an automated message.

@perminder-17 perminder-17 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this change might also affect mask(). It already passes imgScaleFactor * this.width and imgScaleFactor * this.height to copy(). Since _copyHelper() now scales the destination dimensions again, could this cause double scaling when the image’s pixel density is greater than 1?

mask() was pre-multiplying destination coords by imgScaleFactor before
passing them to copy(). Since _copyHelper() now scales destination
coords by the destination pixel density internally, this caused
double-scaling when pixelDensity > 1.

Fix: pass logical (unscaled) this.width / this.height from mask() and
let _copyHelper() handle the scaling.

Added unit test verifying mask() works correctly at pixelDensity 2.
@Pcmhacker-piro

Copy link
Copy Markdown
Author

Great catch @perminder-17! You were right — mask() was pre-multiplying the destination coordinates by imgScaleFactor (lines 1013–1014), and since _copyHelper() now also scales by the destination pixel density, this caused double-scaling when pixelDensity > 1.

Fix: Updated mask() to pass logical (unscaled) this.width / this.height as destination dimensions, letting _copyHelper() handle the pixel density scaling internally.

Added: A unit test for mask() at pixelDensity(2) that verifies no double-scaling occurs.

All tests pass (8 passed, 7 todo). Lint is clean (0 errors).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: p5.Image.prototype.copy and blend do not scale destination coordinates for high pixel density

3 participants