Reverted converting L with transparency to LA after first frame

This commit is contained in:
Andrew Murray 2022-03-22 22:07:37 +11:00
parent ce8c682748
commit c5efe60c37
4 changed files with 37 additions and 44 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 B

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -59,23 +59,15 @@ def test_invalid_file():
GifImagePlugin.GifImageFile(invalid_file) GifImagePlugin.GifImageFile(invalid_file)
def test_l_mode_subsequent_frames(): def test_l_mode_transparency():
with Image.open("Tests/images/no_palette.gif") as im:
assert im.mode == "L"
assert im.load()[0, 0] == 0
im.seek(1)
assert im.mode == "L"
assert im.load()[0, 0] == 0
with Image.open("Tests/images/no_palette_with_transparency.gif") as im: with Image.open("Tests/images/no_palette_with_transparency.gif") as im:
assert im.mode == "L" assert im.mode == "L"
assert im.load()[0, 0] == 0 assert im.load()[0, 0] == 128
assert im.info["transparency"] == 255 assert im.info["transparency"] == 255
im.seek(1) im.seek(1)
assert im.mode == "LA" assert im.mode == "L"
assert im.load()[0, 0] == (0, 255) assert im.load()[0, 0] == 128
def test_strategy(): def test_strategy():
@ -439,14 +431,6 @@ def test_dispose_background_transparency():
@pytest.mark.parametrize( @pytest.mark.parametrize(
"mode_strategy, expected_colors", "mode_strategy, expected_colors",
( (
(
GifImagePlugin.ModeStrategy.DIFFERENT_PALETTE_ONLY,
(
(2, 1, 2),
(0, 1, 0),
(2, 1, 2),
),
),
( (
GifImagePlugin.ModeStrategy.AFTER_FIRST, GifImagePlugin.ModeStrategy.AFTER_FIRST,
( (
@ -455,16 +439,27 @@ def test_dispose_background_transparency():
((0, 0, 0, 0), (0, 0, 255, 255), (0, 0, 0, 0)), ((0, 0, 0, 0), (0, 0, 255, 255), (0, 0, 0, 0)),
), ),
), ),
(
GifImagePlugin.ModeStrategy.DIFFERENT_PALETTE_ONLY,
(
(2, 1, 2),
(0, 1, 0),
(2, 1, 2),
),
),
), ),
) )
def test_transparent_dispose(mode_strategy, expected_colors): def test_transparent_dispose(mode_strategy, expected_colors):
GifImagePlugin.PALETTE_TO_RGB = mode_strategy GifImagePlugin.PALETTE_TO_RGB = mode_strategy
try:
with Image.open("Tests/images/transparent_dispose.gif") as img: with Image.open("Tests/images/transparent_dispose.gif") as img:
for frame in range(3): for frame in range(3):
img.seek(frame) img.seek(frame)
for x in range(3): for x in range(3):
color = img.getpixel((x, 0)) color = img.getpixel((x, 0))
assert color == expected_colors[frame][x] assert color == expected_colors[frame][x]
finally:
GifImagePlugin.PALETTE_TO_RGB = GifImagePlugin.ModeStrategy.AFTER_FIRST
def test_dispose_previous(): def test_dispose_previous():

View File

@ -107,8 +107,7 @@ are used or GIF89a is already in use.
GIF files are initially read as grayscale (``L``) or palette mode (``P``) GIF files are initially read as grayscale (``L``) or palette mode (``P``)
images. Seeking to later frames in a ``P`` image will change the image to images. Seeking to later frames in a ``P`` image will change the image to
``RGB`` (or ``RGBA`` if the first frame had transparency). ``L`` images will ``RGB`` (or ``RGBA`` if the first frame had transparency).
stay in ``L`` mode (or change to ``LA`` if the first frame had transparency).
``P`` mode images are changed to ``RGB`` because each frame of a GIF may ``P`` mode images are changed to ``RGB`` because each frame of a GIF may
introduce up to 256 colors. Because ``P`` can only have up to 256 colors, the introduce up to 256 colors. Because ``P`` can only have up to 256 colors, the

View File

