Moved decoder names out of MODES

This commit is contained in:
Andrew Murray 2022-03-12 17:32:15 +11:00
parent 0215175e1d
commit 073acd4c82
2 changed files with 43 additions and 43 deletions

View File

@ -51,35 +51,45 @@ def test_pnm(tmp_path):
def test_plain_pbm(tmp_path): def test_plain_pbm(tmp_path):
# P1
with Image.open("Tests/images/hopper_1bit_plain.pbm") as im: with Image.open("Tests/images/hopper_1bit_plain.pbm") as im:
# P4
assert_image_equal_tofile(im, "Tests/images/hopper_1bit.pbm") assert_image_equal_tofile(im, "Tests/images/hopper_1bit.pbm")
def test_8bit_plain_pgm(tmp_path): def test_8bit_plain_pgm(tmp_path):
# P2
with Image.open("Tests/images/hopper_8bit_plain.pgm") as im: with Image.open("Tests/images/hopper_8bit_plain.pgm") as im:
# P5
assert_image_equal_tofile(im, "Tests/images/hopper_8bit.pgm") assert_image_equal_tofile(im, "Tests/images/hopper_8bit.pgm")
def test_8bit_plain_ppm(tmp_path): def test_8bit_plain_ppm(tmp_path):
# P3
with Image.open("Tests/images/hopper_8bit_plain.ppm") as im: with Image.open("Tests/images/hopper_8bit_plain.ppm") as im:
# P6
assert_image_equal_tofile(im, "Tests/images/hopper_8bit.ppm") assert_image_equal_tofile(im, "Tests/images/hopper_8bit.ppm")
def test_16bit_plain_pgm(tmp_path): def test_16bit_plain_pgm(tmp_path):
# P2 with maxval 2 ** 16 - 1
with Image.open("Tests/images/hopper_16bit_plain.pgm") as im: with Image.open("Tests/images/hopper_16bit_plain.pgm") as im:
assert im.mode == "I" assert im.mode == "I"
assert im.size == (128, 128) assert im.size == (128, 128)
assert im.get_format_mimetype() == "image/x-portable-graymap" assert im.get_format_mimetype() == "image/x-portable-graymap"
# P5 with maxval 2 ** 16 - 1
assert_image_equal_tofile(im, "Tests/images/hopper_16bit.pgm") assert_image_equal_tofile(im, "Tests/images/hopper_16bit.pgm")
def test_32bit_plain_pgm(tmp_path): def test_32bit_plain_pgm(tmp_path):
# P2 with maxval 2 ** 31 - 1
with Image.open("Tests/images/hopper_32bit_plain.pgm") as im: with Image.open("Tests/images/hopper_32bit_plain.pgm") as im:
assert im.mode == "I" assert im.mode == "I"
assert im.size == (128, 128) assert im.size == (128, 128)
assert im.get_format_mimetype() == "image/x-portable-graymap" assert im.get_format_mimetype() == "image/x-portable-graymap"
# P5 with maxval 2 ** 31 - 1
assert_image_equal_tofile(im, "Tests/images/hopper_32bit.pgm") assert_image_equal_tofile(im, "Tests/images/hopper_32bit.pgm")

View File

