Merge pull request #6378 from raygard/fix_get_optimize

Improved GIF optimize condition
This commit is contained in:
Andrew Murray 2022-06-27 09:12:20 +10:00 committed by GitHub
commit fc497ffe5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 13 deletions

View File

@ -158,6 +158,9 @@ def test_optimize_correctness():
assert_image_equal(im.convert("RGB"), reloaded.convert("RGB")) assert_image_equal(im.convert("RGB"), reloaded.convert("RGB"))
# These do optimize the palette # These do optimize the palette
check(256, 511, 256)
check(255, 511, 255)
check(129, 511, 129)
check(128, 511, 128) check(128, 511, 128)
check(64, 511, 64) check(64, 511, 64)
check(4, 511, 4) check(4, 511, 4)
@ -167,11 +170,6 @@ def test_optimize_correctness():
check(64, 513, 256) check(64, 513, 256)
check(4, 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(): def test_optimize_full_l():
im = Image.frombytes("L", (16, 16), bytes(range(256))) im = Image.frombytes("L", (16, 16), bytes(range(256)))
@ -180,6 +178,19 @@ def test_optimize_full_l():
assert im.mode == "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): def test_roundtrip(tmp_path):
out = str(tmp_path / "temp.gif") out = str(tmp_path / "temp.gif")
im = hopper() im = hopper()
@ -982,8 +993,8 @@ def test_append_images(tmp_path):
def test_transparent_optimize(tmp_path): def test_transparent_optimize(tmp_path):
# From issue #2195, if the transparent color is incorrectly optimized out, GIF loses # From issue #2195, if the transparent color is incorrectly optimized out, GIF loses
# transparency. # transparency.
# Need a palette that isn't using the 0 color, and one that's > 128 items where the # Need a palette that isn't using the 0 color,
# transparent color is actually the top palette entry to trigger the bug. # where the transparent color is actually the top palette entry to trigger the bug.
data = bytes(range(1, 254)) data = bytes(range(1, 254))
palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3)
@ -993,10 +1004,10 @@ def test_transparent_optimize(tmp_path):
im.putpalette(palette) im.putpalette(palette)
out = str(tmp_path / "temp.gif") out = str(tmp_path / "temp.gif")
im.save(out, transparency=253) im.save(out, transparency=im.getpixel((252, 0)))
with Image.open(out) as reloaded:
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): def test_rgb_transparency(tmp_path):

View File

@ -824,9 +824,18 @@ def _get_optimize(im, info):
if count: if count:
used_palette_colors.append(i) used_palette_colors.append(i)
if optimise or ( if optimise or max(used_palette_colors) >= len(used_palette_colors):
len(used_palette_colors) <= 128 return used_palette_colors
and max(used_palette_colors) > len(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 return used_palette_colors