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 2a4bcd16bbf..34456f30597 100644 --- a/src/_imaging.c +++ b/src/_imaging.c @@ -759,20 +759,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; @@ -1053,11 +1039,7 @@ _convert_impl(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 )); } @@ -1071,24 +1053,18 @@ _convert(ImagingObject *self, PyObject *args) { } static PyObject * -_convert_into(ImagingObject *self, PyObject *args) { - ImagingObject *imagep; +_convert_block(ImagingObject *self, PyObject *args) { + char *mode_name; Imaging result; - if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) { + if (!PyArg_ParseTuple(args, "s", &mode_name)) { return NULL; } - Py_BEGIN_CRITICAL_SECTION2(self, imagep); - result = ImagingConvert( - imagep->image, self->image, imagep->image->mode, NULL, IMAGING_DITHER_NONE - ); - Py_END_CRITICAL_SECTION2(); - - if (!result) { - return NULL; - } + Py_BEGIN_CRITICAL_SECTION(self); + result = ImagingConvertBlock(self->image, findModeID(mode_name)); + Py_END_CRITICAL_SECTION(); - Py_RETURN_NONE; + return PyImagingNew(result); } static PyObject * @@ -4249,7 +4225,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}, @@ -4817,7 +4793,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 222b527c48a..8cd96260ff7 100644 --- a/src/libImaging/BoxBlur.c +++ b/src/libImaging/BoxBlur.c @@ -395,7 +395,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 2c524002070..5670011438d 100644 --- a/src/libImaging/Convert.c +++ b/src/libImaging/Convert.c @@ -1050,8 +1050,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; @@ -1093,7 +1095,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; } @@ -1121,11 +1123,11 @@ frompalette(Imaging imOut, Imaging imIn, const ModeID mode) { #endif static Imaging topalette( - Imaging imOut, Imaging imIn, const ModeID mode, ImagingPalette inpalette, - ImagingDitherType dither + ImagingDitherType dither, + ImagingAllocator new_image ) { ImagingSectionCookie cookie; int alpha; @@ -1165,7 +1167,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); @@ -1329,7 +1331,7 @@ topalette( } static Imaging -tobilevel(Imaging imOut, Imaging imIn) { +tobilevel(Imaging imIn, ImagingAllocator new_image) { ImagingSectionCookie cookie; int x, y; int *errors; @@ -1339,7 +1341,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; } @@ -1570,35 +1572,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 Dither method when converting to "P", "PA" or "1". - * Currently, for historical PIL reasons, - * any value other than IMAGING_DITHER_NONE uses Floyd-Steinberg - * dithering. - * @return The resulting Imaging object, - * or NULL with a Python exception set on failure. - */ -Imaging -ImagingConvert( - Imaging imOut, +static Imaging +_convert_impl( Imaging imIn, ModeID mode, ImagingPalette palette, - ImagingDitherType dither + ImagingDitherType dither, + ImagingAllocator new_image ) { ImagingSectionCookie cookie; ImagingShuffler convert; + Imaging imOut; if (!imIn) { return (Imaging)ImagingError_ModeError(); @@ -1613,22 +1597,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 != IMAGING_DITHER_NONE && mode == IMAGING_MODE_1) { - return tobilevel(imOut, imIn); + return tobilevel(imIn, new_image); } /* standard conversion machinery */ @@ -1650,7 +1639,7 @@ ImagingConvert( ); } - imOut = ImagingNew2Dirty(mode, imOut, imIn); + imOut = new_image(mode, imIn->xsize, imIn->ysize); if (!imOut) { return NULL; } @@ -1666,6 +1655,42 @@ 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 Dither method when converting to "P", "PA" or "1". + * Currently, for historical PIL reasons, + * any value other than IMAGING_DITHER_NONE uses Floyd-Steinberg + * dithering. + * @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, ImagingDitherType 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, IMAGING_DITHER_NONE, ImagingNewBlock); +} + Imaging ImagingConvertTransparent(Imaging imIn, const ModeID mode, int r, int g, int b) { ImagingSectionCookie cookie; @@ -1720,7 +1745,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 cd4523ea20a..3424c65b642 100644 --- a/src/libImaging/Copy.c +++ b/src/libImaging/Copy.c @@ -17,24 +17,22 @@ #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); if (!imIn->linesize || !imIn->ysize) { - return imOut; + return; } ImagingSectionEnter(&cookie); @@ -48,16 +46,49 @@ _copy(Imaging imOut, Imaging imIn) { } MUTEX_UNLOCK(&imIn->mutex); 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 68b7027e5f6..85216d99c31 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); @@ -324,13 +322,11 @@ extern Imaging ImagingCopy(Imaging im); extern Imaging ImagingConvert( - Imaging imOut, - Imaging imIn, - ModeID mode, - ImagingPalette palette, - ImagingDitherType dither + Imaging imIn, ModeID mode, ImagingPalette palette, ImagingDitherType dither ); extern Imaging +ImagingConvertBlock(Imaging imIn, ModeID mode); +extern Imaging ImagingConvertMatrix(Imaging im, ModeID mode, const float m[12]); extern Imaging ImagingConvertTransparent(Imaging im, ModeID mode, int r, int g, int b); @@ -442,7 +438,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 c79031359de..94de70b2b93 100644 --- a/src/libImaging/Storage.c +++ b/src/libImaging/Storage.c @@ -752,27 +752,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) {