Skip to content

Commit

Permalink
Scale maxval > 255 for P6 to 255
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Mar 9, 2022
1 parent 9af736e commit fefce6a
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 24 deletions.
36 changes: 22 additions & 14 deletions Tests/test_file_ppm.py
Expand Up @@ -29,14 +29,34 @@ def test_arbitrary_maxval():
px = im.load()
assert tuple(px[x, 0] for x in range(3)) == (0, 128, 255)

# P6
# P6 with maxval < 255
fp = BytesIO(b"P6 3 1 17 \x00\x01\x02\x08\x09\x0A\x0F\x10\x11")
with Image.open(fp) as im:
assert im.size == (3, 1)
assert im.mode == "RGB"

px = im.load()
assert tuple(px[x, 0] for x in range(3)) == ((0, 15, 30), (120, 135, 150), (225, 240, 255))
assert tuple(px[x, 0] for x in range(3)) == (
(0, 15, 30),
(120, 135, 150),
(225, 240, 255),
)

# P6 with maxval > 255
fp = BytesIO(
b"P6 3 1 257 \x00\x00\x00\x01\x00\x02"
b"\x00\x80\x00\x81\x00\x82\x01\x00\x01\x01\xFF\xFF"
)
with Image.open(fp) as im:
assert im.size == (3, 1)
assert im.mode == "RGB"

px = im.load()
assert tuple(px[x, 0] for x in range(3)) == (
(0, 1, 2),
(127, 128, 129),
(254, 255, 255),
)


def test_16bit_pgm():
Expand Down Expand Up @@ -107,18 +127,6 @@ def test_token_too_long(tmp_path):
assert str(e.value) == "Token too long in file header: b'01234567890'"


def test_too_many_colors(tmp_path):
path = str(tmp_path / "temp.ppm")
with open(path, "wb") as f:
f.write(b"P6\n1 1\n1000\n")

with pytest.raises(ValueError) as e:
with Image.open(path):
pass

assert str(e.value) == "Too many colors for band: 1000"


def test_truncated_file(tmp_path):
# Test EOF in header
path = str(tmp_path / "temp.pgm")
Expand Down
22 changes: 12 additions & 10 deletions src/PIL/PpmImagePlugin.py
Expand Up @@ -16,7 +16,7 @@


from . import Image, ImageFile

from ._binary import i16be as i16
from ._binary import o8

#
Expand Down Expand Up @@ -115,12 +115,10 @@ def _open(self):
break
elif ix == 2: # token is maxval
maxval = token
if maxval > 255:
if mode != "L":
raise ValueError(f"Too many colors for band: {token}")
if maxval > 255 and mode == "L":
self.mode = "I"
rawmode = "I;16B" if maxval < 2**16 else "I;32B"
elif maxval < 255:
elif maxval != 255:
decoder_name = "ppm"
args = (rawmode, 0, 1) if decoder_name == "raw" else (rawmode, maxval)

Expand All @@ -133,15 +131,19 @@ class PpmDecoder(ImageFile.PyDecoder):

def decode(self, buffer):
data = b""
maxval = self.args[-1]
maxval = min(self.args[-1], 65535)
in_byte_count = 1 if maxval < 256 else 2
bands = Image.getmodebands(self.mode)
while len(data) < self.state.xsize * self.state.ysize * bands:
pixels = self.fd.read(bands)
if len(pixels) < bands:
pixels = self.fd.read(in_byte_count * bands)
if len(pixels) < in_byte_count * bands:
# eof
break
for pixel in pixels:
value = min(255, round(pixel / maxval * 255))
for b in range(bands):
value = (
pixels[b] if in_byte_count == 1 else i16(pixels, b * in_byte_count)
)
value = min(255, round(value / maxval * 255))
data += o8(value)
self.set_as_raw(data, (self.mode, 0, 1))
return -1, 0
Expand Down

0 comments on commit fefce6a

Please sign in to comment.