From 948c064b282f80492f5b88d3f06a599b76516edf Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Feb 2022 09:56:13 +1100 Subject: [PATCH 1/2] Allow getpalette() to return less than 256 colors --- Tests/test_image_putpalette.py | 1 + src/PIL/Image.py | 6 ++---- src/_imaging.c | 9 ++++++--- src/libImaging/Imaging.h | 1 + src/libImaging/Palette.c | 1 + 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index 725ecaade01..3b29769a7a4 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -74,4 +74,5 @@ def test_putpalette_with_alpha_values(): def test_rgba_palette(mode, palette): im = Image.new("P", (1, 1)) im.putpalette(palette, mode) + assert im.getpalette() == [1, 2, 3] assert im.palette.colors == {(1, 2, 3, 4): 0} diff --git a/src/PIL/Image.py b/src/PIL/Image.py index 8d36e8871b0..449d29c4437 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -821,7 +821,7 @@ def load(self): if self.im and self.palette and self.palette.dirty: # realize palette mode, arr = self.palette.getdata() - palette_length = self.im.putpalette(mode, arr) + self.im.putpalette(mode, arr) self.palette.dirty = 0 self.palette.rawmode = None if "transparency" in self.info and mode in ("LA", "PA"): @@ -833,9 +833,7 @@ def load(self): else: palette_mode = "RGBA" if mode.startswith("RGBA") else "RGB" self.palette.mode = palette_mode - self.palette.palette = self.im.getpalette(palette_mode, palette_mode)[ - : palette_length * len(palette_mode) - ] + self.palette.palette = self.im.getpalette(palette_mode, palette_mode) if self.im: if cffi and USE_CFFI_ACCESS: diff --git a/src/_imaging.c b/src/_imaging.c index 2ea517816a0..0888188fb20 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -1063,7 +1063,7 @@ _gaussian_blur(ImagingObject *self, PyObject *args) { static PyObject * _getpalette(ImagingObject *self, PyObject *args) { PyObject *palette; - int palettesize = 256; + int palettesize; int bits; ImagingShuffler pack; @@ -1084,6 +1084,7 @@ _getpalette(ImagingObject *self, PyObject *args) { return NULL; } + palettesize = self->image->palette->size; palette = PyBytes_FromStringAndSize(NULL, palettesize * bits / 8); if (!palette) { return NULL; @@ -1672,9 +1673,11 @@ _putpalette(ImagingObject *self, PyObject *args) { self->image->palette = ImagingPaletteNew(palette_mode); - unpack(self->image->palette->palette, palette, palettesize * 8 / bits); + self->image->palette->size = palettesize * 8 / bits; + unpack(self->image->palette->palette, palette, self->image->palette->size); - return PyLong_FromLong(palettesize * 8 / bits); + Py_INCREF(Py_None); + return Py_None; } static PyObject * diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index 9b1c1024dc4..b65f8eadd51 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -143,6 +143,7 @@ struct ImagingPaletteInstance { char mode[IMAGING_MODE_LENGTH]; /* Band names */ /* Data */ + int size; UINT8 palette[1024]; /* Palette data (same format as image data) */ INT16 *cache; /* Palette cache (used for predefined palettes) */ diff --git a/src/libImaging/Palette.c b/src/libImaging/Palette.c index 43bea61e327..174e58d34ea 100644 --- a/src/libImaging/Palette.c +++ b/src/libImaging/Palette.c @@ -40,6 +40,7 @@ ImagingPaletteNew(const char *mode) { palette->mode[IMAGING_MODE_LENGTH - 1] = 0; /* Initialize to ramp */ + palette->size = 256; for (i = 0; i < 256; i++) { palette->palette[i * 4 + 0] = palette->palette[i * 4 + 1] = palette->palette[i * 4 + 2] = (UINT8)i; From 54cb09d8b48cd6781a261621d596b614687c6246 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Wed, 16 Feb 2022 11:01:00 +1100 Subject: [PATCH 2/2] When converting to P, restrict colors to palette size --- Tests/test_image_quantize.py | 15 +++++++++++++++ src/libImaging/Palette.c | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 16cb8b41a13..a36f789fffe 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -108,3 +108,18 @@ def test_palette(method, color): converted = im.quantize(method=method) converted_px = converted.load() assert converted_px[0, 0] == converted.palette.colors[color] + + +def test_small_palette(): + # Arrange + im = hopper() + + colors = (255, 0, 0, 0, 0, 255) + p = Image.new("P", (1, 1)) + p.putpalette(colors) + + # Act + im = im.quantize(palette=p) + + # Assert + assert len(im.getcolors()) == 2 diff --git a/src/libImaging/Palette.c b/src/libImaging/Palette.c index 174e58d34ea..20c6bc84b1a 100644 --- a/src/libImaging/Palette.c +++ b/src/libImaging/Palette.c @@ -194,7 +194,7 @@ ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) { dmax = (unsigned int)~0; - for (i = 0; i < 256; i++) { + for (i = 0; i < palette->size; i++) { int r, g, b; unsigned int tmin, tmax; @@ -227,7 +227,7 @@ ImagingPaletteCacheUpdate(ImagingPalette palette, int r, int g, int b) { d[i] = (unsigned int)~0; } - for (i = 0; i < 256; i++) { + for (i = 0; i < palette->size; i++) { if (dmin[i] <= dmax) { int rd, gd, bd; int ri, gi, bi;