…ke isVisible transform-aware (#5201)
getTransform() intentionally returns only the affine matrix set via
setTransform, *without* the integer translate accumulated by translate(int,int).
This differs from java.awt.Graphics2D.getTransform() and surprised users
(#5201, follow-up to #3846): getTransform().transformPoint(x,y) does not yield
device coordinates on its own.
The split is load-bearing: setTransform/getTransform/transform() round-trip
correctly only because translate is excluded -- folding it into the returned
matrix would double-count it (once in the matrix, once in the vertex-baking
drawing pipeline on the isTranslationSupported()==false ports).
Rather than change the semantics (which would silently break existing transform
code), this:
- Documents on getTransform() / getTransform(Transform) that the integer
translate is excluded, and shows the correct idiom for mapping a point to
device coordinates (add getTranslateX()/getTranslateY() first).
- Makes isVisible(x,y,w,h) transform-aware: when a non-identity affine
transform is in effect, the rectangle's four corners are mapped through it
and the bounding box is tested against the clip. Previously the rectangle was
compared in untransformed space, so a rotated/scaled canvas got wrong
visibility results -- the exact gap the reporter hit. Falls back to the plain
rectangle test for identity transforms and on ports that throw.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes #5201 (follow-up to #3846).
Background
Graphics.getTransform()returns only the affine matrix set viasetTransform, without the integer translation accumulated bytranslate(int,int). That differs fromjava.awt.Graphics2D.getTransform(), sogetTransform().transformPoint(x,y)does not yield device coordinates on its own — the reporter had to manually addgetTranslateX()/getTranslateY()(plus fudge factors).This is intentional and load-bearing, not a bug:
setTransform/getTransform/transform()round-trip correctly only because the translate is excluded. On every active port (isTranslationSupported() == false: iOS, Android, JavaSE, JS) the integer translate is baked into vertex coordinates by the drawing pipeline, not into the matrix. IfgetTransform()folded it in, agetTransform → concatenate → setTransformcycle would count the translation twice and shift drawing.So changing the semantics to match Java would silently break existing transform code. Instead this PR documents the behavior and closes the actual usability gap.
Changes
getTransform()andgetTransform(Transform): state explicitly that the integer translate is excluded, explain why (round-trip integrity), and show the correct idiom for mapping a point to device coordinates:isVisible(int,int,int,int)is now transform-aware. The primitive was added in feat(Graphics): add isVisible(x,y,w,h) primitive (#3846) #5129 for Add a "is this visible" graphics primitive. #3846, but the reporter's follow-up noted it "doesn't do the job" under a non-trivial transform because the rectangle was compared in untransformed space. Now, when a non-identity affine transform is in effect, the rectangle's four corners are mapped through it and the bounding box is tested against the clip. The integer translate cancels (sincegetClipX/getClipYalready subtract it back out), so only the matrix is applied. Falls back to the plain rectangle test for identity transforms and on ports that throw.No behavioral change for the common (identity-transform) case.
🤖 Generated with Claude Code