Skip to content

Feat/dither algorithms - #534

Merged
knipknap merged 6 commits into
barebaric:mainfrom
switch87:feat/dither-algorithms
Oct 10, 2026
Merged

knipknap merged 6 commits into
barebaric:mainfrom
switch87:feat/dither-algorithms

Conversation

@switch87

@switch87 switch87 commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Added

Engrave: more dithering algorithms for photos. The error-diffusion
kernels Atkinson, Stucki, Jarvis-Judice-Ninke, Sierra (3-row,
2-row, Lite) and Burkes, with an optional serpentine scan, plus a
clustered-dot Newsprint screen and a round-dot Halftone screen with
adjustable cell size and angle. LightBurn imports keep the image
layer's dither mode (#514)

Fixed

Step settings: choice rows backed by an enum (such as the Engrave
step's dithering algorithm) now show the step's actual value when
the dialog opens and after undo, instead of the first entry

@knipknap knipknap left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks again for this PR! The code looks good to me; except for the merge conflicts resulting from your previous PR.

Note that in general I do not usually update .po files as part of new features; I make a single .po update ahead of each release. This avoids merge conflicts like these.

The step settings pages pushed raw attribute values into the varset
rows. For enum-backed attributes such as EngraveStep.dither_algorithm
the combo adapter got "DitherAlgorithm.BAYER4", matched no entry and
kept showing the first one (Floyd Steinberg), both when the dialog
opened and after undo or an external change.

Serialize the values with Step.recipe_value(), the same way recipes
store them, before handing them to the rows.
Extend DitherAlgorithm with Atkinson, Stucki, Jarvis-Judice-Ninke,
Sierra (3-row, 2-row, Lite) and Burkes error diffusion, a clustered-dot
Newsprint screen and a round-dot Halftone screen.

- apply_error_diffusion() takes any forward kernel and an optional
  serpentine scan. The error within a row is propagated per pixel, the
  error for the following rows with one vectorised add per kernel tap,
  which dithers a 2.5 MP image in about 0.6 s. Like raygeo's
  Floyd-Steinberg it works in linear light.
- apply_halftone() lays a cosine spot screen out in millimetres, so
  the dots stay round on the non-square raster pixels, and ranks the
  spot values so the dot area follows the darkness.
- Newsprint tiles a 45 degree clustered-dot matrix whose cells scale
  with the minimum feature size and stay square in millimetres.

Floyd-Steinberg without serpentine and the Bayer screens keep using
raygeo, so existing jobs produce identical output.

Refs barebaric#514
The Engrave step's dither algorithm list now offers every
DitherAlgorithm. New step settings, saved with the document, recipes
and cache params:

- Serpentine: alternate the scan direction per row, shown for the
  error-diffusion algorithms.
- Halftone Cell Size and Halftone Angle, shown for Halftone.

preprocess_raster_image() forwards them together with the Y
resolution, so the screens can keep their cells square in
millimetres. apply_import_settings() accepts the depth mode, threshold
and dither settings so importers can set them.

Refs barebaric#514
CutSetting_Img layers carried their image mode in ditherMode, which
the importer ignored, so every imported photo fell back to Variable
Power. Map it onto the Engrave step: dithered, atkinson, stucki,
jarvis, ordered, newsprint and halftone become the matching dither
algorithm (halftone with its cellsPerInch and halftoneAngle),
threshold becomes Constant Power and grayscale Variable Power. Sketch
has no counterpart and keeps the default.

Refs barebaric#514
@switch87

Copy link
Copy Markdown
Contributor Author

Addressed — rebased onto current main and resolved the conflicts with the previous PR. This PR no longer changes any translation catalogs (zero .po/.pot diff): the new strings live in the source only and the catalog pass stays yours, ahead of each release. The other open PRs (#532, #535, #536, #538, #539, #540) got the same treatment, so none of them should conflict with the release translation pass anymore.

The error-diffusion kernels, the serpentine scan and the halftone and
newsprint screens now live in raygeo (barebaric/raygeo#55, released
as 1.62.0) instead of being reimplemented here. rayforge/image/dither
keeps the DitherAlgorithm catalogue and dispatches to raygeo,
translating the enum values to raygeo's kernel names; the Python-side
kernels, spot function and screen builders are gone.

Bump the raygeo pin to ==1.62.0 in pixi.toml, requirements.txt and
debian/requirements-bundle.txt, and relock pixi.lock.
@knipknap
knipknap merged commit 634d5ce into barebaric:main Oct 10, 2026
32 checks passed
@knipknap

Copy link
Copy Markdown
Contributor

Thanks for updating the PR! I made a bit of a change on top of it: I migrated the actual dither implementation to Raygeo for consistency with the existing algorithms.

Thanks again!

This branch was successfully deployed

1 active deployment
pypi — 27cbfbd1 Deployed Oct 10, 2026 by knipknap via Build and Publish Wheel #2209
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.

2 participants