Skip to content

Commit

Permalink
Allow fewer palette entries than the bit depth maximum
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere authored and Hurstville Presbyterian committed Mar 20, 2021
1 parent 7235cf3 commit dbb9c2c
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 14 deletions.
10 changes: 10 additions & 0 deletions Tests/test_file_png.py
Expand Up @@ -634,6 +634,16 @@ def test_specify_bits(self, tmp_path):
with Image.open(out) as reloaded:
assert len(reloaded.png.im_palette[1]) == 48

def test_plte_length(self, tmp_path):
im = Image.new("P", (1, 1))
im.putpalette((1, 1, 1))

out = str(tmp_path / "temp.png")
im.save(str(tmp_path / "temp.png"))

with Image.open(out) as reloaded:
assert len(reloaded.png.im_palette[1]) == 3

def test_exif(self):
# With an EXIF chunk
with Image.open("Tests/images/exif.png") as im:
Expand Down
26 changes: 12 additions & 14 deletions src/PIL/PngImagePlugin.py
Expand Up @@ -1186,23 +1186,21 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
# attempt to minimize storage requirements for palette images
if "bits" in im.encoderinfo:
# number of bits specified by user
colors = 1 << im.encoderinfo["bits"]
colors = min(1 << im.encoderinfo["bits"], 256)
else:
# check palette contents
if im.palette:
colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 2)
colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 1)
else:
colors = 256

if colors <= 2:
bits = 1
elif colors <= 4:
bits = 2
elif colors <= 16:
bits = 4
else:
bits = 8
if bits != 8:
if colors <= 16:
if colors <= 2:
bits = 1
elif colors <= 4:
bits = 2
else:
bits = 4
mode = f"{mode};{bits}"

# encoder options
Expand Down Expand Up @@ -1270,7 +1268,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
chunk(fp, cid, data)

if im.mode == "P":
palette_byte_number = (2 ** bits) * 3
palette_byte_number = colors * 3
palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
while len(palette_bytes) < palette_byte_number:
palette_bytes += b"\0"
Expand All @@ -1281,7 +1279,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
if transparency or transparency == 0:
if im.mode == "P":
# limit to actual palette size
alpha_bytes = 2 ** bits
alpha_bytes = colors
if isinstance(transparency, bytes):
chunk(fp, b"tRNS", transparency[:alpha_bytes])
else:
Expand All @@ -1302,7 +1300,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
else:
if im.mode == "P" and im.im.getpalettemode() == "RGBA":
alpha = im.im.getpalette("RGBA", "A")
alpha_bytes = 2 ** bits
alpha_bytes = colors
chunk(fp, b"tRNS", alpha[:alpha_bytes])

dpi = im.encoderinfo.get("dpi")
Expand Down

0 comments on commit dbb9c2c

Please sign in to comment.