2023-12-21 14:13:31 +03:00
|
|
|
from __future__ import annotations
|
2024-01-20 14:23:03 +03:00
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
from pathlib import Path
|
|
|
|
|
2020-02-22 16:06:21 +03:00
|
|
|
import pytest
|
2020-08-07 13:28:33 +03:00
|
|
|
|
2016-07-28 05:29:24 +03:00
|
|
|
from PIL import Image, ImageFile, PcxImagePlugin
|
2012-10-16 00:26:38 +04:00
|
|
|
|
2020-03-02 17:02:19 +03:00
|
|
|
from .helper import assert_image_equal, hopper
|
2019-07-06 23:40:53 +03:00
|
|
|
|
2012-10-16 00:26:38 +04:00
|
|
|
|
2024-02-17 07:00:38 +03:00
|
|
|
def _roundtrip(tmp_path: Path, im: Image.Image) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
f = str(tmp_path / "temp.pcx")
|
|
|
|
im.save(f)
|
|
|
|
with Image.open(f) as im2:
|
|
|
|
assert im2.mode == im.mode
|
|
|
|
assert im2.size == im.size
|
|
|
|
assert im2.format == "PCX"
|
|
|
|
assert im2.get_format_mimetype() == "image/x-pcx"
|
|
|
|
assert_image_equal(im2, im)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_sanity(tmp_path: Path) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
for mode in ("1", "L", "P", "RGB"):
|
|
|
|
_roundtrip(tmp_path, hopper(mode))
|
|
|
|
|
2022-06-22 10:27:49 +03:00
|
|
|
# Test a palette with less than 256 colors
|
|
|
|
im = Image.new("P", (1, 1))
|
|
|
|
im.putpalette((255, 0, 0))
|
|
|
|
_roundtrip(tmp_path, im)
|
|
|
|
|
2020-03-02 17:02:19 +03:00
|
|
|
# Test an unsupported mode
|
|
|
|
f = str(tmp_path / "temp.pcx")
|
|
|
|
im = hopper("RGBA")
|
|
|
|
with pytest.raises(ValueError):
|
2014-06-10 13:10:47 +04:00
|
|
|
im.save(f)
|
2020-03-02 17:02:19 +03:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_invalid_file() -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
invalid_file = "Tests/images/flower.jpg"
|
|
|
|
|
|
|
|
with pytest.raises(SyntaxError):
|
|
|
|
PcxImagePlugin.PcxImageFile(invalid_file)
|
|
|
|
|
|
|
|
|
2022-10-03 08:57:42 +03:00
|
|
|
@pytest.mark.parametrize("mode", ("1", "L", "P", "RGB"))
|
2024-02-17 07:00:38 +03:00
|
|
|
def test_odd(tmp_path: Path, mode: str) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
# See issue #523, odd sized images should have a stride that's even.
|
|
|
|
# Not that ImageMagick or GIMP write PCX that way.
|
|
|
|
# We were not handling properly.
|
2022-10-03 08:57:42 +03:00
|
|
|
# larger, odd sized images are better here to ensure that
|
|
|
|
# we handle interrupted scan lines properly.
|
|
|
|
_roundtrip(tmp_path, hopper(mode).resize((511, 511)))
|
2020-03-02 17:02:19 +03:00
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_odd_read() -> None:
|
2021-01-21 11:29:11 +03:00
|
|
|
# Reading an image with an odd stride, making it malformed
|
|
|
|
with Image.open("Tests/images/odd_stride.pcx") as im:
|
|
|
|
im.load()
|
|
|
|
|
|
|
|
assert im.size == (371, 150)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_pil184() -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
# Check reading of files where xmin/xmax is not zero.
|
|
|
|
|
|
|
|
test_file = "Tests/images/pil184.pcx"
|
|
|
|
with Image.open(test_file) as im:
|
|
|
|
assert im.size == (447, 144)
|
|
|
|
assert im.tile[0][1] == (0, 0, 447, 144)
|
|
|
|
|
|
|
|
# Make sure all pixels are either 0 or 255.
|
|
|
|
assert im.histogram()[0] + im.histogram()[255] == 447 * 144
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_1px_width(tmp_path: Path) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
im = Image.new("L", (1, 256))
|
|
|
|
px = im.load()
|
2024-07-02 13:10:47 +03:00
|
|
|
assert px is not None
|
2020-03-02 17:02:19 +03:00
|
|
|
for y in range(256):
|
|
|
|
px[0, y] = y
|
|
|
|
_roundtrip(tmp_path, im)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_large_count(tmp_path: Path) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
im = Image.new("L", (256, 1))
|
|
|
|
px = im.load()
|
2024-07-02 13:10:47 +03:00
|
|
|
assert px is not None
|
2020-03-02 17:02:19 +03:00
|
|
|
for x in range(256):
|
|
|
|
px[x, 0] = x // 67 * 67
|
|
|
|
_roundtrip(tmp_path, im)
|
|
|
|
|
|
|
|
|
2024-02-17 07:00:38 +03:00
|
|
|
def _test_buffer_overflow(tmp_path: Path, im: Image.Image, size: int = 1024) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
_last = ImageFile.MAXBLOCK
|
|
|
|
ImageFile.MAXBLOCK = size
|
|
|
|
try:
|
|
|
|
_roundtrip(tmp_path, im)
|
|
|
|
finally:
|
|
|
|
ImageFile.MAXBLOCK = _last
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_break_in_count_overflow(tmp_path: Path) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
im = Image.new("L", (256, 5))
|
|
|
|
px = im.load()
|
2024-07-02 13:10:47 +03:00
|
|
|
assert px is not None
|
2020-03-02 17:02:19 +03:00
|
|
|
for y in range(4):
|
|
|
|
for x in range(256):
|
|
|
|
px[x, y] = x % 128
|
|
|
|
_test_buffer_overflow(tmp_path, im)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_break_one_in_loop(tmp_path: Path) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
im = Image.new("L", (256, 5))
|
|
|
|
px = im.load()
|
2024-07-02 13:10:47 +03:00
|
|
|
assert px is not None
|
2020-03-02 17:02:19 +03:00
|
|
|
for y in range(5):
|
|
|
|
for x in range(256):
|
|
|
|
px[x, y] = x % 128
|
|
|
|
_test_buffer_overflow(tmp_path, im)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_break_many_in_loop(tmp_path: Path) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
im = Image.new("L", (256, 5))
|
|
|
|
px = im.load()
|
2024-07-02 13:10:47 +03:00
|
|
|
assert px is not None
|
2020-03-02 17:02:19 +03:00
|
|
|
for y in range(4):
|
|
|
|
for x in range(256):
|
|
|
|
px[x, y] = x % 128
|
|
|
|
for x in range(8):
|
|
|
|
px[x, 4] = 16
|
|
|
|
_test_buffer_overflow(tmp_path, im)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_break_one_at_end(tmp_path: Path) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
im = Image.new("L", (256, 5))
|
|
|
|
px = im.load()
|
2024-07-02 13:10:47 +03:00
|
|
|
assert px is not None
|
2020-03-02 17:02:19 +03:00
|
|
|
for y in range(5):
|
|
|
|
for x in range(256):
|
|
|
|
px[x, y] = x % 128
|
|
|
|
px[0, 3] = 128 + 64
|
|
|
|
_test_buffer_overflow(tmp_path, im)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_break_many_at_end(tmp_path: Path) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
im = Image.new("L", (256, 5))
|
|
|
|
px = im.load()
|
2024-07-02 13:10:47 +03:00
|
|
|
assert px is not None
|
2020-03-02 17:02:19 +03:00
|
|
|
for y in range(5):
|
2016-07-28 05:29:24 +03:00
|
|
|
for x in range(256):
|
2020-03-02 17:02:19 +03:00
|
|
|
px[x, y] = x % 128
|
|
|
|
for x in range(4):
|
|
|
|
px[x * 2, 3] = 128 + 64
|
|
|
|
px[x + 256 - 4, 3] = 0
|
|
|
|
_test_buffer_overflow(tmp_path, im)
|
|
|
|
|
|
|
|
|
2024-01-31 12:12:58 +03:00
|
|
|
def test_break_padding(tmp_path: Path) -> None:
|
2020-03-02 17:02:19 +03:00
|
|
|
im = Image.new("L", (257, 5))
|
|
|
|
px = im.load()
|
2024-07-02 13:10:47 +03:00
|
|
|
assert px is not None
|
2020-03-02 17:02:19 +03:00
|
|
|
for y in range(5):
|
|
|
|
for x in range(257):
|
|
|
|
px[x, y] = x % 128
|
|
|
|
for x in range(5):
|
|
|
|
px[x, 3] = 0
|
|
|
|
_test_buffer_overflow(tmp_path, im)
|