Added transparency for all PNG greyscale modes

This commit is contained in:
Andrew Murray 2019-03-27 07:41:33 +11:00
parent e6db8dee0c
commit 4a5666f1f4
7 changed files with 67 additions and 33 deletions

BIN
Tests/images/1_trns.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

BIN
Tests/images/i_trns.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -291,30 +291,32 @@ class TestFilePng(PillowTestCase):
self.assert_image(im, "RGBA", (10, 10))
self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))])
def test_save_l_transparency(self):
# There are 559 transparent pixels in l_trns.png.
num_transparent = 559
def test_save_greyscale_transparency(self):
for mode, num_transparent in {
"1": 1994,
"L": 559,
"I": 559,
}.items():
in_file = "Tests/images/"+mode.lower()+"_trns.png"
im = Image.open(in_file)
self.assertEqual(im.mode, mode)
self.assertEqual(im.info["transparency"], 255)
in_file = "Tests/images/l_trns.png"
im = Image.open(in_file)
self.assertEqual(im.mode, "L")
self.assertEqual(im.info["transparency"], 255)
im_rgba = im.convert('RGBA')
self.assertEqual(
im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
im_rgba = im.convert('RGBA')
self.assertEqual(
im_rgba.getchannel("A").getcolors()[0][0], num_transparent)
test_file = self.tempfile("temp.png")
im.save(test_file)
test_file = self.tempfile("temp.png")
im.save(test_file)
test_im = Image.open(test_file)
self.assertEqual(test_im.mode, mode)
self.assertEqual(test_im.info["transparency"], 255)
self.assert_image_equal(im, test_im)
test_im = Image.open(test_file)
self.assertEqual(test_im.mode, "L")
self.assertEqual(test_im.info["transparency"], 255)
self.assert_image_equal(im, test_im)
test_im_rgba = test_im.convert('RGBA')
self.assertEqual(
test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent)
test_im_rgba = test_im.convert('RGBA')
self.assertEqual(
test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent)
def test_save_rgb_single_transparency(self):
in_file = "Tests/images/caption_6_33_22.png"

View File

@ -490,12 +490,12 @@ The :py:meth:`~PIL.Image.Image.open` method sets the following
For ``P`` images: Either the palette index for full transparent pixels,
or a byte string with alpha values for each palette entry.
For ``L`` and ``RGB`` images, the color that represents full transparent
pixels in this image.
For ``1``, ``L``, ``I`` and ``RGB`` images, the color that represents
full transparent pixels in this image.
This key is omitted if the image is not a transparent palette image.
``Open`` also sets ``Image.text`` to a dictionary of the values of the
``open`` also sets ``Image.text`` to a dictionary of the values of the
``tEXt``, ``zTXt``, and ``iTXt`` chunks of the PNG image. Individual
compressed chunks are limited to a decompressed size of
``PngImagePlugin.MAX_TEXT_CHUNK``, by default 1MB, to prevent
@ -511,8 +511,8 @@ The :py:meth:`~PIL.Image.Image.save` method supports the following options:
encoder settings.
**transparency**
For ``P``, ``L``, and ``RGB`` images, this option controls what
color image to mark as transparent.
For ``P``, ``1``, ``L``, ``I``, and ``RGB`` images, this option controls
what color from the image to mark as transparent.
For ``P`` images, this can be a either the palette index,
or a byte string with alpha values for each palette entry.

View File

@ -950,7 +950,7 @@ class Image(object):
delete_trns = False
# transparency handling
if has_transparency:
if self.mode in ('L', 'RGB') and mode == 'RGBA':
if self.mode in ('1', 'L', 'I', 'RGB') and mode == 'RGBA':
# Use transparent conversion to promote from transparent
# color to an alpha channel.
new_im = self._new(self.im.convert_transparent(

View File

@ -54,19 +54,24 @@ _MAGIC = b"\211PNG\r\n\032\n"
_MODES = {
# supported bits/color combinations, and corresponding modes/rawmodes
# Greyscale
(1, 0): ("1", "1"),
(2, 0): ("L", "L;2"),
(4, 0): ("L", "L;4"),
(8, 0): ("L", "L"),
(16, 0): ("I", "I;16B"),
# Truecolour
(8, 2): ("RGB", "RGB"),
(16, 2): ("RGB", "RGB;16B"),
# Indexed-colour
(1, 3): ("P", "P;1"),
(2, 3): ("P", "P;2"),
(4, 3): ("P", "P;4"),
(8, 3): ("P", "P"),
# Greyscale with alpha
(8, 4): ("LA", "LA"),
(16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
# Truecolour with alpha
(8, 6): ("RGBA", "RGBA"),
(16, 6): ("RGBA", "RGBA;16B"),
}
@ -386,7 +391,7 @@ class PngStream(ChunkStream):
# otherwise, we have a byte string with one alpha value
# for each palette entry
self.im_info["transparency"] = s
elif self.im_mode == "L":
elif self.im_mode in ("1", "L", "I"):
self.im_info["transparency"] = i16(s)
elif self.im_mode == "RGB":
self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
@ -841,7 +846,7 @@ def _save(im, fp, filename, chunk=putchunk):
transparency = max(0, min(255, transparency))
alpha = b'\xFF' * transparency + b'\0'
chunk(fp, b"tRNS", alpha[:alpha_bytes])
elif im.mode == "L":
elif im.mode in ("1", "L", "I"):
transparency = max(0, min(65535, transparency))
chunk(fp, b"tRNS", o16(transparency))
elif im.mode == "RGB":

View File

@ -592,6 +592,22 @@ i2f(UINT8* out_, const UINT8* in_, int xsize)
*out++ = (FLOAT32) *in++;
}
static void
i2rgb(UINT8* out, const UINT8* in_, int xsize)
{
int x;
INT32* in = (INT32*) in_;
for (x = 0; x < xsize; x++, in++, out+=4) {
if (*in <= 0)
out[0] = out[1] = out[2] = 0;
else if (*in >= 255)
out[0] = out[1] = out[2] = 255;
else
out[0] = out[1] = out[2] = (UINT8) *in;
out[3] = 255;
}
}
/* ------------- */
/* F conversions */
/* ------------- */
@ -807,11 +823,14 @@ static struct {
{ "La", "LA", la2lA },
{ "I", "L", i2l },
{ "I", "F", i2f },
{ "I", "L", i2l },
{ "I", "F", i2f },
{ "I", "RGB", i2rgb },
{ "I", "RGBA", i2rgb },
{ "I", "RGBX", i2rgb },
{ "F", "L", f2l },
{ "F", "I", f2i },
{ "F", "L", f2l },
{ "F", "I", f2i },
{ "RGB", "1", rgb2bit },
{ "RGB", "L", rgb2l },
@ -1385,6 +1404,8 @@ ImagingConvertTransparent(Imaging imIn, const char *mode,
}
if (!((strcmp(imIn->mode, "RGB") == 0 ||
strcmp(imIn->mode, "1") == 0 ||
strcmp(imIn->mode, "I") == 0 ||
strcmp(imIn->mode, "L") == 0)
&& strcmp(mode, "RGBA") == 0))
#ifdef notdef
@ -1403,7 +1424,13 @@ ImagingConvertTransparent(Imaging imIn, const char *mode,
if (strcmp(imIn->mode, "RGB") == 0) {
convert = rgb2rgba;
} else {
convert = l2rgb;
if (strcmp(imIn->mode, "1") == 0) {
convert = bit2rgb;
} else if (strcmp(imIn->mode, "I") == 0) {
convert = i2rgb;
} else {
convert = l2rgb;
}
g = b = r;
}