summaryrefslogtreecommitdiffstats
path: root/contrib/python/Pillow/py3/PIL/QoiImagePlugin.py
diff options
context:
space:
mode:
authorzverevgeny <[email protected]>2025-05-13 19:00:02 +0300
committerzverevgeny <[email protected]>2025-05-13 19:13:54 +0300
commit92e06374736aa28637dc0e706455b65c8268a5e6 (patch)
tree3df370c199ae25d308e542f02af20f43eab78f8a /contrib/python/Pillow/py3/PIL/QoiImagePlugin.py
parentdc63d5794da99c2ebe3f32914d0351d9707660b0 (diff)
Import matplotlib
commit_hash:d59c2338025ef8fd1e1f961ed9d8d5fd52d0bd96
Diffstat (limited to 'contrib/python/Pillow/py3/PIL/QoiImagePlugin.py')
-rw-r--r--contrib/python/Pillow/py3/PIL/QoiImagePlugin.py106
1 files changed, 106 insertions, 0 deletions
diff --git a/contrib/python/Pillow/py3/PIL/QoiImagePlugin.py b/contrib/python/Pillow/py3/PIL/QoiImagePlugin.py
new file mode 100644
index 00000000000..a7b9d4a9e3a
--- /dev/null
+++ b/contrib/python/Pillow/py3/PIL/QoiImagePlugin.py
@@ -0,0 +1,106 @@
+#
+# The Python Imaging Library.
+#
+# QOI support for PIL
+#
+# See the README file for information on usage and redistribution.
+#
+from __future__ import annotations
+
+import os
+
+from . import Image, ImageFile
+from ._binary import i32be as i32
+from ._binary import o8
+
+
+def _accept(prefix):
+ return prefix[:4] == b"qoif"
+
+
+class QoiImageFile(ImageFile.ImageFile):
+ format = "QOI"
+ format_description = "Quite OK Image"
+
+ def _open(self):
+ if not _accept(self.fp.read(4)):
+ msg = "not a QOI file"
+ raise SyntaxError(msg)
+
+ self._size = tuple(i32(self.fp.read(4)) for i in range(2))
+
+ channels = self.fp.read(1)[0]
+ self._mode = "RGB" if channels == 3 else "RGBA"
+
+ self.fp.seek(1, os.SEEK_CUR) # colorspace
+ self.tile = [("qoi", (0, 0) + self._size, self.fp.tell(), None)]
+
+
+class QoiDecoder(ImageFile.PyDecoder):
+ _pulls_fd = True
+
+ def _add_to_previous_pixels(self, value):
+ self._previous_pixel = value
+
+ r, g, b, a = value
+ hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64
+ self._previously_seen_pixels[hash_value] = value
+
+ def decode(self, buffer):
+ self._previously_seen_pixels = {}
+ self._previous_pixel = None
+ self._add_to_previous_pixels(b"".join(o8(i) for i in (0, 0, 0, 255)))
+
+ data = bytearray()
+ bands = Image.getmodebands(self.mode)
+ while len(data) < self.state.xsize * self.state.ysize * bands:
+ byte = self.fd.read(1)[0]
+ if byte == 0b11111110: # QOI_OP_RGB
+ value = self.fd.read(3) + self._previous_pixel[3:]
+ elif byte == 0b11111111: # QOI_OP_RGBA
+ value = self.fd.read(4)
+ else:
+ op = byte >> 6
+ if op == 0: # QOI_OP_INDEX
+ op_index = byte & 0b00111111
+ value = self._previously_seen_pixels.get(op_index, (0, 0, 0, 0))
+ elif op == 1: # QOI_OP_DIFF
+ value = (
+ (self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2)
+ % 256,
+ (self._previous_pixel[1] + ((byte & 0b00001100) >> 2) - 2)
+ % 256,
+ (self._previous_pixel[2] + (byte & 0b00000011) - 2) % 256,
+ )
+ value += (self._previous_pixel[3],)
+ elif op == 2: # QOI_OP_LUMA
+ second_byte = self.fd.read(1)[0]
+ diff_green = (byte & 0b00111111) - 32
+ diff_red = ((second_byte & 0b11110000) >> 4) - 8
+ diff_blue = (second_byte & 0b00001111) - 8
+
+ value = tuple(
+ (self._previous_pixel[i] + diff_green + diff) % 256
+ for i, diff in enumerate((diff_red, 0, diff_blue))
+ )
+ value += (self._previous_pixel[3],)
+ elif op == 3: # QOI_OP_RUN
+ run_length = (byte & 0b00111111) + 1
+ value = self._previous_pixel
+ if bands == 3:
+ value = value[:3]
+ data += value * run_length
+ continue
+ value = b"".join(o8(i) for i in value)
+ self._add_to_previous_pixels(value)
+
+ if bands == 3:
+ value = value[:3]
+ data += value
+ self.set_as_raw(bytes(data))
+ return -1, 0
+
+
+Image.register_open(QoiImageFile.format, QoiImageFile, _accept)
+Image.register_decoder("qoi", QoiDecoder)
+Image.register_extension(QoiImageFile.format, ".qoi")