mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-28 17:10:02 +03:00
commit
2a1ed6ee3f
|
@ -6,11 +6,7 @@ import pytest
|
||||||
|
|
||||||
from PIL import Image, QoiImagePlugin
|
from PIL import Image, QoiImagePlugin
|
||||||
|
|
||||||
from .helper import (
|
from .helper import assert_image_equal_tofile, hopper
|
||||||
assert_image_equal,
|
|
||||||
assert_image_equal_tofile,
|
|
||||||
hopper,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_sanity() -> None:
|
def test_sanity() -> None:
|
||||||
|
@ -42,15 +38,13 @@ def test_save(tmp_path: Path) -> None:
|
||||||
im = hopper("RGB")
|
im = hopper("RGB")
|
||||||
im.save(f, qoi_colorspace="sRGB")
|
im.save(f, qoi_colorspace="sRGB")
|
||||||
|
|
||||||
with Image.open(f) as reloaded:
|
assert_image_equal_tofile(im, f)
|
||||||
assert_image_equal(im, reloaded)
|
|
||||||
|
|
||||||
for image in ["Tests/images/default_font.png", "Tests/images/pil123rgba.png"]:
|
for image in ["Tests/images/default_font.png", "Tests/images/pil123rgba.png"]:
|
||||||
with Image.open(image) as im:
|
with Image.open(image) as im:
|
||||||
im.save(f)
|
im.save(f)
|
||||||
|
|
||||||
with Image.open(f) as reloaded:
|
assert_image_equal_tofile(im, f)
|
||||||
assert_image_equal(im, reloaded)
|
|
||||||
|
|
||||||
im = hopper("P")
|
im = hopper("P")
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
|
|
|
@ -113,7 +113,7 @@ class QoiDecoder(ImageFile.PyDecoder):
|
||||||
return -1, 0
|
return -1, 0
|
||||||
|
|
||||||
|
|
||||||
def _save(im: Image.image, fp: IO[bytes], filename: str | bytes) -> None:
|
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||||
if im.mode == "RGB":
|
if im.mode == "RGB":
|
||||||
channels = 3
|
channels = 3
|
||||||
elif im.mode == "RGBA":
|
elif im.mode == "RGBA":
|
||||||
|
@ -133,18 +133,18 @@ def _save(im: Image.image, fp: IO[bytes], filename: str | bytes) -> None:
|
||||||
fp.write(o8(channels))
|
fp.write(o8(channels))
|
||||||
fp.write(o8(colorspace))
|
fp.write(o8(colorspace))
|
||||||
|
|
||||||
ImageFile._save(im, fp, [ImageFile._Tile("qoi", (0, 0) + im.size, 0, im.mode)])
|
ImageFile._save(im, fp, [ImageFile._Tile("qoi", (0, 0) + im.size)])
|
||||||
|
|
||||||
|
|
||||||
class QoiEncoder(ImageFile.PyEncoder):
|
class QoiEncoder(ImageFile.PyEncoder):
|
||||||
_pushes_fd = True
|
_pushes_fd = True
|
||||||
_previous_pixel: tuple[int] | None = None
|
_previous_pixel: tuple[int, int, int, int] | None = None
|
||||||
_previously_seen_pixels: dict[int, tuple[int]] = {}
|
_previously_seen_pixels: dict[int, tuple[int, int, int, int]] = {}
|
||||||
|
|
||||||
def _write_run(self, run):
|
def _write_run(self, run: int) -> bytes:
|
||||||
return o8(0xC0 | (run - 1)) # QOI_OP_RUN
|
return o8(0xC0 | (run - 1)) # QOI_OP_RUN
|
||||||
|
|
||||||
def _delta(self, left, right):
|
def _delta(self, left: int, right: int) -> int:
|
||||||
result = (left - right) & 0xFF
|
result = (left - right) & 0xFF
|
||||||
if result >= 0x80:
|
if result >= 0x80:
|
||||||
result -= 0x100
|
result -= 0x100
|
||||||
|
@ -181,7 +181,7 @@ class QoiEncoder(ImageFile.PyEncoder):
|
||||||
hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64
|
hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64
|
||||||
if self._previously_seen_pixels.get(hash_value) == pixel:
|
if self._previously_seen_pixels.get(hash_value) == pixel:
|
||||||
data += o8(hash_value) # QOI_OP_INDEX
|
data += o8(hash_value) # QOI_OP_INDEX
|
||||||
else:
|
elif self._previous_pixel:
|
||||||
self._previously_seen_pixels[hash_value] = pixel
|
self._previously_seen_pixels[hash_value] = pixel
|
||||||
|
|
||||||
pr, pg, pb, pa = self._previous_pixel
|
pr, pg, pb, pa = self._previous_pixel
|
||||||
|
|
Loading…
Reference in New Issue
Block a user