You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
More dithering algorithms for photo engraving (Stucki, Jarvis, Atkinson, Sierra, Burkes, halftone) #514
Is your feature request related to a problem? Please describe.
For photo engraving the Engrave step offers Floyd–Steinberg and Bayer 2/4/8 only. On a diode laser, error-diffusion kernels that spread the error wider (Stucki, Jarvis–Judice–Ninke) give a smoother, less "wormy" grain in flat areas such as skin tones, and Atkinson keeps more contrast on wood. LightBurn projects that use these modes (<ditherMode Value="stucki"/> in a CutSetting_Img) currently import without any dither setting at all, so the image layer falls back to the default mode.
Describe the solution you'd like
Extend DitherAlgorithm with the common error-diffusion kernels: Atkinson, Stucki, Jarvis–Judice–Ninke, Sierra (3-row), Sierra 2-row, Sierra Lite and Burkes.
Add two screen-based modes: a clustered-dot ordered dither ("Newsprint") and a classic halftone with round dots, where the cell size (mm) and screen angle are step settings.
A generic error-diffusion helper that takes the kernel, with an optional serpentine (alternating direction) scan. Floyd–Steinberg without serpentine keeps using raygeo.image.dither, so existing jobs produce identical output.
New Engrave-step rows, only visible where they apply: "Serpentine" for the error-diffusion algorithms, "Halftone Cell Size" and "Halftone Angle" for Halftone. Recipes and cache params follow the new fields.
Map LightBurn's ditherMode (and its halftone cells/angle) on import.
Describe alternatives you've considered
Dithering the photo in GIMP first works, but it is an extra step per photo, and the dither then no longer follows the step's spot size and line interval. The error-diffusion loop could also live in raygeo; I'm starting with a row-vectorised NumPy implementation in rayforge/image/dither.py (about 1 s for a 2 MP image, with the error spread to later rows done per row in NumPy) and I'm happy to move the kernels into raygeo if you'd rather have them there.
Additional context
I'm working on this and will report back with a branch.
Thanks for the detailed proposal. Expanding the dithering selection is long overdue — the wider error-diffusion kernels (Stucki, Jarvis) genuinely do give smoother flats on diode lasers, and Atkinson's contrast retention on wood is a real effect people ask for. The generic kernel-driven error-diffusion helper with optional serpentine is the right architecture, and keeping plain Floyd–Steinberg on the existing implementation so current jobs produce identical output is important.
The two screen-based modes are a good addition: a clustered-dot ordered dither and a true halftone with cell size and angle as step settings cover the "newsprint" looks that error diffusion can't produce. Only showing the new rows where they apply, and extending recipes and cache params with the new fields, is exactly right.
Mapping LightBurn's ditherMode on import fixes a real gap — those projects currently lose their dither setting entirely, which you've correctly identified.
A row-vectorised NumPy implementation at about a second for 2 MP is acceptable; I'm open to moving the kernels into the geometry engine later if profiling says so, but there's no need to decide that up front. Please go ahead with the branch — happy to review.
Implemented in #534 (merged 2026-10-10): the Atkinson, Stucki, Jarvis-Judice-Ninke, Sierra and Burkes kernels with optional serpentine, the Newsprint and Halftone screens, and LightBurn's dither mode on import. Thanks for moving the implementation to raygeo. Closing.
Activity
knipknap commentedon Oct 9, 2026
Thanks for the detailed proposal. Expanding the dithering selection is long overdue — the wider error-diffusion kernels (Stucki, Jarvis) genuinely do give smoother flats on diode lasers, and Atkinson's contrast retention on wood is a real effect people ask for. The generic kernel-driven error-diffusion helper with optional serpentine is the right architecture, and keeping plain Floyd–Steinberg on the existing implementation so current jobs produce identical output is important.
The two screen-based modes are a good addition: a clustered-dot ordered dither and a true halftone with cell size and angle as step settings cover the "newsprint" looks that error diffusion can't produce. Only showing the new rows where they apply, and extending recipes and cache params with the new fields, is exactly right.
Mapping LightBurn's ditherMode on import fixes a real gap — those projects currently lose their dither setting entirely, which you've correctly identified.
A row-vectorised NumPy implementation at about a second for 2 MP is acceptable; I'm open to moving the kernels into the geometry engine later if profiling says so, but there's no need to decide that up front. Please go ahead with the branch — happy to review.
feat(image): add error-diffusion kernels and halftone screens
feat(engrave): expose serpentine and halftone settings
feat(lightburn): keep the image layer's dither mode on import
docs: document the new dithering algorithms
switch87 commentedon Oct 13, 2026
Implemented in #534 (merged 2026-10-10): the Atkinson, Stucki, Jarvis-Judice-Ninke, Sierra and Burkes kernels with optional serpentine, the Newsprint and Halftone screens, and LightBurn's dither mode on import. Thanks for moving the implementation to raygeo. Closing.