prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Bug in QOI decoder leading to corrupted images
  ### What did you do?
  
  Loaded dice.qoi from the official test image set (https://qoiformat.org/qoi_test_images.zip).
  
  ### What did you expect to happen?
  
  Image is not corrupted.
  
  ### What actually happened?
  
  Image is corrupted.
  
  ### What are your OS, Python and Pillow versions?
  
  * OS: doesn't matter
  * Python: doesn't matter
  * Pillow: all versions with QOI support
  
  The problem is the faulty implementation of the QOI_OP_RGB operation here https://github.com/python-pillow/Pillow/blob/24606216e1e5931a8fe6f41acde9e7e67489905d/src/PIL/QoiImagePlugin.py#L58C10-L58C10
  The implementation sets the alpha channel to 255, however, the QOI specification says it should use the previous alpha value.
  Replacing that line with something like `value = self.fd.read(3) + o8(self._previous_pixel[3])` fixes the problem.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/PIL/QoiImagePlugin.py`
  Content:
  ```
  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 
  9 import os
  10 
  11 from . import Image, ImageFile
  12 from ._binary import i32be as i32
  13 from ._binary import o8
  14 
  15 
  16 def _accept(prefix):
  17     return prefix[:4] == b"qoif"
  18 
  19 
  20 class QoiImageFile(ImageFile.ImageFile):
  21     format = "QOI"
  22     format_description = "Quite OK Image"
  23 
  24     def _open(self):
  25         if not _accept(self.fp.read(4)):
  26             msg = "not a QOI file"
  27             raise SyntaxError(msg)
  28 
  29         self._size = tuple(i32(self.fp.read(4)) for i in range(2))
  30 
  31         channels = self.fp.read(1)[0]
  32         self._mode = "RGB" if channels == 3 else "RGBA"
  33 
  34         self.fp.seek(1, os.SEEK_CUR)  # colorspace
  35         self.tile = [("qoi", (0, 0) + self._size, self.fp.tell(), None)]
  36 
  37 
  38 class QoiDecoder(ImageFile.PyDecoder):
  39     _pulls_fd = True
  40 
  41     def _add_to_previous_pixels(self, value):
  42         self._previous_pixel = value
  43 
  44         r, g, b, a = value
  45         hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64
  46         self._previously_seen_pixels[hash_value] = value
  47 
  48     def decode(self, buffer):
  49         self._previously_seen_pixels = {}
  50         self._previous_pixel = None
  51         self._add_to_previous_pixels(b"".join(o8(i) for i in (0, 0, 0, 255)))
  52 
  53         data = bytearray()
  54         bands = Image.getmodebands(self.mode)
  55         while len(data) < self.state.xsize * self.state.ysize * bands:
  56             byte = self.fd.read(1)[0]
  57             if byte == 0b11111110:  # QOI_OP_RGB
  58                 value = self.fd.read(3) + o8(255)
  59             elif byte == 0b11111111:  # QOI_OP_RGBA
  60                 value = self.fd.read(4)
  61             else:
  62                 op = byte >> 6
  63                 if op == 0:  # QOI_OP_INDEX
  64                     op_index = byte & 0b00111111
  65                     value = self._previously_seen_pixels.get(op_index, (0, 0, 0, 0))
  66                 elif op == 1:  # QOI_OP_DIFF
  67                     value = (
  68                         (self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2)
  69                         % 256,
  70                         (self._previous_pixel[1] + ((byte & 0b00001100) >> 2) - 2)
  71                         % 256,
  72                         (self._previous_pixel[2] + (byte & 0b00000011) - 2) % 256,
  73                     )
  74                     value += (self._previous_pixel[3],)
  75                 elif op == 2:  # QOI_OP_LUMA
  76                     second_byte = self.fd.read(1)[0]
  77                     diff_green = (byte & 0b00111111) - 32
  78                     diff_red = ((second_byte & 0b11110000) >> 4) - 8
  79                     diff_blue = (second_byte & 0b00001111) - 8
  80 
  81                     value = tuple(
  82                         (self._previous_pixel[i] + diff_green + diff) % 256
  83                         for i, diff in enumerate((diff_red, 0, diff_blue))
  84                     )
  85                     value += (self._previous_pixel[3],)
  86                 elif op == 3:  # QOI_OP_RUN
  87                     run_length = (byte & 0b00111111) + 1
  88                     value = self._previous_pixel
  89                     if bands == 3:
  90                         value = value[:3]
  91                     data += value * run_length
  92                     continue
  93                 value = b"".join(o8(i) for i in value)
  94             self._add_to_previous_pixels(value)
  95 
  96             if bands == 3:
  97                 value = value[:3]
  98             data += value
  99         self.set_as_raw(bytes(data))
  100         return -1, 0
  101 
  102 
  103 Image.register_open(QoiImageFile.format, QoiImageFile, _accept)
  104 Image.register_decoder("qoi", QoiDecoder)
  105 Image.register_extension(QoiImageFile.format, ".qoi")
  106
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py
  --- a/src/PIL/QoiImagePlugin.py
  +++ b/src/PIL/QoiImagePlugin.py
  @@ -55,7 +55,7 @@
           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) + o8(255)
  +                value = self.fd.read(3) + self._previous_pixel[3:]
               elif byte == 0b11111111:  # QOI_OP_RGBA
                   value = self.fd.read(4)
               else:
  
