{
    "file_diffs": [
        {
            "old_file_content": "import pytest\n\nfrom PIL import Image, QoiImagePlugin\n\nfrom .helper import assert_image_equal_tofile, assert_image_similar_tofile\n\n\ndef test_sanity():\n    with Image.open(\"Tests/images/hopper.qoi\") as im:\n        assert im.mode == \"RGB\"\n        assert im.size == (128, 128)\n        assert im.format == \"QOI\"\n\n        assert_image_equal_tofile(im, \"Tests/images/hopper.png\")\n\n    with Image.open(\"Tests/images/pil123rgba.qoi\") as im:\n        assert im.mode == \"RGBA\"\n        assert im.size == (162, 150)\n        assert im.format == \"QOI\"\n\n        assert_image_similar_tofile(im, \"Tests/images/pil123rgba.png\", 0.03)\n\n\ndef test_invalid_file():\n    invalid_file = \"Tests/images/flower.jpg\"\n\n    with pytest.raises(SyntaxError):\n        QoiImagePlugin.QoiImageFile(invalid_file)\n",
            "new_file_content": "import pytest\n\nfrom PIL import Image, QoiImagePlugin\n\nfrom .helper import assert_image_equal_tofile\n\n\ndef test_sanity():\n    with Image.open(\"Tests/images/hopper.qoi\") as im:\n        assert im.mode == \"RGB\"\n        assert im.size == (128, 128)\n        assert im.format == \"QOI\"\n\n        assert_image_equal_tofile(im, \"Tests/images/hopper.png\")\n\n    with Image.open(\"Tests/images/pil123rgba.qoi\") as im:\n        assert im.mode == \"RGBA\"\n        assert im.size == (162, 150)\n        assert im.format == \"QOI\"\n\n        assert_image_equal_tofile(im, \"Tests/images/pil123rgba.png\")\n\n\ndef test_invalid_file():\n    invalid_file = \"Tests/images/flower.jpg\"\n\n    with pytest.raises(SyntaxError):\n        QoiImagePlugin.QoiImageFile(invalid_file)\n",
            "header": {
                "file": {
                    "path": "Tests/test_file_qoi.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "f33eada61",
                "new_commit_hash": "0a835dcf6",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Tests/test_file_qoi.py"
            },
            "plus_file": {
                "path": "b/Tests/test_file_qoi.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 2,
                            "length": 7
                        },
                        "new_range": {
                            "start": 2,
                            "length": 7
                        },
                        "section": "import pytest"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "from PIL import Image, QoiImagePlugin",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "from .helper import assert_image_equal_tofile, assert_image_similar_tofile",
                                "type": "deleted"
                            },
                            {
                                "content": "from .helper import assert_image_equal_tofile",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "def test_sanity():",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Tests/test_file_qoi.py",
                            "type": "statement",
                            "name": "",
                            "content": "from .helper import assert_image_equal_tofile",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 5,
                            "end_lineno": 5,
                            "parent": null
                        }
                    ],
                    "deleted_entities": [
                        {
                            "file_name": "Tests/test_file_qoi.py",
                            "type": "statement",
                            "name": "",
                            "content": "from .helper import assert_image_equal_tofile, assert_image_similar_tofile",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 5,
                            "end_lineno": 5,
                            "parent": null
                        }
                    ]
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 18,
                            "length": 7
                        },
                        "new_range": {
                            "start": 18,
                            "length": 7
                        },
                        "section": "def test_sanity():"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        assert im.size == (162, 150)",
                                "type": "context"
                            },
                            {
                                "content": "        assert im.format == \"QOI\"",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        assert_image_similar_tofile(im, \"Tests/images/pil123rgba.png\", 0.03)",
                                "type": "deleted"
                            },
                            {
                                "content": "        assert_image_equal_tofile(im, \"Tests/images/pil123rgba.png\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "def test_invalid_file():",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "Tests/test_file_qoi.py",
                            "type": "function",
                            "name": "test_sanity",
                            "content": "def test_sanity():\n    with Image.open(\"Tests/images/hopper.qoi\") as im:\n        assert im.mode == \"RGB\"\n        assert im.size == (128, 128)\n        assert im.format == \"QOI\"\n\n        assert_image_equal_tofile(im, \"Tests/images/hopper.png\")\n\n    with Image.open(\"Tests/images/pil123rgba.qoi\") as im:\n        assert im.mode == \"RGBA\"\n        assert im.size == (162, 150)\n        assert im.format == \"QOI\"\n\n        assert_image_similar_tofile(im, \"Tests/images/pil123rgba.png\", 0.03)",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 8,
                            "end_lineno": 21,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_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",
            "new_file_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",
            "header": {
                "file": {
                    "path": "src/PIL/QoiImagePlugin.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "5c3407503",
                "new_commit_hash": "66344faac",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/src/PIL/QoiImagePlugin.py"
            },
            "plus_file": {
                "path": "b/src/PIL/QoiImagePlugin.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 55,
                            "length": 7
                        },
                        "new_range": {
                            "start": 55,
                            "length": 7
                        },
                        "section": "class QoiDecoder(ImageFile.PyDecoder):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        while len(data) < self.state.xsize * self.state.ysize * bands:",
                                "type": "context"
                            },
                            {
                                "content": "            byte = self.fd.read(1)[0]",
                                "type": "context"
                            },
                            {
                                "content": "            if byte == 0b11111110:  # QOI_OP_RGB",
                                "type": "context"
                            },
                            {
                                "content": "                value = self.fd.read(3) + o8(255)",
                                "type": "deleted"
                            },
                            {
                                "content": "                value = self.fd.read(3) + self._previous_pixel[3:]",
                                "type": "added"
                            },
                            {
                                "content": "            elif byte == 0b11111111:  # QOI_OP_RGBA",
                                "type": "context"
                            },
                            {
                                "content": "                value = self.fd.read(4)",
                                "type": "context"
                            },
                            {
                                "content": "            else:",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "src/PIL/QoiImagePlugin.py",
                            "type": "method",
                            "name": "QoiDecoder.decode",
                            "content": "class 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",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 48,
                            "end_lineno": 100,
                            "parent": {
                                "file_name": "src/PIL/QoiImagePlugin.py",
                                "type": "class",
                                "name": "QoiDecoder",
                                "content": "class 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",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 38,
                                "end_lineno": 100,
                                "parent": null
                            }
                        },
                        {
                            "file_name": "src/PIL/QoiImagePlugin.py",
                            "type": "class",
                            "name": "QoiDecoder",
                            "content": "class 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",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 38,
                            "end_lineno": 100,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "faa2375f810c8dc75224f3666831d5f491352280^",
    "new_commit_hash": "faa2375f810c8dc75224f3666831d5f491352280",
    "commit_message": "Merge pull request #7357 from radarhere/qoi",
    "commit_date": "2023-09-04T09:55:29-06:00",
    "metadata": {}
}
{
    "repo_name": "pillow",
    "new_commit_hash": "faa2375f810c8dc75224f3666831d5f491352280",
    "test_file_codes": [
        "import pytest\n\nfrom PIL import Image, QoiImagePlugin\n\nfrom .helper import assert_image_equal_tofile\n\n\ndef test_sanity():\n    with Image.open(\"Tests/images/hopper.qoi\") as im:\n        assert im.mode == \"RGB\"\n        assert im.size == (128, 128)\n        assert im.format == \"QOI\"\n\n        assert_image_equal_tofile(im, \"Tests/images/hopper.png\")\n\n    with Image.open(\"Tests/images/pil123rgba.qoi\") as im:\n        assert im.mode == \"RGBA\"\n        assert im.size == (162, 150)\n        assert im.format == \"QOI\"\n\n        assert_image_equal_tofile(im, \"Tests/images/pil123rgba.png\")\n\n\ndef test_invalid_file():\n    invalid_file = \"Tests/images/flower.jpg\"\n\n    with pytest.raises(SyntaxError):\n        QoiImagePlugin.QoiImageFile(invalid_file)\n"
    ],
    "test_file_names": [
        "test_1.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Pillow installation attempts...\nVerifying Pillow installation...\n✅ Pillow installation successful!\nSuccessfully installed Pillow\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 12 packages in 15ms\nInstalled 12 packages in 115ms\n + coverage==7.6.10\n + exceptiongroup==1.2.2\n + iniconfig==2.0.0\n + packaging==24.2\n + pluggy==1.5.0\n + pyqt5==5.15.11\n + pyqt5-qt5==5.15.16\n + pyqt5-sip==12.16.1\n + pytest==8.3.4\n + pytest-cov==6.0.0\n + setuptools==75.8.0\n + tomli==2.2.1\nResolved 1 package in 703ms\nPrepared 1 package in 36.23s\nInstalled 1 package in 1ms\n + pillow==10.1.0.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_faa2375f810c8dc75224f3666831d5f491352280)\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "\u001b[1m============================= test session starts ==============================\u001b[0m\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_faa2375f810c8dc75224f3666831d5f491352280\nconfigfile: setup.cfg\nplugins: cov-6.0.0\ncollected 2 items\n\nr2e_tests/test_1.py \u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m                                                   [100%]\u001b[0m\n\n==================================== PASSES ====================================\n\u001b[36m\u001b[1m=========================== short test summary info ============================\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_sanity\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_invalid_file\u001b[0m\n\u001b[32m============================== \u001b[32m\u001b[1m2 passed\u001b[0m\u001b[32m in 0.13s\u001b[0m\u001b[32m ===============================\u001b[0m\n",
    "new_commit_res_stderr": "",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "\u001b[1m============================= test session starts ==============================\u001b[0m\nplatform linux -- Python 3.9.21, pytest-8.3.4, pluggy-1.5.0\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_faa2375f810c8dc75224f3666831d5f491352280\nconfigfile: setup.cfg\nplugins: cov-6.0.0\ncollected 2 items\n\nr2e_tests/test_1.py \u001b[31mF\u001b[0m\u001b[32m.\u001b[0m\u001b[31m                                                   [100%]\u001b[0m\n\n=================================== FAILURES ===================================\n\u001b[31m\u001b[1m_________________________________ test_sanity __________________________________\u001b[0m\n\n    def test_sanity():\n        with Image.open(\"Tests/images/hopper.qoi\") as im:\n            assert im.mode == \"RGB\"\n            assert im.size == (128, 128)\n            assert im.format == \"QOI\"\n    \n            assert_image_equal_tofile(im, \"Tests/images/hopper.png\")\n    \n        with Image.open(\"Tests/images/pil123rgba.qoi\") as im:\n            assert im.mode == \"RGBA\"\n            assert im.size == (162, 150)\n            assert im.format == \"QOI\"\n    \n>           assert_image_equal_tofile(im, \"Tests/images/pil123rgba.png\")\n\n\u001b[1m\u001b[31mr2e_tests/test_1.py\u001b[0m:21: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\u001b[1m\u001b[31mr2e_tests/helper.py\u001b[0m:105: in assert_image_equal_tofile\n    assert_image_equal(a, img, msg)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\na = <PIL.QoiImagePlugin.QoiImageFile image mode=RGBA size=162x150 at 0x7453F26D0490>\nb = <PIL.PngImagePlugin.PngImageFile image mode=RGBA size=162x150 at 0x7453F26D0880>\nmsg = None\n\n    def assert_image_equal(a, b, msg=None):\n        assert a.mode == b.mode, msg or f\"got mode {repr(a.mode)}, expected {repr(b.mode)}\"\n        assert a.size == b.size, msg or f\"got size {repr(a.size)}, expected {repr(b.size)}\"\n        if a.tobytes() != b.tobytes():\n            if HAS_UPLOADER:\n                try:\n                    url = test_image_results.upload(a, b)\n                    logger.error(f\"Url for test images: {url}\")\n                except Exception:\n                    pass\n    \n>           assert False, msg or \"got different content\"\n\u001b[1m\u001b[31mE           AssertionError: got different content\u001b[0m\n\n\u001b[1m\u001b[31mr2e_tests/helper.py\u001b[0m:98: AssertionError\n==================================== PASSES ====================================\n\u001b[36m\u001b[1m=========================== short test summary info ============================\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_invalid_file\u001b[0m\n\u001b[31mFAILED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_sanity\u001b[0m - AssertionError: got different content\n\u001b[31m========================= \u001b[31m\u001b[1m1 failed\u001b[0m, \u001b[32m1 passed\u001b[0m\u001b[31m in 0.13s\u001b[0m\u001b[31m ==========================\u001b[0m\n",
    "old_commit_res_stderr": ""
}
Tests/test_file_qoi.py src/PIL/QoiImagePlugin.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Incorrect Alpha Channel Handling in QOI Decoder Causes Loss of Transparency

**Description:**

When decoding QOI images that include transparency, the alpha channel is incorrectly set to fully opaque, disregarding the original transparency information. This results in images losing their intended transparency and appearing fully opaque.

**Example of the Issue:**

In the `QoiDecoder` class, during the RGB operation, the alpha value is incorrectly appended as shown below:

```python
value = self.fd.read(3) + o8(255)  # Sets alpha to 255 instead of preserving it
```

**Expected Behavior:**

The decoder should preserve the alpha channel based on the previous pixel's alpha value to maintain the image's transparency. Instead of setting the alpha to a fixed value, it should use the existing alpha information to ensure accurate image rendering.

**Actual Behavior:**

The alpha channel is always set to 255, which forces the image to be fully opaque. This overrides any existing transparency data, leading to discrepancies between the decoded image and the original source.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### src/PIL/QoiImagePlugin.py
#
# The Python Imaging Library.
#
# QOI support for PIL
#
# See the README file for information on usage and redistribution.
#

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) + o8(255)
            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")

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** Incorrect Alpha Channel Handling in QOI Decoder Causes Loss of Transparency

**Description:**

When decoding QOI images that include transparency, the alpha channel is incorrectly set to fully opaque, disregarding the original transparency information. This results in images losing their intended transparency and appearing fully opaque.

**Example of the Issue:**

In the `QoiDecoder` class, during the RGB operation, the alpha value is incorrectly appended as shown below:

```python
value = self.fd.read(3) + o8(255)  # Sets alpha to 255 instead of preserving it
```

**Expected Behavior:**

The decoder should preserve the alpha channel based on the previous pixel's alpha value to maintain the image's transparency. Instead of setting the alpha to a fixed value, it should use the existing alpha information to ensure accurate image rendering.

**Actual Behavior:**

The alpha channel is always set to 255, which forces the image to be fully opaque. This overrides any existing transparency data, leading to discrepancies between the decoded image and the original source.

[/ISSUE]
{
    "\u001b[1mtest_sanity\u001b[0m": "PASSED",
    "\u001b[1mtest_invalid_file\u001b[0m": "PASSED"
}
diff --git a/src/PIL/QoiImagePlugin.py b/src/PIL/QoiImagePlugin.py
index 5c3407503..66344faac 100644
--- a/src/PIL/QoiImagePlugin.py
+++ b/src/PIL/QoiImagePlugin.py
@@ -55,7 +55,7 @@ class QoiDecoder(ImageFile.PyDecoder):
         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:
 

{"src/PIL/QoiImagePlugin.py": "#\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"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Incorrect Alpha Channel Handling in QOI Decoder Causes Loss of Transparency

**Description:**

When decoding QOI images that include transparency, the alpha channel is incorrectly set to fully opaque, disregarding the original transparency information. This results in images losing their intended transparency and appearing fully opaque.

**Example of the Issue:**

In the `QoiDecoder` class, during the RGB operation, the alpha value is incorrectly appended as shown below:

```python
value = self.fd.read(3) + o8(255)  # Sets alpha to 255 instead of preserving it
```

**Expected Behavior:**

The decoder should preserve the alpha channel based on the previous pixel's alpha value to maintain the image's transparency. Instead of setting the alpha to a fixed value, it should use the existing alpha information to ensure accurate image rendering.

**Actual Behavior:**

The alpha channel is always set to 255, which forces the image to be fully opaque. This overrides any existing transparency data, leading to discrepancies between the decoded image and the original source.

[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### src/PIL/QoiImagePlugin.py
#
# The Python Imaging Library.
#
# QOI support for PIL
#
# See the README file for information on usage and redistribution.
#

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) + o8(255)
            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")

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user