From f9f7ba4ce962558581ec5786397efad96e2ccc53 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 23 Aug 2024 18:24:04 +1000 Subject: [PATCH 1/7] Do not raise error if bitmap buffer is empty --- Tests/test_font_crash.py | 11 +- src/_imagingft.c | 266 ++++++++++++++++++++------------------- 2 files changed, 138 insertions(+), 139 deletions(-) diff --git a/Tests/test_font_crash.py b/Tests/test_font_crash.py index b82340ef7..c394551f5 100644 --- a/Tests/test_font_crash.py +++ b/Tests/test_font_crash.py @@ -1,10 +1,8 @@ from __future__ import annotations -import pytest - from PIL import Image, ImageDraw, ImageFont -from .helper import skip_unless_feature +from .helper import skip_unless_feature_version class TestFontCrash: @@ -17,8 +15,7 @@ class TestFontCrash: draw.multiline_textbbox((10, 10), "ABC\nAaaa", font, stroke_width=2) draw.text((10, 10), "Test Text", font=font, fill="#000") - @skip_unless_feature("freetype2") + @skip_unless_feature_version("freetype2", "2.12.0") def test_segfault(self) -> None: - with pytest.raises(OSError): - font = ImageFont.truetype("Tests/fonts/fuzz_font-5203009437302784") - self._fuzz_font(font) + font = ImageFont.truetype("Tests/fonts/fuzz_font-5203009437302784") + self._fuzz_font(font) diff --git a/src/_imagingft.c b/src/_imagingft.c index f8143e0cc..54b78d5bc 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -1025,130 +1025,99 @@ font_render(FontObject *self, PyObject *args) { yy = -(py + glyph_slot->bitmap_top); } - // Null buffer, is dereferenced in FT_Bitmap_Convert - if (!bitmap.buffer && bitmap.rows) { - PyErr_SetString(PyExc_OSError, "Bitmap missing for glyph"); - goto glyph_error; - } - - /* convert non-8bpp bitmaps */ - switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: - convert_scale = 255; - break; - case FT_PIXEL_MODE_GRAY2: - convert_scale = 255 / 3; - break; - case FT_PIXEL_MODE_GRAY4: - convert_scale = 255 / 15; - break; - default: - convert_scale = 1; - } - switch (bitmap.pixel_mode) { - case FT_PIXEL_MODE_MONO: - case FT_PIXEL_MODE_GRAY2: - case FT_PIXEL_MODE_GRAY4: - if (!bitmap_converted_ready) { - FT_Bitmap_Init(&bitmap_converted); - bitmap_converted_ready = 1; - } - error = FT_Bitmap_Convert(library, &bitmap, &bitmap_converted, 1); - if (error) { - geterror(error); - goto glyph_error; - } - bitmap = bitmap_converted; - /* bitmap is now FT_PIXEL_MODE_GRAY, fall through */ - case FT_PIXEL_MODE_GRAY: - break; - case FT_PIXEL_MODE_BGRA: - if (color) { + if (bitmap.buffer) { + /* convert non-8bpp bitmaps */ + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: + convert_scale = 255; break; - } - /* we didn't ask for color, fall through to default */ - default: - PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); - goto glyph_error; - } - - /* clip glyph bitmap width to target image bounds */ - x0 = 0; - x1 = bitmap.width; - if (xx < 0) { - x0 = -xx; - } - if (xx + x1 > im->xsize) { - x1 = im->xsize - xx; - } - - source = (unsigned char *)bitmap.buffer; - for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) { - /* clip glyph bitmap height to target image bounds */ - if (yy >= 0 && yy < im->ysize) { - /* blend this glyph into the buffer */ - int k; - unsigned char *target; - unsigned int tmp; - if (color) { - /* target[RGB] returns the color, target[A] returns the mask */ - /* target bands get split again in ImageDraw.text */ - target = (unsigned char *)im->image[yy] + xx * 4; - } else { - target = im->image8[yy] + xx; - } - if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { - /* paste color glyph */ - for (k = x0; k < x1; k++) { - unsigned int src_alpha = source[k * 4 + 3]; - - /* paste only if source has data */ - if (src_alpha > 0) { - /* unpremultiply BGRa */ - int src_red = - CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha); - int src_green = - CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha); - int src_blue = - CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha); - - /* blend required if target has data */ - if (target[k * 4 + 3] > 0) { - /* blend RGBA colors */ - target[k * 4 + 0] = - BLEND(src_alpha, target[k * 4 + 0], src_red, tmp); - target[k * 4 + 1] = - BLEND(src_alpha, target[k * 4 + 1], src_green, tmp); - target[k * 4 + 2] = - BLEND(src_alpha, target[k * 4 + 2], src_blue, tmp); - target[k * 4 + 3] = CLIP8( - src_alpha + - MULDIV255(target[k * 4 + 3], (255 - src_alpha), tmp) - ); - } else { - /* paste unpremultiplied RGBA values */ - target[k * 4 + 0] = src_red; - target[k * 4 + 1] = src_green; - target[k * 4 + 2] = src_blue; - target[k * 4 + 3] = src_alpha; - } - } + case FT_PIXEL_MODE_GRAY2: + convert_scale = 255 / 3; + break; + case FT_PIXEL_MODE_GRAY4: + convert_scale = 255 / 15; + break; + default: + convert_scale = 1; + } + switch (bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: + case FT_PIXEL_MODE_GRAY2: + case FT_PIXEL_MODE_GRAY4: + if (!bitmap_converted_ready) { + FT_Bitmap_Init(&bitmap_converted); + bitmap_converted_ready = 1; } - } else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { + error = FT_Bitmap_Convert(library, &bitmap, &bitmap_converted, 1); + if (error) { + geterror(error); + goto glyph_error; + } + bitmap = bitmap_converted; + /* bitmap is now FT_PIXEL_MODE_GRAY, fall through */ + case FT_PIXEL_MODE_GRAY: + break; + case FT_PIXEL_MODE_BGRA: if (color) { - unsigned char *ink = (unsigned char *)&foreground_ink; + break; + } + /* we didn't ask for color, fall through to default */ + default: + PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); + goto glyph_error; + } + + /* clip glyph bitmap width to target image bounds */ + x0 = 0; + x1 = bitmap.width; + if (xx < 0) { + x0 = -xx; + } + if (xx + x1 > im->xsize) { + x1 = im->xsize - xx; + } + + source = (unsigned char *)bitmap.buffer; + for (bitmap_y = 0; bitmap_y < bitmap.rows; bitmap_y++, yy++) { + /* clip glyph bitmap height to target image bounds */ + if (yy >= 0 && yy < im->ysize) { + /* blend this glyph into the buffer */ + int k; + unsigned char *target; + unsigned int tmp; + if (color) { + /* target[RGB] returns the color, target[A] returns the mask */ + /* target bands get split again in ImageDraw.text */ + target = (unsigned char *)im->image[yy] + xx * 4; + } else { + target = im->image8[yy] + xx; + } + if (color && bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { + /* paste color glyph */ for (k = x0; k < x1; k++) { - unsigned int src_alpha = source[k] * convert_scale; + unsigned int src_alpha = source[k * 4 + 3]; + + /* paste only if source has data */ if (src_alpha > 0) { + /* unpremultiply BGRa */ + int src_red = + CLIP8((255 * (int)source[k * 4 + 2]) / src_alpha); + int src_green = + CLIP8((255 * (int)source[k * 4 + 1]) / src_alpha); + int src_blue = + CLIP8((255 * (int)source[k * 4 + 0]) / src_alpha); + + /* blend required if target has data */ if (target[k * 4 + 3] > 0) { + /* blend RGBA colors */ target[k * 4 + 0] = BLEND( - src_alpha, target[k * 4 + 0], ink[0], tmp + src_alpha, target[k * 4 + 0], src_red, tmp ); target[k * 4 + 1] = BLEND( - src_alpha, target[k * 4 + 1], ink[1], tmp + src_alpha, target[k * 4 + 1], src_green, tmp ); target[k * 4 + 2] = BLEND( - src_alpha, target[k * 4 + 2], ink[2], tmp + src_alpha, target[k * 4 + 2], src_blue, tmp ); target[k * 4 + 3] = CLIP8( src_alpha + @@ -1157,35 +1126,68 @@ font_render(FontObject *self, PyObject *args) { ) ); } else { - target[k * 4 + 0] = ink[0]; - target[k * 4 + 1] = ink[1]; - target[k * 4 + 2] = ink[2]; + /* paste unpremultiplied RGBA values */ + target[k * 4 + 0] = src_red; + target[k * 4 + 1] = src_green; + target[k * 4 + 2] = src_blue; target[k * 4 + 3] = src_alpha; } } } - } else { - for (k = x0; k < x1; k++) { - unsigned int src_alpha = source[k] * convert_scale; - if (src_alpha > 0) { - target[k] = - target[k] > 0 - ? CLIP8( - src_alpha + - MULDIV255( - target[k], (255 - src_alpha), tmp + } else if (bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { + if (color) { + unsigned char *ink = (unsigned char *)&foreground_ink; + for (k = x0; k < x1; k++) { + unsigned int src_alpha = source[k] * convert_scale; + if (src_alpha > 0) { + if (target[k * 4 + 3] > 0) { + target[k * 4 + 0] = BLEND( + src_alpha, target[k * 4 + 0], ink[0], tmp + ); + target[k * 4 + 1] = BLEND( + src_alpha, target[k * 4 + 1], ink[1], tmp + ); + target[k * 4 + 2] = BLEND( + src_alpha, target[k * 4 + 2], ink[2], tmp + ); + target[k * 4 + 3] = CLIP8( + src_alpha + MULDIV255( + target[k * 4 + 3], + (255 - src_alpha), + tmp + ) + ); + } else { + target[k * 4 + 0] = ink[0]; + target[k * 4 + 1] = ink[1]; + target[k * 4 + 2] = ink[2]; + target[k * 4 + 3] = src_alpha; + } + } + } + } else { + for (k = x0; k < x1; k++) { + unsigned int src_alpha = source[k] * convert_scale; + if (src_alpha > 0) { + target[k] = + target[k] > 0 + ? CLIP8( + src_alpha + + MULDIV255( + target[k], (255 - src_alpha), tmp + ) ) - ) - : src_alpha; + : src_alpha; + } } } + } else { + PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); + goto glyph_error; } - } else { - PyErr_SetString(PyExc_OSError, "unsupported bitmap pixel mode"); - goto glyph_error; } + source += bitmap.pitch; } - source += bitmap.pitch; } x += glyph_info[i].x_advance; y += glyph_info[i].y_advance; From d5d0734169ac4c593caac4f5769e60a01574fa09 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 16 Mar 2026 20:48:25 +1100 Subject: [PATCH 2/7] Add CMYK palettes --- Tests/test_image_putpalette.py | 15 +++++++++++++++ src/PIL/Image.py | 11 ++++++++--- src/libImaging/Pack.c | 14 ++++++++++++++ src/libImaging/Palette.c | 3 ++- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index 661764b60..237de6330 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -91,6 +91,21 @@ def test_rgba_palette(mode: str, palette: tuple[int, ...]) -> None: assert im.palette.colors == {(1, 2, 3, 4): 0} +@pytest.mark.parametrize( + "mode, palette", + ( + ("CMYK", (1, 2, 3, 4)), + ("CMYKX", (1, 2, 3, 4, 0)), + ), +) +def test_cmyk_palette(mode: str, palette: tuple[int, ...]) -> None: + im = Image.new("P", (1, 1)) + im.putpalette(palette, mode) + assert im.getpalette() == [250, 249, 248] + assert im.palette is not None + assert im.palette.colors == {(1, 2, 3, 4): 0} + + def test_empty_palette() -> None: im = Image.new("P", (1, 1)) assert im.getpalette() == [] diff --git a/src/PIL/Image.py b/src/PIL/Image.py index cc431a86a..c9db97319 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -2145,8 +2145,8 @@ class Image: Alternatively, an 8-bit string may be used instead of an integer sequence. :param data: A palette sequence (either a list or a string). - :param rawmode: The raw mode of the palette. Either "RGB", "RGBA", or a mode - that can be transformed to "RGB" or "RGBA" (e.g. "R", "BGR;15", "RGBA;L"). + :param rawmode: The raw mode of the palette. Either "RGB", "RGBA", "CMYK", or a + mode that can be transformed to one of those modes (e.g. "R", "RGBA;L"). """ from . import ImagePalette @@ -2165,7 +2165,12 @@ class Image: palette = ImagePalette.raw(rawmode, data) self._mode = "PA" if "A" in self.mode else "P" self.palette = palette - self.palette.mode = "RGBA" if "A" in rawmode else "RGB" + if rawmode.startswith("CMYK"): + self.palette.mode = "CMYK" + elif "A" in rawmode: + self.palette.mode = "RGBA" + else: + self.palette.mode = "RGB" self.load() # install new palette def putpixel( diff --git a/src/libImaging/Pack.c b/src/libImaging/Pack.c index fdf5a72aa..161d82f2e 100644 --- a/src/libImaging/Pack.c +++ b/src/libImaging/Pack.c @@ -325,6 +325,19 @@ ImagingPackXBGR(UINT8 *out, const UINT8 *in, int pixels) { } } +void +ImagingPackCMYK2RGB(UINT8 *out, const UINT8 *in, int xsize) { + int x, nk, tmp; + for (x = 0; x < xsize; x++) { + nk = 255 - in[3]; + out[0] = CLIP8(nk - MULDIV255(in[0], nk, tmp)); + out[1] = CLIP8(nk - MULDIV255(in[1], nk, tmp)); + out[2] = CLIP8(nk - MULDIV255(in[2], nk, tmp)); + out += 3; + in += 4; + } +} + void ImagingPackBGRA(UINT8 *out, const UINT8 *in, int pixels) { int i; @@ -605,6 +618,7 @@ static struct { {IMAGING_MODE_CMYK, IMAGING_RAWMODE_M, 8, band1}, {IMAGING_MODE_CMYK, IMAGING_RAWMODE_Y, 8, band2}, {IMAGING_MODE_CMYK, IMAGING_RAWMODE_K, 8, band3}, + {IMAGING_MODE_CMYK, IMAGING_RAWMODE_RGB, 24, ImagingPackCMYK2RGB}, /* video (YCbCr) */ {IMAGING_MODE_YCbCr, IMAGING_RAWMODE_YCbCr, 24, ImagingPackRGB}, diff --git a/src/libImaging/Palette.c b/src/libImaging/Palette.c index 371ba644b..b2dacf656 100644 --- a/src/libImaging/Palette.c +++ b/src/libImaging/Palette.c @@ -27,7 +27,8 @@ ImagingPaletteNew(const ModeID mode) { int i; ImagingPalette palette; - if (mode != IMAGING_MODE_RGB && mode != IMAGING_MODE_RGBA) { + if (mode != IMAGING_MODE_RGB && mode != IMAGING_MODE_RGBA && + mode != IMAGING_MODE_CMYK) { return (ImagingPalette)ImagingError_ModeError(); } From 29509ffa753011d250e976e941a88a28a8277e12 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 16 Mar 2026 20:21:53 +1100 Subject: [PATCH 3/7] Detect CMYK palette in JPEG2000 images --- Tests/test_file_jpeg2k.py | 1 + src/PIL/Jpeg2KImagePlugin.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 575d911de..df686df32 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -445,6 +445,7 @@ def test_pclr() -> None: ) as im: assert im.mode == "P" assert im.palette is not None + assert im.palette.mode == "CMYK" assert len(im.palette.colors) == 139 assert im.palette.colors[(0, 0, 0, 0)] == 0 diff --git a/src/PIL/Jpeg2KImagePlugin.py b/src/PIL/Jpeg2KImagePlugin.py index d6ec38d43..e5d735c73 100644 --- a/src/PIL/Jpeg2KImagePlugin.py +++ b/src/PIL/Jpeg2KImagePlugin.py @@ -176,6 +176,7 @@ def _parse_jp2_header( nc = None dpi = None # 2-tuple of DPI info, or None palette = None + cmyk = False while header.has_next_box(): tbox = header.next_box_type() @@ -196,10 +197,11 @@ def _parse_jp2_header( mode = "RGB" elif nc == 4: mode = "RGBA" - elif tbox == b"colr" and nc == 4: + elif tbox == b"colr": meth, _, _, enumcs = header.read_fields(">BBBI") - if meth == 1 and enumcs == 12: - mode = "CMYK" + if cmyk := (meth == 1 and enumcs == 12): + if nc == 4: + mode = "CMYK" elif tbox == b"pclr" and mode in ("L", "LA"): ne, npc = header.read_fields(">HB") assert isinstance(ne, int) @@ -210,7 +212,11 @@ def _parse_jp2_header( if bitdepth > max_bitdepth: max_bitdepth = bitdepth if max_bitdepth <= 8: - palette = ImagePalette.ImagePalette("RGBA" if npc == 4 else "RGB") + if npc == 4: + palette_mode = "CMYK" if cmyk else "RGBA" + else: + palette_mode = "RGB" + palette = ImagePalette.ImagePalette(palette_mode) for i in range(ne): color: list[int] = [] for value in header.read_fields(">" + ("B" * npc)): From 6a06285bf8832a87cc5a1c8d4d850e90051a87dd Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 16 Mar 2026 23:52:33 +1100 Subject: [PATCH 4/7] Support reading JPEG2000 images with CMYK palettes --- src/libImaging/Jpeg2KDecode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libImaging/Jpeg2KDecode.c b/src/libImaging/Jpeg2KDecode.c index 1b496f45e..1123d7bc9 100644 --- a/src/libImaging/Jpeg2KDecode.c +++ b/src/libImaging/Jpeg2KDecode.c @@ -601,6 +601,7 @@ j2ku_sycca_rgba( static const struct j2k_decode_unpacker j2k_unpackers[] = { {IMAGING_MODE_L, OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_l}, {IMAGING_MODE_P, OPJ_CLRSPC_SRGB, 1, 0, j2ku_gray_l}, + {IMAGING_MODE_P, OPJ_CLRSPC_CMYK, 1, 0, j2ku_gray_l}, {IMAGING_MODE_PA, OPJ_CLRSPC_SRGB, 2, 0, j2ku_graya_la}, {IMAGING_MODE_I_16, OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_i}, {IMAGING_MODE_I_16B, OPJ_CLRSPC_GRAY, 1, 0, j2ku_gray_i}, From 8442a8541c486eb19a3bc3d940c43a45241f2e44 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 16 Mar 2026 22:12:58 +1100 Subject: [PATCH 5/7] Support saving images with non-RGB palettes as PNGs --- Tests/test_file_png.py | 10 ++++++++++ src/PIL/PngImagePlugin.py | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 2e0af5041..3f08d1ad3 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -707,6 +707,16 @@ class TestFilePng: assert reloaded.png.im_palette is not None assert len(reloaded.png.im_palette[1]) == 3 + def test_plte_cmyk(self, tmp_path: Path) -> None: + im = Image.new("P", (1, 1)) + im.putpalette((0, 100, 150, 200), "CMYK") + + out = tmp_path / "temp.png" + im.save(out) + + with Image.open(out) as reloaded: + assert reloaded.convert("CMYK").getpixel((0, 0)) == (200, 222, 232, 0) + def test_getxmp(self) -> None: with Image.open("Tests/images/color_snakes.png") as im: if ElementTree is None: diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py index 572762e6c..4e082a293 100644 --- a/src/PIL/PngImagePlugin.py +++ b/src/PIL/PngImagePlugin.py @@ -1353,6 +1353,9 @@ def _save( mode = im.mode outmode = mode + palette = [] + if im.palette: + palette = im.getpalette() or [] if mode == "P": # # attempt to minimize storage requirements for palette images @@ -1362,7 +1365,7 @@ def _save( else: # check palette contents if im.palette: - colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 1) + colors = max(min(len(palette) // 3, 256), 1) else: colors = 256 @@ -1435,7 +1438,7 @@ def _save( if im.mode == "P": palette_byte_number = colors * 3 - palette_bytes = im.im.getpalette("RGB")[:palette_byte_number] + palette_bytes = bytes(palette[:palette_byte_number]) while len(palette_bytes) < palette_byte_number: palette_bytes += b"\0" chunk(fp, b"PLTE", palette_bytes) From c3041861905e690b3f71901fd1cab4667a82c46a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Fri, 20 Mar 2026 10:02:14 +1100 Subject: [PATCH 6/7] Simplified code --- Tests/test_file_tga.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index bb8d3eefc..277515fd4 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -13,8 +13,6 @@ _TGA_DIR = os.path.join("Tests", "images", "tga") _TGA_DIR_COMMON = os.path.join(_TGA_DIR, "common") -_ORIGINS = ("tl", "bl") - _ORIGIN_TO_ORIENTATION = {"tl": 1, "bl": -1} @@ -29,7 +27,7 @@ _ORIGIN_TO_ORIENTATION = {"tl": 1, "bl": -1} ("200x32", "RGBA"), ), ) -@pytest.mark.parametrize("origin", _ORIGINS) +@pytest.mark.parametrize("origin", _ORIGIN_TO_ORIENTATION) @pytest.mark.parametrize("rle", (True, False)) def test_sanity( size_mode: tuple[str, str], origin: str, rle: str, tmp_path: Path From 3b1f70da61df02378594ba6f490e937da127d40f Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sat, 21 Mar 2026 01:01:20 +1100 Subject: [PATCH 7/7] Simplify `setimage()` by always passing extents (#9395) --- src/PIL/Image.py | 4 ++-- src/decode.c | 15 +++++---------- src/encode.c | 15 +++++---------- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/PIL/Image.py b/src/PIL/Image.py index cc431a86a..01f49ccc0 100644 --- a/src/PIL/Image.py +++ b/src/PIL/Image.py @@ -885,7 +885,7 @@ class Image: # unpack data e = _getencoder(self.mode, encoder_name, encoder_args) - e.setimage(self.im) + e.setimage(self.im, (0, 0) + self.size) from . import ImageFile @@ -956,7 +956,7 @@ class Image: # unpack data d = _getdecoder(self.mode, decoder_name, decoder_args) - d.setimage(self.im) + d.setimage(self.im, (0, 0) + self.size) s = d.decode(data) if s[0] >= 0: diff --git a/src/decode.c b/src/decode.c index 7ec461c0e..c5c9cf56f 100644 --- a/src/decode.c +++ b/src/decode.c @@ -163,7 +163,7 @@ _setimage(ImagingDecoderObject *decoder, PyObject *args) { x0 = y0 = x1 = y1 = 0; /* FIXME: should publish the ImagingType descriptor */ - if (!PyArg_ParseTuple(args, "O|(iiii)", &op, &x0, &y0, &x1, &y1)) { + if (!PyArg_ParseTuple(args, "O(iiii)", &op, &x0, &y0, &x1, &y1)) { return NULL; } im = PyImaging_AsImaging(op); @@ -176,15 +176,10 @@ _setimage(ImagingDecoderObject *decoder, PyObject *args) { state = &decoder->state; /* Setup decoding tile extent */ - if (x0 == 0 && x1 == 0) { - state->xsize = im->xsize; - state->ysize = im->ysize; - } else { - state->xoff = x0; - state->yoff = y0; - state->xsize = x1 - x0; - state->ysize = y1 - y0; - } + state->xoff = x0; + state->yoff = y0; + state->xsize = x1 - x0; + state->ysize = y1 - y0; if (state->xoff < 0 || state->xsize <= 0 || state->xsize + state->xoff > (int)im->xsize || state->yoff < 0 || diff --git a/src/encode.c b/src/encode.c index 01b3af13f..f2bb464fa 100644 --- a/src/encode.c +++ b/src/encode.c @@ -232,7 +232,7 @@ _setimage(ImagingEncoderObject *encoder, PyObject *args) { x0 = y0 = x1 = y1 = 0; /* FIXME: should publish the ImagingType descriptor */ - if (!PyArg_ParseTuple(args, "O|(nnnn)", &op, &x0, &y0, &x1, &y1)) { + if (!PyArg_ParseTuple(args, "O(nnnn)", &op, &x0, &y0, &x1, &y1)) { return NULL; } im = PyImaging_AsImaging(op); @@ -248,15 +248,10 @@ _setimage(ImagingEncoderObject *encoder, PyObject *args) { state = &encoder->state; - if (x0 == 0 && x1 == 0) { - state->xsize = im->xsize; - state->ysize = im->ysize; - } else { - state->xoff = x0; - state->yoff = y0; - state->xsize = x1 - x0; - state->ysize = y1 - y0; - } + state->xoff = x0; + state->yoff = y0; + state->xsize = x1 - x0; + state->ysize = y1 - y0; if (state->xoff < 0 || state->xsize <= 0 || state->xsize + state->xoff > im->xsize || state->yoff < 0 ||