From 4e16a9a94212a0c0765f4dc8d234710fa0305848 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Mar 2022 15:14:36 +1100 Subject: [PATCH 1/2] Retain RGBA transparency when saving multiple frames --- Tests/test_file_gif.py | 11 +++++++++++ src/PIL/GifImagePlugin.py | 37 ++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index 924adad9e62..05cc082b5d8 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -842,6 +842,17 @@ def test_rgb_transparency(tmp_path): assert "transparency" not in reloaded.info +def test_rgba_transparency(tmp_path): + out = str(tmp_path / "temp.gif") + + im = hopper("P") + im.save(out, save_all=True, append_images=[Image.new("RGBA", im.size)]) + + with Image.open(out) as reloaded: + reloaded.seek(1) + assert_image_equal(hopper("P").convert("RGB"), reloaded) + + def test_bbox(tmp_path): out = str(tmp_path / "temp.gif") diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 9cf27b0261b..9cdd987da9f 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -424,7 +424,7 @@ def _close__fp(self): RAWMODE = {"1": "L", "L": "L", "P": "P"} -def _normalize_mode(im, initial_call=False): +def _normalize_mode(im): """ Takes an image (or frame), returns an image in a mode that is appropriate for saving in a Gif. @@ -437,26 +437,22 @@ def _normalize_mode(im, initial_call=False): get returned in the RAWMODE clause. :param im: Image object - :param initial_call: Default false, set to true for a single frame. :returns: Image object """ if im.mode in RAWMODE: im.load() return im if Image.getmodebase(im.mode) == "RGB": - if initial_call: - palette_size = 256 - if im.palette: - palette_size = len(im.palette.getdata()[1]) // 3 - im = im.convert("P", palette=Image.Palette.ADAPTIVE, colors=palette_size) - if im.palette.mode == "RGBA": - for rgba in im.palette.colors.keys(): - if rgba[3] == 0: - im.info["transparency"] = im.palette.colors[rgba] - break - return im - else: - return im.convert("P") + palette_size = 256 + if im.palette: + palette_size = len(im.palette.getdata()[1]) // 3 + im = im.convert("P", palette=Image.Palette.ADAPTIVE, colors=palette_size) + if im.palette.mode == "RGBA": + for rgba in im.palette.colors.keys(): + if rgba[3] == 0: + im.info["transparency"] = im.palette.colors[rgba] + break + return im return im.convert("L") @@ -514,7 +510,7 @@ def _normalize_palette(im, palette, info): def _write_single_frame(im, fp, palette): - im_out = _normalize_mode(im, True) + im_out = _normalize_mode(im) for k, v in im_out.info.items(): im.encoderinfo.setdefault(k, v) im_out = _normalize_palette(im_out, palette, im.encoderinfo) @@ -646,11 +642,14 @@ def get_interlace(im): def _write_local_header(fp, im, offset, flags): transparent_color_exists = False try: - transparency = im.encoderinfo["transparency"] - except KeyError: + if "transparency" in im.encoderinfo: + transparency = im.encoderinfo["transparency"] + else: + transparency = im.info["transparency"] + transparency = int(transparency) + except (KeyError, ValueError): pass else: - transparency = int(transparency) # optimize the block away if transparent color is not used transparent_color_exists = True From 76871795f787756ab1978772b53237948bec377a Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sat, 12 Mar 2022 15:54:08 +1100 Subject: [PATCH 2/2] Resolved UNDONE by removing code --- src/PIL/GifImagePlugin.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 9cdd987da9f..25f7d9f4037 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -432,10 +432,6 @@ def _normalize_mode(im): It may return the original image, or it may return an image converted to palette or 'L' mode. - UNDONE: What is the point of mucking with the initial call palette, for - an image that shouldn't have a palette, or it would be a mode 'P' and - get returned in the RAWMODE clause. - :param im: Image object :returns: Image object """ @@ -443,10 +439,7 @@ def _normalize_mode(im): im.load() return im if Image.getmodebase(im.mode) == "RGB": - palette_size = 256 - if im.palette: - palette_size = len(im.palette.getdata()[1]) // 3 - im = im.convert("P", palette=Image.Palette.ADAPTIVE, colors=palette_size) + im = im.convert("P", palette=Image.Palette.ADAPTIVE) if im.palette.mode == "RGBA": for rgba in im.palette.colors.keys(): if rgba[3] == 0: