Skip to content

Preserve HiDPI image detail when painting ROIs and overlays - #302

Open
mikelxc wants to merge 2 commits into
imagej:masterfrom
mikelxc:fix/hidpi-roi-double-buffer
Open

mikelxc wants to merge 2 commits into
imagej:masterfrom
mikelxc:fix/hidpi-roi-double-buffer

Conversation

@mikelxc

@mikelxc mikelxc commented Oct 1, 2026 •

Copy link
Copy Markdown

Creating an ROI or enabling an overlay sends ImageCanvas through paintDoubleBuffered. Its offscreen image currently uses logical srcRect × magnification dimensions and loses the destination graphics scale. This patch allocates a raster for the transformed image bounds, preserves the full destination transform (including fractional pixel phase) while painting it, and presents it at an integer device-pixel origin using the inverse transform. A cleared ARGB raster handles fractional-edge padding. It keeps double buffering and the existing 1× image creation path; it does not disable acceleration or alter image data.

Evidence

This originated from a user report: native Apple Silicon Fiji is sharp until an ROI is created; removing it restores sharpness. Both portable and bundled ARM distributions reportedly reproduce, Intel/Rosetta reportedly avoids it, and forcing Metal off/OpenGL on reportedly did not help. A live native ARM64 Retina diagnostic now reproduces the same sharp → ROI detail loss → deselection restores detail sequence on synthetic data, and the patched canvas preserves detail. This does not establish the result for the user's microscopy image or every Fiji installation; no Intel runtime control was available.

Live native Retina comparison

Using the official Fiji-bundled Azul Zulu 21.0.7+6 ARM64 runtime, a side-by-side AWT diagnostic displayed baseline 4c4975d6 and patched fd942d4a canvases with the same 512×512 one-pixel checkerboard at 50% zoom. The diagnostic subclass delegates to ImageCanvas.paint and does not override Canvas.createImage, so the baseline uses a real macOS canvas peer. Actual incoming graphics and graphics-configuration default transforms both reported scale(2,2).

Screenshots of the live window were inspected for three states:

State Baseline Patched
No ROI Fine checkerboard visible Fine checkerboard visible
Rectangle ROI Image interior becomes flat gray Fine checkerboard remains visible
ROI cleared Fine checkerboard returns Fine checkerboard remains visible

The saved 1300×782 window captures are JPEGs. Comparing matched 300×300 image-interior regions away from the ROI and controls, adding the ROI changed 90,000/90,000 baseline screenshot pixels and 0/90,000 patched screenshot pixels. After clearing, both regions matched their no-ROI capture exactly. These are measurements of decoded screenshot pixels, separate from the headless raster measurements below; JPEG color values are not source-image pixel values. The source is synthetic and non-sensitive. Native-peer offscreen backing dimensions were not measured in this live comparison; the raster sizes in the next table are headless measurements. The harness uses ImageCanvas in an AWT frame rather than the full Fiji application UI.

Deterministic raster comparison

A deterministic headless AWT test on native ARM64 does reproduce the logical-buffer loss using a synthetic one-pixel checkerboard. The test overrides Canvas.createImage with a logical-resolution BufferedImage; this deliberately tests a defined raster behavior, not the undocumented backing behavior of a particular macOS AWT peer.

At 50% zoom on a simulated 2× destination:

Measurement Before (master 4c4975d6) After
Source 256×256 256×256
Logical destination 128×128 128×128
Offscreen raster 128×128 256×256
Pixels changed by adding ROI, in a 120×120 interior 14,400 / 14,400 0 / 14,400
Image processor pixels changed No No

In these headless measurements, the target transform was explicitly scale(2,2) and the graphics configuration default transform was identity. Thus the patch uses the actual destination transform, not a hard-coded Retina multiplier or a platform/architecture switch.

Validation

  • Official mvn package with workspace-local Temurin 11.0.32.1 ARM64: passes. Production ImageCanvas.class remains Java 6 bytecode (major 50); no newer production APIs added.
  • Eight standalone JUnit 4.13.2 regression checks pass on native ARM Temurin 11.0.32.1, Homebrew OpenJDK 17.0.20.1 and the official Fiji-bundled Azul Zulu 21.0.7 runtime. They fail on unmodified master and pass with the patch. They cover 50%/100%/200% zoom; 1×/2×/1.5× and anisotropic 1.25×/1.5× destinations; rectangle, oval, line, point, text, polygon and freehand ROIs; overlay, Show All rendering list, forced double buffering; cropped source and interpolation; repeated deselection and device-scale changes using one cached canvas; unchanged processor pixels. The matrix includes odd source sizes 129/130/131, odd cropped extents, and fractional target translations (0.25/0.5 pixels). The initial candidate resampled rounded fractional buffers and failed the odd-size tests; the final correction preserves the original transform and avoids a second resampling. Cropped-image comparisons hide the zoom indicator to isolate image detail.
  • Enabling upstream's Maven test phase fails before execution with the existing empty maven.compiler.release property (For input string: "" in testCompile). The same failure was reproduced on an untouched archived checkout of base 4c4975d6. No POM change is included. Focused tests were compiled separately against the Maven-built production classes and run with -Djava.awt.headless=true.
  • git diff --check: passes.

Standalone test command after mvn package (JDK 11, with JUnit and Hamcrest on $TEST_LIBS):

javac -source 8 -target 8 -cp "target/classes:$TEST_LIBS" -d /tmp/imagej-hidpi-tests tests/ij/gui/ImageCanvasHiDpiTest.java
java -Djava.awt.headless=true -cp "target/classes:/tmp/imagej-hidpi-tests:$TEST_LIBS" org.junit.runner.JUnitCore ij.gui.ImageCanvasHiDpiTest

Remaining validation / tradeoffs

The real-peer Retina diagnostic confirms the ROI-triggered loss and its correction on synthetic input with the Fiji-bundled ARM runtime. Validation on the reported microscopy image, the full Fiji UI, native-peer backing dimensions, direct-paint bypass, other installed JDKs on a live display, Intel/Rosetta control, multi-monitor moves, interactive ROI handles/hit testing and flicker/performance remain untested. Live ROI types beyond the rectangle were not exercised; the wider ROI matrix is headless. Arbitrary rotated/sheared graphics and very large-buffer memory pressure are not covered; a 2× raster uses roughly four times the backing pixels. The added tests compare image interiors, not complete UI fidelity.

Ready for upstream review with the limits above. The current commit is signed with the author's existing GPG key. GitHub Actions run 36937838743 for exact HEAD fd942d4a737a190b7daab85dd18ed9c845e22eba is action_required, awaiting maintainer approval; no upstream CI pass is claimed. No merge requested.

@mikelxc
mikelxc marked this pull request as ready for review October 2, 2026 00:54
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.

1 participant