diff --git a/src/PIL/ImageTk.py b/src/PIL/ImageTk.py index f2dd618cd01..2be54fb1297 100644 --- a/src/PIL/ImageTk.py +++ b/src/PIL/ImageTk.py @@ -184,8 +184,7 @@ def paste(self, im: Image.Image) -> None: ptr = im.getim() image = im.im if not image.isblock() or im.mode != self.__mode: - block = Image.core.new_block(self.__mode, im.size) - image.convert_into(block) # convert directly between buffers + block = image.convert_block(self.__mode) # contiguous, for Tk ptr = block.ptr _pyimagingtkcall("PyImagingPhoto", self.__photo, ptr) diff --git a/src/_imaging.c b/src/_imaging.c index fa42332a815..6e1f08ee157 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -761,20 +761,6 @@ _new(PyObject *self, PyObject *args) { return PyImagingNew(ImagingNew(mode, xsize, ysize)); } -static PyObject * -_new_block(PyObject *self, PyObject *args) { - char *mode_name; - int xsize, ysize; - - if (!PyArg_ParseTuple(args, "s(ii)", &mode_name, &xsize, &ysize)) { - return NULL; - } - - const ModeID mode = findModeID(mode_name); - - return PyImagingNew(ImagingNewBlock(mode, xsize, ysize)); -} - static PyObject * _linear_gradient(PyObject *self, PyObject *args) { char *mode_name; @@ -1038,26 +1024,18 @@ _convert(ImagingObject *self, PyObject *args) { const ModeID mode = findModeID(mode_name); return PyImagingNew(ImagingConvert( - NULL, - self->image, - mode, - paletteimage ? paletteimage->image->palette : NULL, - dither + self->image, mode, paletteimage ? paletteimage->image->palette : NULL, dither )); } static PyObject * -_convert_into(ImagingObject *self, PyObject *args) { - ImagingObject *imagep; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { - return NULL; - } - - if (!ImagingConvert(imagep->image, self->image, imagep->image->mode, NULL, 0)) { +_convert_block(ImagingObject *self, PyObject *args) { + char *mode_name; + if (!PyArg_ParseTuple(args, "s", &mode_name)) { return NULL; } - Py_RETURN_NONE; + return PyImagingNew(ImagingConvertBlock(self->image, findModeID(mode_name))); } static PyObject * @@ -3731,7 +3709,7 @@ static struct PyMethodDef methods[] = { /* Standard processing methods (Image) */ {"color_lut_3d", (PyCFunction)_color_lut_3d, METH_VARARGS}, {"convert", (PyCFunction)_convert, METH_VARARGS}, - {"convert_into", (PyCFunction)_convert_into, METH_VARARGS}, + {"convert_block", (PyCFunction)_convert_block, METH_VARARGS}, {"convert_matrix", (PyCFunction)_convert_matrix, METH_VARARGS}, {"convert_transparent", (PyCFunction)_convert_transparent, METH_VARARGS}, {"copy", (PyCFunction)_copy, METH_VARARGS}, @@ -4240,7 +4218,6 @@ static PyMethodDef functions[] = { {"blend", (PyCFunction)_blend, METH_VARARGS}, {"fill", (PyCFunction)_fill, METH_VARARGS}, {"new", (PyCFunction)_new, METH_VARARGS}, - {"new_block", (PyCFunction)_new_block, METH_VARARGS}, {"new_arrow", (PyCFunction)_new_arrow, METH_VARARGS}, {"merge", (PyCFunction)_merge, METH_VARARGS}, diff --git a/src/libImaging/Bands.c b/src/libImaging/Bands.c index dd90a36a70f..e4b1be622a8 100644 --- a/src/libImaging/Bands.c +++ b/src/libImaging/Bands.c @@ -214,7 +214,7 @@ ImagingPutBand(Imaging imOut, Imaging imIn, int band) { /* Shortcuts */ if (imOut->bands == 1) { - return ImagingCopy2(imOut, imIn); + return ImagingCopyInto(imOut, imIn); } /* Special case for LXXA etc */ @@ -320,7 +320,7 @@ ImagingMerge(const ModeID mode, Imaging bands[4]) { } if (bandsCount == 1) { - return ImagingCopy2(imOut, firstBand); + return ImagingCopyInto(imOut, firstBand); } // restrict safe: the input bands are read-only and imOut is a fresh diff --git a/src/libImaging/BoxBlur.c b/src/libImaging/BoxBlur.c index 9c4005c3071..6a54cb28736 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -294,7 +294,7 @@ ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n) ImagingDelete(imTransposed); } if (xradius == 0 && yradius == 0) { - if (!ImagingCopy2(imOut, imIn)) { + if (!ImagingCopyInto(imOut, imIn)) { return NULL; } } diff --git a/src/libImaging/Convert.c b/src/libImaging/Convert.c index b2b7ff26a22..a3f53a45bbf 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -1062,8 +1062,10 @@ pa2ycbcr(UINT8 *out, const UINT8 *in, int xsize, ImagingPalette palette) { ImagingConvertRGB2YCbCr(out, out, xsize); } +typedef Imaging (*ImagingAllocator)(ModeID mode, int xsize, int ysize); + static Imaging -frompalette(Imaging imOut, Imaging imIn, const ModeID mode) { +frompalette(Imaging imIn, const ModeID mode, ImagingAllocator new_image) { ImagingSectionCookie cookie; int alpha; int y; @@ -1105,7 +1107,7 @@ frompalette(Imaging imOut, Imaging imIn, const ModeID mode) { return (Imaging)ImagingError_ValueError("conversion not supported"); } - imOut = ImagingNew2Dirty(mode, imOut, imIn); + Imaging imOut = new_image(mode, imIn->xsize, imIn->ysize); if (!imOut) { return NULL; } @@ -1133,7 +1135,11 @@ frompalette(Imaging imOut, Imaging imIn, const ModeID mode) { #endif static Imaging topalette( - Imaging imOut, Imaging imIn, const ModeID mode, ImagingPalette inpalette, int dither + Imaging imIn, + const ModeID mode, + ImagingPalette inpalette, + int dither, + ImagingAllocator new_image ) { ImagingSectionCookie cookie; int alpha; @@ -1169,7 +1175,7 @@ topalette( return (Imaging)ImagingError_ValueError("no palette"); } - imOut = ImagingNew2Dirty(mode, imOut, imIn); + Imaging imOut = new_image(mode, imIn->xsize, imIn->ysize); if (!imOut) { if (palette != inpalette) { ImagingPaletteDelete(palette); @@ -1333,7 +1339,7 @@ topalette( } static Imaging -tobilevel(Imaging imOut, Imaging imIn) { +tobilevel(Imaging imIn, ImagingAllocator new_image) { ImagingSectionCookie cookie; int x, y; int *errors; @@ -1343,7 +1349,7 @@ tobilevel(Imaging imOut, Imaging imIn) { return (Imaging)ImagingError_ValueError("conversion not supported"); } - imOut = ImagingNew2Dirty(IMAGING_MODE_1, imOut, imIn); + Imaging imOut = new_image(IMAGING_MODE_1, imIn->xsize, imIn->ysize); if (!imOut) { return NULL; } @@ -1574,28 +1580,17 @@ static struct { #endif }; -/** - * Convert imIn to `mode`. - * If imIn is already in `mode`, this performs a copy into imOut - * (or a newly allocated image if imOut is NULL). - * - * @param imOut Existing image to write into - * (must already be in `mode` and the same size as imIn), - * or NULL to allocate a new image for the result. - * @param imIn Source image to convert. - * @param mode Target mode. - * @param palette Target palette for conversions to "P" or "PA"; - * NULL to use a default palette. - * @param dither Nonzero to dither when converting to "P", "PA" or "1". - * @return The resulting Imaging object, - * or NULL with a Python exception set on failure. - */ -Imaging -ImagingConvert( - Imaging imOut, Imaging imIn, ModeID mode, ImagingPalette palette, int dither +static Imaging +_convert_impl( + Imaging imIn, + ModeID mode, + ImagingPalette palette, + int dither, + ImagingAllocator new_image ) { ImagingSectionCookie cookie; ImagingShuffler convert; + Imaging imOut; if (!imIn) { return (Imaging)ImagingError_ModeError(); @@ -1610,22 +1605,27 @@ ImagingConvert( } else { /* Same mode? */ if (imIn->mode == mode) { - return ImagingCopy2(imOut, imIn); + imOut = new_image(mode, imIn->xsize, imIn->ysize); + if (imOut && !ImagingCopyInto(imOut, imIn)) { + ImagingDelete(imOut); + return NULL; + } + return imOut; } } /* test for special conversions */ if (imIn->mode == IMAGING_MODE_P || imIn->mode == IMAGING_MODE_PA) { - return frompalette(imOut, imIn, mode); + return frompalette(imIn, mode, new_image); } if (mode == IMAGING_MODE_P || mode == IMAGING_MODE_PA) { - return topalette(imOut, imIn, mode, palette, dither); + return topalette(imIn, mode, palette, dither, new_image); } if (dither && mode == IMAGING_MODE_1) { - return tobilevel(imOut, imIn); + return tobilevel(imIn, new_image); } /* standard conversion machinery */ @@ -1647,7 +1647,7 @@ ImagingConvert( ); } - imOut = ImagingNew2Dirty(mode, imOut, imIn); + imOut = new_image(mode, imIn->xsize, imIn->ysize); if (!imOut) { return NULL; } @@ -1661,6 +1661,37 @@ ImagingConvert( return imOut; } +/** + * Convert imIn to `mode`, returning a new image. + * If imIn is already in `mode`, this returns a copy. + * + * @param imIn Source image to convert. + * @param mode Target mode. + * @param palette Target palette for conversions to "P" or "PA"; + * NULL to use a default palette. + * @param dither Nonzero to dither when converting to "P", "PA" or "1". + * @return A new image owned by the caller, + * or NULL with a Python exception set on failure. + */ +Imaging +ImagingConvert(Imaging imIn, ModeID mode, ImagingPalette palette, int dither) { + return _convert_impl(imIn, mode, palette, dither, ImagingNewDirty); +} + +/** + * Like ImagingConvert (without palette or dithering), + * but the new image is always allocated as a single contiguous block. + * + * @param imIn Source image to convert. + * @param mode Target mode. + * @return A new image owned by the caller, + * or NULL with a Python exception set on failure. + */ +Imaging +ImagingConvertBlock(Imaging imIn, ModeID mode) { + return _convert_impl(imIn, mode, NULL, 0, ImagingNewBlock); +} + Imaging ImagingConvertTransparent(Imaging imIn, const ModeID mode, int r, int g, int b) { ImagingSectionCookie cookie; @@ -1715,7 +1746,7 @@ ImagingConvertTransparent(Imaging imIn, const ModeID mode, int r, int g, int b) ); } - imOut = ImagingNew2Dirty(mode, imOut, imIn); + imOut = ImagingNewDirty(mode, imIn->xsize, imIn->ysize); if (!imOut) { return NULL; } diff --git a/src/libImaging/Copy.c b/src/libImaging/Copy.c index 9808c9c7bc7..76af036cd74 100644 --- a/src/libImaging/Copy.c +++ b/src/libImaging/Copy.c @@ -17,20 +17,18 @@ #include "Imaging.h" -static Imaging +/** + * Copy imIn into imOut. + * This internal function requires that imOut matches imIn in mode and size. + * + * @param imOut Caller-owned image to copy into. + * @param imIn Image to copy from. + */ +static void _copy(Imaging imOut, Imaging imIn) { ImagingSectionCookie cookie; int y; - if (!imIn) { - return (Imaging)ImagingError_ValueError(NULL); - } - - imOut = ImagingNew2Dirty(imIn->mode, imOut, imIn); - if (!imOut) { - return NULL; - } - ImagingCopyPalette(imOut, imIn); ImagingSectionEnter(&cookie); @@ -42,16 +40,49 @@ _copy(Imaging imOut, Imaging imIn) { } } ImagingSectionLeave(&cookie); - - return imOut; } +/** + * Create a new image by copying imIn. The caller owns the returned image. + * + * The new image is allocated with the current default allocation strategy. + * + * @param imIn Image to copy from. + * @return A new image on success, or NULL on error with a Python exception set. + */ Imaging ImagingCopy(Imaging imIn) { - return _copy(NULL, imIn); + if (!imIn) { + return (Imaging)ImagingError_ValueError(NULL); + } + + Imaging imOut = ImagingNewDirty(imIn->mode, imIn->xsize, imIn->ysize); + if (!imOut) { + return NULL; + } + + _copy(imOut, imIn); + return imOut; } +/** + * Copy imIn into the caller-owned imOut, which must match it in mode and size. + * + * @param imOut Caller-owned image to copy into. + * @param imIn Image to copy from. + * @return imOut on success, or NULL on error with a Python exception set. + */ Imaging -ImagingCopy2(Imaging imOut, Imaging imIn) { - return _copy(imOut, imIn); +ImagingCopyInto(Imaging imOut, Imaging imIn) { + if (!imIn || !imOut) { + return (Imaging)ImagingError_ValueError(NULL); + } + + if (imOut->mode != imIn->mode || imOut->xsize != imIn->xsize || + imOut->ysize != imIn->ysize) { + return ImagingError_Mismatch(); + } + + _copy(imOut, imIn); + return imOut; } diff --git a/src/libImaging/Imaging.h b/src/libImaging/Imaging.h index fd4628c40ad..f49923509b0 100644 --- a/src/libImaging/Imaging.h +++ b/src/libImaging/Imaging.h @@ -196,8 +196,6 @@ extern Imaging ImagingNew(ModeID mode, int xsize, int ysize); extern Imaging ImagingNewDirty(ModeID mode, int xsize, int ysize); -extern Imaging -ImagingNew2Dirty(ModeID mode, Imaging imOut, Imaging imIn); extern void ImagingDelete(Imaging im); @@ -305,9 +303,9 @@ ImagingBlend(Imaging imIn1, Imaging imIn2, float alpha); extern Imaging ImagingCopy(Imaging im); extern Imaging -ImagingConvert( - Imaging imOut, Imaging imIn, ModeID mode, ImagingPalette palette, int dither -); +ImagingConvert(Imaging imIn, ModeID mode, ImagingPalette palette, int dither); +extern Imaging +ImagingConvertBlock(Imaging imIn, ModeID mode); extern Imaging ImagingConvertMatrix(Imaging im, ModeID mode, const float m[12]); extern Imaging @@ -418,7 +416,7 @@ ImagingColorLUT3D_linear( ); extern Imaging -ImagingCopy2(Imaging imOut, Imaging imIn); +ImagingCopyInto(Imaging imOut, Imaging imIn); /* Channel operations */ /* any mode, except "F" */ diff --git a/src/libImaging/Storage.c b/src/libImaging/Storage.c index 0d33c48ed53..7661dc09c57 100644 --- a/src/libImaging/Storage.c +++ b/src/libImaging/Storage.c @@ -738,27 +738,6 @@ ImagingNewArrow( ); } -Imaging -ImagingNew2Dirty(const ModeID mode, Imaging imOut, Imaging imIn) { - /* allocate or validate output image */ - - if (imOut) { - /* make sure images match */ - if (imOut->mode != mode || imOut->xsize != imIn->xsize || - imOut->ysize != imIn->ysize) { - return ImagingError_Mismatch(); - } - } else { - /* create new image */ - imOut = ImagingNewDirty(mode, imIn->xsize, imIn->ysize); - if (!imOut) { - return NULL; - } - } - - return imOut; -} - void ImagingCopyPalette(Imaging destination, Imaging source) { if (source->palette) {