Skip to content

Commit

Permalink
Merge pull request #6378 from raygard/fix_get_optimize
Browse files Browse the repository at this point in the history
Improved GIF optimize condition
  • Loading branch information
radarhere committed Jun 26, 2022
2 parents ef93892 + 7c839d8 commit fc497ff
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 13 deletions.
31 changes: 21 additions & 10 deletions Tests/test_file_gif.py
Expand Up @@ -158,6 +158,9 @@ def check(colors, size, expected_palette_length):
assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))

# These do optimize the palette
check(256, 511, 256)
check(255, 511, 255)
check(129, 511, 129)
check(128, 511, 128)
check(64, 511, 64)
check(4, 511, 4)
Expand All @@ -167,11 +170,6 @@ def check(colors, size, expected_palette_length):
check(64, 513, 256)
check(4, 513, 256)

# Other limits that don't optimize the palette
check(129, 511, 256)
check(255, 511, 256)
check(256, 511, 256)


def test_optimize_full_l():
im = Image.frombytes("L", (16, 16), bytes(range(256)))
Expand All @@ -180,6 +178,19 @@ def test_optimize_full_l():
assert im.mode == "L"


def test_optimize_if_palette_can_be_reduced_by_half():
with Image.open("Tests/images/test.colors.gif") as im:
# Reduce dimensions because original is too big for _get_optimize()
im = im.resize((591, 443))
im_rgb = im.convert("RGB")

for (optimize, colors) in ((False, 256), (True, 8)):
out = BytesIO()
im_rgb.save(out, "GIF", optimize=optimize)
with Image.open(out) as reloaded:
assert len(reloaded.palette.palette) // 3 == colors


def test_roundtrip(tmp_path):
out = str(tmp_path / "temp.gif")
im = hopper()
Expand Down Expand Up @@ -982,8 +993,8 @@ def im_generator(ims):
def test_transparent_optimize(tmp_path):
# From issue #2195, if the transparent color is incorrectly optimized out, GIF loses
# transparency.
# Need a palette that isn't using the 0 color, and one that's > 128 items where the
# transparent color is actually the top palette entry to trigger the bug.
# Need a palette that isn't using the 0 color,
# where the transparent color is actually the top palette entry to trigger the bug.

data = bytes(range(1, 254))
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
Expand All @@ -993,10 +1004,10 @@ def test_transparent_optimize(tmp_path):
im.putpalette(palette)

out = str(tmp_path / "temp.gif")
im.save(out, transparency=253)
with Image.open(out) as reloaded:
im.save(out, transparency=im.getpixel((252, 0)))

assert reloaded.info["transparency"] == 253
with Image.open(out) as reloaded:
assert reloaded.info["transparency"] == reloaded.getpixel((252, 0))


def test_rgb_transparency(tmp_path):
Expand Down
15 changes: 12 additions & 3 deletions src/PIL/GifImagePlugin.py
Expand Up @@ -824,9 +824,18 @@ def _get_optimize(im, info):
if count:
used_palette_colors.append(i)

if optimise or (
len(used_palette_colors) <= 128
and max(used_palette_colors) > len(used_palette_colors)
if optimise or max(used_palette_colors) >= len(used_palette_colors):
return used_palette_colors

num_palette_colors = len(im.palette.palette) // Image.getmodebands(
im.palette.mode
)
current_palette_size = 1 << (num_palette_colors - 1).bit_length()
if (
# check that the palette would become smaller when saved
len(used_palette_colors) <= current_palette_size // 2
# check that the palette is not already the smallest possible size
and current_palette_size > 2
):
return used_palette_colors

Expand Down

0 comments on commit fc497ff

Please sign in to comment.