Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Tests/test_image_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,3 +377,28 @@ def test_matrix_identity() -> None:
# Test list
converted_im = im.convert("RGB", list(identity_matrix))
assert_image_equal(converted_im, im)


def test_adaptive_convert_honors_dither(tmp_path: Path) -> None:
# Regression test for #5836: convert("P", palette=ADAPTIVE, colors=N,
# dither=...) must apply dithering, not just nearest-color mapping.
im = hopper("RGB")

no_dither = im.convert(
"P", palette=Image.Palette.ADAPTIVE, colors=8, dither=Image.Dither.NONE
)
with_dither = im.convert(
"P",
palette=Image.Palette.ADAPTIVE,
colors=8,
dither=Image.Dither.FLOYDSTEINBERG,
)

# Both should produce valid palette images with the same palette size.
assert no_dither.mode == "P"
assert with_dither.mode == "P"
assert len(no_dither.getpalette()) == len(with_dither.getpalette()) == 24

# Dithered and non-dithered results must differ. Histograms should spread
# differently because dithering distributes error across neighboring pixels.
assert no_dither.histogram() != with_dither.histogram()
8 changes: 7 additions & 1 deletion src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -1204,7 +1204,13 @@ def convert_transparency(
raise ValueError(msg)

if mode == "P" and palette == Palette.ADAPTIVE:
im = self.im.quantize(colors)
# quantize() maps each pixel to the nearest palette entry without
# dithering, so it silently drops the dither argument. Build the
# adaptive palette first, then re-convert with dither so the
# dither argument is honored (see #5836).
adaptive = self.im.quantize(colors)
palette_im = self._new(adaptive)
im = self.im.convert(mode, dither, palette_im.im)
new_im = self._new(im)
from . import ImagePalette

Expand Down
Loading