@ -23,20 +23,19 @@ from . import Image, ImageFile
b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d" b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d"
MODES = { MODES = {
# standard, plain # standard
b"P1": ("ppm_plain", "1"), b"P1": "1",
b"P2": ("ppm_plain", "L"), b"P2": "L",
b"P3": ("ppm_plain", "RGB"), b"P3": "RGB",
# standard, raw b"P4": "1",
b"P4": ("raw", "1"), b"P5": "L",
b"P5": ("raw", "L"), b"P6": "RGB",
b"P6": ("raw", "RGB"),
# extensions # extensions
b"P0CMYK": ("raw", "CMYK"), b"P0CMYK": "CMYK",
# PIL extensions (for test purposes only) # PIL extensions (for test purposes only)
b"PyP": ("raw", "P"), b"PyP": "P",
b"PyRGBA": ("raw", "RGBA"), b"PyRGBA": "RGBA",
b"PyCMYK": ("raw", "CMYK"), b"PyCMYK": "CMYK",
} }
@ -90,18 +89,16 @@ class PpmImageFile(ImageFile.ImageFile):
def _open(self): def _open(self):
magic_number = self._read_magic() magic_number = self._read_magic()
try: try:
decoder, mode = MODES[magic_number] mode = MODES[magic_number]
except KeyError: except KeyError:
raise SyntaxError("not a PPM file") raise SyntaxError("not a PPM file")
self.custom_mimetype = { if magic_number in (b"P1", b"P4"):
b"P1": "image/x-portable-bitmap", self.custom_mimetype = "image/x-portable-bitmap"
b"P2": "image/x-portable-graymap", elif magic_number in (b"P2", b"P5"):
b"P3": "image/x-portable-pixmap", self.custom_mimetype = "image/x-portable-graymap"
b"P4": "image/x-portable-bitmap", elif magic_number in (b"P3", b"P6"):
b"P5": "image/x-portable-graymap", self.custom_mimetype = "image/x-portable-pixmap"
b"P6": "image/x-portable-pixmap",
}.get(magic_number)
for ix in range(3): for ix in range(3):
token = int(self._read_token()) token = int(self._read_token())
@ -127,14 +124,12 @@ class PpmImageFile(ImageFile.ImageFile):
self.mode = "I" self.mode = "I"
rawmode = "I;32B" rawmode = "I;32B"
decoder_name = "raw"
if magic_number in (b"P1", b"P2", b"P3"):
decoder_name = "ppm_plain"
self._size = xsize, ysize self._size = xsize, ysize
self.tile = [ self.tile = [
( (decoder_name, (0, 0, xsize, ysize), self.fp.tell(), (rawmode, 0, 1))
decoder, # decoder
(0, 0, xsize, ysize), # region: whole image
self.fp.tell(), # offset to image data
(rawmode, 0, 1), # parameters for decoder
)
] ]
@ -145,8 +140,8 @@ class PpmImageFile(ImageFile.ImageFile):
class PpmPlainDecoder(ImageFile.PyDecoder): class PpmPlainDecoder(ImageFile.PyDecoder):
_pulls_fd = True _pulls_fd = True
def _read_block(self, block_size=10**6): def _read_block(self):
return bytearray(self.fd.read(block_size)) return self.fd.read(10**6)
def _find_comment_end(self, block, start=0): def _find_comment_end(self, block, start=0):
a = block.find(b"\n", start) a = block.find(b"\n", start)
@ -155,10 +150,9 @@ class PpmPlainDecoder(ImageFile.PyDecoder):
def _ignore_comments(self, block): def _ignore_comments(self, block):
""" """
Deletes comments from block. If comment does not end in this Deletes comments from block.
block, raises a flag. If comment does not end in this block, raises a flag.
""" """
comment_spans = False comment_spans = False
while True: while True:
comment_start = block.find(b"#") # look for next comment comment_start = block.find(b"#") # look for next comment
@ -166,9 +160,8 @@ class PpmPlainDecoder(ImageFile.PyDecoder):
break break
comment_end = self._find_comment_end(block, comment_start) comment_end = self._find_comment_end(block, comment_start)
if comment_end != -1: # comment ends in this block if comment_end != -1: # comment ends in this block
block = ( # delete comment
block[:comment_start] + block[comment_end + 1 :] block = block[:comment_start] + block[comment_end + 1 :]
) # delete comment
else: # last comment continues to next block(s) else: # last comment continues to next block(s)
block = block[:comment_start] block = block[:comment_start]
comment_spans = True comment_spans = True
@ -177,9 +170,8 @@ class PpmPlainDecoder(ImageFile.PyDecoder):
def _decode_bitonal(self): def _decode_bitonal(self):
""" """
The reason this is a separate method is that in the plain PBM This is a separate method because the plain PBM format all data tokens
format all data tokens are exactly one byte, and so the are exactly one byte, and so the inter-token whitespace is optional.
inter-token whitespace is optional.
""" """
decoded_data = bytearray() decoded_data = bytearray()
total_tokens = self.size total_tokens = self.size
@ -217,10 +209,8 @@ class PpmPlainDecoder(ImageFile.PyDecoder):
def _decode_blocks(self, channels=1, depth=8): def _decode_blocks(self, channels=1, depth=8):
decoded_data = bytearray() decoded_data = bytearray()
if depth == 32: # HACK: 32-bit grayscale uses signed int
maxval = 2**31 - 1 # HACK: 32-bit grayscale uses signed int maxval = 2 ** (31 if depth == 32 else depth) - 1
else:
maxval = 2**depth - 1 # FIXME: should be passed by _open
max_len = 10 max_len = 10
bytes_per_sample = depth // 8 bytes_per_sample = depth // 8
total_tokens = self.size * channels total_tokens = self.size * channels