From b1ec2e0ff6c79a37ea615a504532741f2f418663 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Mon, 28 Sep 2026 10:55:09 +0300 Subject: [PATCH 1/2] Speed up and simplify GIF optimization --- src/PIL/GifImagePlugin.py | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index 0e05d8761be..c3b21b82847 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -43,8 +43,6 @@ Image, ImageChops, ImageFile, - ImageMath, - ImageOps, ImagePalette, ImageSequence, ) @@ -735,37 +733,18 @@ def _write_multiple_frames( pass if "transparency" in encoderinfo: # When the delta is zero, fill the image with transparency - diff_frame = im_frame.copy() - fill = Image.new("P", delta.size, encoderinfo["transparency"]) if delta.mode == "RGBA": + # A pixel is unchanged only if all four bands are zero r, g, b, a = delta.split() - mask = ImageMath.lambda_eval( - lambda args: args["convert"]( - args["max"]( - args["max"]( - args["max"](args["r"], args["g"]), args["b"] - ), - args["a"], - ) - * 255, - "1", - ), - r=r, - g=g, - b=b, - a=a, - ) - else: - if delta.mode == "P": - # Convert to L without considering palette - delta_l = Image.new("L", delta.size) - delta_l.putdata(delta.get_flattened_data()) - delta = delta_l - mask = ImageMath.lambda_eval( - lambda args: args["convert"](args["im"] * 255, "1"), - im=delta, + delta = ImageChops.lighter( + ImageChops.lighter(r, g), + ImageChops.lighter(b, a), ) - diff_frame.paste(fill, mask=ImageOps.invert(mask)) + # Map zero pixels (palette indices, for "P") + # to a "1" mask without consulting the palette. + unchanged = delta.point([255, *(0,) * 255], "1") + diff_frame = im_frame.copy() + diff_frame.paste(encoderinfo["transparency"], mask=unchanged) else: bbox = None previous_im = im_frame From 5fbb13c98f90bbefebd1abb18f103252de6ed0b4 Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Mon, 28 Sep 2026 23:26:11 +1000 Subject: [PATCH 2/2] Use 'each' --- src/PIL/GifImagePlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py index c3b21b82847..48ba0f9deb1 100644 --- a/src/PIL/GifImagePlugin.py +++ b/src/PIL/GifImagePlugin.py @@ -734,7 +734,7 @@ def _write_multiple_frames( if "transparency" in encoderinfo: # When the delta is zero, fill the image with transparency if delta.mode == "RGBA": - # A pixel is unchanged only if all four bands are zero + # Each pixel is unchanged only if all four bands are zero r, g, b, a = delta.split() delta = ImageChops.lighter( ImageChops.lighter(r, g),