@ -71,6 +71,12 @@ class GifImageFile(ImageFile.ImageFile):
return self.fp.read(s[0]) return self.fp.read(s[0])
return None return None
def _is_palette_needed(self, p):
for i in range(0, len(p), 3):
if not (i // 3 == p[i] == p[i + 1] == p[i + 2]):
return True
return False
def _open(self): def _open(self):
# Screen # Screen
@ -89,11 +95,9 @@ class GifImageFile(ImageFile.ImageFile):
self.info["background"] = s[11] self.info["background"] = s[11]
# check if palette contains colour indices # check if palette contains colour indices
p = self.fp.read(3 << bits) p = self.fp.read(3 << bits)
for i in range(0, len(p), 3): if self._is_palette_needed(p):
if not (i // 3 == p[i] == p[i + 1] == p[i + 2]):
p = ImagePalette.raw("RGB", p) p = ImagePalette.raw("RGB", p)
self.global_palette = self.palette = p self.global_palette = self.palette = p
break
self.__fp = self.fp # FIXME: hack self.__fp = self.fp # FIXME: hack
self.__rewind = self.fp.tell() self.__rewind = self.fp.tell()
@ -259,7 +263,9 @@ class GifImageFile(ImageFile.ImageFile):
if flags & 128: if flags & 128:
bits = (flags & 7) + 1 bits = (flags & 7) + 1
palette = ImagePalette.raw("RGB", self.fp.read(3 << bits)) p = self.fp.read(3 << bits)
if self._is_palette_needed(p):
palette = ImagePalette.raw("RGB", p)
# image data # image data
bits = self.fp.read(1)[0] bits = self.fp.read(1)[0]
@ -305,11 +311,6 @@ class GifImageFile(ImageFile.ImageFile):
else: else:
self.mode = "RGB" self.mode = "RGB"
self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG) self.im = self.im.convert("RGB", Image.Dither.FLOYDSTEINBERG)
elif self.mode == "L" and "transparency" in self.info:
self.pyaccess = None
self.im = self.im.convert_transparent("LA", self.info["transparency"])
self.mode = "LA"
del self.info["transparency"]
def _rgb(color): def _rgb(color):
if self._frame_palette: if self._frame_palette:
@ -369,7 +370,7 @@ class GifImageFile(ImageFile.ImageFile):
if frame_transparency is not None: if frame_transparency is not None:
if frame == 0: if frame == 0:
self.info["transparency"] = frame_transparency self.info["transparency"] = frame_transparency
elif self.mode not in ("RGB", "RGBA", "LA"): elif self.mode not in ("RGB", "RGBA"):
transparency = frame_transparency transparency = frame_transparency
self.tile = [ self.tile = [
( (
@ -394,7 +395,7 @@ class GifImageFile(ImageFile.ImageFile):
self.im = Image.core.fill( self.im = Image.core.fill(
temp_mode, self.size, self.info["transparency"] temp_mode, self.size, self.info["transparency"]
) )
elif self.mode in ("RGB", "RGBA", "LA"): elif self.mode in ("RGB", "RGBA"):
self._prev_im = self.im self._prev_im = self.im
if self._frame_palette: if self._frame_palette:
self.im = Image.core.fill("P", self.size, self._frame_transparency or 0) self.im = Image.core.fill("P", self.size, self._frame_transparency or 0)
@ -418,8 +419,6 @@ class GifImageFile(ImageFile.ImageFile):
frame_im = self.im.convert("RGBA") frame_im = self.im.convert("RGBA")
else: else:
frame_im = self.im.convert("RGB") frame_im = self.im.convert("RGB")
elif self.mode == "L" and self._frame_transparency is not None:
frame_im = self.im.convert_transparent("LA", self._frame_transparency)
else: else:
if not self._prev_im: if not self._prev_im:
return return
@ -428,7 +427,7 @@ class GifImageFile(ImageFile.ImageFile):
self.im = self._prev_im self.im = self._prev_im
self.mode = self.im.mode self.mode = self.im.mode
if frame_im.mode in ("LA", "RGBA"): if frame_im.mode == "RGBA":
self.im.paste(frame_im, self.dispose_extent, frame_im) self.im.paste(frame_im, self.dispose_extent, frame_im)
else: else:
self.im.paste(frame_im, self.dispose_extent) self.im.paste(frame_im, self.dispose_extent)