From 5b0d565ff1941b828eaf568fd5fbe18505f169c3 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 16 Sep 2026 10:00:11 +0300 Subject: [PATCH 1/2] Quantize benchmark: spell out quantization methods --- Tests/benchmarks.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/Tests/benchmarks.py b/Tests/benchmarks.py index 11ed3cf743b..6e6492a56bc 100644 --- a/Tests/benchmarks.py +++ b/Tests/benchmarks.py @@ -818,12 +818,27 @@ def test_font_getmask(bench: BenchmarkFixture, mode: str) -> None: @pytest.mark.benchmark(group="quantize") -@pytest.mark.parametrize("mode", [m for m in MODES if m in ("L", "RGB", "RGBA")]) +@pytest.mark.parametrize( + "mode, method", + [ + ("L", Image.Quantize.MEDIANCUT), + ("L", Image.Quantize.MAXCOVERAGE), + ("RGB", Image.Quantize.MEDIANCUT), + ("RGB", Image.Quantize.MAXCOVERAGE), + ("RGBA", Image.Quantize.FASTOCTREE), + ], + ids=lambda p: getattr(p, "name", p), +) @pytest.mark.parametrize("size", SIZES, ids=_format_size) -def test_quantize(bench: BenchmarkFixture, mode: str, size: tuple[int, int]) -> None: +def test_quantize( + bench: BenchmarkFixture, + mode: str, + method: Image.Quantize, + size: tuple[int, int], +) -> None: im = make_pillow_image(mode, size) - bench.extra_info["label"] = [f"quantize {mode}"] - result = bench(im.quantize, 256) + bench.extra_info["label"] = [f"quantize {mode} {method.name}"] + result = bench(im.quantize, 256, method=method) assert result.mode == "P" From b94592001c3a4a8732bec1b1823e30d838601ce0 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 16 Sep 2026 09:39:13 +0300 Subject: [PATCH 2/2] Apply restrict optimizations to ImagingQuantize's output loop --- src/libImaging/Quant.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libImaging/Quant.c b/src/libImaging/Quant.c index 5712f6bd39c..a0796763751 100644 --- a/src/libImaging/Quant.c +++ b/src/libImaging/Quant.c @@ -1831,10 +1831,15 @@ ImagingQuantize(Imaging im, int colors, int mode, int kmeans) { } ImagingSectionEnter(&cookie); + // restrict safe: imOut is a fresh allocation and + // newData was just allocated by the quantizer. for (i = y = 0; y < ysize; y++) { + UINT8 *restrict out = imOut->image8[y]; + const uint32_t *restrict in = newData + i; for (x = 0; x < xsize; x++) { - imOut->image8[y][x] = (unsigned char)newData[i++]; + out[x] = (UINT8)in[x]; } + i += xsize; } free(newData);