diff --git a/PIL/Image.py b/PIL/Image.py index cf5c4f477..f70b49d75 100644 --- a/PIL/Image.py +++ b/PIL/Image.py @@ -724,16 +724,10 @@ class Image: if dither is None: dither = FLOYDSTEINBERG - # fake a P-mode image, otherwise the transparency will get lost as there is - # currently no other way to convert transparency into an RGBA image - if self.mode == "L" and mode == "RGBA" and "transparency" in self.info: - from PIL import ImagePalette - self.mode = "P" - bytePalette = bytes(bytearray([i//3 for i in range(768)])) - self.palette = ImagePalette.raw("RGB", bytePalette) - self.palette.dirty = 1 - self.load() - + # Use transparent conversion to promote from transparent color to an alpha channel. + if self.mode in ("L", "RGB") and mode == "RGBA" and "transparency" in self.info: + return self._new(self.im.convert_transparent(mode, self.info['transparency'])) + try: im = self.im.convert(mode, dither) except ValueError: diff --git a/Tests/images/rgb_trns.png b/Tests/images/rgb_trns.png new file mode 100644 index 000000000..dee80acdc Binary files /dev/null and b/Tests/images/rgb_trns.png differ diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 5ae726ad8..3f61f39bd 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -150,6 +150,17 @@ def test_load_transparent_p(): # image has 124 uniqe qlpha values assert_equal(len(im.split()[3].getcolors()), 124) +def test_load_transparent_rgb(): + file = "Tests/images/rgb_trns.png" + im = Image.open(file) + + assert_image(im, "RGB", (64, 64)) + im = im.convert("RGBA") + assert_image(im, "RGBA", (64, 64)) + + # image has 876 transparent pixels + assert_equal(im.split()[3].getcolors()[0][0], 876) + def test_save_p_transparent_palette(): in_file = "Tests/images/pil123p.png" im = Image.open(in_file) @@ -171,6 +182,10 @@ def test_save_l_transparency(): file = tempfile("temp.png") assert_no_exception(lambda: im.save(file)) + # There are 559 transparent pixels. + im = im.convert('RGBA') + assert_equal(im.split()[3].getcolors()[0][0], 559) + def test_save_rgb_single_transparency(): in_file = "Tests/images/caption_6_33_22.png" im = Image.open(in_file) diff --git a/_imaging.c b/_imaging.c index a0dc79253..c62f0257d 100644 --- a/_imaging.c +++ b/_imaging.c @@ -168,12 +168,12 @@ PyImagingNew(Imaging imOut) ImagingObject* imagep; if (!imOut) - return NULL; + return NULL; imagep = PyObject_New(ImagingObject, &Imaging_Type); if (imagep == NULL) { - ImagingDelete(imOut); - return NULL; + ImagingDelete(imOut); + return NULL; } #ifdef VERBOSE @@ -195,7 +195,7 @@ _dealloc(ImagingObject* imagep) #endif if (imagep->access) - ImagingAccessDelete(imagep->image, imagep->access); + ImagingAccessDelete(imagep->image, imagep->access); ImagingDelete(imagep->image); PyObject_Del(imagep); } @@ -205,8 +205,8 @@ _dealloc(ImagingObject* imagep) Imaging PyImaging_AsImaging(PyObject *op) { if (!PyImaging_Check(op)) { - PyErr_BadInternalCall(); - return NULL; + PyErr_BadInternalCall(); + return NULL; } return ((ImagingObject *)op)->image; @@ -369,14 +369,14 @@ getlist(PyObject* arg, int* length, const char* wrong_length, int type) void* list; if (!PySequence_Check(arg)) { - PyErr_SetString(PyExc_TypeError, must_be_sequence); - return NULL; + PyErr_SetString(PyExc_TypeError, must_be_sequence); + return NULL; } n = PyObject_Length(arg); if (length && wrong_length && n != *length) { - PyErr_SetString(PyExc_ValueError, wrong_length); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_length); + return NULL; } list = malloc(n * (type & 0xff)); @@ -469,33 +469,33 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) } pixel; if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { - PyErr_SetString(PyExc_IndexError, outside_image); - return NULL; + PyErr_SetString(PyExc_IndexError, outside_image); + return NULL; } access->get_pixel(im, x, y, &pixel); switch (im->type) { case IMAGING_TYPE_UINT8: - switch (im->bands) { - case 1: - return PyInt_FromLong(pixel.b[0]); - case 2: - return Py_BuildValue("BB", pixel.b[0], pixel.b[1]); - case 3: - return Py_BuildValue("BBB", pixel.b[0], pixel.b[1], pixel.b[2]); - case 4: - return Py_BuildValue("BBBB", pixel.b[0], pixel.b[1], pixel.b[2], pixel.b[3]); - } - break; + switch (im->bands) { + case 1: + return PyInt_FromLong(pixel.b[0]); + case 2: + return Py_BuildValue("BB", pixel.b[0], pixel.b[1]); + case 3: + return Py_BuildValue("BBB", pixel.b[0], pixel.b[1], pixel.b[2]); + case 4: + return Py_BuildValue("BBBB", pixel.b[0], pixel.b[1], pixel.b[2], pixel.b[3]); + } + break; case IMAGING_TYPE_INT32: - return PyInt_FromLong(pixel.i); + return PyInt_FromLong(pixel.i); case IMAGING_TYPE_FLOAT32: - return PyFloat_FromDouble(pixel.f); + return PyFloat_FromDouble(pixel.f); case IMAGING_TYPE_SPECIAL: - if (strncmp(im->mode, "I;16", 4) == 0) - return PyInt_FromLong(pixel.h); - break; + if (strncmp(im->mode, "I;16", 4) == 0) + return PyInt_FromLong(pixel.h); + break; } /* unknown type */ @@ -603,7 +603,7 @@ _fill(PyObject* self, PyObject* args) color = NULL; if (!PyArg_ParseTuple(args, "s|(ii)O", &mode, &xsize, &ysize, &color)) - return NULL; + return NULL; im = ImagingNew(mode, xsize, ysize); if (!im) @@ -629,7 +629,7 @@ _new(PyObject* self, PyObject* args) int xsize, ysize; if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) - return NULL; + return NULL; return PyImagingNew(ImagingNew(mode, xsize, ysize)); } @@ -641,7 +641,7 @@ _new_array(PyObject* self, PyObject* args) int xsize, ysize; if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) - return NULL; + return NULL; return PyImagingNew(ImagingNewArray(mode, xsize, ysize)); } @@ -653,7 +653,7 @@ _new_block(PyObject* self, PyObject* args) int xsize, ysize; if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) - return NULL; + return NULL; return PyImagingNew(ImagingNewBlock(mode, xsize, ysize)); } @@ -662,7 +662,7 @@ static PyObject* _getcount(PyObject* self, PyObject* args) { if (!PyArg_ParseTuple(args, ":getcount")) - return NULL; + return NULL; return PyInt_FromLong(ImagingNewCount); } @@ -673,7 +673,7 @@ _linear_gradient(PyObject* self, PyObject* args) char* mode; if (!PyArg_ParseTuple(args, "s", &mode)) - return NULL; + return NULL; return PyImagingNew(ImagingFillLinearGradient(mode)); } @@ -684,7 +684,7 @@ _radial_gradient(PyObject* self, PyObject* args) char* mode; if (!PyArg_ParseTuple(args, "s", &mode)) - return NULL; + return NULL; return PyImagingNew(ImagingFillRadialGradient(mode)); } @@ -695,7 +695,7 @@ _open_ppm(PyObject* self, PyObject* args) char* filename; if (!PyArg_ParseTuple(args, "s", &filename)) - return NULL; + return NULL; return PyImagingNew(ImagingOpenPPM(filename)); } @@ -709,7 +709,7 @@ _alpha_composite(ImagingObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O!O!", &Imaging_Type, &imagep1, &Imaging_Type, &imagep2)) - return NULL; + return NULL; return PyImagingNew(ImagingAlphaComposite(imagep1->image, imagep2->image)); } @@ -723,10 +723,10 @@ _blend(ImagingObject* self, PyObject* args) alpha = 0.5; if (!PyArg_ParseTuple(args, "O!O!|d", - &Imaging_Type, &imagep1, - &Imaging_Type, &imagep2, - &alpha)) - return NULL; + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2, + &alpha)) + return NULL; return PyImagingNew(ImagingBlend(imagep1->image, imagep2->image, (float) alpha)); @@ -744,17 +744,17 @@ _convert(ImagingObject* self, PyObject* args) ImagingObject *paletteimage = NULL; if (!PyArg_ParseTuple(args, "s|iO", &mode, &dither, &paletteimage)) - return NULL; + return NULL; if (paletteimage != NULL) { - if (!PyImaging_Check(paletteimage)) { - PyObject_Print((PyObject *)paletteimage, stderr, 0); - PyErr_SetString(PyExc_ValueError, "palette argument must be image with mode 'P'"); - return NULL; - } - if (paletteimage->image->palette == NULL) { - PyErr_SetString(PyExc_ValueError, "null palette"); - return NULL; - } + if (!PyImaging_Check(paletteimage)) { + PyObject_Print((PyObject *)paletteimage, stderr, 0); + PyErr_SetString(PyExc_ValueError, "palette argument must be image with mode 'P'"); + return NULL; + } + if (paletteimage->image->palette == NULL) { + PyErr_SetString(PyExc_ValueError, "null palette"); + return NULL; + } } return PyImagingNew(ImagingConvert(self->image, mode, paletteimage ? paletteimage->image->palette : NULL, dither)); @@ -766,9 +766,9 @@ _convert2(ImagingObject* self, PyObject* args) ImagingObject* imagep1; ImagingObject* imagep2; if (!PyArg_ParseTuple(args, "O!O!", - &Imaging_Type, &imagep1, - &Imaging_Type, &imagep2)) - return NULL; + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2)) + return NULL; if (!ImagingConvert2(imagep1->image, imagep2->image)) return NULL; @@ -783,7 +783,7 @@ _convert_matrix(ImagingObject* self, PyObject* args) char* mode; float m[12]; if (!PyArg_ParseTuple(args, "s(ffff)", &mode, m+0, m+1, m+2, m+3)) { - PyErr_Clear(); + PyErr_Clear(); if (!PyArg_ParseTuple(args, "s(ffffffffffff)", &mode, m+0, m+1, m+2, m+3, m+4, m+5, m+6, m+7, @@ -794,11 +794,26 @@ _convert_matrix(ImagingObject* self, PyObject* args) return PyImagingNew(ImagingConvertMatrix(self->image, mode, m)); } +static PyObject* +_convert_transparent(ImagingObject* self, PyObject* args) +{ + char* mode; + int r,g,b; + if (PyArg_ParseTuple(args, "s(iii)", &mode, &r, &g, &b)) { + return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, g, b)); + } + PyErr_Clear(); + if (PyArg_ParseTuple(args, "si", &mode, &r)) { + return PyImagingNew(ImagingConvertTransparent(self->image, mode, r, 0, 0)); + } + return NULL; +} + static PyObject* _copy(ImagingObject* self, PyObject* args) { if (!PyArg_ParseTuple(args, "")) - return NULL; + return NULL; return PyImagingNew(ImagingCopy(self->image)); } @@ -809,9 +824,9 @@ _copy2(ImagingObject* self, PyObject* args) ImagingObject* imagep1; ImagingObject* imagep2; if (!PyArg_ParseTuple(args, "O!O!", - &Imaging_Type, &imagep1, - &Imaging_Type, &imagep2)) - return NULL; + &Imaging_Type, &imagep1, + &Imaging_Type, &imagep2)) + return NULL; if (!ImagingCopy2(imagep1->image, imagep2->image)) return NULL; @@ -825,7 +840,7 @@ _crop(ImagingObject* self, PyObject* args) { int x0, y0, x1, y1; if (!PyArg_ParseTuple(args, "(iiii)", &x0, &y0, &x1, &y1)) - return NULL; + return NULL; return PyImagingNew(ImagingCrop(self->image, x0, y0, x1, y1)); } @@ -836,7 +851,7 @@ _expand(ImagingObject* self, PyObject* args) int x, y; int mode = 0; if (!PyArg_ParseTuple(args, "ii|i", &x, &y, &mode)) - return NULL; + return NULL; return PyImagingNew(ImagingExpand(self->image, x, y, mode)); } @@ -852,7 +867,7 @@ _filter(ImagingObject* self, PyObject* args) float divisor, offset; PyObject* kernel = NULL; if (!PyArg_ParseTuple(args, "(ii)ffO", &xsize, &ysize, - &divisor, &offset, &kernel)) + &divisor, &offset, &kernel)) return NULL; /* get user-defined kernel */ @@ -907,25 +922,25 @@ _getpalette(ImagingObject* self, PyObject* args) char* mode = "RGB"; char* rawmode = "RGB"; if (!PyArg_ParseTuple(args, "|ss", &mode, &rawmode)) - return NULL; + return NULL; if (!self->image->palette) { - PyErr_SetString(PyExc_ValueError, no_palette); - return NULL; + PyErr_SetString(PyExc_ValueError, no_palette); + return NULL; } pack = ImagingFindPacker(mode, rawmode, &bits); if (!pack) { - PyErr_SetString(PyExc_ValueError, wrong_raw_mode); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_raw_mode); + return NULL; } palette = PyBytes_FromStringAndSize(NULL, palettesize * bits / 8); if (!palette) - return NULL; + return NULL; pack((UINT8*) PyBytes_AsString(palette), - self->image->palette->palette, palettesize); + self->image->palette->palette, palettesize); return palette; } @@ -1066,14 +1081,14 @@ _histogram(ImagingObject* self, PyObject* args) /* Build an integer list containing the histogram */ list = PyList_New(h->bands * 256); for (i = 0; i < h->bands * 256; i++) { - PyObject* item; - item = PyInt_FromLong(h->histogram[i]); - if (item == NULL) { - Py_DECREF(list); - list = NULL; - break; - } - PyList_SetItem(list, i, item); + PyObject* item; + item = PyInt_FromLong(h->histogram[i]); + if (item == NULL) { + Py_DECREF(list); + list = NULL; + break; + } + PyList_SetItem(list, i, item); } ImagingHistogramDelete(h); @@ -1087,7 +1102,7 @@ _modefilter(ImagingObject* self, PyObject* args) { int size; if (!PyArg_ParseTuple(args, "i", &size)) - return NULL; + return NULL; return PyImagingNew(ImagingModeFilter(self->image, size)); } @@ -1098,7 +1113,7 @@ _offset(ImagingObject* self, PyObject* args) { int xoffset, yoffset; if (!PyArg_ParseTuple(args, "ii", &xoffset, &yoffset)) - return NULL; + return NULL; return PyImagingNew(ImagingOffset(self->image, xoffset, yoffset)); } @@ -1379,7 +1394,7 @@ _quantize(ImagingObject* self, PyObject* args) int method = 0; int kmeans = 0; if (!PyArg_ParseTuple(args, "|iii", &colours, &method, &kmeans)) - return NULL; + return NULL; if (!self->image->xsize || !self->image->ysize) { /* no content; return an empty image */ @@ -1402,22 +1417,22 @@ _putpalette(ImagingObject* self, PyObject* args) UINT8* palette; int palettesize; if (!PyArg_ParseTuple(args, "s"PY_ARG_BYTES_LENGTH, &rawmode, &palette, &palettesize)) - return NULL; + return NULL; if (strcmp(self->image->mode, "L") != 0 && strcmp(self->image->mode, "P")) { - PyErr_SetString(PyExc_ValueError, wrong_mode); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_mode); + return NULL; } unpack = ImagingFindUnpacker("RGB", rawmode, &bits); if (!unpack) { - PyErr_SetString(PyExc_ValueError, wrong_raw_mode); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_raw_mode); + return NULL; } if ( palettesize * 8 / bits > 256) { - PyErr_SetString(PyExc_ValueError, wrong_palette_size); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_palette_size); + return NULL; } ImagingPaletteDelete(self->image->palette); @@ -1464,21 +1479,21 @@ _putpalettealphas(ImagingObject* self, PyObject* args) UINT8 *values; int length; if (!PyArg_ParseTuple(args, "s#", &values, &length)) - return NULL; + return NULL; if (!self->image->palette) { - PyErr_SetString(PyExc_ValueError, no_palette); - return NULL; + PyErr_SetString(PyExc_ValueError, no_palette); + return NULL; } if (length > 256) { - PyErr_SetString(PyExc_ValueError, outside_palette); - return NULL; + PyErr_SetString(PyExc_ValueError, outside_palette); + return NULL; } strcpy(self->image->palette->mode, "RGBA"); for (i=0; iimage->palette->palette[i*4+3] = (UINT8) values[i]; + self->image->palette->palette[i*4+3] = (UINT8) values[i]; } Py_INCREF(Py_None); @@ -1494,13 +1509,13 @@ _putpixel(ImagingObject* self, PyObject* args) int x, y; PyObject* color; if (!PyArg_ParseTuple(args, "(ii)O", &x, &y, &color)) - return NULL; + return NULL; im = self->image; if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { - PyErr_SetString(PyExc_IndexError, outside_image); - return NULL; + PyErr_SetString(PyExc_IndexError, outside_image); + return NULL; } if (!getink(color, im, ink)) @@ -1519,7 +1534,7 @@ _rankfilter(ImagingObject* self, PyObject* args) { int size, rank; if (!PyArg_ParseTuple(args, "ii", &size, &rank)) - return NULL; + return NULL; return PyImagingNew(ImagingRankFilter(self->image, size, rank)); } @@ -1534,13 +1549,13 @@ _resize(ImagingObject* self, PyObject* args) int xsize, ysize; int filter = IMAGING_TRANSFORM_NEAREST; if (!PyArg_ParseTuple(args, "(ii)|i", &xsize, &ysize, &filter)) - return NULL; + return NULL; imIn = self->image; imOut = ImagingNew(imIn->mode, xsize, ysize); if (imOut) - (void) ImagingResize(imOut, imIn, filter); + (void) ImagingResize(imOut, imIn, filter); return PyImagingNew(imOut); } @@ -1554,7 +1569,7 @@ _rotate(ImagingObject* self, PyObject* args) double theta; int filter = IMAGING_TRANSFORM_NEAREST; if (!PyArg_ParseTuple(args, "d|i", &theta, &filter)) - return NULL; + return NULL; imIn = self->image; @@ -1842,8 +1857,8 @@ _getbbox(ImagingObject* self, PyObject* args) { int bbox[4]; if (!ImagingGetBBox(self->image, bbox)) { - Py_INCREF(Py_None); - return Py_None; + Py_INCREF(Py_None); + return Py_None; } return Py_BuildValue("iiii", bbox[0], bbox[1], bbox[2], bbox[3]); @@ -1858,7 +1873,7 @@ _getcolors(ImagingObject* self, PyObject* args) int maxcolors = 256; if (!PyArg_ParseTuple(args, "i:getcolors", &maxcolors)) - return NULL; + return NULL; items = ImagingGetColors(self->image, maxcolors, &colors); if (!items) @@ -1922,9 +1937,9 @@ _getprojection(ImagingObject* self, PyObject* args) yprofile = malloc(self->image->ysize); if (xprofile == NULL || yprofile == NULL) { - free(xprofile); - free(yprofile); - return PyErr_NoMemory(); + free(xprofile); + free(yprofile); + return PyErr_NoMemory(); } ImagingGetProjection(self->image, (unsigned char *)xprofile, (unsigned char *)yprofile); @@ -1947,7 +1962,7 @@ _getband(ImagingObject* self, PyObject* args) int band; if (!PyArg_ParseTuple(args, "i", &band)) - return NULL; + return NULL; return PyImagingNew(ImagingGetBand(self->image, band)); } @@ -1959,7 +1974,7 @@ _fillband(ImagingObject* self, PyObject* args) int color; if (!PyArg_ParseTuple(args, "ii", &band, &color)) - return NULL; + return NULL; if (!ImagingFillBand(self->image, band, color)) return NULL; @@ -1974,12 +1989,12 @@ _putband(ImagingObject* self, PyObject* args) ImagingObject* imagep; int band; if (!PyArg_ParseTuple(args, "O!i", - &Imaging_Type, &imagep, - &band)) - return NULL; + &Imaging_Type, &imagep, + &band)) + return NULL; if (!ImagingPutBand(self->image, imagep->image, band)) - return NULL; + return NULL; Py_INCREF(Py_None); return Py_None; @@ -2001,7 +2016,7 @@ _chop_lighter(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopLighter(self->image, imagep->image)); } @@ -2012,7 +2027,7 @@ _chop_darker(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopDarker(self->image, imagep->image)); } @@ -2023,7 +2038,7 @@ _chop_difference(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopDifference(self->image, imagep->image)); } @@ -2034,7 +2049,7 @@ _chop_multiply(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopMultiply(self->image, imagep->image)); } @@ -2045,7 +2060,7 @@ _chop_screen(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopScreen(self->image, imagep->image)); } @@ -2061,8 +2076,8 @@ _chop_add(ImagingObject* self, PyObject* args) offset = 0; if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, - &scale, &offset)) - return NULL; + &scale, &offset)) + return NULL; return PyImagingNew(ImagingChopAdd(self->image, imagep->image, scale, offset)); @@ -2079,8 +2094,8 @@ _chop_subtract(ImagingObject* self, PyObject* args) offset = 0; if (!PyArg_ParseTuple(args, "O!|fi", &Imaging_Type, &imagep, - &scale, &offset)) - return NULL; + &scale, &offset)) + return NULL; return PyImagingNew(ImagingChopSubtract(self->image, imagep->image, scale, offset)); @@ -2092,7 +2107,7 @@ _chop_and(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopAnd(self->image, imagep->image)); } @@ -2103,7 +2118,7 @@ _chop_or(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopOr(self->image, imagep->image)); } @@ -2114,7 +2129,7 @@ _chop_xor(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopXor(self->image, imagep->image)); } @@ -2125,7 +2140,7 @@ _chop_add_modulo(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopAddModulo(self->image, imagep->image)); } @@ -2136,7 +2151,7 @@ _chop_subtract_modulo(ImagingObject* self, PyObject* args) ImagingObject* imagep; if (!PyArg_ParseTuple(args, "O!", &Imaging_Type, &imagep)) - return NULL; + return NULL; return PyImagingNew(ImagingChopSubtractModulo(self->image, imagep->image)); } @@ -2159,18 +2174,18 @@ _font_new(PyObject* self_, PyObject* args) unsigned char* glyphdata; int glyphdata_length; if (!PyArg_ParseTuple(args, "O!"PY_ARG_BYTES_LENGTH, - &Imaging_Type, &imagep, - &glyphdata, &glyphdata_length)) + &Imaging_Type, &imagep, + &glyphdata, &glyphdata_length)) return NULL; if (glyphdata_length != 256 * 20) { - PyErr_SetString(PyExc_ValueError, wrong_length); - return NULL; + PyErr_SetString(PyExc_ValueError, wrong_length); + return NULL; } self = PyObject_New(ImagingFontObject, &ImagingFont_Type); if (self == NULL) - return NULL; + return NULL; /* glyph bitmap */ self->bitmap = imagep->image; @@ -2302,7 +2317,7 @@ _draw_new(PyObject* self_, PyObject* args) self = PyObject_New(ImagingDrawObject, &ImagingDraw_Type); if (self == NULL) - return NULL; + return NULL; /* keep a reference to the image object */ Py_INCREF(imagep); @@ -2349,7 +2364,7 @@ _draw_arc(ImagingDrawObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "(iiii)iii|i", &x0, &y0, &x1, &y1, &start, &end, &ink)) - return NULL; + return NULL; if (ImagingDrawArc(self->image->image, x0, y0, x1, y1, start, end, &ink, op) < 0) @@ -2369,17 +2384,16 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args) ImagingObject* bitmap; int ink; if (!PyArg_ParseTuple(args, "OO!i", &data, &Imaging_Type, &bitmap, &ink)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (n != 1) { - PyErr_SetString( - PyExc_TypeError, - "coordinate list must contain exactly 1 coordinate" - ); - return NULL; + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain exactly 1 coordinate" + ); + return NULL; } n = ImagingDrawBitmap( @@ -2404,7 +2418,7 @@ _draw_chord(ImagingDrawObject* self, PyObject* args) int start, end; if (!PyArg_ParseTuple(args, "(iiii)iiii", &x0, &y0, &x1, &y1, &start, &end, &ink, &fill)) - return NULL; + return NULL; if (ImagingDrawChord(self->image->image, x0, y0, x1, y1, start, end, &ink, fill, self->blend) < 0) @@ -2424,23 +2438,23 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (n != 2) { - PyErr_SetString( - PyExc_TypeError, - "coordinate list must contain exactly 2 coordinates" - ); - return NULL; + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain exactly 2 coordinates" + ); + return NULL; } - n = ImagingDrawEllipse( - self->image->image, (int) xy[0], (int) xy[1], (int) xy[2], (int) xy[3], - &ink, fill, self->blend - ); + n = ImagingDrawEllipse(self->image->image, + (int) xy[0], (int) xy[1], + (int) xy[2], (int) xy[3], + &ink, fill, self->blend + ); free(xy); @@ -2457,11 +2471,11 @@ _draw_line(ImagingDrawObject* self, PyObject* args) int x0, y0, x1, y1; int ink; if (!PyArg_ParseTuple(args, "(ii)(ii)i", &x0, &y0, &x1, &y1, &ink)) - return NULL; + return NULL; if (ImagingDrawLine(self->image->image, x0, y0, x1, y1, &ink, self->blend) < 0) - return NULL; + return NULL; Py_INCREF(Py_None); return Py_None; @@ -2477,11 +2491,11 @@ _draw_lines(ImagingDrawObject* self, PyObject* args) int ink; int width = 0; if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &width)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (width <= 1) { double *p = NULL; @@ -2526,10 +2540,10 @@ _draw_point(ImagingDrawObject* self, PyObject* args) int x, y; int ink; if (!PyArg_ParseTuple(args, "(ii)i", &x, &y, &ink)) - return NULL; + return NULL; if (ImagingDrawPoint(self->image->image, x, y, &ink, self->blend) < 0) - return NULL; + return NULL; Py_INCREF(Py_None); return Py_None; @@ -2544,11 +2558,11 @@ _draw_points(ImagingDrawObject* self, PyObject* args) PyObject *data; int ink; if (!PyArg_ParseTuple(args, "Oi", &data, &ink)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; for (i = 0; i < n; i++) { double *p = &xy[i+i]; @@ -2579,7 +2593,7 @@ _draw_outline(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; if (!PyArg_ParseTuple(args, "Oi|i", &outline_, &ink, &fill)) - return NULL; + return NULL; outline = PyOutline_AsOutline(outline_); if (!outline) { @@ -2605,7 +2619,7 @@ _draw_pieslice(ImagingDrawObject* self, PyObject* args) int start, end; if (!PyArg_ParseTuple(args, "(iiii)iiii", &x0, &y0, &x1, &y1, &start, &end, &ink, &fill)) - return NULL; + return NULL; if (ImagingDrawPieslice(self->image->image, x0, y0, x1, y1, start, end, &ink, fill, self->blend) < 0) @@ -2626,33 +2640,32 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (n < 2) { - PyErr_SetString( - PyExc_TypeError, - "coordinate list must contain at least 2 coordinates" - ); - return NULL; + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain at least 2 coordinates" + ); + return NULL; } /* Copy list of vertices to array */ ixy = (int*) malloc(n * 2 * sizeof(int)); for (i = 0; i < n; i++) { - ixy[i+i] = (int) xy[i+i]; - ixy[i+i+1] = (int) xy[i+i+1]; + ixy[i+i] = (int) xy[i+i]; + ixy[i+i+1] = (int) xy[i+i+1]; } free(xy); if (ImagingDrawPolygon(self->image->image, n, ixy, &ink, fill, self->blend) < 0) { - free(ixy); - return NULL; + free(ixy); + return NULL; } free(ixy); @@ -2671,23 +2684,23 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args) int ink; int fill = 0; if (!PyArg_ParseTuple(args, "Oi|i", &data, &ink, &fill)) - return NULL; + return NULL; n = PyPath_Flatten(data, &xy); if (n < 0) - return NULL; + return NULL; if (n != 2) { - PyErr_SetString( - PyExc_TypeError, - "coordinate list must contain exactly 2 coordinates" - ); - return NULL; + PyErr_SetString(PyExc_TypeError, + "coordinate list must contain exactly 2 coordinates" + ); + return NULL; } - n = ImagingDrawRectangle( - self->image->image, (int) xy[0], (int) xy[1], - (int) xy[2], (int) xy[3], &ink, fill, self->blend - ); + n = ImagingDrawRectangle(self->image->image, + (int) xy[0], (int) xy[1], + (int) xy[2], (int) xy[3], + &ink, fill, self->blend + ); free(xy); @@ -2734,7 +2747,7 @@ pixel_access_new(ImagingObject* imagep, PyObject* args) self = PyObject_New(PixelAccessObject, &PixelAccess_Type); if (self == NULL) - return NULL; + return NULL; /* keep a reference to the image object */ Py_INCREF(imagep); @@ -2778,8 +2791,8 @@ pixel_access_setitem(PixelAccessObject *self, PyObject *xy, PyObject *color) return -1; if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) { - PyErr_SetString(PyExc_IndexError, outside_image); - return -1; + PyErr_SetString(PyExc_IndexError, outside_image); + return -1; } if (!color) /* FIXME: raise exception? */ @@ -2824,7 +2837,7 @@ _effect_noise(ImagingObject* self, PyObject* args) int xsize, ysize; float sigma = 128; if (!PyArg_ParseTuple(args, "(ii)|f", &xsize, &ysize, &sigma)) - return NULL; + return NULL; return PyImagingNew(ImagingEffectNoise(xsize, ysize, sigma)); } @@ -2835,7 +2848,7 @@ _effect_spread(ImagingObject* self, PyObject* args) int dist; if (!PyArg_ParseTuple(args, "i", &dist)) - return NULL; + return NULL; return PyImagingNew(ImagingEffectSpread(self->image, dist)); } @@ -2858,7 +2871,7 @@ _crc32(PyObject* self, PyObject* args) if (!PyArg_ParseTuple(args, PY_ARG_BYTES_LENGTH"|(ii)", &buffer, &bytes, &hi, &lo)) - return NULL; + return NULL; crc = ((UINT32) (hi & 0xFFFF) << 16) + (lo & 0xFFFF); @@ -2874,19 +2887,19 @@ _getcodecstatus(PyObject* self, PyObject* args) char* msg; if (!PyArg_ParseTuple(args, "i", &status)) - return NULL; + return NULL; switch (status) { case IMAGING_CODEC_OVERRUN: - msg = "buffer overrun"; break; + msg = "buffer overrun"; break; case IMAGING_CODEC_BROKEN: - msg = "broken data stream"; break; + msg = "broken data stream"; break; case IMAGING_CODEC_UNKNOWN: - msg = "unrecognized data stream contents"; break; + msg = "unrecognized data stream contents"; break; case IMAGING_CODEC_CONFIG: - msg = "codec configuration error"; break; + msg = "codec configuration error"; break; case IMAGING_CODEC_MEMORY: - msg = "out of memory"; break; + msg = "out of memory"; break; default: Py_RETURN_NONE; } @@ -2907,7 +2920,7 @@ _save_ppm(ImagingObject* self, PyObject* args) char* filename; if (!PyArg_ParseTuple(args, "s", &filename)) - return NULL; + return NULL; if (!ImagingSavePPM(self->image, filename)) return NULL; @@ -2934,6 +2947,7 @@ static struct PyMethodDef methods[] = { {"convert", (PyCFunction)_convert, 1}, {"convert2", (PyCFunction)_convert2, 1}, {"convert_matrix", (PyCFunction)_convert_matrix, 1}, + {"convert_transparent", (PyCFunction)_convert_transparent, 1}, {"copy", (PyCFunction)_copy, 1}, {"copy2", (PyCFunction)_copy2, 1}, #ifdef WITH_CRACKCODE diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index a326cb56d..21cd615fc 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -187,9 +187,9 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options: possible. This includes extra processing in order to find optimal encoder settings. -**transparency** - For ``P`` and ``L`` images, this option controls what color image to mark as - transparent. +**transparency** + For ``P``, ``L``, and ``RGB`` images, this option controls what + color image to mark as transparent. **bits (experimental)** For ``P`` images, this option controls how many bits to store. If omitted, diff --git a/libImaging/Convert.c b/libImaging/Convert.c index 2caf219ea..631263b31 100644 --- a/libImaging/Convert.c +++ b/libImaging/Convert.c @@ -312,7 +312,33 @@ rgba2rgbA(UINT8* out, const UINT8* in, int xsize) } } +/* + * Conversion of RGB + single transparent color to RGBA, + * where any pixel that matches the color will have the + * alpha channel set to 0 + */ + +static void +rgbT2rgba(UINT8* out, int xsize, int r, int g, int b) +{ +#ifdef WORDS_BIGENDIAN + UINT32 trns = ((r & 0xff)<<24) | ((g & 0xff)<<16) | ((b & 0xff)<<8) | 0xff; + UINT32 repl = trns & 0xffffff00; +#else + UINT32 trns = (0xff <<24) | ((b & 0xff)<<16) | ((g & 0xff)<<8) | (r & 0xff); + UINT32 repl = trns & 0x00ffffff; +#endif + UINT32* tmp = (UINT32 *)out; + int i; + + for (i=0; i < xsize; i++ ,tmp++) { + if (tmp[0]==trns) { + tmp[0]=repl; + } + } +} + /* ---------------- */ /* CMYK conversions */ @@ -1162,6 +1188,60 @@ ImagingConvert2(Imaging imOut, Imaging imIn) return convert(imOut, imIn, imOut->mode, NULL, 0); } + +Imaging +ImagingConvertTransparent(Imaging imIn, const char *mode, + int r, int g, int b) +{ + ImagingSectionCookie cookie; + ImagingShuffler convert; + Imaging imOut = NULL; + int y; + + if (!imIn){ + return (Imaging) ImagingError_ModeError(); + } + + if (!((strcmp(imIn->mode, "RGB") == 0 || + strcmp(imIn->mode, "L") == 0) + && strcmp(mode, "RGBA") == 0)) +#ifdef notdef + { + return (Imaging) ImagingError_ValueError("conversion not supported"); + } +#else + { + static char buf[256]; + /* FIXME: may overflow if mode is too large */ + sprintf(buf, "conversion from %s to %s not supported in convert_transparent", imIn->mode, mode); + return (Imaging) ImagingError_ValueError(buf); + } +#endif + + if (strcmp(imIn->mode, "RGB") == 0) { + convert = rgb2rgba; + } else { + convert = l2rgb; + g = b = r; + } + + imOut = ImagingNew2(mode, imOut, imIn); + if (!imOut){ + return NULL; + } + + ImagingSectionEnter(&cookie); + for (y = 0; y < imIn->ysize; y++) { + (*convert)((UINT8*) imOut->image[y], (UINT8*) imIn->image[y], + imIn->xsize); + rgbT2rgba((UINT8*) imOut->image[y], imIn->xsize, r, g, b); + } + ImagingSectionLeave(&cookie); + + return imOut; + +} + Imaging ImagingConvertInPlace(Imaging imIn, const char* mode) { diff --git a/libImaging/Imaging.h b/libImaging/Imaging.h index c139aed88..b45dcbe23 100644 --- a/libImaging/Imaging.h +++ b/libImaging/Imaging.h @@ -248,6 +248,7 @@ extern Imaging ImagingCopy(Imaging im); extern Imaging ImagingConvert(Imaging im, const char* mode, ImagingPalette palette, int dither); extern Imaging ImagingConvertInPlace(Imaging im, const char* mode); extern Imaging ImagingConvertMatrix(Imaging im, const char *mode, float m[]); +extern Imaging ImagingConvertTransparent(Imaging im, const char *mode, int r, int g, int b); extern Imaging ImagingCrop(Imaging im, int x0, int y0, int x1, int y1); extern Imaging ImagingExpand(Imaging im, int x, int y, int mode); extern Imaging ImagingFill(Imaging im, const void* ink);