Skip to content

Commit ddcc936

Browse files
authored
Merge pull request #5330 from radarhere/png_plte
Allow fewer PNG palette entries than the bit depth maximum when saving
2 parents 7ab8ec9 + 754752e commit ddcc936

File tree

2 files changed

+22
-14
lines changed

2 files changed

+22
-14
lines changed

Tests/test_file_png.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,16 @@ def test_specify_bits(self, tmp_path):
634634
with Image.open(out) as reloaded:
635635
assert len(reloaded.png.im_palette[1]) == 48
636636

637+
def test_plte_length(self, tmp_path):
638+
im = Image.new("P", (1, 1))
639+
im.putpalette((1, 1, 1))
640+
641+
out = str(tmp_path / "temp.png")
642+
im.save(str(tmp_path / "temp.png"))
643+
644+
with Image.open(out) as reloaded:
645+
assert len(reloaded.png.im_palette[1]) == 3
646+
637647
def test_exif(self):
638648
# With an EXIF chunk
639649
with Image.open("Tests/images/exif.png") as im:

src/PIL/PngImagePlugin.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,23 +1186,21 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
11861186
# attempt to minimize storage requirements for palette images
11871187
if "bits" in im.encoderinfo:
11881188
# number of bits specified by user
1189-
colors = 1 << im.encoderinfo["bits"]
1189+
colors = min(1 << im.encoderinfo["bits"], 256)
11901190
else:
11911191
# check palette contents
11921192
if im.palette:
1193-
colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 2)
1193+
colors = max(min(len(im.palette.getdata()[1]) // 3, 256), 1)
11941194
else:
11951195
colors = 256
11961196

1197-
if colors <= 2:
1198-
bits = 1
1199-
elif colors <= 4:
1200-
bits = 2
1201-
elif colors <= 16:
1202-
bits = 4
1203-
else:
1204-
bits = 8
1205-
if bits != 8:
1197+
if colors <= 16:
1198+
if colors <= 2:
1199+
bits = 1
1200+
elif colors <= 4:
1201+
bits = 2
1202+
else:
1203+
bits = 4
12061204
mode = f"{mode};{bits}"
12071205

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

12721270
if im.mode == "P":
1273-
palette_byte_number = (2 ** bits) * 3
1271+
palette_byte_number = colors * 3
12741272
palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
12751273
while len(palette_bytes) < palette_byte_number:
12761274
palette_bytes += b"\0"
@@ -1281,7 +1279,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
12811279
if transparency or transparency == 0:
12821280
if im.mode == "P":
12831281
# limit to actual palette size
1284-
alpha_bytes = 2 ** bits
1282+
alpha_bytes = colors
12851283
if isinstance(transparency, bytes):
12861284
chunk(fp, b"tRNS", transparency[:alpha_bytes])
12871285
else:
@@ -1302,7 +1300,7 @@ def _save(im, fp, filename, chunk=putchunk, save_all=False):
13021300
else:
13031301
if im.mode == "P" and im.im.getpalettemode() == "RGBA":
13041302
alpha = im.im.getpalette("RGBA", "A")
1305-
alpha_bytes = 2 ** bits
1303+
alpha_bytes = colors
13061304
chunk(fp, b"tRNS", alpha[:alpha_bytes])
13071305

13081306
dpi = im.encoderinfo.get("dpi")

0 commit comments

Comments
 (0)