@ai-ecoverse/py-pillow 12.3.0-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +30 -0
- package/README.md +4 -0
- package/lib/python3.14/site-packages/PIL/AvifImagePlugin.py +295 -0
- package/lib/python3.14/site-packages/PIL/BdfFontFile.py +124 -0
- package/lib/python3.14/site-packages/PIL/BlpImagePlugin.py +498 -0
- package/lib/python3.14/site-packages/PIL/BmpImagePlugin.py +514 -0
- package/lib/python3.14/site-packages/PIL/BufrStubImagePlugin.py +72 -0
- package/lib/python3.14/site-packages/PIL/ContainerIO.py +173 -0
- package/lib/python3.14/site-packages/PIL/CurImagePlugin.py +103 -0
- package/lib/python3.14/site-packages/PIL/DcxImagePlugin.py +84 -0
- package/lib/python3.14/site-packages/PIL/DdsImagePlugin.py +632 -0
- package/lib/python3.14/site-packages/PIL/EpsImagePlugin.py +484 -0
- package/lib/python3.14/site-packages/PIL/ExifTags.py +384 -0
- package/lib/python3.14/site-packages/PIL/FitsImagePlugin.py +153 -0
- package/lib/python3.14/site-packages/PIL/FliImagePlugin.py +184 -0
- package/lib/python3.14/site-packages/PIL/FontFile.py +160 -0
- package/lib/python3.14/site-packages/PIL/FpxImagePlugin.py +262 -0
- package/lib/python3.14/site-packages/PIL/FtexImagePlugin.py +115 -0
- package/lib/python3.14/site-packages/PIL/GbrImagePlugin.py +103 -0
- package/lib/python3.14/site-packages/PIL/GdImageFile.py +104 -0
- package/lib/python3.14/site-packages/PIL/GifImagePlugin.py +1223 -0
- package/lib/python3.14/site-packages/PIL/GimpGradientFile.py +154 -0
- package/lib/python3.14/site-packages/PIL/GimpPaletteFile.py +75 -0
- package/lib/python3.14/site-packages/PIL/GribStubImagePlugin.py +72 -0
- package/lib/python3.14/site-packages/PIL/Hdf5StubImagePlugin.py +72 -0
- package/lib/python3.14/site-packages/PIL/IcnsImagePlugin.py +401 -0
- package/lib/python3.14/site-packages/PIL/IcoImagePlugin.py +396 -0
- package/lib/python3.14/site-packages/PIL/ImImagePlugin.py +390 -0
- package/lib/python3.14/site-packages/PIL/Image.py +4385 -0
- package/lib/python3.14/site-packages/PIL/ImageChops.py +311 -0
- package/lib/python3.14/site-packages/PIL/ImageCms.py +1078 -0
- package/lib/python3.14/site-packages/PIL/ImageColor.py +320 -0
- package/lib/python3.14/site-packages/PIL/ImageDraw.py +1002 -0
- package/lib/python3.14/site-packages/PIL/ImageDraw2.py +244 -0
- package/lib/python3.14/site-packages/PIL/ImageEnhance.py +113 -0
- package/lib/python3.14/site-packages/PIL/ImageFile.py +938 -0
- package/lib/python3.14/site-packages/PIL/ImageFilter.py +617 -0
- package/lib/python3.14/site-packages/PIL/ImageFont.py +1330 -0
- package/lib/python3.14/site-packages/PIL/ImageGrab.py +234 -0
- package/lib/python3.14/site-packages/PIL/ImageMath.py +314 -0
- package/lib/python3.14/site-packages/PIL/ImageMode.py +85 -0
- package/lib/python3.14/site-packages/PIL/ImageMorph.py +317 -0
- package/lib/python3.14/site-packages/PIL/ImageOps.py +749 -0
- package/lib/python3.14/site-packages/PIL/ImagePalette.py +290 -0
- package/lib/python3.14/site-packages/PIL/ImagePath.py +20 -0
- package/lib/python3.14/site-packages/PIL/ImageQt.py +219 -0
- package/lib/python3.14/site-packages/PIL/ImageSequence.py +88 -0
- package/lib/python3.14/site-packages/PIL/ImageShow.py +367 -0
- package/lib/python3.14/site-packages/PIL/ImageStat.py +167 -0
- package/lib/python3.14/site-packages/PIL/ImageText.py +509 -0
- package/lib/python3.14/site-packages/PIL/ImageTk.py +266 -0
- package/lib/python3.14/site-packages/PIL/ImageTransform.py +136 -0
- package/lib/python3.14/site-packages/PIL/ImageWin.py +247 -0
- package/lib/python3.14/site-packages/PIL/ImtImagePlugin.py +103 -0
- package/lib/python3.14/site-packages/PIL/IptcImagePlugin.py +226 -0
- package/lib/python3.14/site-packages/PIL/Jpeg2KImagePlugin.py +466 -0
- package/lib/python3.14/site-packages/PIL/JpegImagePlugin.py +889 -0
- package/lib/python3.14/site-packages/PIL/JpegPresets.py +242 -0
- package/lib/python3.14/site-packages/PIL/McIdasImagePlugin.py +78 -0
- package/lib/python3.14/site-packages/PIL/MicImagePlugin.py +103 -0
- package/lib/python3.14/site-packages/PIL/MpegImagePlugin.py +84 -0
- package/lib/python3.14/site-packages/PIL/MpoImagePlugin.py +203 -0
- package/lib/python3.14/site-packages/PIL/MspImagePlugin.py +200 -0
- package/lib/python3.14/site-packages/PIL/PSDraw.py +238 -0
- package/lib/python3.14/site-packages/PIL/PaletteFile.py +54 -0
- package/lib/python3.14/site-packages/PIL/PalmImagePlugin.py +217 -0
- package/lib/python3.14/site-packages/PIL/PcdImagePlugin.py +68 -0
- package/lib/python3.14/site-packages/PIL/PcfFontFile.py +256 -0
- package/lib/python3.14/site-packages/PIL/PcxImagePlugin.py +232 -0
- package/lib/python3.14/site-packages/PIL/PdfImagePlugin.py +315 -0
- package/lib/python3.14/site-packages/PIL/PdfParser.py +1092 -0
- package/lib/python3.14/site-packages/PIL/PixarImagePlugin.py +72 -0
- package/lib/python3.14/site-packages/PIL/PngImagePlugin.py +1578 -0
- package/lib/python3.14/site-packages/PIL/PpmImagePlugin.py +375 -0
- package/lib/python3.14/site-packages/PIL/PsdImagePlugin.py +339 -0
- package/lib/python3.14/site-packages/PIL/QoiImagePlugin.py +235 -0
- package/lib/python3.14/site-packages/PIL/SgiImagePlugin.py +231 -0
- package/lib/python3.14/site-packages/PIL/SpiderImagePlugin.py +332 -0
- package/lib/python3.14/site-packages/PIL/SunImagePlugin.py +145 -0
- package/lib/python3.14/site-packages/PIL/TarIO.py +61 -0
- package/lib/python3.14/site-packages/PIL/TgaImagePlugin.py +284 -0
- package/lib/python3.14/site-packages/PIL/TiffImagePlugin.py +2354 -0
- package/lib/python3.14/site-packages/PIL/TiffTags.py +566 -0
- package/lib/python3.14/site-packages/PIL/WalImageFile.py +129 -0
- package/lib/python3.14/site-packages/PIL/WebPImagePlugin.py +322 -0
- package/lib/python3.14/site-packages/PIL/WmfImagePlugin.py +183 -0
- package/lib/python3.14/site-packages/PIL/XVThumbImagePlugin.py +83 -0
- package/lib/python3.14/site-packages/PIL/XbmImagePlugin.py +98 -0
- package/lib/python3.14/site-packages/PIL/XpmImagePlugin.py +157 -0
- package/lib/python3.14/site-packages/PIL/__init__.py +87 -0
- package/lib/python3.14/site-packages/PIL/__main__.py +7 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/AvifImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/BdfFontFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/BlpImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/BmpImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/BufrStubImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ContainerIO.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/CurImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/DcxImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/DdsImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/EpsImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ExifTags.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FitsImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FliImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FontFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FpxImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/FtexImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GbrImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GdImageFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GifImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GimpGradientFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GimpPaletteFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/GribStubImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/Hdf5StubImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/IcnsImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/IcoImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/Image.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageChops.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageCms.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageColor.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageDraw.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageDraw2.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageEnhance.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageFilter.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageFont.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageGrab.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageMath.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageMode.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageMorph.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageOps.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImagePalette.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImagePath.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageQt.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageSequence.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageShow.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageStat.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageText.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageTk.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageTransform.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImageWin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/ImtImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/IptcImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/Jpeg2KImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/JpegImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/JpegPresets.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/McIdasImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/MicImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/MpegImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/MpoImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/MspImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PSDraw.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PaletteFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PalmImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PcdImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PcfFontFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PcxImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PdfImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PdfParser.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PixarImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PngImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PpmImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/PsdImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/QoiImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/SgiImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/SpiderImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/SunImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/TarIO.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/TgaImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/TiffTags.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/WalImageFile.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/WebPImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/WmfImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/XVThumbImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/XbmImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/XpmImagePlugin.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/__init__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/__main__.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_binary.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_deprecate.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_tkinter_finder.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_typing.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_util.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/_version.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/features.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/__pycache__/report.cpython-314.pyc +0 -0
- package/lib/python3.14/site-packages/PIL/_avif.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/_binary.py +113 -0
- package/lib/python3.14/site-packages/PIL/_deprecate.py +72 -0
- package/lib/python3.14/site-packages/PIL/_imaging.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imaging.pyi +31 -0
- package/lib/python3.14/site-packages/PIL/_imagingcms.pyi +143 -0
- package/lib/python3.14/site-packages/PIL/_imagingft.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imagingft.pyi +70 -0
- package/lib/python3.14/site-packages/PIL/_imagingmath.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imagingmath.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/_imagingmorph.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imagingmorph.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/_imagingtk.cpython-314-wasm32-wasix.so +0 -0
- package/lib/python3.14/site-packages/PIL/_imagingtk.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/_tkinter_finder.py +20 -0
- package/lib/python3.14/site-packages/PIL/_typing.py +45 -0
- package/lib/python3.14/site-packages/PIL/_util.py +29 -0
- package/lib/python3.14/site-packages/PIL/_version.py +4 -0
- package/lib/python3.14/site-packages/PIL/_webp.pyi +3 -0
- package/lib/python3.14/site-packages/PIL/features.py +343 -0
- package/lib/python3.14/site-packages/PIL/py.typed +0 -0
- package/lib/python3.14/site-packages/PIL/report.py +5 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/METADATA +180 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/RECORD +117 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/WHEEL +5 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/licenses/LICENSE +30 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/top_level.txt +1 -0
- package/lib/python3.14/site-packages/pillow-12.3.0.dist-info/zip-safe +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
#
|
|
2
|
+
# The Python Imaging Library.
|
|
3
|
+
#
|
|
4
|
+
# QOI support for PIL
|
|
5
|
+
#
|
|
6
|
+
# See the README file for information on usage and redistribution.
|
|
7
|
+
#
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from typing import IO
|
|
12
|
+
|
|
13
|
+
from . import Image, ImageFile
|
|
14
|
+
from ._binary import i32be as i32
|
|
15
|
+
from ._binary import o8
|
|
16
|
+
from ._binary import o32be as o32
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _accept(prefix: bytes) -> bool:
|
|
20
|
+
return prefix.startswith(b"qoif")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class QoiImageFile(ImageFile.ImageFile):
|
|
24
|
+
format = "QOI"
|
|
25
|
+
format_description = "Quite OK Image"
|
|
26
|
+
|
|
27
|
+
def _open(self) -> None:
|
|
28
|
+
assert self.fp is not None
|
|
29
|
+
if not _accept(self.fp.read(4)):
|
|
30
|
+
msg = "not a QOI file"
|
|
31
|
+
raise SyntaxError(msg)
|
|
32
|
+
|
|
33
|
+
self._size = i32(self.fp.read(4)), i32(self.fp.read(4))
|
|
34
|
+
|
|
35
|
+
channels = self.fp.read(1)[0]
|
|
36
|
+
self._mode = "RGB" if channels == 3 else "RGBA"
|
|
37
|
+
|
|
38
|
+
self.fp.seek(1, os.SEEK_CUR) # colorspace
|
|
39
|
+
self.tile = [ImageFile._Tile("qoi", (0, 0) + self._size, self.fp.tell())]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class QoiDecoder(ImageFile.PyDecoder):
|
|
43
|
+
_pulls_fd = True
|
|
44
|
+
_previous_pixel: bytes | bytearray | None = None
|
|
45
|
+
_previously_seen_pixels: dict[int, bytes | bytearray] = {}
|
|
46
|
+
|
|
47
|
+
def _add_to_previous_pixels(self, value: bytes | bytearray) -> None:
|
|
48
|
+
self._previous_pixel = value
|
|
49
|
+
|
|
50
|
+
r, g, b, a = value
|
|
51
|
+
hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64
|
|
52
|
+
self._previously_seen_pixels[hash_value] = value
|
|
53
|
+
|
|
54
|
+
def decode(self, buffer: Image.DecoderInput) -> tuple[int, int]:
|
|
55
|
+
assert self.fd is not None
|
|
56
|
+
|
|
57
|
+
self._previously_seen_pixels = {}
|
|
58
|
+
self._previous_pixel = bytearray((0, 0, 0, 255))
|
|
59
|
+
|
|
60
|
+
data = bytearray()
|
|
61
|
+
bands = Image.getmodebands(self.mode)
|
|
62
|
+
dest_length = self.state.xsize * self.state.ysize * bands
|
|
63
|
+
while len(data) < dest_length:
|
|
64
|
+
byte = self.fd.read(1)[0]
|
|
65
|
+
value: bytes | bytearray
|
|
66
|
+
if byte == 0b11111110 and self._previous_pixel: # QOI_OP_RGB
|
|
67
|
+
value = bytearray(self.fd.read(3)) + self._previous_pixel[3:]
|
|
68
|
+
elif byte == 0b11111111: # QOI_OP_RGBA
|
|
69
|
+
value = self.fd.read(4)
|
|
70
|
+
else:
|
|
71
|
+
op = byte >> 6
|
|
72
|
+
if op == 0: # QOI_OP_INDEX
|
|
73
|
+
op_index = byte & 0b00111111
|
|
74
|
+
value = self._previously_seen_pixels.get(
|
|
75
|
+
op_index, bytearray((0, 0, 0, 0))
|
|
76
|
+
)
|
|
77
|
+
elif op == 1 and self._previous_pixel: # QOI_OP_DIFF
|
|
78
|
+
value = bytearray(
|
|
79
|
+
(
|
|
80
|
+
(self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2)
|
|
81
|
+
% 256,
|
|
82
|
+
(self._previous_pixel[1] + ((byte & 0b00001100) >> 2) - 2)
|
|
83
|
+
% 256,
|
|
84
|
+
(self._previous_pixel[2] + (byte & 0b00000011) - 2) % 256,
|
|
85
|
+
self._previous_pixel[3],
|
|
86
|
+
)
|
|
87
|
+
)
|
|
88
|
+
elif op == 2 and self._previous_pixel: # QOI_OP_LUMA
|
|
89
|
+
second_byte = self.fd.read(1)[0]
|
|
90
|
+
diff_green = (byte & 0b00111111) - 32
|
|
91
|
+
diff_red = ((second_byte & 0b11110000) >> 4) - 8
|
|
92
|
+
diff_blue = (second_byte & 0b00001111) - 8
|
|
93
|
+
|
|
94
|
+
value = bytearray(
|
|
95
|
+
tuple(
|
|
96
|
+
(self._previous_pixel[i] + diff_green + diff) % 256
|
|
97
|
+
for i, diff in enumerate((diff_red, 0, diff_blue))
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
value += self._previous_pixel[3:]
|
|
101
|
+
elif op == 3 and self._previous_pixel: # QOI_OP_RUN
|
|
102
|
+
run_length = (byte & 0b00111111) + 1
|
|
103
|
+
value = self._previous_pixel
|
|
104
|
+
if bands == 3:
|
|
105
|
+
value = value[:3]
|
|
106
|
+
data += value * run_length
|
|
107
|
+
continue
|
|
108
|
+
self._add_to_previous_pixels(value)
|
|
109
|
+
|
|
110
|
+
if bands == 3:
|
|
111
|
+
value = value[:3]
|
|
112
|
+
data += value
|
|
113
|
+
self.set_as_raw(data)
|
|
114
|
+
return -1, 0
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
|
118
|
+
if im.mode == "RGB":
|
|
119
|
+
channels = 3
|
|
120
|
+
elif im.mode == "RGBA":
|
|
121
|
+
channels = 4
|
|
122
|
+
else:
|
|
123
|
+
msg = "Unsupported QOI image mode"
|
|
124
|
+
raise ValueError(msg)
|
|
125
|
+
|
|
126
|
+
colorspace = 0 if im.encoderinfo.get("colorspace") == "sRGB" else 1
|
|
127
|
+
|
|
128
|
+
fp.write(b"qoif")
|
|
129
|
+
fp.write(o32(im.size[0]))
|
|
130
|
+
fp.write(o32(im.size[1]))
|
|
131
|
+
fp.write(o8(channels))
|
|
132
|
+
fp.write(o8(colorspace))
|
|
133
|
+
|
|
134
|
+
ImageFile._save(im, fp, [ImageFile._Tile("qoi", (0, 0) + im.size)])
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class QoiEncoder(ImageFile.PyEncoder):
|
|
138
|
+
_pushes_fd = True
|
|
139
|
+
_previous_pixel: tuple[int, int, int, int] | None = None
|
|
140
|
+
_previously_seen_pixels: dict[int, tuple[int, int, int, int]] = {}
|
|
141
|
+
_run = 0
|
|
142
|
+
|
|
143
|
+
def _write_run(self) -> bytes:
|
|
144
|
+
data = o8(0b11000000 | (self._run - 1)) # QOI_OP_RUN
|
|
145
|
+
self._run = 0
|
|
146
|
+
return data
|
|
147
|
+
|
|
148
|
+
def _delta(self, left: int, right: int) -> int:
|
|
149
|
+
result = (left - right) & 255
|
|
150
|
+
if result >= 128:
|
|
151
|
+
result -= 256
|
|
152
|
+
return result
|
|
153
|
+
|
|
154
|
+
def encode(self, bufsize: int) -> tuple[int, int, bytes]:
|
|
155
|
+
assert self.im is not None
|
|
156
|
+
|
|
157
|
+
self._previously_seen_pixels = {0: (0, 0, 0, 0)}
|
|
158
|
+
self._previous_pixel = (0, 0, 0, 255)
|
|
159
|
+
|
|
160
|
+
data = bytearray()
|
|
161
|
+
w, h = self.im.size
|
|
162
|
+
bands = Image.getmodebands(self.mode)
|
|
163
|
+
|
|
164
|
+
for y in range(h):
|
|
165
|
+
for x in range(w):
|
|
166
|
+
pixel = self.im.getpixel((x, y))
|
|
167
|
+
if bands == 3:
|
|
168
|
+
pixel = (*pixel, 255)
|
|
169
|
+
|
|
170
|
+
if pixel == self._previous_pixel:
|
|
171
|
+
self._run += 1
|
|
172
|
+
if self._run == 62:
|
|
173
|
+
data += self._write_run()
|
|
174
|
+
else:
|
|
175
|
+
if self._run:
|
|
176
|
+
data += self._write_run()
|
|
177
|
+
|
|
178
|
+
r, g, b, a = pixel
|
|
179
|
+
hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64
|
|
180
|
+
if self._previously_seen_pixels.get(hash_value) == pixel:
|
|
181
|
+
data += o8(hash_value) # QOI_OP_INDEX
|
|
182
|
+
elif self._previous_pixel:
|
|
183
|
+
self._previously_seen_pixels[hash_value] = pixel
|
|
184
|
+
|
|
185
|
+
prev_r, prev_g, prev_b, prev_a = self._previous_pixel
|
|
186
|
+
if prev_a == a:
|
|
187
|
+
delta_r = self._delta(r, prev_r)
|
|
188
|
+
delta_g = self._delta(g, prev_g)
|
|
189
|
+
delta_b = self._delta(b, prev_b)
|
|
190
|
+
|
|
191
|
+
if (
|
|
192
|
+
-2 <= delta_r < 2
|
|
193
|
+
and -2 <= delta_g < 2
|
|
194
|
+
and -2 <= delta_b < 2
|
|
195
|
+
):
|
|
196
|
+
data += o8(
|
|
197
|
+
0b01000000
|
|
198
|
+
| (delta_r + 2) << 4
|
|
199
|
+
| (delta_g + 2) << 2
|
|
200
|
+
| (delta_b + 2)
|
|
201
|
+
) # QOI_OP_DIFF
|
|
202
|
+
else:
|
|
203
|
+
delta_gr = self._delta(delta_r, delta_g)
|
|
204
|
+
delta_gb = self._delta(delta_b, delta_g)
|
|
205
|
+
if (
|
|
206
|
+
-8 <= delta_gr < 8
|
|
207
|
+
and -32 <= delta_g < 32
|
|
208
|
+
and -8 <= delta_gb < 8
|
|
209
|
+
):
|
|
210
|
+
data += o8(
|
|
211
|
+
0b10000000 | (delta_g + 32)
|
|
212
|
+
) # QOI_OP_LUMA
|
|
213
|
+
data += o8((delta_gr + 8) << 4 | (delta_gb + 8))
|
|
214
|
+
else:
|
|
215
|
+
data += o8(0b11111110) # QOI_OP_RGB
|
|
216
|
+
data += bytes(pixel[:3])
|
|
217
|
+
else:
|
|
218
|
+
data += o8(0b11111111) # QOI_OP_RGBA
|
|
219
|
+
data += bytes(pixel)
|
|
220
|
+
|
|
221
|
+
self._previous_pixel = pixel
|
|
222
|
+
|
|
223
|
+
if self._run:
|
|
224
|
+
data += self._write_run()
|
|
225
|
+
data += bytes((0, 0, 0, 0, 0, 0, 0, 1)) # padding
|
|
226
|
+
|
|
227
|
+
return len(data), 0, bytes(data)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
Image.register_open(QoiImageFile.format, QoiImageFile, _accept)
|
|
231
|
+
Image.register_decoder("qoi", QoiDecoder)
|
|
232
|
+
Image.register_extension(QoiImageFile.format, ".qoi")
|
|
233
|
+
|
|
234
|
+
Image.register_save(QoiImageFile.format, _save)
|
|
235
|
+
Image.register_encoder("qoi", QoiEncoder)
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#
|
|
2
|
+
# The Python Imaging Library.
|
|
3
|
+
# $Id$
|
|
4
|
+
#
|
|
5
|
+
# SGI image file handling
|
|
6
|
+
#
|
|
7
|
+
# See "The SGI Image File Format (Draft version 0.97)", Paul Haeberli.
|
|
8
|
+
# <ftp://ftp.sgi.com/graphics/SGIIMAGESPEC>
|
|
9
|
+
#
|
|
10
|
+
#
|
|
11
|
+
# History:
|
|
12
|
+
# 2017-22-07 mb Add RLE decompression
|
|
13
|
+
# 2016-16-10 mb Add save method without compression
|
|
14
|
+
# 1995-09-10 fl Created
|
|
15
|
+
#
|
|
16
|
+
# Copyright (c) 2016 by Mickael Bonfill.
|
|
17
|
+
# Copyright (c) 2008 by Karsten Hiddemann.
|
|
18
|
+
# Copyright (c) 1997 by Secret Labs AB.
|
|
19
|
+
# Copyright (c) 1995 by Fredrik Lundh.
|
|
20
|
+
#
|
|
21
|
+
# See the README file for information on usage and redistribution.
|
|
22
|
+
#
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
import struct
|
|
27
|
+
from typing import IO
|
|
28
|
+
|
|
29
|
+
from . import Image, ImageFile
|
|
30
|
+
from ._binary import i16be as i16
|
|
31
|
+
from ._binary import o8
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _accept(prefix: bytes) -> bool:
|
|
35
|
+
return len(prefix) >= 2 and i16(prefix) == 474
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
MODES = {
|
|
39
|
+
(1, 1, 1): "L",
|
|
40
|
+
(1, 2, 1): "L",
|
|
41
|
+
(2, 1, 1): "L;16B",
|
|
42
|
+
(2, 2, 1): "L;16B",
|
|
43
|
+
(1, 3, 3): "RGB",
|
|
44
|
+
(2, 3, 3): "RGB;16B",
|
|
45
|
+
(1, 3, 4): "RGBA",
|
|
46
|
+
(2, 3, 4): "RGBA;16B",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# Image plugin for SGI images.
|
|
52
|
+
class SgiImageFile(ImageFile.ImageFile):
|
|
53
|
+
format = "SGI"
|
|
54
|
+
format_description = "SGI Image File Format"
|
|
55
|
+
|
|
56
|
+
def _open(self) -> None:
|
|
57
|
+
# HEAD
|
|
58
|
+
assert self.fp is not None
|
|
59
|
+
|
|
60
|
+
headlen = 512
|
|
61
|
+
s = self.fp.read(headlen)
|
|
62
|
+
|
|
63
|
+
if not _accept(s):
|
|
64
|
+
msg = "Not an SGI image file"
|
|
65
|
+
raise ValueError(msg)
|
|
66
|
+
|
|
67
|
+
# compression : verbatim or RLE
|
|
68
|
+
compression = s[2]
|
|
69
|
+
|
|
70
|
+
# bpc : 1 or 2 bytes (8bits or 16bits)
|
|
71
|
+
bpc = s[3]
|
|
72
|
+
|
|
73
|
+
# dimension : 1, 2 or 3 (depending on xsize, ysize and zsize)
|
|
74
|
+
dimension = i16(s, 4)
|
|
75
|
+
|
|
76
|
+
# xsize : width
|
|
77
|
+
xsize = i16(s, 6)
|
|
78
|
+
|
|
79
|
+
# ysize : height
|
|
80
|
+
ysize = i16(s, 8)
|
|
81
|
+
|
|
82
|
+
# zsize : channels count
|
|
83
|
+
zsize = i16(s, 10)
|
|
84
|
+
|
|
85
|
+
# determine mode from bits/zsize
|
|
86
|
+
try:
|
|
87
|
+
rawmode = MODES[(bpc, dimension, zsize)]
|
|
88
|
+
except KeyError:
|
|
89
|
+
msg = "Unsupported SGI image mode"
|
|
90
|
+
raise ValueError(msg)
|
|
91
|
+
|
|
92
|
+
self._size = xsize, ysize
|
|
93
|
+
self._mode = rawmode.split(";")[0]
|
|
94
|
+
if self.mode == "RGB":
|
|
95
|
+
self.custom_mimetype = "image/rgb"
|
|
96
|
+
|
|
97
|
+
# orientation -1 : scanlines begins at the bottom-left corner
|
|
98
|
+
orientation = -1
|
|
99
|
+
|
|
100
|
+
# decoder info
|
|
101
|
+
if compression == 0:
|
|
102
|
+
pagesize = xsize * ysize * bpc
|
|
103
|
+
if bpc == 2:
|
|
104
|
+
self.tile = [
|
|
105
|
+
ImageFile._Tile(
|
|
106
|
+
"SGI16",
|
|
107
|
+
(0, 0) + self.size,
|
|
108
|
+
headlen,
|
|
109
|
+
(self.mode, 0, orientation),
|
|
110
|
+
)
|
|
111
|
+
]
|
|
112
|
+
else:
|
|
113
|
+
self.tile = []
|
|
114
|
+
offset = headlen
|
|
115
|
+
for layer in self.mode:
|
|
116
|
+
self.tile.append(
|
|
117
|
+
ImageFile._Tile(
|
|
118
|
+
"raw", (0, 0) + self.size, offset, (layer, 0, orientation)
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
offset += pagesize
|
|
122
|
+
elif compression == 1:
|
|
123
|
+
self.tile = [
|
|
124
|
+
ImageFile._Tile(
|
|
125
|
+
"sgi_rle", (0, 0) + self.size, headlen, (rawmode, orientation, bpc)
|
|
126
|
+
)
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
|
|
131
|
+
if im.mode not in {"RGB", "RGBA", "L"}:
|
|
132
|
+
msg = "Unsupported SGI image mode"
|
|
133
|
+
raise ValueError(msg)
|
|
134
|
+
|
|
135
|
+
# Get the keyword arguments
|
|
136
|
+
info = im.encoderinfo
|
|
137
|
+
|
|
138
|
+
# Byte-per-pixel precision, 1 = 8bits per pixel
|
|
139
|
+
bpc = info.get("bpc", 1)
|
|
140
|
+
|
|
141
|
+
if bpc not in (1, 2):
|
|
142
|
+
msg = "Unsupported number of bytes per pixel"
|
|
143
|
+
raise ValueError(msg)
|
|
144
|
+
|
|
145
|
+
# Flip the image, since the origin of SGI file is the bottom-left corner
|
|
146
|
+
orientation = -1
|
|
147
|
+
# Define the file as SGI File Format
|
|
148
|
+
magic_number = 474
|
|
149
|
+
# Run-Length Encoding Compression - Unsupported at this time
|
|
150
|
+
rle = 0
|
|
151
|
+
|
|
152
|
+
# X Dimension = width / Y Dimension = height
|
|
153
|
+
x, y = im.size
|
|
154
|
+
# Z Dimension: Number of channels
|
|
155
|
+
z = len(im.mode)
|
|
156
|
+
# Number of dimensions (x,y,z)
|
|
157
|
+
if im.mode == "L":
|
|
158
|
+
dimension = 1 if y == 1 else 2
|
|
159
|
+
else:
|
|
160
|
+
dimension = 3
|
|
161
|
+
|
|
162
|
+
# Minimum Byte value
|
|
163
|
+
pinmin = 0
|
|
164
|
+
# Maximum Byte value (255 = 8bits per pixel)
|
|
165
|
+
pinmax = 255
|
|
166
|
+
# Image name (79 characters max, truncated below in write)
|
|
167
|
+
img_name = os.path.splitext(os.path.basename(filename))[0]
|
|
168
|
+
if isinstance(img_name, str):
|
|
169
|
+
img_name = img_name.encode("ascii", "ignore")
|
|
170
|
+
# Standard representation of pixel in the file
|
|
171
|
+
colormap = 0
|
|
172
|
+
fp.write(struct.pack(">h", magic_number))
|
|
173
|
+
fp.write(o8(rle))
|
|
174
|
+
fp.write(o8(bpc))
|
|
175
|
+
fp.write(struct.pack(">H", dimension))
|
|
176
|
+
fp.write(struct.pack(">H", x))
|
|
177
|
+
fp.write(struct.pack(">H", y))
|
|
178
|
+
fp.write(struct.pack(">H", z))
|
|
179
|
+
fp.write(struct.pack(">l", pinmin))
|
|
180
|
+
fp.write(struct.pack(">l", pinmax))
|
|
181
|
+
fp.write(struct.pack("4s", b"")) # dummy
|
|
182
|
+
fp.write(struct.pack("79s", img_name)) # truncates to 79 chars
|
|
183
|
+
fp.write(struct.pack("s", b"")) # force null byte after img_name
|
|
184
|
+
fp.write(struct.pack(">l", colormap))
|
|
185
|
+
fp.write(struct.pack("404s", b"")) # dummy
|
|
186
|
+
|
|
187
|
+
rawmode = "L"
|
|
188
|
+
if bpc == 2:
|
|
189
|
+
rawmode = "L;16B"
|
|
190
|
+
|
|
191
|
+
for channel in im.split():
|
|
192
|
+
fp.write(channel.tobytes("raw", rawmode, 0, orientation))
|
|
193
|
+
|
|
194
|
+
if hasattr(fp, "flush"):
|
|
195
|
+
fp.flush()
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class SGI16Decoder(ImageFile.PyDecoder):
|
|
199
|
+
_pulls_fd = True
|
|
200
|
+
|
|
201
|
+
def decode(self, buffer: Image.DecoderInput) -> tuple[int, int]:
|
|
202
|
+
assert self.fd is not None
|
|
203
|
+
assert self.im is not None
|
|
204
|
+
|
|
205
|
+
rawmode, stride, orientation = self.args
|
|
206
|
+
pagesize = self.state.xsize * self.state.ysize
|
|
207
|
+
zsize = len(self.mode)
|
|
208
|
+
self.fd.seek(512)
|
|
209
|
+
|
|
210
|
+
for band in range(zsize):
|
|
211
|
+
channel = Image.new("L", (self.state.xsize, self.state.ysize))
|
|
212
|
+
channel.frombytes(
|
|
213
|
+
self.fd.read(2 * pagesize), "raw", "L;16B", stride, orientation
|
|
214
|
+
)
|
|
215
|
+
self.im.putband(channel.im, band)
|
|
216
|
+
|
|
217
|
+
return -1, 0
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
#
|
|
221
|
+
# registry
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
Image.register_decoder("SGI16", SGI16Decoder)
|
|
225
|
+
Image.register_open(SgiImageFile.format, SgiImageFile, _accept)
|
|
226
|
+
Image.register_save(SgiImageFile.format, _save)
|
|
227
|
+
Image.register_mime(SgiImageFile.format, "image/sgi")
|
|
228
|
+
|
|
229
|
+
Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"])
|
|
230
|
+
|
|
231
|
+
# End of file
|