From a9fccfada1754a049f0e5c1dbb3eb69bb678ad86 Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Sun, 18 Jul 2021 12:35:27 +1000 Subject: [PATCH] Added WalImageFile class --- Tests/images/hopper_wal.png | Bin 0 -> 6469 bytes Tests/test_file_wal.py | 18 ++++++--- src/PIL/WalImageFile.py | 71 ++++++++++++++++++------------------ 3 files changed, 48 insertions(+), 41 deletions(-) create mode 100644 Tests/images/hopper_wal.png diff --git a/Tests/images/hopper_wal.png b/Tests/images/hopper_wal.png new file mode 100644 index 0000000000000000000000000000000000000000..b6067c219c49324518e42cdcbfaded433964f688 GIT binary patch literal 6469 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 literal 0 HcmV?d00001 diff --git a/Tests/test_file_wal.py b/Tests/test_file_wal.py index 60be1d5bc..f25b42fe0 100644 --- a/Tests/test_file_wal.py +++ b/Tests/test_file_wal.py @@ -1,15 +1,21 @@ from PIL import WalImageFile +from .helper import assert_image_equal_tofile + def test_open(): # Arrange TEST_FILE = "Tests/images/hopper.wal" # Act - im = WalImageFile.open(TEST_FILE) + with WalImageFile.open(TEST_FILE) as im: - # Assert - assert im.format == "WAL" - assert im.format_description == "Quake2 Texture" - assert im.mode == "P" - assert im.size == (128, 128) + # Assert + assert im.format == "WAL" + assert im.format_description == "Quake2 Texture" + assert im.mode == "P" + assert im.size == (128, 128) + + assert isinstance(im, WalImageFile.WalImageFile) + + assert_image_equal_tofile(im, "Tests/images/hopper_wal.png") diff --git a/src/PIL/WalImageFile.py b/src/PIL/WalImageFile.py index b578d6981..1354ad32b 100644 --- a/src/PIL/WalImageFile.py +++ b/src/PIL/WalImageFile.py @@ -23,12 +23,44 @@ and has been tested with a few sample files found using google. To open a WAL file, use the :py:func:`PIL.WalImageFile.open()` function instead. """ -import builtins - -from . import Image +from . import Image, ImageFile from ._binary import i32le as i32 +class WalImageFile(ImageFile.ImageFile): + + format = "WAL" + format_description = "Quake2 Texture" + + def _open(self): + self.mode = "P" + + # read header fields + header = self.fp.read(32 + 24 + 32 + 12) + self._size = i32(header, 32), i32(header, 36) + Image._decompression_bomb_check(self.size) + + # load pixel data + offset = i32(header, 40) + self.fp.seek(offset) + + # strings are null-terminated + self.info["name"] = header[:32].split(b"\0", 1)[0] + next_name = header[56 : 56 + 32].split(b"\0", 1)[0] + if next_name: + self.info["next_name"] = next_name + + def load(self): + if self.im: + # Already loaded + return + + self.im = Image.core.new(self.mode, self.size) + self.frombytes(self.fp.read(self.size[0] * self.size[1])) + self.putpalette(quake2palette) + Image.Image.load(self) + + def open(filename): """ Load texture from a Quake2 WAL texture file. @@ -39,38 +71,7 @@ def open(filename): :param filename: WAL file name, or an opened file handle. :returns: An image instance. """ - # FIXME: modify to return a WalImageFile instance instead of - # plain Image object ? - - def imopen(fp): - # read header fields - header = fp.read(32 + 24 + 32 + 12) - size = i32(header, 32), i32(header, 36) - offset = i32(header, 40) - - # load pixel data - fp.seek(offset) - - Image._decompression_bomb_check(size) - im = Image.frombytes("P", size, fp.read(size[0] * size[1])) - im.putpalette(quake2palette) - - im.format = "WAL" - im.format_description = "Quake2 Texture" - - # strings are null-terminated - im.info["name"] = header[:32].split(b"\0", 1)[0] - next_name = header[56 : 56 + 32].split(b"\0", 1)[0] - if next_name: - im.info["next_name"] = next_name - - return im - - if hasattr(filename, "read"): - return imopen(filename) - else: - with builtins.open(filename, "rb") as fp: - return imopen(fp) + return WalImageFile(filename) quake2palette = (