Upstream line quality improvements#425
Open
edwardhorsford wants to merge 2 commits intoPattern-Projector:betafrom
Open
Upstream line quality improvements#425edwardhorsford wants to merge 2 commits intoPattern-Projector:betafrom
edwardhorsford wants to merge 2 commits intoPattern-Projector:betafrom
Conversation
After feMorphology erode, anti-aliased line edges become grey and blurry. This adds a two-step quality pass to counteract that: 1. A push-darks SVG filter (feComponentTransfer with gamma=2) that darkens grey midtones toward black while leaving white backgrounds unchanged. 2. A contrast(1.5) CSS filter that further separates lines from background. On Chrome/Firefox these run as part of the canvas draw filter chain: erode → url(#push-darks) → contrast(1.5) On Safari, SVG filter references on canvas CSS aren't supported, so an equivalent pixel-level pass (enhanceLineQualityFast) runs directly on the ImageData after erosion. It uses a pre-built LUT for gamma + contrast, which is 10-100× faster than per-pixel Math.pow() calls. Both paths produce visually equivalent results.
…olorMatrix The Green theme previously used a chain of CSS filters to approximate green lines on black: invert(1) sepia(1) saturate(10000%) hue-rotate(65deg) brightness(1.5) This had several problems (tracked in issue Pattern-Projector#418): - Colours were never exact; hue-rotate is an approximation - Cross-browser inconsistencies between Chrome and Safari - Different code paths produced visually different results This commit replaces that chain with a single feColorMatrix SVG filter (the `#recolor` filter) that directly maps pixel luminance to the target colour: output = targetColour × (1 - luminance(input)) So black input → target colour at full intensity, white input → black (invisible on dark background), grey inputs → proportionally dimmer shades of the target colour. On Chrome/Firefox the filter is applied via ctx.filter at canvas draw time. On Safari, where SVG filter references are not supported on canvas contexts, pixel-level processing (recolourImageData) is used instead. Safari rendering improvements: - A back buffer canvas is used as the render target. The visible canvas only receives committed pixels once processing is complete, eliminating the grey flash that previously appeared during zoom changes. - The visible canvas starts at 0×0 on mount to avoid a white rectangle appearing before the first render. - During theme switches the canvas is hidden until the new render is ready; the parent div's backgroundColor shows through in the interim. - onPageRenderSuccess is now wrapped in useCallback so its stable reference does not cause the render effect to re-fire on every parent re-render (which was causing extremely slow panning on Safari). Changes: - filters.tsx: adds the `#recolor` feColorMatrix filter, computed from the strokeColor hex passed as a prop from the page - erode.ts: erosionFilter() always applies push-darks + contrast(1.5), even at 0 erosions; accepts an optional useRecolour flag to append url(#recolor); adds recolourImageData() for Safari pixel-level colour - display-settings.ts: Green themeFilter returns "none"; adds isColourTheme() helper; Green strokeColor set to #00FFCC - use-render-context.ts: adds recolourHex and themeFilter fields - pdf-viewer.tsx: passes recolourHex and themeFilter to RenderContext; switches blend mode to "lighten" for overlapping dark-background pages; wraps onPageRenderSuccess in useCallback - pdf-custom-renderer.tsx: Safari back-buffer render path; Chrome/Firefox ctx.filter draw path; zoom-out skip via content key; mount-zero canvas - calibrate/page.tsx: computes recolourHex from strokeColor and wires it through to Filters and PdfViewer
|
@edwardhorsford is attempting to deploy a commit to the Courtney Pattison's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Collaborator
|
Awesome, thank you @edwardhorsford ! |
Author
|
@courtneypattison no worries. Hopefully it makes sense. I had to tinker quite a bit - esp trying to reduce flashes of un-inverted styles. I held off moving to workers in this, though Claude suggested it several times. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This upstreams some of the work I did in my fork to improve the line quality.
Commit 1 — Push darks and contrast boost
Introduces a
push-darksSVG filter (gamma=2 feComponentTransfer) and acontrast(1.5)step applied to every PDF canvas render. These are always applied, even at zero line thickness, because they improve the raw PDF output: grey anti-aliased edges are pushed back toward black, producing sharper, cleaner lines.On Safari, where SVG filter references cannot be applied to canvas contexts, an equivalent pixel-level LUT (
enhanceLineQualityFast) is used instead.Commit 2 — feColorMatrix recolour
Replaces the Green theme's CSS filter chain (
invert sepia saturate hue-rotate brightness) with a single#recolorfeColorMatrix SVG filter that directly maps luminance to the target colour:Black input → full target colour. White input → black. Grey inputs → proportionally dimmer shades. This gives exact, consistent colour across browsers, and makes the target colour configurable as a hex value rather than an approximation via hue-rotate. I've picked a light greenish hue similar to the brightest in the existing Pattern Projector, but you might want to tweak it.
On Chrome/Firefox, the recolour is applied via
ctx.filterat canvas draw time. On Safari, pixel-level processing (recolourImageData) handles it.