Optimise palettes with more than 128 colors

This commit is contained in:
Andrew Murray 2022-06-19 16:47:50 +10:00
parent f656711c80
commit 709744432a
2 changed files with 20 additions and 27 deletions

View File

@ -158,6 +158,9 @@ def test_optimize_correctness():
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)
@ -167,11 +170,6 @@ def test_optimize_correctness():
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)))
@ -182,17 +180,15 @@ def test_optimize_full_l():
def test_optimize_if_palette_can_be_reduced_by_half():
with Image.open("Tests/images/test.colors.gif") as im:
# Reduce because original is too big for _get_optimize()
# Reduce dimensions because original is too big for _get_optimize()
im = im.resize((591, 443))
imrgb = im.convert("RGB")
im_rgb = im.convert("RGB")
for (optimize, colors) in ((False, 256), (True, 8)):
out = BytesIO()
imrgb.save(out, "GIF", optimize=False)
im_rgb.save(out, "GIF", optimize=optimize)
with Image.open(out) as reloaded:
assert len(reloaded.palette.palette) // 3 == 256
out = BytesIO()
imrgb.save(out, "GIF", optimize=True)
with Image.open(out) as reloaded:
assert len(reloaded.palette.palette) // 3 == 8
assert len(reloaded.palette.palette) // 3 == colors
def test_roundtrip(tmp_path):
@ -997,8 +993,8 @@ def test_append_images(tmp_path):
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)
@ -1008,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):

View File

@ -824,18 +824,15 @@ def _get_optimize(im, info):
if count:
used_palette_colors.append(i)
num_palette_colors = (
len(im.palette.palette) // 4
if im.palette.mode == "RGBA"
else len(im.palette.palette) // 3
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
)
# Round up to power of 2 but at least 4
num_palette_colors = max(4, 1 << (num_palette_colors - 1).bit_length())
if optimise or (
len(used_palette_colors) <= 128
and max(used_palette_colors) >= len(used_palette_colors)
or len(used_palette_colors) <= num_palette_colors // 2
):
if len(used_palette_colors) <= num_palette_colors // 2:
return used_palette_colors