verification_info: {"golden_diff": "diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py\n--- a/src/PIL/QoiImagePlugin.py\n+++ b/src/PIL/QoiImagePlugin.py\n@@ -55,7 +55,7 @@\n         while len(data) < self.state.xsize * self.state.ysize * bands:\n             byte = self.fd.read(1)[0]\n             if byte == 0b11111110:  # QOI_OP_RGB\n-                value = self.fd.read(3) + o8(255)\n+                value = self.fd.read(3) + self._previous_pixel[3:]\n             elif byte == 0b11111111:  # QOI_OP_RGBA\n                 value = self.fd.read(4)\n             else:\n", "issue": "Bug in QOI decoder leading to corrupted images\n### What did you do?\r\n\r\nLoaded dice.qoi from the official test image set (https://qoiformat.org/qoi_test_images.zip).\r\n\r\n### What did you expect to happen?\r\n\r\nImage is not corrupted.\r\n\r\n### What actually happened?\r\n\r\nImage is corrupted.\r\n\r\n### What are your OS, Python and Pillow versions?\r\n\r\n* OS: doesn't matter\r\n* Python: doesn't matter\r\n* Pillow: all versions with QOI support\r\n\r\nThe problem is the faulty implementation of the QOI_OP_RGB operation here https://github.com/python-pillow/Pillow/blob/24606216e1e5931a8fe6f41acde9e7e67489905d/src/PIL/QoiImagePlugin.py#L58C10-L58C10\r\nThe implementation sets the alpha channel to 255, however, the QOI specification says it should use the previous alpha value.\r\nReplacing that line with something like `value = self.fd.read(3) + o8(self._previous_pixel[3])` fixes the problem.\n", "before_files": [{"content": "#\n# The Python Imaging Library.\n#\n# QOI support for PIL\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport os\n\nfrom . import Image, ImageFile\nfrom ._binary import i32be as i32\nfrom ._binary import o8\n\n\ndef _accept(prefix):\n    return prefix[:4] == b\"qoif\"\n\n\nclass QoiImageFile(ImageFile.ImageFile):\n    format = \"QOI\"\n    format_description = \"Quite OK Image\"\n\n    def _open(self):\n        if not _accept(self.fp.read(4)):\n            msg = \"not a QOI file\"\n            raise SyntaxError(msg)\n\n        self._size = tuple(i32(self.fp.read(4)) for i in range(2))\n\n        channels = self.fp.read(1)[0]\n        self._mode = \"RGB\" if channels == 3 else \"RGBA\"\n\n        self.fp.seek(1, os.SEEK_CUR)  # colorspace\n        self.tile = [(\"qoi\", (0, 0) + self._size, self.fp.tell(), None)]\n\n\nclass QoiDecoder(ImageFile.PyDecoder):\n    _pulls_fd = True\n\n    def _add_to_previous_pixels(self, value):\n        self._previous_pixel = value\n\n        r, g, b, a = value\n        hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64\n        self._previously_seen_pixels[hash_value] = value\n\n    def decode(self, buffer):\n        self._previously_seen_pixels = {}\n        self._previous_pixel = None\n        self._add_to_previous_pixels(b\"\".join(o8(i) for i in (0, 0, 0, 255)))\n\n        data = bytearray()\n        bands = Image.getmodebands(self.mode)\n        while len(data) < self.state.xsize * self.state.ysize * bands:\n            byte = self.fd.read(1)[0]\n            if byte == 0b11111110:  # QOI_OP_RGB\n                value = self.fd.read(3) + o8(255)\n            elif byte == 0b11111111:  # QOI_OP_RGBA\n                value = self.fd.read(4)\n            else:\n                op = byte >> 6\n                if op == 0:  # QOI_OP_INDEX\n                    op_index = byte & 0b00111111\n                    value = self._previously_seen_pixels.get(op_index, (0, 0, 0, 0))\n                elif op == 1:  # QOI_OP_DIFF\n                    value = (\n                        (self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2)\n                        % 256,\n                        (self._previous_pixel[1] + ((byte & 0b00001100) >> 2) - 2)\n                        % 256,\n                        (self._previous_pixel[2] + (byte & 0b00000011) - 2) % 256,\n                    )\n                    value += (self._previous_pixel[3],)\n                elif op == 2:  # QOI_OP_LUMA\n                    second_byte = self.fd.read(1)[0]\n                    diff_green = (byte & 0b00111111) - 32\n                    diff_red = ((second_byte & 0b11110000) >> 4) - 8\n                    diff_blue = (second_byte & 0b00001111) - 8\n\n                    value = tuple(\n                        (self._previous_pixel[i] + diff_green + diff) % 256\n                        for i, diff in enumerate((diff_red, 0, diff_blue))\n                    )\n                    value += (self._previous_pixel[3],)\n                elif op == 3:  # QOI_OP_RUN\n                    run_length = (byte & 0b00111111) + 1\n                    value = self._previous_pixel\n                    if bands == 3:\n                        value = value[:3]\n                    data += value * run_length\n                    continue\n                value = b\"\".join(o8(i) for i in value)\n            self._add_to_previous_pixels(value)\n\n            if bands == 3:\n                value = value[:3]\n            data += value\n        self.set_as_raw(bytes(data))\n        return -1, 0\n\n\nImage.register_open(QoiImageFile.format, QoiImageFile, _accept)\nImage.register_decoder(\"qoi\", QoiDecoder)\nImage.register_extension(QoiImageFile.format, \".qoi\")\n", "path": "src/PIL/QoiImagePlugin.py"}], "after_files": [{"content": "#\n# The Python Imaging Library.\n#\n# QOI support for PIL\n#\n# See the README file for information on usage and redistribution.\n#\n\nimport os\n\nfrom . import Image, ImageFile\nfrom ._binary import i32be as i32\nfrom ._binary import o8\n\n\ndef _accept(prefix):\n    return prefix[:4] == b\"qoif\"\n\n\nclass QoiImageFile(ImageFile.ImageFile):\n    format = \"QOI\"\n    format_description = \"Quite OK Image\"\n\n    def _open(self):\n        if not _accept(self.fp.read(4)):\n            msg = \"not a QOI file\"\n            raise SyntaxError(msg)\n\n        self._size = tuple(i32(self.fp.read(4)) for i in range(2))\n\n        channels = self.fp.read(1)[0]\n        self._mode = \"RGB\" if channels == 3 else \"RGBA\"\n\n        self.fp.seek(1, os.SEEK_CUR)  # colorspace\n        self.tile = [(\"qoi\", (0, 0) + self._size, self.fp.tell(), None)]\n\n\nclass QoiDecoder(ImageFile.PyDecoder):\n    _pulls_fd = True\n\n    def _add_to_previous_pixels(self, value):\n        self._previous_pixel = value\n\n        r, g, b, a = value\n        hash_value = (r * 3 + g * 5 + b * 7 + a * 11) % 64\n        self._previously_seen_pixels[hash_value] = value\n\n    def decode(self, buffer):\n        self._previously_seen_pixels = {}\n        self._previous_pixel = None\n        self._add_to_previous_pixels(b\"\".join(o8(i) for i in (0, 0, 0, 255)))\n\n        data = bytearray()\n        bands = Image.getmodebands(self.mode)\n        while len(data) < self.state.xsize * self.state.ysize * bands:\n            byte = self.fd.read(1)[0]\n            if byte == 0b11111110:  # QOI_OP_RGB\n                value = self.fd.read(3) + self._previous_pixel[3:]\n            elif byte == 0b11111111:  # QOI_OP_RGBA\n                value = self.fd.read(4)\n            else:\n                op = byte >> 6\n                if op == 0:  # QOI_OP_INDEX\n                    op_index = byte & 0b00111111\n                    value = self._previously_seen_pixels.get(op_index, (0, 0, 0, 0))\n                elif op == 1:  # QOI_OP_DIFF\n                    value = (\n                        (self._previous_pixel[0] + ((byte & 0b00110000) >> 4) - 2)\n                        % 256,\n                        (self._previous_pixel[1] + ((byte & 0b00001100) >> 2) - 2)\n                        % 256,\n                        (self._previous_pixel[2] + (byte & 0b00000011) - 2) % 256,\n                    )\n                    value += (self._previous_pixel[3],)\n                elif op == 2:  # QOI_OP_LUMA\n                    second_byte = self.fd.read(1)[0]\n                    diff_green = (byte & 0b00111111) - 32\n                    diff_red = ((second_byte & 0b11110000) >> 4) - 8\n                    diff_blue = (second_byte & 0b00001111) - 8\n\n                    value = tuple(\n                        (self._previous_pixel[i] + diff_green + diff) % 256\n                        for i, diff in enumerate((diff_red, 0, diff_blue))\n                    )\n                    value += (self._previous_pixel[3],)\n                elif op == 3:  # QOI_OP_RUN\n                    run_length = (byte & 0b00111111) + 1\n                    value = self._previous_pixel\n                    if bands == 3:\n                        value = value[:3]\n                    data += value * run_length\n                    continue\n                value = b\"\".join(o8(i) for i in value)\n            self._add_to_previous_pixels(value)\n\n            if bands == 3:\n                value = value[:3]\n            data += value\n        self.set_as_raw(bytes(data))\n        return -1, 0\n\n\nImage.register_open(QoiImageFile.format, QoiImageFile, _accept)\nImage.register_decoder(\"qoi\", QoiDecoder)\nImage.register_extension(QoiImageFile.format, \".qoi\")\n", "path": "src/PIL/QoiImagePlugin.py"}]}