Skip to content

fix(editor): keep zoomed previews sharp on Linux - #1960

Open
prosthetiknow wants to merge 2 commits into
CyberTimon:mainfrom
prosthetiknow:fix/linux-zoom-sharpness
Open

prosthetiknow wants to merge 2 commits into
CyberTimon:mainfrom
prosthetiknow:fix/linux-zoom-sharpness

Conversation

@prosthetiknow

@prosthetiknow prosthetiknow commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Description

Fixes #1891, the open report of the bug behind #1160 and #1779. On Linux the editor never shows full detail when zoomed in. The backend renders the full-resolution preview, but WebKitGTK paints the CSS-scaled preview layer at its fit-to-window size and magnifies that bitmap. At 100% the canvas shows a stretched fit-size image, soft or blocky depending on isMaxZoom. The wgpu renderer has been off on Linux since #1061, so every Linux user takes this path.

This keeps zoom sharp on Linux and makes high zoom faster.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Performance improvement
  • Code refactoring
  • Documentation update
  • UI/UX improvement
  • Build/CI or Dependency update

Changes Made

  • Sharp layer - once zoom settles, ImageCanvas portals an <img> of the current preview into a layer that Editor places under the scaled content. The layer is laid out at the zoomed size with no scale(), so the engine paints it at screen resolution. Panning and zooming still move the CSS transform. The stretched SVG image stays until the sharp one loads, so thumbnails and loading look as before. Masks, crop and other overlays draw above it unchanged. It only applies when the wgpu renderer is off.
  • Visible-region renders - once the hi-fi target reaches 60% of the original long edge (about 50% zoom), the frontend requests the visible area plus a 15% margin at full resolution, through a new optional regionOnly flag on apply_adjustments. A second preview worker serves these, with its own CachedPreview and GpuImageCache slots. Region and whole-frame renders no longer evict each other's caches or drop each other's queued jobs. Region renders reuse the interactive ROI path at full quality and skip analytics. Whole-frame renders stay at fit size while zoomed, so the histogram still updates.
  • Stale preview fix - CachedPreview records the image path. The transform hash covers adjustments only, so a newly loaded photo with identical edits could reuse the previous photo's preview. load_image and clear_image_caches clear the region slots too.
  • Region requests build their payload with prepareAdjustmentsPayload, like the other preview requests. decodePatchResponse in useImageProcessing.ts replaces the inline patch-header parsing in executeApplyAdjustments.

Screenshots/Videos

Before:
Screenshot From 2026-10-19 20-01-12

After:
Screenshot From 2026-10-19 20-01-54

Testing

  • These changes were tested locally by a human and confirmed to work.
  • I haven't added any automated tests to the code because the codebase currently lacks a test suite.

Test Configuration:

  • OS: Fedora 44, GNOME 50.6 on Wayland, WebKitGTK 2.54.1, 166% display scaling
  • Hardware: ThinkPad X1 Carbon Gen 14, Intel Core Ultra X7 358H, Intel Arc B390 (Mesa 26.2.4)
  • Files: Fujifilm GFX100 II RAF, 11648×8736, 14-bit and 16-bit lossless compressed

With "Render at native DPI" on, reaching full detail at 100% used to take four whole-frame renders over about 15 s, the last at 11648×8736. A region render now takes 250 to 450 ms, after a one-off cache build per photo.

Not tested on macOS or Windows. Both changes stay off while the wgpu renderer is active.

Checklist

  • My code follows the project's code style
  • I haven't added unnecessary AI-generated code comments
  • My changes generate no new warnings or errors

Additional Notes

This builds on earlier reports and attempts.

Related

AI Disclaimer:

Please state the involvement of AI in this PR:

  • This PR is created by an AI agent
  • This PR is mostly AI-generated but edited/merged together by a human
  • This PR was handwritten with AI assistance (spell check, logic suggestions, error resolving)
  • This PR contains only blood, sweat, and coffee (AI-free)

WebKitGTK paints the CSS-scaled preview layer at its fit-to-window size
and magnifies it, so zoom showed a stretched copy whatever the render
resolution.

Once zoom settles, the editor draws the preview in a layer laid out at
its zoomed size, outside the scaled content. The stretched copy stays
until it loads, and panning still moves the CSS transform.

At about 50% zoom and closer, the backend renders the visible area plus
a 15% margin at full resolution. A second preview worker with its own
preview and GPU texture caches runs these, so whole-frame renders keep
theirs and stay at fit size while zoomed. On a GFX100 II, 100% sharpens
in about 0.3 s after a one-off build per photo, down from 8 to 15 s.

Cached previews record their source file. The transform hash covers
adjustments only, so photos with identical edits shared an entry.

Fixes CyberTimon#1891
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: Images not rendering in full detail at 100% zoom 1.6.5

1 participant