mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-26 01:04:29 +03:00
Merge pull request #423 from wiredfool/trns-png
Support for PNG tRNS header when converting from RGB->RGBA
This commit is contained in:
commit
d48f301d57
12
PIL/Image.py
12
PIL/Image.py
|
@ -724,15 +724,9 @@ class Image:
|
||||||
if dither is None:
|
if dither is None:
|
||||||
dither = FLOYDSTEINBERG
|
dither = FLOYDSTEINBERG
|
||||||
|
|
||||||
# fake a P-mode image, otherwise the transparency will get lost as there is
|
# Use transparent conversion to promote from transparent color to an alpha channel.
|
||||||
# currently no other way to convert transparency into an RGBA image
|
if self.mode in ("L", "RGB") and mode == "RGBA" and "transparency" in self.info:
|
||||||
if self.mode == "L" and mode == "RGBA" and "transparency" in self.info:
|
return self._new(self.im.convert_transparent(mode, self.info['transparency']))
|
||||||
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()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
im = self.im.convert(mode, dither)
|
im = self.im.convert(mode, dither)
|
||||||
|
|
BIN
Tests/images/rgb_trns.png
Normal file
BIN
Tests/images/rgb_trns.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
|
@ -150,6 +150,17 @@ def test_load_transparent_p():
|
||||||
# image has 124 uniqe qlpha values
|
# image has 124 uniqe qlpha values
|
||||||
assert_equal(len(im.split()[3].getcolors()), 124)
|
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():
|
def test_save_p_transparent_palette():
|
||||||
in_file = "Tests/images/pil123p.png"
|
in_file = "Tests/images/pil123p.png"
|
||||||
im = Image.open(in_file)
|
im = Image.open(in_file)
|
||||||
|
@ -171,6 +182,10 @@ def test_save_l_transparency():
|
||||||
file = tempfile("temp.png")
|
file = tempfile("temp.png")
|
||||||
assert_no_exception(lambda: im.save(file))
|
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():
|
def test_save_rgb_single_transparency():
|
||||||
in_file = "Tests/images/caption_6_33_22.png"
|
in_file = "Tests/images/caption_6_33_22.png"
|
||||||
im = Image.open(in_file)
|
im = Image.open(in_file)
|
||||||
|
|
40
_imaging.c
40
_imaging.c
|
@ -794,6 +794,21 @@ _convert_matrix(ImagingObject* self, PyObject* args)
|
||||||
return PyImagingNew(ImagingConvertMatrix(self->image, mode, m));
|
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*
|
static PyObject*
|
||||||
_copy(ImagingObject* self, PyObject* args)
|
_copy(ImagingObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
|
@ -2375,8 +2390,7 @@ _draw_bitmap(ImagingDrawObject* self, PyObject* args)
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (n != 1) {
|
if (n != 1) {
|
||||||
PyErr_SetString(
|
PyErr_SetString(PyExc_TypeError,
|
||||||
PyExc_TypeError,
|
|
||||||
"coordinate list must contain exactly 1 coordinate"
|
"coordinate list must contain exactly 1 coordinate"
|
||||||
);
|
);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2430,15 +2444,15 @@ _draw_ellipse(ImagingDrawObject* self, PyObject* args)
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (n != 2) {
|
if (n != 2) {
|
||||||
PyErr_SetString(
|
PyErr_SetString(PyExc_TypeError,
|
||||||
PyExc_TypeError,
|
|
||||||
"coordinate list must contain exactly 2 coordinates"
|
"coordinate list must contain exactly 2 coordinates"
|
||||||
);
|
);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = ImagingDrawEllipse(
|
n = ImagingDrawEllipse(self->image->image,
|
||||||
self->image->image, (int) xy[0], (int) xy[1], (int) xy[2], (int) xy[3],
|
(int) xy[0], (int) xy[1],
|
||||||
|
(int) xy[2], (int) xy[3],
|
||||||
&ink, fill, self->blend
|
&ink, fill, self->blend
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2632,8 +2646,7 @@ _draw_polygon(ImagingDrawObject* self, PyObject* args)
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (n < 2) {
|
if (n < 2) {
|
||||||
PyErr_SetString(
|
PyErr_SetString(PyExc_TypeError,
|
||||||
PyExc_TypeError,
|
|
||||||
"coordinate list must contain at least 2 coordinates"
|
"coordinate list must contain at least 2 coordinates"
|
||||||
);
|
);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2677,16 +2690,16 @@ _draw_rectangle(ImagingDrawObject* self, PyObject* args)
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (n != 2) {
|
if (n != 2) {
|
||||||
PyErr_SetString(
|
PyErr_SetString(PyExc_TypeError,
|
||||||
PyExc_TypeError,
|
|
||||||
"coordinate list must contain exactly 2 coordinates"
|
"coordinate list must contain exactly 2 coordinates"
|
||||||
);
|
);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = ImagingDrawRectangle(
|
n = ImagingDrawRectangle(self->image->image,
|
||||||
self->image->image, (int) xy[0], (int) xy[1],
|
(int) xy[0], (int) xy[1],
|
||||||
(int) xy[2], (int) xy[3], &ink, fill, self->blend
|
(int) xy[2], (int) xy[3],
|
||||||
|
&ink, fill, self->blend
|
||||||
);
|
);
|
||||||
|
|
||||||
free(xy);
|
free(xy);
|
||||||
|
@ -2934,6 +2947,7 @@ static struct PyMethodDef methods[] = {
|
||||||
{"convert", (PyCFunction)_convert, 1},
|
{"convert", (PyCFunction)_convert, 1},
|
||||||
{"convert2", (PyCFunction)_convert2, 1},
|
{"convert2", (PyCFunction)_convert2, 1},
|
||||||
{"convert_matrix", (PyCFunction)_convert_matrix, 1},
|
{"convert_matrix", (PyCFunction)_convert_matrix, 1},
|
||||||
|
{"convert_transparent", (PyCFunction)_convert_transparent, 1},
|
||||||
{"copy", (PyCFunction)_copy, 1},
|
{"copy", (PyCFunction)_copy, 1},
|
||||||
{"copy2", (PyCFunction)_copy2, 1},
|
{"copy2", (PyCFunction)_copy2, 1},
|
||||||
#ifdef WITH_CRACKCODE
|
#ifdef WITH_CRACKCODE
|
||||||
|
|
|
@ -188,8 +188,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
|
||||||
settings.
|
settings.
|
||||||
|
|
||||||
**transparency**
|
**transparency**
|
||||||
For ``P`` and ``L`` images, this option controls what color image to mark as
|
For ``P``, ``L``, and ``RGB`` images, this option controls what
|
||||||
transparent.
|
color image to mark as transparent.
|
||||||
|
|
||||||
**bits (experimental)**
|
**bits (experimental)**
|
||||||
For ``P`` images, this option controls how many bits to store. If omitted,
|
For ``P`` images, this option controls how many bits to store. If omitted,
|
||||||
|
|
|
@ -312,6 +312,32 @@ 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ---------------- */
|
/* ---------------- */
|
||||||
|
@ -1162,6 +1188,60 @@ ImagingConvert2(Imaging imOut, Imaging imIn)
|
||||||
return convert(imOut, imIn, imOut->mode, NULL, 0);
|
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
|
Imaging
|
||||||
ImagingConvertInPlace(Imaging imIn, const char* mode)
|
ImagingConvertInPlace(Imaging imIn, const char* mode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -248,6 +248,7 @@ extern Imaging ImagingCopy(Imaging im);
|
||||||
extern Imaging ImagingConvert(Imaging im, const char* mode, ImagingPalette palette, int dither);
|
extern Imaging ImagingConvert(Imaging im, const char* mode, ImagingPalette palette, int dither);
|
||||||
extern Imaging ImagingConvertInPlace(Imaging im, const char* mode);
|
extern Imaging ImagingConvertInPlace(Imaging im, const char* mode);
|
||||||
extern Imaging ImagingConvertMatrix(Imaging im, const char *mode, float m[]);
|
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 ImagingCrop(Imaging im, int x0, int y0, int x1, int y1);
|
||||||
extern Imaging ImagingExpand(Imaging im, int x, int y, int mode);
|
extern Imaging ImagingExpand(Imaging im, int x, int y, int mode);
|
||||||
extern Imaging ImagingFill(Imaging im, const void* ink);
|
extern Imaging ImagingFill(Imaging im, const void* ink);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user