{
    "file_diffs": [
        {
            "old_file_content": "from __future__ import annotations\n\nimport io\nimport os\nfrom pathlib import Path\n\nimport pytest\n\nfrom PIL import IcoImagePlugin, Image, ImageDraw\n\nfrom .helper import assert_image_equal, assert_image_equal_tofile, hopper\n\nTEST_ICO_FILE = \"Tests/images/hopper.ico\"\n\n\ndef test_sanity() -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        im.load()\n    assert im.mode == \"RGBA\"\n    assert im.size == (16, 16)\n    assert im.format == \"ICO\"\n    assert im.get_format_mimetype() == \"image/x-icon\"\n\n\ndef test_load() -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        assert im.load()[0, 0] == (1, 1, 9, 255)\n\n\ndef test_mask() -> None:\n    with Image.open(\"Tests/images/hopper_mask.ico\") as im:\n        assert_image_equal_tofile(im, \"Tests/images/hopper_mask.png\")\n\n\ndef test_black_and_white() -> None:\n    with Image.open(\"Tests/images/black_and_white.ico\") as im:\n        assert im.mode == \"RGBA\"\n        assert im.size == (16, 16)\n\n\ndef test_palette(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n\n    im = Image.new(\"P\", (16, 16))\n    im.save(temp_file)\n\n    with Image.open(temp_file) as reloaded:\n        assert reloaded.mode == \"P\"\n        assert reloaded.palette is not None\n\n\ndef test_invalid_file() -> None:\n    with open(\"Tests/images/flower.jpg\", \"rb\") as fp:\n        with pytest.raises(SyntaxError):\n            IcoImagePlugin.IcoImageFile(fp)\n\n\ndef test_save_to_bytes() -> None:\n    output = io.BytesIO()\n    im = hopper()\n    im.save(output, \"ico\", sizes=[(32, 32), (64, 64)])\n\n    # The default image\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert reloaded.info[\"sizes\"] == {(32, 32), (64, 64)}\n\n        assert im.mode == reloaded.mode\n        assert (64, 64) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        assert_image_equal(\n            reloaded, hopper().resize((64, 64), Image.Resampling.LANCZOS)\n        )\n\n    # The other one\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        reloaded.size = (32, 32)\n\n        assert im.mode == reloaded.mode\n        assert (32, 32) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        assert_image_equal(\n            reloaded, hopper().resize((32, 32), Image.Resampling.LANCZOS)\n        )\n\n\ndef test_getpixel(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n\n    im = hopper()\n    im.save(temp_file, \"ico\", sizes=[(32, 32), (64, 64)])\n\n    with Image.open(temp_file) as reloaded:\n        reloaded.load()\n        reloaded.size = (32, 32)\n\n        assert reloaded.getpixel((0, 0)) == (18, 20, 62)\n\n\ndef test_no_duplicates(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n    temp_file2 = str(tmp_path / \"temp2.ico\")\n\n    im = hopper()\n    sizes = [(32, 32), (64, 64)]\n    im.save(temp_file, \"ico\", sizes=sizes)\n\n    sizes.append(sizes[-1])\n    im.save(temp_file2, \"ico\", sizes=sizes)\n\n    assert os.path.getsize(temp_file) == os.path.getsize(temp_file2)\n\n\ndef test_different_bit_depths(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n    temp_file2 = str(tmp_path / \"temp2.ico\")\n\n    im = hopper()\n    im.save(temp_file, \"ico\", bitmap_format=\"bmp\", sizes=[(128, 128)])\n\n    hopper(\"1\").save(\n        temp_file2,\n        \"ico\",\n        bitmap_format=\"bmp\",\n        sizes=[(128, 128)],\n        append_images=[im],\n    )\n\n    assert os.path.getsize(temp_file) != os.path.getsize(temp_file2)\n\n    # Test that only matching sizes of different bit depths are saved\n    temp_file3 = str(tmp_path / \"temp3.ico\")\n    temp_file4 = str(tmp_path / \"temp4.ico\")\n\n    im.save(temp_file3, \"ico\", bitmap_format=\"bmp\", sizes=[(128, 128)])\n    im.save(\n        temp_file4,\n        \"ico\",\n        bitmap_format=\"bmp\",\n        sizes=[(128, 128)],\n        append_images=[Image.new(\"P\", (64, 64))],\n    )\n\n    assert os.path.getsize(temp_file3) == os.path.getsize(temp_file4)\n\n\n@pytest.mark.parametrize(\"mode\", (\"1\", \"L\", \"P\", \"RGB\", \"RGBA\"))\ndef test_save_to_bytes_bmp(mode: str) -> None:\n    output = io.BytesIO()\n    im = hopper(mode)\n    im.save(output, \"ico\", bitmap_format=\"bmp\", sizes=[(32, 32), (64, 64)])\n\n    # The default image\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert reloaded.info[\"sizes\"] == {(32, 32), (64, 64)}\n\n        assert \"RGBA\" == reloaded.mode\n        assert (64, 64) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        im = hopper(mode).resize((64, 64), Image.Resampling.LANCZOS).convert(\"RGBA\")\n        assert_image_equal(reloaded, im)\n\n    # The other one\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        reloaded.size = (32, 32)\n\n        assert \"RGBA\" == reloaded.mode\n        assert (32, 32) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        im = hopper(mode).resize((32, 32), Image.Resampling.LANCZOS).convert(\"RGBA\")\n        assert_image_equal(reloaded, im)\n\n\ndef test_incorrect_size() -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        with pytest.raises(ValueError):\n            im.size = (1, 1)\n\n\ndef test_save_256x256(tmp_path: Path) -> None:\n    \"\"\"Issue #2264 https://github.com/python-pillow/Pillow/issues/2264\"\"\"\n    # Arrange\n    with Image.open(\"Tests/images/hopper_256x256.ico\") as im:\n        outfile = str(tmp_path / \"temp_saved_hopper_256x256.ico\")\n\n        # Act\n        im.save(outfile)\n    with Image.open(outfile) as im_saved:\n        # Assert\n        assert im_saved.size == (256, 256)\n\n\ndef test_only_save_relevant_sizes(tmp_path: Path) -> None:\n    \"\"\"Issue #2266 https://github.com/python-pillow/Pillow/issues/2266\n    Should save in 16x16, 24x24, 32x32, 48x48 sizes\n    and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes\n    \"\"\"\n    # Arrange\n    with Image.open(\"Tests/images/python.ico\") as im:  # 16x16, 32x32, 48x48\n        outfile = str(tmp_path / \"temp_saved_python.ico\")\n        # Act\n        im.save(outfile)\n\n    with Image.open(outfile) as im_saved:\n        # Assert\n        assert im_saved.info[\"sizes\"] == {(16, 16), (24, 24), (32, 32), (48, 48)}\n\n\ndef test_save_append_images(tmp_path: Path) -> None:\n    # append_images should be used for scaled down versions of the image\n    im = hopper(\"RGBA\")\n    provided_im = Image.new(\"RGBA\", (32, 32), (255, 0, 0))\n    outfile = str(tmp_path / \"temp_saved_multi_icon.ico\")\n    im.save(outfile, sizes=[(32, 32), (128, 128)], append_images=[provided_im])\n\n    with Image.open(outfile) as reread:\n        assert_image_equal(reread, hopper(\"RGBA\"))\n\n        reread.size = (32, 32)\n        assert_image_equal(reread, provided_im)\n\n\ndef test_unexpected_size() -> None:\n    # This image has been manually hexedited to state that it is 16x32\n    # while the image within is still 16x16\n    with pytest.warns(UserWarning):\n        with Image.open(\"Tests/images/hopper_unexpected.ico\") as im:\n            assert im.size == (16, 16)\n\n\ndef test_draw_reloaded(tmp_path: Path) -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        outfile = str(tmp_path / \"temp_saved_hopper_draw.ico\")\n\n        draw = ImageDraw.Draw(im)\n        draw.line((0, 0) + im.size, \"#f00\")\n        im.save(outfile)\n\n    with Image.open(outfile) as im:\n        assert_image_equal_tofile(im, \"Tests/images/hopper_draw.ico\")\n",
            "new_file_content": "from __future__ import annotations\n\nimport io\nimport os\nfrom pathlib import Path\n\nimport pytest\n\nfrom PIL import IcoImagePlugin, Image, ImageDraw, ImageFile\n\nfrom .helper import assert_image_equal, assert_image_equal_tofile, hopper\n\nTEST_ICO_FILE = \"Tests/images/hopper.ico\"\n\n\ndef test_sanity() -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        im.load()\n    assert im.mode == \"RGBA\"\n    assert im.size == (16, 16)\n    assert im.format == \"ICO\"\n    assert im.get_format_mimetype() == \"image/x-icon\"\n\n\ndef test_load() -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        assert im.load()[0, 0] == (1, 1, 9, 255)\n\n\ndef test_mask() -> None:\n    with Image.open(\"Tests/images/hopper_mask.ico\") as im:\n        assert_image_equal_tofile(im, \"Tests/images/hopper_mask.png\")\n\n\ndef test_black_and_white() -> None:\n    with Image.open(\"Tests/images/black_and_white.ico\") as im:\n        assert im.mode == \"RGBA\"\n        assert im.size == (16, 16)\n\n\ndef test_palette(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n\n    im = Image.new(\"P\", (16, 16))\n    im.save(temp_file)\n\n    with Image.open(temp_file) as reloaded:\n        assert reloaded.mode == \"P\"\n        assert reloaded.palette is not None\n\n\ndef test_invalid_file() -> None:\n    with open(\"Tests/images/flower.jpg\", \"rb\") as fp:\n        with pytest.raises(SyntaxError):\n            IcoImagePlugin.IcoImageFile(fp)\n\n\ndef test_save_to_bytes() -> None:\n    output = io.BytesIO()\n    im = hopper()\n    im.save(output, \"ico\", sizes=[(32, 32), (64, 64)])\n\n    # The default image\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert reloaded.info[\"sizes\"] == {(32, 32), (64, 64)}\n\n        assert im.mode == reloaded.mode\n        assert (64, 64) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        assert_image_equal(\n            reloaded, hopper().resize((64, 64), Image.Resampling.LANCZOS)\n        )\n\n    # The other one\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        reloaded.size = (32, 32)\n\n        assert im.mode == reloaded.mode\n        assert (32, 32) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        assert_image_equal(\n            reloaded, hopper().resize((32, 32), Image.Resampling.LANCZOS)\n        )\n\n\ndef test_getpixel(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n\n    im = hopper()\n    im.save(temp_file, \"ico\", sizes=[(32, 32), (64, 64)])\n\n    with Image.open(temp_file) as reloaded:\n        reloaded.load()\n        reloaded.size = (32, 32)\n\n        assert reloaded.getpixel((0, 0)) == (18, 20, 62)\n\n\ndef test_no_duplicates(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n    temp_file2 = str(tmp_path / \"temp2.ico\")\n\n    im = hopper()\n    sizes = [(32, 32), (64, 64)]\n    im.save(temp_file, \"ico\", sizes=sizes)\n\n    sizes.append(sizes[-1])\n    im.save(temp_file2, \"ico\", sizes=sizes)\n\n    assert os.path.getsize(temp_file) == os.path.getsize(temp_file2)\n\n\ndef test_different_bit_depths(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n    temp_file2 = str(tmp_path / \"temp2.ico\")\n\n    im = hopper()\n    im.save(temp_file, \"ico\", bitmap_format=\"bmp\", sizes=[(128, 128)])\n\n    hopper(\"1\").save(\n        temp_file2,\n        \"ico\",\n        bitmap_format=\"bmp\",\n        sizes=[(128, 128)],\n        append_images=[im],\n    )\n\n    assert os.path.getsize(temp_file) != os.path.getsize(temp_file2)\n\n    # Test that only matching sizes of different bit depths are saved\n    temp_file3 = str(tmp_path / \"temp3.ico\")\n    temp_file4 = str(tmp_path / \"temp4.ico\")\n\n    im.save(temp_file3, \"ico\", bitmap_format=\"bmp\", sizes=[(128, 128)])\n    im.save(\n        temp_file4,\n        \"ico\",\n        bitmap_format=\"bmp\",\n        sizes=[(128, 128)],\n        append_images=[Image.new(\"P\", (64, 64))],\n    )\n\n    assert os.path.getsize(temp_file3) == os.path.getsize(temp_file4)\n\n\n@pytest.mark.parametrize(\"mode\", (\"1\", \"L\", \"P\", \"RGB\", \"RGBA\"))\ndef test_save_to_bytes_bmp(mode: str) -> None:\n    output = io.BytesIO()\n    im = hopper(mode)\n    im.save(output, \"ico\", bitmap_format=\"bmp\", sizes=[(32, 32), (64, 64)])\n\n    # The default image\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert reloaded.info[\"sizes\"] == {(32, 32), (64, 64)}\n\n        assert \"RGBA\" == reloaded.mode\n        assert (64, 64) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        im = hopper(mode).resize((64, 64), Image.Resampling.LANCZOS).convert(\"RGBA\")\n        assert_image_equal(reloaded, im)\n\n    # The other one\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        reloaded.size = (32, 32)\n\n        assert \"RGBA\" == reloaded.mode\n        assert (32, 32) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        im = hopper(mode).resize((32, 32), Image.Resampling.LANCZOS).convert(\"RGBA\")\n        assert_image_equal(reloaded, im)\n\n\ndef test_incorrect_size() -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        with pytest.raises(ValueError):\n            im.size = (1, 1)\n\n\ndef test_save_256x256(tmp_path: Path) -> None:\n    \"\"\"Issue #2264 https://github.com/python-pillow/Pillow/issues/2264\"\"\"\n    # Arrange\n    with Image.open(\"Tests/images/hopper_256x256.ico\") as im:\n        outfile = str(tmp_path / \"temp_saved_hopper_256x256.ico\")\n\n        # Act\n        im.save(outfile)\n    with Image.open(outfile) as im_saved:\n        # Assert\n        assert im_saved.size == (256, 256)\n\n\ndef test_only_save_relevant_sizes(tmp_path: Path) -> None:\n    \"\"\"Issue #2266 https://github.com/python-pillow/Pillow/issues/2266\n    Should save in 16x16, 24x24, 32x32, 48x48 sizes\n    and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes\n    \"\"\"\n    # Arrange\n    with Image.open(\"Tests/images/python.ico\") as im:  # 16x16, 32x32, 48x48\n        outfile = str(tmp_path / \"temp_saved_python.ico\")\n        # Act\n        im.save(outfile)\n\n    with Image.open(outfile) as im_saved:\n        # Assert\n        assert im_saved.info[\"sizes\"] == {(16, 16), (24, 24), (32, 32), (48, 48)}\n\n\ndef test_save_append_images(tmp_path: Path) -> None:\n    # append_images should be used for scaled down versions of the image\n    im = hopper(\"RGBA\")\n    provided_im = Image.new(\"RGBA\", (32, 32), (255, 0, 0))\n    outfile = str(tmp_path / \"temp_saved_multi_icon.ico\")\n    im.save(outfile, sizes=[(32, 32), (128, 128)], append_images=[provided_im])\n\n    with Image.open(outfile) as reread:\n        assert_image_equal(reread, hopper(\"RGBA\"))\n\n        reread.size = (32, 32)\n        assert_image_equal(reread, provided_im)\n\n\ndef test_unexpected_size() -> None:\n    # This image has been manually hexedited to state that it is 16x32\n    # while the image within is still 16x16\n    with pytest.warns(UserWarning):\n        with Image.open(\"Tests/images/hopper_unexpected.ico\") as im:\n            assert im.size == (16, 16)\n\n\ndef test_draw_reloaded(tmp_path: Path) -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        outfile = str(tmp_path / \"temp_saved_hopper_draw.ico\")\n\n        draw = ImageDraw.Draw(im)\n        draw.line((0, 0) + im.size, \"#f00\")\n        im.save(outfile)\n\n    with Image.open(outfile) as im:\n        assert_image_equal_tofile(im, \"Tests/images/hopper_draw.ico\")\n\n\ndef test_truncated_mask() -> None:\n    # 1 bpp\n    with open(\"Tests/images/hopper_mask.ico\", \"rb\") as fp:\n        data = fp.read()\n\n    ImageFile.LOAD_TRUNCATED_IMAGES = True\n    data = data[:-3]\n\n    try:\n        with Image.open(io.BytesIO(data)) as im:\n            with Image.open(\"Tests/images/hopper_mask.png\") as expected:\n                assert im.mode == \"1\"\n\n        # 32 bpp\n        output = io.BytesIO()\n        expected = hopper(\"RGBA\")\n        expected.save(output, \"ico\", bitmap_format=\"bmp\")\n\n        data = output.getvalue()[:-1]\n\n        with Image.open(io.BytesIO(data)) as im:\n            assert im.mode == \"RGB\"\n    finally:\n        ImageFile.LOAD_TRUNCATED_IMAGES = False\n",
            "header": {
                "file": {
                    "path": "Tests/test_file_ico.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "fa8c11d5a",
                "new_commit_hash": "37770498a",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Tests/test_file_ico.py"
            },
            "plus_file": {
                "path": "b/Tests/test_file_ico.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 6,
                            "length": 7
                        },
                        "new_range": {
                            "start": 6,
                            "length": 7
                        },
                        "section": "from pathlib import Path"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "import pytest",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "from PIL import IcoImagePlugin, Image, ImageDraw",
                                "type": "deleted"
                            },
                            {
                                "content": "from PIL import IcoImagePlugin, Image, ImageDraw, ImageFile",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "from .helper import assert_image_equal, assert_image_equal_tofile, hopper",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Tests/test_file_ico.py",
                            "type": "statement",
                            "name": "",
                            "content": "from PIL import IcoImagePlugin, Image, ImageDraw, ImageFile",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 9,
                            "end_lineno": 9,
                            "parent": null
                        }
                    ],
                    "deleted_entities": [
                        {
                            "file_name": "Tests/test_file_ico.py",
                            "type": "statement",
                            "name": "",
                            "content": "from PIL import IcoImagePlugin, Image, ImageDraw",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 9,
                            "end_lineno": 9,
                            "parent": null
                        }
                    ]
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 241,
                            "length": 3
                        },
                        "new_range": {
                            "start": 241,
                            "length": 29
                        },
                        "section": "def test_draw_reloaded(tmp_path: Path) -> None:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    with Image.open(outfile) as im:",
                                "type": "context"
                            },
                            {
                                "content": "        assert_image_equal_tofile(im, \"Tests/images/hopper_draw.ico\")",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "def test_truncated_mask() -> None:",
                                "type": "added"
                            },
                            {
                                "content": "    # 1 bpp",
                                "type": "added"
                            },
                            {
                                "content": "    with open(\"Tests/images/hopper_mask.ico\", \"rb\") as fp:",
                                "type": "added"
                            },
                            {
                                "content": "        data = fp.read()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    ImageFile.LOAD_TRUNCATED_IMAGES = True",
                                "type": "added"
                            },
                            {
                                "content": "    data = data[:-3]",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    try:",
                                "type": "added"
                            },
                            {
                                "content": "        with Image.open(io.BytesIO(data)) as im:",
                                "type": "added"
                            },
                            {
                                "content": "            with Image.open(\"Tests/images/hopper_mask.png\") as expected:",
                                "type": "added"
                            },
                            {
                                "content": "                assert im.mode == \"1\"",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        # 32 bpp",
                                "type": "added"
                            },
                            {
                                "content": "        output = io.BytesIO()",
                                "type": "added"
                            },
                            {
                                "content": "        expected = hopper(\"RGBA\")",
                                "type": "added"
                            },
                            {
                                "content": "        expected.save(output, \"ico\", bitmap_format=\"bmp\")",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        data = output.getvalue()[:-1]",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        with Image.open(io.BytesIO(data)) as im:",
                                "type": "added"
                            },
                            {
                                "content": "            assert im.mode == \"RGB\"",
                                "type": "added"
                            },
                            {
                                "content": "    finally:",
                                "type": "added"
                            },
                            {
                                "content": "        ImageFile.LOAD_TRUNCATED_IMAGES = False",
                                "type": "added"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Tests/test_file_ico.py",
                            "type": "function",
                            "name": "test_truncated_mask",
                            "content": "def test_truncated_mask() -> None:\n    # 1 bpp\n    with open(\"Tests/images/hopper_mask.ico\", \"rb\") as fp:\n        data = fp.read()\n\n    ImageFile.LOAD_TRUNCATED_IMAGES = True\n    data = data[:-3]\n\n    try:\n        with Image.open(io.BytesIO(data)) as im:\n            with Image.open(\"Tests/images/hopper_mask.png\") as expected:\n                assert im.mode == \"1\"\n\n        # 32 bpp\n        output = io.BytesIO()\n        expected = hopper(\"RGBA\")\n        expected.save(output, \"ico\", bitmap_format=\"bmp\")\n\n        data = output.getvalue()[:-1]\n\n        with Image.open(io.BytesIO(data)) as im:\n            assert im.mode == \"RGB\"\n    finally:\n        ImageFile.LOAD_TRUNCATED_IMAGES = False",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 246,
                            "end_lineno": 269,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "#\n# The Python Imaging Library.\n# $Id$\n#\n# Windows Icon support for PIL\n#\n# History:\n#       96-05-27 fl     Created\n#\n# Copyright (c) Secret Labs AB 1997.\n# Copyright (c) Fredrik Lundh 1996.\n#\n# See the README file for information on usage and redistribution.\n#\n\n# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis\n# <casadebender@gmail.com>.\n# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki\n#\n# Icon format references:\n#   * https://en.wikipedia.org/wiki/ICO_(file_format)\n#   * https://msdn.microsoft.com/en-us/library/ms997538.aspx\nfrom __future__ import annotations\n\nimport warnings\nfrom io import BytesIO\nfrom math import ceil, log\nfrom typing import IO\n\nfrom . import BmpImagePlugin, Image, ImageFile, PngImagePlugin\nfrom ._binary import i16le as i16\nfrom ._binary import i32le as i32\nfrom ._binary import o8\nfrom ._binary import o16le as o16\nfrom ._binary import o32le as o32\n\n#\n# --------------------------------------------------------------------\n\n_MAGIC = b\"\\0\\0\\1\\0\"\n\n\ndef _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:\n    fp.write(_MAGIC)  # (2+2)\n    bmp = im.encoderinfo.get(\"bitmap_format\") == \"bmp\"\n    sizes = im.encoderinfo.get(\n        \"sizes\",\n        [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],\n    )\n    frames = []\n    provided_ims = [im] + im.encoderinfo.get(\"append_images\", [])\n    width, height = im.size\n    for size in sorted(set(sizes)):\n        if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256:\n            continue\n\n        for provided_im in provided_ims:\n            if provided_im.size != size:\n                continue\n            frames.append(provided_im)\n            if bmp:\n                bits = BmpImagePlugin.SAVE[provided_im.mode][1]\n                bits_used = [bits]\n                for other_im in provided_ims:\n                    if other_im.size != size:\n                        continue\n                    bits = BmpImagePlugin.SAVE[other_im.mode][1]\n                    if bits not in bits_used:\n                        # Another image has been supplied for this size\n                        # with a different bit depth\n                        frames.append(other_im)\n                        bits_used.append(bits)\n            break\n        else:\n            # TODO: invent a more convenient method for proportional scalings\n            frame = provided_im.copy()\n            frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None)\n            frames.append(frame)\n    fp.write(o16(len(frames)))  # idCount(2)\n    offset = fp.tell() + len(frames) * 16\n    for frame in frames:\n        width, height = frame.size\n        # 0 means 256\n        fp.write(o8(width if width < 256 else 0))  # bWidth(1)\n        fp.write(o8(height if height < 256 else 0))  # bHeight(1)\n\n        bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0)\n        fp.write(o8(colors))  # bColorCount(1)\n        fp.write(b\"\\0\")  # bReserved(1)\n        fp.write(b\"\\0\\0\")  # wPlanes(2)\n        fp.write(o16(bits))  # wBitCount(2)\n\n        image_io = BytesIO()\n        if bmp:\n            frame.save(image_io, \"dib\")\n\n            if bits != 32:\n                and_mask = Image.new(\"1\", size)\n                ImageFile._save(\n                    and_mask, image_io, [(\"raw\", (0, 0) + size, 0, (\"1\", 0, -1))]\n                )\n        else:\n            frame.save(image_io, \"png\")\n        image_io.seek(0)\n        image_bytes = image_io.read()\n        if bmp:\n            image_bytes = image_bytes[:8] + o32(height * 2) + image_bytes[12:]\n        bytes_len = len(image_bytes)\n        fp.write(o32(bytes_len))  # dwBytesInRes(4)\n        fp.write(o32(offset))  # dwImageOffset(4)\n        current = fp.tell()\n        fp.seek(offset)\n        fp.write(image_bytes)\n        offset = offset + bytes_len\n        fp.seek(current)\n\n\ndef _accept(prefix: bytes) -> bool:\n    return prefix[:4] == _MAGIC\n\n\nclass IcoFile:\n    def __init__(self, buf):\n        \"\"\"\n        Parse image from file-like object containing ico file data\n        \"\"\"\n\n        # check magic\n        s = buf.read(6)\n        if not _accept(s):\n            msg = \"not an ICO file\"\n            raise SyntaxError(msg)\n\n        self.buf = buf\n        self.entry = []\n\n        # Number of items in file\n        self.nb_items = i16(s, 4)\n\n        # Get headers for each item\n        for i in range(self.nb_items):\n            s = buf.read(16)\n\n            icon_header = {\n                \"width\": s[0],\n                \"height\": s[1],\n                \"nb_color\": s[2],  # No. of colors in image (0 if >=8bpp)\n                \"reserved\": s[3],\n                \"planes\": i16(s, 4),\n                \"bpp\": i16(s, 6),\n                \"size\": i32(s, 8),\n                \"offset\": i32(s, 12),\n            }\n\n            # See Wikipedia\n            for j in (\"width\", \"height\"):\n                if not icon_header[j]:\n                    icon_header[j] = 256\n\n            # See Wikipedia notes about color depth.\n            # We need this just to differ images with equal sizes\n            icon_header[\"color_depth\"] = (\n                icon_header[\"bpp\"]\n                or (\n                    icon_header[\"nb_color\"] != 0\n                    and ceil(log(icon_header[\"nb_color\"], 2))\n                )\n                or 256\n            )\n\n            icon_header[\"dim\"] = (icon_header[\"width\"], icon_header[\"height\"])\n            icon_header[\"square\"] = icon_header[\"width\"] * icon_header[\"height\"]\n\n            self.entry.append(icon_header)\n\n        self.entry = sorted(self.entry, key=lambda x: x[\"color_depth\"])\n        # ICO images are usually squares\n        self.entry = sorted(self.entry, key=lambda x: x[\"square\"], reverse=True)\n\n    def sizes(self):\n        \"\"\"\n        Get a list of all available icon sizes and color depths.\n        \"\"\"\n        return {(h[\"width\"], h[\"height\"]) for h in self.entry}\n\n    def getentryindex(self, size, bpp=False):\n        for i, h in enumerate(self.entry):\n            if size == h[\"dim\"] and (bpp is False or bpp == h[\"color_depth\"]):\n                return i\n        return 0\n\n    def getimage(self, size, bpp=False):\n        \"\"\"\n        Get an image from the icon\n        \"\"\"\n        return self.frame(self.getentryindex(size, bpp))\n\n    def frame(self, idx: int) -> Image.Image:\n        \"\"\"\n        Get an image from frame idx\n        \"\"\"\n\n        header = self.entry[idx]\n\n        self.buf.seek(header[\"offset\"])\n        data = self.buf.read(8)\n        self.buf.seek(header[\"offset\"])\n\n        im: Image.Image\n        if data[:8] == PngImagePlugin._MAGIC:\n            # png frame\n            im = PngImagePlugin.PngImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n        else:\n            # XOR + AND mask bmp frame\n            im = BmpImagePlugin.DibImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n\n            # change tile dimension to only encompass XOR image\n            im._size = (im.size[0], int(im.size[1] / 2))\n            d, e, o, a = im.tile[0]\n            im.tile[0] = d, (0, 0) + im.size, o, a\n\n            # figure out where AND mask image starts\n            bpp = header[\"bpp\"]\n            if 32 == bpp:\n                # 32-bit color depth icon image allows semitransparent areas\n                # PIL's DIB format ignores transparency bits, recover them.\n                # The DIB is packed in BGRX byte order where X is the alpha\n                # channel.\n\n                # Back up to start of bmp data\n                self.buf.seek(o)\n                # extract every 4th byte (eg. 3,7,11,15,...)\n                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]\n\n                # convert to an 8bpp grayscale image\n                mask = Image.frombuffer(\n                    \"L\",  # 8bpp\n                    im.size,  # (w, h)\n                    alpha_bytes,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed\n                )\n            else:\n                # get AND image from end of bitmap\n                w = im.size[0]\n                if (w % 32) > 0:\n                    # bitmap row data is aligned to word boundaries\n                    w += 32 - (im.size[0] % 32)\n\n                # the total mask data is\n                # padded row size * height / bits per char\n\n                total_bytes = int((w * im.size[1]) / 8)\n                and_mask_offset = header[\"offset\"] + header[\"size\"] - total_bytes\n\n                self.buf.seek(and_mask_offset)\n                mask_data = self.buf.read(total_bytes)\n\n                # convert raw data to image\n                mask = Image.frombuffer(\n                    \"1\",  # 1 bpp\n                    im.size,  # (w, h)\n                    mask_data,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed\n                )\n\n                # now we have two images, im is XOR image and mask is AND image\n\n            # apply mask image as alpha channel\n            im = im.convert(\"RGBA\")\n            im.putalpha(mask)\n\n        return im\n\n\n##\n# Image plugin for Windows Icon files.\n\n\nclass IcoImageFile(ImageFile.ImageFile):\n    \"\"\"\n    PIL read-only image support for Microsoft Windows .ico files.\n\n    By default the largest resolution image in the file will be loaded. This\n    can be changed by altering the 'size' attribute before calling 'load'.\n\n    The info dictionary has a key 'sizes' that is a list of the sizes available\n    in the icon file.\n\n    Handles classic, XP and Vista icon formats.\n\n    When saving, PNG compression is used. Support for this was only added in\n    Windows Vista. If you are unable to view the icon in Windows, convert the\n    image to \"RGBA\" mode before saving.\n\n    This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis\n    <casadebender@gmail.com>.\n    https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki\n    \"\"\"\n\n    format = \"ICO\"\n    format_description = \"Windows Icon\"\n\n    def _open(self) -> None:\n        self.ico = IcoFile(self.fp)\n        self.info[\"sizes\"] = self.ico.sizes()\n        self.size = self.ico.entry[0][\"dim\"]\n        self.load()\n\n    @property\n    def size(self):\n        return self._size\n\n    @size.setter\n    def size(self, value):\n        if value not in self.info[\"sizes\"]:\n            msg = \"This is not one of the allowed sizes of this image\"\n            raise ValueError(msg)\n        self._size = value\n\n    def load(self):\n        if self.im is not None and self.im.size == self.size:\n            # Already loaded\n            return Image.Image.load(self)\n        im = self.ico.getimage(self.size)\n        # if tile is PNG, it won't really be loaded yet\n        im.load()\n        self.im = im.im\n        self._mode = im.mode\n        if im.palette:\n            self.palette = im.palette\n        if im.size != self.size:\n            warnings.warn(\"Image was not the expected size\")\n\n            index = self.ico.getentryindex(self.size)\n            sizes = list(self.info[\"sizes\"])\n            sizes[index] = im.size\n            self.info[\"sizes\"] = set(sizes)\n\n            self.size = im.size\n\n    def load_seek(self, pos: int) -> None:\n        # Flag the ImageFile.Parser so that it\n        # just does all the decode at the end.\n        pass\n\n\n#\n# --------------------------------------------------------------------\n\n\nImage.register_open(IcoImageFile.format, IcoImageFile, _accept)\nImage.register_save(IcoImageFile.format, _save)\nImage.register_extension(IcoImageFile.format, \".ico\")\n\nImage.register_mime(IcoImageFile.format, \"image/x-icon\")\n",
            "new_file_content": "#\n# The Python Imaging Library.\n# $Id$\n#\n# Windows Icon support for PIL\n#\n# History:\n#       96-05-27 fl     Created\n#\n# Copyright (c) Secret Labs AB 1997.\n# Copyright (c) Fredrik Lundh 1996.\n#\n# See the README file for information on usage and redistribution.\n#\n\n# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis\n# <casadebender@gmail.com>.\n# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki\n#\n# Icon format references:\n#   * https://en.wikipedia.org/wiki/ICO_(file_format)\n#   * https://msdn.microsoft.com/en-us/library/ms997538.aspx\nfrom __future__ import annotations\n\nimport warnings\nfrom io import BytesIO\nfrom math import ceil, log\nfrom typing import IO\n\nfrom . import BmpImagePlugin, Image, ImageFile, PngImagePlugin\nfrom ._binary import i16le as i16\nfrom ._binary import i32le as i32\nfrom ._binary import o8\nfrom ._binary import o16le as o16\nfrom ._binary import o32le as o32\n\n#\n# --------------------------------------------------------------------\n\n_MAGIC = b\"\\0\\0\\1\\0\"\n\n\ndef _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:\n    fp.write(_MAGIC)  # (2+2)\n    bmp = im.encoderinfo.get(\"bitmap_format\") == \"bmp\"\n    sizes = im.encoderinfo.get(\n        \"sizes\",\n        [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],\n    )\n    frames = []\n    provided_ims = [im] + im.encoderinfo.get(\"append_images\", [])\n    width, height = im.size\n    for size in sorted(set(sizes)):\n        if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256:\n            continue\n\n        for provided_im in provided_ims:\n            if provided_im.size != size:\n                continue\n            frames.append(provided_im)\n            if bmp:\n                bits = BmpImagePlugin.SAVE[provided_im.mode][1]\n                bits_used = [bits]\n                for other_im in provided_ims:\n                    if other_im.size != size:\n                        continue\n                    bits = BmpImagePlugin.SAVE[other_im.mode][1]\n                    if bits not in bits_used:\n                        # Another image has been supplied for this size\n                        # with a different bit depth\n                        frames.append(other_im)\n                        bits_used.append(bits)\n            break\n        else:\n            # TODO: invent a more convenient method for proportional scalings\n            frame = provided_im.copy()\n            frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None)\n            frames.append(frame)\n    fp.write(o16(len(frames)))  # idCount(2)\n    offset = fp.tell() + len(frames) * 16\n    for frame in frames:\n        width, height = frame.size\n        # 0 means 256\n        fp.write(o8(width if width < 256 else 0))  # bWidth(1)\n        fp.write(o8(height if height < 256 else 0))  # bHeight(1)\n\n        bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0)\n        fp.write(o8(colors))  # bColorCount(1)\n        fp.write(b\"\\0\")  # bReserved(1)\n        fp.write(b\"\\0\\0\")  # wPlanes(2)\n        fp.write(o16(bits))  # wBitCount(2)\n\n        image_io = BytesIO()\n        if bmp:\n            frame.save(image_io, \"dib\")\n\n            if bits != 32:\n                and_mask = Image.new(\"1\", size)\n                ImageFile._save(\n                    and_mask, image_io, [(\"raw\", (0, 0) + size, 0, (\"1\", 0, -1))]\n                )\n        else:\n            frame.save(image_io, \"png\")\n        image_io.seek(0)\n        image_bytes = image_io.read()\n        if bmp:\n            image_bytes = image_bytes[:8] + o32(height * 2) + image_bytes[12:]\n        bytes_len = len(image_bytes)\n        fp.write(o32(bytes_len))  # dwBytesInRes(4)\n        fp.write(o32(offset))  # dwImageOffset(4)\n        current = fp.tell()\n        fp.seek(offset)\n        fp.write(image_bytes)\n        offset = offset + bytes_len\n        fp.seek(current)\n\n\ndef _accept(prefix: bytes) -> bool:\n    return prefix[:4] == _MAGIC\n\n\nclass IcoFile:\n    def __init__(self, buf):\n        \"\"\"\n        Parse image from file-like object containing ico file data\n        \"\"\"\n\n        # check magic\n        s = buf.read(6)\n        if not _accept(s):\n            msg = \"not an ICO file\"\n            raise SyntaxError(msg)\n\n        self.buf = buf\n        self.entry = []\n\n        # Number of items in file\n        self.nb_items = i16(s, 4)\n\n        # Get headers for each item\n        for i in range(self.nb_items):\n            s = buf.read(16)\n\n            icon_header = {\n                \"width\": s[0],\n                \"height\": s[1],\n                \"nb_color\": s[2],  # No. of colors in image (0 if >=8bpp)\n                \"reserved\": s[3],\n                \"planes\": i16(s, 4),\n                \"bpp\": i16(s, 6),\n                \"size\": i32(s, 8),\n                \"offset\": i32(s, 12),\n            }\n\n            # See Wikipedia\n            for j in (\"width\", \"height\"):\n                if not icon_header[j]:\n                    icon_header[j] = 256\n\n            # See Wikipedia notes about color depth.\n            # We need this just to differ images with equal sizes\n            icon_header[\"color_depth\"] = (\n                icon_header[\"bpp\"]\n                or (\n                    icon_header[\"nb_color\"] != 0\n                    and ceil(log(icon_header[\"nb_color\"], 2))\n                )\n                or 256\n            )\n\n            icon_header[\"dim\"] = (icon_header[\"width\"], icon_header[\"height\"])\n            icon_header[\"square\"] = icon_header[\"width\"] * icon_header[\"height\"]\n\n            self.entry.append(icon_header)\n\n        self.entry = sorted(self.entry, key=lambda x: x[\"color_depth\"])\n        # ICO images are usually squares\n        self.entry = sorted(self.entry, key=lambda x: x[\"square\"], reverse=True)\n\n    def sizes(self):\n        \"\"\"\n        Get a list of all available icon sizes and color depths.\n        \"\"\"\n        return {(h[\"width\"], h[\"height\"]) for h in self.entry}\n\n    def getentryindex(self, size, bpp=False):\n        for i, h in enumerate(self.entry):\n            if size == h[\"dim\"] and (bpp is False or bpp == h[\"color_depth\"]):\n                return i\n        return 0\n\n    def getimage(self, size, bpp=False):\n        \"\"\"\n        Get an image from the icon\n        \"\"\"\n        return self.frame(self.getentryindex(size, bpp))\n\n    def frame(self, idx: int) -> Image.Image:\n        \"\"\"\n        Get an image from frame idx\n        \"\"\"\n\n        header = self.entry[idx]\n\n        self.buf.seek(header[\"offset\"])\n        data = self.buf.read(8)\n        self.buf.seek(header[\"offset\"])\n\n        im: Image.Image\n        if data[:8] == PngImagePlugin._MAGIC:\n            # png frame\n            im = PngImagePlugin.PngImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n        else:\n            # XOR + AND mask bmp frame\n            im = BmpImagePlugin.DibImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n\n            # change tile dimension to only encompass XOR image\n            im._size = (im.size[0], int(im.size[1] / 2))\n            d, e, o, a = im.tile[0]\n            im.tile[0] = d, (0, 0) + im.size, o, a\n\n            # figure out where AND mask image starts\n            bpp = header[\"bpp\"]\n            if 32 == bpp:\n                # 32-bit color depth icon image allows semitransparent areas\n                # PIL's DIB format ignores transparency bits, recover them.\n                # The DIB is packed in BGRX byte order where X is the alpha\n                # channel.\n\n                # Back up to start of bmp data\n                self.buf.seek(o)\n                # extract every 4th byte (eg. 3,7,11,15,...)\n                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]\n\n                # convert to an 8bpp grayscale image\n                try:\n                    mask = Image.frombuffer(\n                        \"L\",  # 8bpp\n                        im.size,  # (w, h)\n                        alpha_bytes,  # source chars\n                        \"raw\",  # raw decoder\n                        (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed\n                    )\n                except ValueError:\n                    if ImageFile.LOAD_TRUNCATED_IMAGES:\n                        mask = None\n                    else:\n                        raise\n            else:\n                # get AND image from end of bitmap\n                w = im.size[0]\n                if (w % 32) > 0:\n                    # bitmap row data is aligned to word boundaries\n                    w += 32 - (im.size[0] % 32)\n\n                # the total mask data is\n                # padded row size * height / bits per char\n\n                total_bytes = int((w * im.size[1]) / 8)\n                and_mask_offset = header[\"offset\"] + header[\"size\"] - total_bytes\n\n                self.buf.seek(and_mask_offset)\n                mask_data = self.buf.read(total_bytes)\n\n                # convert raw data to image\n                try:\n                    mask = Image.frombuffer(\n                        \"1\",  # 1 bpp\n                        im.size,  # (w, h)\n                        mask_data,  # source chars\n                        \"raw\",  # raw decoder\n                        (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed\n                    )\n                except ValueError:\n                    if ImageFile.LOAD_TRUNCATED_IMAGES:\n                        mask = None\n                    else:\n                        raise\n\n                # now we have two images, im is XOR image and mask is AND image\n\n            # apply mask image as alpha channel\n            if mask:\n                im = im.convert(\"RGBA\")\n                im.putalpha(mask)\n\n        return im\n\n\n##\n# Image plugin for Windows Icon files.\n\n\nclass IcoImageFile(ImageFile.ImageFile):\n    \"\"\"\n    PIL read-only image support for Microsoft Windows .ico files.\n\n    By default the largest resolution image in the file will be loaded. This\n    can be changed by altering the 'size' attribute before calling 'load'.\n\n    The info dictionary has a key 'sizes' that is a list of the sizes available\n    in the icon file.\n\n    Handles classic, XP and Vista icon formats.\n\n    When saving, PNG compression is used. Support for this was only added in\n    Windows Vista. If you are unable to view the icon in Windows, convert the\n    image to \"RGBA\" mode before saving.\n\n    This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis\n    <casadebender@gmail.com>.\n    https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki\n    \"\"\"\n\n    format = \"ICO\"\n    format_description = \"Windows Icon\"\n\n    def _open(self) -> None:\n        self.ico = IcoFile(self.fp)\n        self.info[\"sizes\"] = self.ico.sizes()\n        self.size = self.ico.entry[0][\"dim\"]\n        self.load()\n\n    @property\n    def size(self):\n        return self._size\n\n    @size.setter\n    def size(self, value):\n        if value not in self.info[\"sizes\"]:\n            msg = \"This is not one of the allowed sizes of this image\"\n            raise ValueError(msg)\n        self._size = value\n\n    def load(self):\n        if self.im is not None and self.im.size == self.size:\n            # Already loaded\n            return Image.Image.load(self)\n        im = self.ico.getimage(self.size)\n        # if tile is PNG, it won't really be loaded yet\n        im.load()\n        self.im = im.im\n        self._mode = im.mode\n        if im.palette:\n            self.palette = im.palette\n        if im.size != self.size:\n            warnings.warn(\"Image was not the expected size\")\n\n            index = self.ico.getentryindex(self.size)\n            sizes = list(self.info[\"sizes\"])\n            sizes[index] = im.size\n            self.info[\"sizes\"] = set(sizes)\n\n            self.size = im.size\n\n    def load_seek(self, pos: int) -> None:\n        # Flag the ImageFile.Parser so that it\n        # just does all the decode at the end.\n        pass\n\n\n#\n# --------------------------------------------------------------------\n\n\nImage.register_open(IcoImageFile.format, IcoImageFile, _accept)\nImage.register_save(IcoImageFile.format, _save)\nImage.register_extension(IcoImageFile.format, \".ico\")\n\nImage.register_mime(IcoImageFile.format, \"image/x-icon\")\n",
            "header": {
                "file": {
                    "path": "src/PIL/IcoImagePlugin.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "086c87b1a",
                "new_commit_hash": "f331caad7",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/src/PIL/IcoImagePlugin.py"
            },
            "plus_file": {
                "path": "b/src/PIL/IcoImagePlugin.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 235,
                            "length": 13
                        },
                        "new_range": {
                            "start": 235,
                            "length": 19
                        },
                        "section": "class IcoFile:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "                # convert to an 8bpp grayscale image",
                                "type": "context"
                            },
                            {
                                "content": "                mask = Image.frombuffer(",
                                "type": "deleted"
                            },
                            {
                                "content": "                    \"L\",  # 8bpp",
                                "type": "deleted"
                            },
                            {
                                "content": "                    im.size,  # (w, h)",
                                "type": "deleted"
                            },
                            {
                                "content": "                    alpha_bytes,  # source chars",
                                "type": "deleted"
                            },
                            {
                                "content": "                    \"raw\",  # raw decoder",
                                "type": "deleted"
                            },
                            {
                                "content": "                    (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed",
                                "type": "deleted"
                            },
                            {
                                "content": "                )",
                                "type": "deleted"
                            },
                            {
                                "content": "                try:",
                                "type": "added"
                            },
                            {
                                "content": "                    mask = Image.frombuffer(",
                                "type": "added"
                            },
                            {
                                "content": "                        \"L\",  # 8bpp",
                                "type": "added"
                            },
                            {
                                "content": "                        im.size,  # (w, h)",
                                "type": "added"
                            },
                            {
                                "content": "                        alpha_bytes,  # source chars",
                                "type": "added"
                            },
                            {
                                "content": "                        \"raw\",  # raw decoder",
                                "type": "added"
                            },
                            {
                                "content": "                        (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed",
                                "type": "added"
                            },
                            {
                                "content": "                    )",
                                "type": "added"
                            },
                            {
                                "content": "                except ValueError:",
                                "type": "added"
                            },
                            {
                                "content": "                    if ImageFile.LOAD_TRUNCATED_IMAGES:",
                                "type": "added"
                            },
                            {
                                "content": "                        mask = None",
                                "type": "added"
                            },
                            {
                                "content": "                    else:",
                                "type": "added"
                            },
                            {
                                "content": "                        raise",
                                "type": "added"
                            },
                            {
                                "content": "            else:",
                                "type": "context"
                            },
                            {
                                "content": "                # get AND image from end of bitmap",
                                "type": "context"
                            },
                            {
                                "content": "                w = im.size[0]",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "src/PIL/IcoImagePlugin.py",
                            "type": "class",
                            "name": "IcoFile",
                            "content": "class IcoFile:\n    def __init__(self, buf):\n        \"\"\"\n        Parse image from file-like object containing ico file data\n        \"\"\"\n\n        # check magic\n        s = buf.read(6)\n        if not _accept(s):\n            msg = \"not an ICO file\"\n            raise SyntaxError(msg)\n\n        self.buf = buf\n        self.entry = []\n\n        # Number of items in file\n        self.nb_items = i16(s, 4)\n\n        # Get headers for each item\n        for i in range(self.nb_items):\n            s = buf.read(16)\n\n            icon_header = {\n                \"width\": s[0],\n                \"height\": s[1],\n                \"nb_color\": s[2],  # No. of colors in image (0 if >=8bpp)\n                \"reserved\": s[3],\n                \"planes\": i16(s, 4),\n                \"bpp\": i16(s, 6),\n                \"size\": i32(s, 8),\n                \"offset\": i32(s, 12),\n            }\n\n            # See Wikipedia\n            for j in (\"width\", \"height\"):\n                if not icon_header[j]:\n                    icon_header[j] = 256\n\n            # See Wikipedia notes about color depth.\n            # We need this just to differ images with equal sizes\n            icon_header[\"color_depth\"] = (\n                icon_header[\"bpp\"]\n                or (\n                    icon_header[\"nb_color\"] != 0\n                    and ceil(log(icon_header[\"nb_color\"], 2))\n                )\n                or 256\n            )\n\n            icon_header[\"dim\"] = (icon_header[\"width\"], icon_header[\"height\"])\n            icon_header[\"square\"] = icon_header[\"width\"] * icon_header[\"height\"]\n\n            self.entry.append(icon_header)\n\n        self.entry = sorted(self.entry, key=lambda x: x[\"color_depth\"])\n        # ICO images are usually squares\n        self.entry = sorted(self.entry, key=lambda x: x[\"square\"], reverse=True)\n\n    def sizes(self):\n        \"\"\"\n        Get a list of all available icon sizes and color depths.\n        \"\"\"\n        return {(h[\"width\"], h[\"height\"]) for h in self.entry}\n\n    def getentryindex(self, size, bpp=False):\n        for i, h in enumerate(self.entry):\n            if size == h[\"dim\"] and (bpp is False or bpp == h[\"color_depth\"]):\n                return i\n        return 0\n\n    def getimage(self, size, bpp=False):\n        \"\"\"\n        Get an image from the icon\n        \"\"\"\n        return self.frame(self.getentryindex(size, bpp))\n\n    def frame(self, idx: int) -> Image.Image:\n        \"\"\"\n        Get an image from frame idx\n        \"\"\"\n\n        header = self.entry[idx]\n\n        self.buf.seek(header[\"offset\"])\n        data = self.buf.read(8)\n        self.buf.seek(header[\"offset\"])\n\n        im: Image.Image\n        if data[:8] == PngImagePlugin._MAGIC:\n            # png frame\n            im = PngImagePlugin.PngImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n        else:\n            # XOR + AND mask bmp frame\n            im = BmpImagePlugin.DibImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n\n            # change tile dimension to only encompass XOR image\n            im._size = (im.size[0], int(im.size[1] / 2))\n            d, e, o, a = im.tile[0]\n            im.tile[0] = d, (0, 0) + im.size, o, a\n\n            # figure out where AND mask image starts\n            bpp = header[\"bpp\"]\n            if 32 == bpp:\n                # 32-bit color depth icon image allows semitransparent areas\n                # PIL's DIB format ignores transparency bits, recover them.\n                # The DIB is packed in BGRX byte order where X is the alpha\n                # channel.\n\n                # Back up to start of bmp data\n                self.buf.seek(o)\n                # extract every 4th byte (eg. 3,7,11,15,...)\n                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]\n\n                # convert to an 8bpp grayscale image\n                mask = Image.frombuffer(\n                    \"L\",  # 8bpp\n                    im.size,  # (w, h)\n                    alpha_bytes,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed\n                )\n            else:\n                # get AND image from end of bitmap\n                w = im.size[0]\n                if (w % 32) > 0:\n                    # bitmap row data is aligned to word boundaries\n                    w += 32 - (im.size[0] % 32)\n\n                # the total mask data is\n                # padded row size * height / bits per char\n\n                total_bytes = int((w * im.size[1]) / 8)\n                and_mask_offset = header[\"offset\"] + header[\"size\"] - total_bytes\n\n                self.buf.seek(and_mask_offset)\n                mask_data = self.buf.read(total_bytes)\n\n                # convert raw data to image\n                mask = Image.frombuffer(\n                    \"1\",  # 1 bpp\n                    im.size,  # (w, h)\n                    mask_data,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed\n                )\n\n                # now we have two images, im is XOR image and mask is AND image\n\n            # apply mask image as alpha channel\n            im = im.convert(\"RGBA\")\n            im.putalpha(mask)\n\n        return im",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 122,
                            "end_lineno": 276,
                            "parent": null
                        },
                        {
                            "file_name": "src/PIL/IcoImagePlugin.py",
                            "type": "method",
                            "name": "IcoFile.frame",
                            "content": "class IcoFile:\n    def __init__(self, buf):\n        \"\"\"\n        Parse image from file-like object containing ico file data\n        \"\"\"\n\n        # check magic\n        s = buf.read(6)\n        if not _accept(s):\n            msg = \"not an ICO file\"\n            raise SyntaxError(msg)\n\n        self.buf = buf\n        self.entry = []\n\n        # Number of items in file\n        self.nb_items = i16(s, 4)\n\n        # Get headers for each item\n        for i in range(self.nb_items):\n            s = buf.read(16)\n\n            icon_header = {\n                \"width\": s[0],\n                \"height\": s[1],\n                \"nb_color\": s[2],  # No. of colors in image (0 if >=8bpp)\n                \"reserved\": s[3],\n                \"planes\": i16(s, 4),\n                \"bpp\": i16(s, 6),\n                \"size\": i32(s, 8),\n                \"offset\": i32(s, 12),\n            }\n\n            # See Wikipedia\n            for j in (\"width\", \"height\"):\n                if not icon_header[j]:\n                    icon_header[j] = 256\n\n            # See Wikipedia notes about color depth.\n            # We need this just to differ images with equal sizes\n            icon_header[\"color_depth\"] = (\n                icon_header[\"bpp\"]\n                or (\n                    icon_header[\"nb_color\"] != 0\n                    and ceil(log(icon_header[\"nb_color\"], 2))\n                )\n                or 256\n            )\n\n            icon_header[\"dim\"] = (icon_header[\"width\"], icon_header[\"height\"])\n            icon_header[\"square\"] = icon_header[\"width\"] * icon_header[\"height\"]\n\n            self.entry.append(icon_header)\n\n        self.entry = sorted(self.entry, key=lambda x: x[\"color_depth\"])\n        # ICO images are usually squares\n        self.entry = sorted(self.entry, key=lambda x: x[\"square\"], reverse=True)\n\n    def sizes(self):\n        \"\"\"\n        Get a list of all available icon sizes and color depths.\n        \"\"\"\n        return {(h[\"width\"], h[\"height\"]) for h in self.entry}\n\n    def getentryindex(self, size, bpp=False):\n        for i, h in enumerate(self.entry):\n            if size == h[\"dim\"] and (bpp is False or bpp == h[\"color_depth\"]):\n                return i\n        return 0\n\n    def getimage(self, size, bpp=False):\n        \"\"\"\n        Get an image from the icon\n        \"\"\"\n        return self.frame(self.getentryindex(size, bpp))\n\n    def frame(self, idx: int) -> Image.Image:\n        \"\"\"\n        Get an image from frame idx\n        \"\"\"\n\n        header = self.entry[idx]\n\n        self.buf.seek(header[\"offset\"])\n        data = self.buf.read(8)\n        self.buf.seek(header[\"offset\"])\n\n        im: Image.Image\n        if data[:8] == PngImagePlugin._MAGIC:\n            # png frame\n            im = PngImagePlugin.PngImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n        else:\n            # XOR + AND mask bmp frame\n            im = BmpImagePlugin.DibImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n\n            # change tile dimension to only encompass XOR image\n            im._size = (im.size[0], int(im.size[1] / 2))\n            d, e, o, a = im.tile[0]\n            im.tile[0] = d, (0, 0) + im.size, o, a\n\n            # figure out where AND mask image starts\n            bpp = header[\"bpp\"]\n            if 32 == bpp:\n                # 32-bit color depth icon image allows semitransparent areas\n                # PIL's DIB format ignores transparency bits, recover them.\n                # The DIB is packed in BGRX byte order where X is the alpha\n                # channel.\n\n                # Back up to start of bmp data\n                self.buf.seek(o)\n                # extract every 4th byte (eg. 3,7,11,15,...)\n                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]\n\n                # convert to an 8bpp grayscale image\n                mask = Image.frombuffer(\n                    \"L\",  # 8bpp\n                    im.size,  # (w, h)\n                    alpha_bytes,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed\n                )\n            else:\n                # get AND image from end of bitmap\n                w = im.size[0]\n                if (w % 32) > 0:\n                    # bitmap row data is aligned to word boundaries\n                    w += 32 - (im.size[0] % 32)\n\n                # the total mask data is\n                # padded row size * height / bits per char\n\n                total_bytes = int((w * im.size[1]) / 8)\n                and_mask_offset = header[\"offset\"] + header[\"size\"] - total_bytes\n\n                self.buf.seek(and_mask_offset)\n                mask_data = self.buf.read(total_bytes)\n\n                # convert raw data to image\n                mask = Image.frombuffer(\n                    \"1\",  # 1 bpp\n                    im.size,  # (w, h)\n                    mask_data,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed\n                )\n\n                # now we have two images, im is XOR image and mask is AND image\n\n            # apply mask image as alpha channel\n            im = im.convert(\"RGBA\")\n            im.putalpha(mask)\n\n        return im",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 198,
                            "end_lineno": 276,
                            "parent": {
                                "file_name": "src/PIL/IcoImagePlugin.py",
                                "type": "class",
                                "name": "IcoFile",
                                "content": "class IcoFile:\n    def __init__(self, buf):\n        \"\"\"\n        Parse image from file-like object containing ico file data\n        \"\"\"\n\n        # check magic\n        s = buf.read(6)\n        if not _accept(s):\n            msg = \"not an ICO file\"\n            raise SyntaxError(msg)\n\n        self.buf = buf\n        self.entry = []\n\n        # Number of items in file\n        self.nb_items = i16(s, 4)\n\n        # Get headers for each item\n        for i in range(self.nb_items):\n            s = buf.read(16)\n\n            icon_header = {\n                \"width\": s[0],\n                \"height\": s[1],\n                \"nb_color\": s[2],  # No. of colors in image (0 if >=8bpp)\n                \"reserved\": s[3],\n                \"planes\": i16(s, 4),\n                \"bpp\": i16(s, 6),\n                \"size\": i32(s, 8),\n                \"offset\": i32(s, 12),\n            }\n\n            # See Wikipedia\n            for j in (\"width\", \"height\"):\n                if not icon_header[j]:\n                    icon_header[j] = 256\n\n            # See Wikipedia notes about color depth.\n            # We need this just to differ images with equal sizes\n            icon_header[\"color_depth\"] = (\n                icon_header[\"bpp\"]\n                or (\n                    icon_header[\"nb_color\"] != 0\n                    and ceil(log(icon_header[\"nb_color\"], 2))\n                )\n                or 256\n            )\n\n            icon_header[\"dim\"] = (icon_header[\"width\"], icon_header[\"height\"])\n            icon_header[\"square\"] = icon_header[\"width\"] * icon_header[\"height\"]\n\n            self.entry.append(icon_header)\n\n        self.entry = sorted(self.entry, key=lambda x: x[\"color_depth\"])\n        # ICO images are usually squares\n        self.entry = sorted(self.entry, key=lambda x: x[\"square\"], reverse=True)\n\n    def sizes(self):\n        \"\"\"\n        Get a list of all available icon sizes and color depths.\n        \"\"\"\n        return {(h[\"width\"], h[\"height\"]) for h in self.entry}\n\n    def getentryindex(self, size, bpp=False):\n        for i, h in enumerate(self.entry):\n            if size == h[\"dim\"] and (bpp is False or bpp == h[\"color_depth\"]):\n                return i\n        return 0\n\n    def getimage(self, size, bpp=False):\n        \"\"\"\n        Get an image from the icon\n        \"\"\"\n        return self.frame(self.getentryindex(size, bpp))\n\n    def frame(self, idx: int) -> Image.Image:\n        \"\"\"\n        Get an image from frame idx\n        \"\"\"\n\n        header = self.entry[idx]\n\n        self.buf.seek(header[\"offset\"])\n        data = self.buf.read(8)\n        self.buf.seek(header[\"offset\"])\n\n        im: Image.Image\n        if data[:8] == PngImagePlugin._MAGIC:\n            # png frame\n            im = PngImagePlugin.PngImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n        else:\n            # XOR + AND mask bmp frame\n            im = BmpImagePlugin.DibImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n\n            # change tile dimension to only encompass XOR image\n            im._size = (im.size[0], int(im.size[1] / 2))\n            d, e, o, a = im.tile[0]\n            im.tile[0] = d, (0, 0) + im.size, o, a\n\n            # figure out where AND mask image starts\n            bpp = header[\"bpp\"]\n            if 32 == bpp:\n                # 32-bit color depth icon image allows semitransparent areas\n                # PIL's DIB format ignores transparency bits, recover them.\n                # The DIB is packed in BGRX byte order where X is the alpha\n                # channel.\n\n                # Back up to start of bmp data\n                self.buf.seek(o)\n                # extract every 4th byte (eg. 3,7,11,15,...)\n                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]\n\n                # convert to an 8bpp grayscale image\n                mask = Image.frombuffer(\n                    \"L\",  # 8bpp\n                    im.size,  # (w, h)\n                    alpha_bytes,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed\n                )\n            else:\n                # get AND image from end of bitmap\n                w = im.size[0]\n                if (w % 32) > 0:\n                    # bitmap row data is aligned to word boundaries\n                    w += 32 - (im.size[0] % 32)\n\n                # the total mask data is\n                # padded row size * height / bits per char\n\n                total_bytes = int((w * im.size[1]) / 8)\n                and_mask_offset = header[\"offset\"] + header[\"size\"] - total_bytes\n\n                self.buf.seek(and_mask_offset)\n                mask_data = self.buf.read(total_bytes)\n\n                # convert raw data to image\n                mask = Image.frombuffer(\n                    \"1\",  # 1 bpp\n                    im.size,  # (w, h)\n                    mask_data,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed\n                )\n\n                # now we have two images, im is XOR image and mask is AND image\n\n            # apply mask image as alpha channel\n            im = im.convert(\"RGBA\")\n            im.putalpha(mask)\n\n        return im",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 122,
                                "end_lineno": 276,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 259,
                            "length": 19
                        },
                        "new_range": {
                            "start": 265,
                            "length": 26
                        },
                        "section": "class IcoFile:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "                mask_data = self.buf.read(total_bytes)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "                # convert raw data to image",
                                "type": "context"
                            },
                            {
                                "content": "                mask = Image.frombuffer(",
                                "type": "deleted"
                            },
                            {
                                "content": "                    \"1\",  # 1 bpp",
                                "type": "deleted"
                            },
                            {
                                "content": "                    im.size,  # (w, h)",
                                "type": "deleted"
                            },
                            {
                                "content": "                    mask_data,  # source chars",
                                "type": "deleted"
                            },
                            {
                                "content": "                    \"raw\",  # raw decoder",
                                "type": "deleted"
                            },
                            {
                                "content": "                    (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed",
                                "type": "deleted"
                            },
                            {
                                "content": "                )",
                                "type": "deleted"
                            },
                            {
                                "content": "                try:",
                                "type": "added"
                            },
                            {
                                "content": "                    mask = Image.frombuffer(",
                                "type": "added"
                            },
                            {
                                "content": "                        \"1\",  # 1 bpp",
                                "type": "added"
                            },
                            {
                                "content": "                        im.size,  # (w, h)",
                                "type": "added"
                            },
                            {
                                "content": "                        mask_data,  # source chars",
                                "type": "added"
                            },
                            {
                                "content": "                        \"raw\",  # raw decoder",
                                "type": "added"
                            },
                            {
                                "content": "                        (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed",
                                "type": "added"
                            },
                            {
                                "content": "                    )",
                                "type": "added"
                            },
                            {
                                "content": "                except ValueError:",
                                "type": "added"
                            },
                            {
                                "content": "                    if ImageFile.LOAD_TRUNCATED_IMAGES:",
                                "type": "added"
                            },
                            {
                                "content": "                        mask = None",
                                "type": "added"
                            },
                            {
                                "content": "                    else:",
                                "type": "added"
                            },
                            {
                                "content": "                        raise",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "                # now we have two images, im is XOR image and mask is AND image",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "            # apply mask image as alpha channel",
                                "type": "context"
                            },
                            {
                                "content": "            im = im.convert(\"RGBA\")",
                                "type": "deleted"
                            },
                            {
                                "content": "            im.putalpha(mask)",
                                "type": "deleted"
                            },
                            {
                                "content": "            if mask:",
                                "type": "added"
                            },
                            {
                                "content": "                im = im.convert(\"RGBA\")",
                                "type": "added"
                            },
                            {
                                "content": "                im.putalpha(mask)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        return im",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "src/PIL/IcoImagePlugin.py",
                            "type": "class",
                            "name": "IcoFile",
                            "content": "class IcoFile:\n    def __init__(self, buf):\n        \"\"\"\n        Parse image from file-like object containing ico file data\n        \"\"\"\n\n        # check magic\n        s = buf.read(6)\n        if not _accept(s):\n            msg = \"not an ICO file\"\n            raise SyntaxError(msg)\n\n        self.buf = buf\n        self.entry = []\n\n        # Number of items in file\n        self.nb_items = i16(s, 4)\n\n        # Get headers for each item\n        for i in range(self.nb_items):\n            s = buf.read(16)\n\n            icon_header = {\n                \"width\": s[0],\n                \"height\": s[1],\n                \"nb_color\": s[2],  # No. of colors in image (0 if >=8bpp)\n                \"reserved\": s[3],\n                \"planes\": i16(s, 4),\n                \"bpp\": i16(s, 6),\n                \"size\": i32(s, 8),\n                \"offset\": i32(s, 12),\n            }\n\n            # See Wikipedia\n            for j in (\"width\", \"height\"):\n                if not icon_header[j]:\n                    icon_header[j] = 256\n\n            # See Wikipedia notes about color depth.\n            # We need this just to differ images with equal sizes\n            icon_header[\"color_depth\"] = (\n                icon_header[\"bpp\"]\n                or (\n                    icon_header[\"nb_color\"] != 0\n                    and ceil(log(icon_header[\"nb_color\"], 2))\n                )\n                or 256\n            )\n\n            icon_header[\"dim\"] = (icon_header[\"width\"], icon_header[\"height\"])\n            icon_header[\"square\"] = icon_header[\"width\"] * icon_header[\"height\"]\n\n            self.entry.append(icon_header)\n\n        self.entry = sorted(self.entry, key=lambda x: x[\"color_depth\"])\n        # ICO images are usually squares\n        self.entry = sorted(self.entry, key=lambda x: x[\"square\"], reverse=True)\n\n    def sizes(self):\n        \"\"\"\n        Get a list of all available icon sizes and color depths.\n        \"\"\"\n        return {(h[\"width\"], h[\"height\"]) for h in self.entry}\n\n    def getentryindex(self, size, bpp=False):\n        for i, h in enumerate(self.entry):\n            if size == h[\"dim\"] and (bpp is False or bpp == h[\"color_depth\"]):\n                return i\n        return 0\n\n    def getimage(self, size, bpp=False):\n        \"\"\"\n        Get an image from the icon\n        \"\"\"\n        return self.frame(self.getentryindex(size, bpp))\n\n    def frame(self, idx: int) -> Image.Image:\n        \"\"\"\n        Get an image from frame idx\n        \"\"\"\n\n        header = self.entry[idx]\n\n        self.buf.seek(header[\"offset\"])\n        data = self.buf.read(8)\n        self.buf.seek(header[\"offset\"])\n\n        im: Image.Image\n        if data[:8] == PngImagePlugin._MAGIC:\n            # png frame\n            im = PngImagePlugin.PngImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n        else:\n            # XOR + AND mask bmp frame\n            im = BmpImagePlugin.DibImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n\n            # change tile dimension to only encompass XOR image\n            im._size = (im.size[0], int(im.size[1] / 2))\n            d, e, o, a = im.tile[0]\n            im.tile[0] = d, (0, 0) + im.size, o, a\n\n            # figure out where AND mask image starts\n            bpp = header[\"bpp\"]\n            if 32 == bpp:\n                # 32-bit color depth icon image allows semitransparent areas\n                # PIL's DIB format ignores transparency bits, recover them.\n                # The DIB is packed in BGRX byte order where X is the alpha\n                # channel.\n\n                # Back up to start of bmp data\n                self.buf.seek(o)\n                # extract every 4th byte (eg. 3,7,11,15,...)\n                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]\n\n                # convert to an 8bpp grayscale image\n                mask = Image.frombuffer(\n                    \"L\",  # 8bpp\n                    im.size,  # (w, h)\n                    alpha_bytes,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed\n                )\n            else:\n                # get AND image from end of bitmap\n                w = im.size[0]\n                if (w % 32) > 0:\n                    # bitmap row data is aligned to word boundaries\n                    w += 32 - (im.size[0] % 32)\n\n                # the total mask data is\n                # padded row size * height / bits per char\n\n                total_bytes = int((w * im.size[1]) / 8)\n                and_mask_offset = header[\"offset\"] + header[\"size\"] - total_bytes\n\n                self.buf.seek(and_mask_offset)\n                mask_data = self.buf.read(total_bytes)\n\n                # convert raw data to image\n                mask = Image.frombuffer(\n                    \"1\",  # 1 bpp\n                    im.size,  # (w, h)\n                    mask_data,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed\n                )\n\n                # now we have two images, im is XOR image and mask is AND image\n\n            # apply mask image as alpha channel\n            im = im.convert(\"RGBA\")\n            im.putalpha(mask)\n\n        return im",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 122,
                            "end_lineno": 276,
                            "parent": null
                        },
                        {
                            "file_name": "src/PIL/IcoImagePlugin.py",
                            "type": "method",
                            "name": "IcoFile.frame",
                            "content": "class IcoFile:\n    def __init__(self, buf):\n        \"\"\"\n        Parse image from file-like object containing ico file data\n        \"\"\"\n\n        # check magic\n        s = buf.read(6)\n        if not _accept(s):\n            msg = \"not an ICO file\"\n            raise SyntaxError(msg)\n\n        self.buf = buf\n        self.entry = []\n\n        # Number of items in file\n        self.nb_items = i16(s, 4)\n\n        # Get headers for each item\n        for i in range(self.nb_items):\n            s = buf.read(16)\n\n            icon_header = {\n                \"width\": s[0],\n                \"height\": s[1],\n                \"nb_color\": s[2],  # No. of colors in image (0 if >=8bpp)\n                \"reserved\": s[3],\n                \"planes\": i16(s, 4),\n                \"bpp\": i16(s, 6),\n                \"size\": i32(s, 8),\n                \"offset\": i32(s, 12),\n            }\n\n            # See Wikipedia\n            for j in (\"width\", \"height\"):\n                if not icon_header[j]:\n                    icon_header[j] = 256\n\n            # See Wikipedia notes about color depth.\n            # We need this just to differ images with equal sizes\n            icon_header[\"color_depth\"] = (\n                icon_header[\"bpp\"]\n                or (\n                    icon_header[\"nb_color\"] != 0\n                    and ceil(log(icon_header[\"nb_color\"], 2))\n                )\n                or 256\n            )\n\n            icon_header[\"dim\"] = (icon_header[\"width\"], icon_header[\"height\"])\n            icon_header[\"square\"] = icon_header[\"width\"] * icon_header[\"height\"]\n\n            self.entry.append(icon_header)\n\n        self.entry = sorted(self.entry, key=lambda x: x[\"color_depth\"])\n        # ICO images are usually squares\n        self.entry = sorted(self.entry, key=lambda x: x[\"square\"], reverse=True)\n\n    def sizes(self):\n        \"\"\"\n        Get a list of all available icon sizes and color depths.\n        \"\"\"\n        return {(h[\"width\"], h[\"height\"]) for h in self.entry}\n\n    def getentryindex(self, size, bpp=False):\n        for i, h in enumerate(self.entry):\n            if size == h[\"dim\"] and (bpp is False or bpp == h[\"color_depth\"]):\n                return i\n        return 0\n\n    def getimage(self, size, bpp=False):\n        \"\"\"\n        Get an image from the icon\n        \"\"\"\n        return self.frame(self.getentryindex(size, bpp))\n\n    def frame(self, idx: int) -> Image.Image:\n        \"\"\"\n        Get an image from frame idx\n        \"\"\"\n\n        header = self.entry[idx]\n\n        self.buf.seek(header[\"offset\"])\n        data = self.buf.read(8)\n        self.buf.seek(header[\"offset\"])\n\n        im: Image.Image\n        if data[:8] == PngImagePlugin._MAGIC:\n            # png frame\n            im = PngImagePlugin.PngImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n        else:\n            # XOR + AND mask bmp frame\n            im = BmpImagePlugin.DibImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n\n            # change tile dimension to only encompass XOR image\n            im._size = (im.size[0], int(im.size[1] / 2))\n            d, e, o, a = im.tile[0]\n            im.tile[0] = d, (0, 0) + im.size, o, a\n\n            # figure out where AND mask image starts\n            bpp = header[\"bpp\"]\n            if 32 == bpp:\n                # 32-bit color depth icon image allows semitransparent areas\n                # PIL's DIB format ignores transparency bits, recover them.\n                # The DIB is packed in BGRX byte order where X is the alpha\n                # channel.\n\n                # Back up to start of bmp data\n                self.buf.seek(o)\n                # extract every 4th byte (eg. 3,7,11,15,...)\n                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]\n\n                # convert to an 8bpp grayscale image\n                mask = Image.frombuffer(\n                    \"L\",  # 8bpp\n                    im.size,  # (w, h)\n                    alpha_bytes,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed\n                )\n            else:\n                # get AND image from end of bitmap\n                w = im.size[0]\n                if (w % 32) > 0:\n                    # bitmap row data is aligned to word boundaries\n                    w += 32 - (im.size[0] % 32)\n\n                # the total mask data is\n                # padded row size * height / bits per char\n\n                total_bytes = int((w * im.size[1]) / 8)\n                and_mask_offset = header[\"offset\"] + header[\"size\"] - total_bytes\n\n                self.buf.seek(and_mask_offset)\n                mask_data = self.buf.read(total_bytes)\n\n                # convert raw data to image\n                mask = Image.frombuffer(\n                    \"1\",  # 1 bpp\n                    im.size,  # (w, h)\n                    mask_data,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed\n                )\n\n                # now we have two images, im is XOR image and mask is AND image\n\n            # apply mask image as alpha channel\n            im = im.convert(\"RGBA\")\n            im.putalpha(mask)\n\n        return im",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 198,
                            "end_lineno": 276,
                            "parent": {
                                "file_name": "src/PIL/IcoImagePlugin.py",
                                "type": "class",
                                "name": "IcoFile",
                                "content": "class IcoFile:\n    def __init__(self, buf):\n        \"\"\"\n        Parse image from file-like object containing ico file data\n        \"\"\"\n\n        # check magic\n        s = buf.read(6)\n        if not _accept(s):\n            msg = \"not an ICO file\"\n            raise SyntaxError(msg)\n\n        self.buf = buf\n        self.entry = []\n\n        # Number of items in file\n        self.nb_items = i16(s, 4)\n\n        # Get headers for each item\n        for i in range(self.nb_items):\n            s = buf.read(16)\n\n            icon_header = {\n                \"width\": s[0],\n                \"height\": s[1],\n                \"nb_color\": s[2],  # No. of colors in image (0 if >=8bpp)\n                \"reserved\": s[3],\n                \"planes\": i16(s, 4),\n                \"bpp\": i16(s, 6),\n                \"size\": i32(s, 8),\n                \"offset\": i32(s, 12),\n            }\n\n            # See Wikipedia\n            for j in (\"width\", \"height\"):\n                if not icon_header[j]:\n                    icon_header[j] = 256\n\n            # See Wikipedia notes about color depth.\n            # We need this just to differ images with equal sizes\n            icon_header[\"color_depth\"] = (\n                icon_header[\"bpp\"]\n                or (\n                    icon_header[\"nb_color\"] != 0\n                    and ceil(log(icon_header[\"nb_color\"], 2))\n                )\n                or 256\n            )\n\n            icon_header[\"dim\"] = (icon_header[\"width\"], icon_header[\"height\"])\n            icon_header[\"square\"] = icon_header[\"width\"] * icon_header[\"height\"]\n\n            self.entry.append(icon_header)\n\n        self.entry = sorted(self.entry, key=lambda x: x[\"color_depth\"])\n        # ICO images are usually squares\n        self.entry = sorted(self.entry, key=lambda x: x[\"square\"], reverse=True)\n\n    def sizes(self):\n        \"\"\"\n        Get a list of all available icon sizes and color depths.\n        \"\"\"\n        return {(h[\"width\"], h[\"height\"]) for h in self.entry}\n\n    def getentryindex(self, size, bpp=False):\n        for i, h in enumerate(self.entry):\n            if size == h[\"dim\"] and (bpp is False or bpp == h[\"color_depth\"]):\n                return i\n        return 0\n\n    def getimage(self, size, bpp=False):\n        \"\"\"\n        Get an image from the icon\n        \"\"\"\n        return self.frame(self.getentryindex(size, bpp))\n\n    def frame(self, idx: int) -> Image.Image:\n        \"\"\"\n        Get an image from frame idx\n        \"\"\"\n\n        header = self.entry[idx]\n\n        self.buf.seek(header[\"offset\"])\n        data = self.buf.read(8)\n        self.buf.seek(header[\"offset\"])\n\n        im: Image.Image\n        if data[:8] == PngImagePlugin._MAGIC:\n            # png frame\n            im = PngImagePlugin.PngImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n        else:\n            # XOR + AND mask bmp frame\n            im = BmpImagePlugin.DibImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n\n            # change tile dimension to only encompass XOR image\n            im._size = (im.size[0], int(im.size[1] / 2))\n            d, e, o, a = im.tile[0]\n            im.tile[0] = d, (0, 0) + im.size, o, a\n\n            # figure out where AND mask image starts\n            bpp = header[\"bpp\"]\n            if 32 == bpp:\n                # 32-bit color depth icon image allows semitransparent areas\n                # PIL's DIB format ignores transparency bits, recover them.\n                # The DIB is packed in BGRX byte order where X is the alpha\n                # channel.\n\n                # Back up to start of bmp data\n                self.buf.seek(o)\n                # extract every 4th byte (eg. 3,7,11,15,...)\n                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]\n\n                # convert to an 8bpp grayscale image\n                mask = Image.frombuffer(\n                    \"L\",  # 8bpp\n                    im.size,  # (w, h)\n                    alpha_bytes,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed\n                )\n            else:\n                # get AND image from end of bitmap\n                w = im.size[0]\n                if (w % 32) > 0:\n                    # bitmap row data is aligned to word boundaries\n                    w += 32 - (im.size[0] % 32)\n\n                # the total mask data is\n                # padded row size * height / bits per char\n\n                total_bytes = int((w * im.size[1]) / 8)\n                and_mask_offset = header[\"offset\"] + header[\"size\"] - total_bytes\n\n                self.buf.seek(and_mask_offset)\n                mask_data = self.buf.read(total_bytes)\n\n                # convert raw data to image\n                mask = Image.frombuffer(\n                    \"1\",  # 1 bpp\n                    im.size,  # (w, h)\n                    mask_data,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed\n                )\n\n                # now we have two images, im is XOR image and mask is AND image\n\n            # apply mask image as alpha channel\n            im = im.convert(\"RGBA\")\n            im.putalpha(mask)\n\n        return im",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 122,
                                "end_lineno": 276,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "d3f343bb9b658ff97cbac826f9d1d4763a352e85^",
    "new_commit_hash": "d3f343bb9b658ff97cbac826f9d1d4763a352e85",
    "commit_message": "Skip truncated mask if LOAD_TRUNCATED_IMAGES is enabled",
    "commit_date": "2024-07-01T23:04:47+10:00",
    "metadata": {}
}
{
    "repo_name": "pillow",
    "new_commit_hash": "d3f343bb9b658ff97cbac826f9d1d4763a352e85",
    "test_file_codes": [
        "from __future__ import annotations\n\nimport io\nimport os\nfrom pathlib import Path\n\nimport pytest\n\nfrom PIL import IcoImagePlugin, Image, ImageDraw, ImageFile\n\nfrom .helper import assert_image_equal, assert_image_equal_tofile, hopper\n\nTEST_ICO_FILE = \"Tests/images/hopper.ico\"\n\n\ndef test_sanity() -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        im.load()\n    assert im.mode == \"RGBA\"\n    assert im.size == (16, 16)\n    assert im.format == \"ICO\"\n    assert im.get_format_mimetype() == \"image/x-icon\"\n\n\ndef test_load() -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        assert im.load()[0, 0] == (1, 1, 9, 255)\n\n\ndef test_mask() -> None:\n    with Image.open(\"Tests/images/hopper_mask.ico\") as im:\n        assert_image_equal_tofile(im, \"Tests/images/hopper_mask.png\")\n\n\ndef test_black_and_white() -> None:\n    with Image.open(\"Tests/images/black_and_white.ico\") as im:\n        assert im.mode == \"RGBA\"\n        assert im.size == (16, 16)\n\n\ndef test_palette(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n\n    im = Image.new(\"P\", (16, 16))\n    im.save(temp_file)\n\n    with Image.open(temp_file) as reloaded:\n        assert reloaded.mode == \"P\"\n        assert reloaded.palette is not None\n\n\ndef test_invalid_file() -> None:\n    with open(\"Tests/images/flower.jpg\", \"rb\") as fp:\n        with pytest.raises(SyntaxError):\n            IcoImagePlugin.IcoImageFile(fp)\n\n\ndef test_save_to_bytes() -> None:\n    output = io.BytesIO()\n    im = hopper()\n    im.save(output, \"ico\", sizes=[(32, 32), (64, 64)])\n\n    # The default image\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert reloaded.info[\"sizes\"] == {(32, 32), (64, 64)}\n\n        assert im.mode == reloaded.mode\n        assert (64, 64) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        assert_image_equal(\n            reloaded, hopper().resize((64, 64), Image.Resampling.LANCZOS)\n        )\n\n    # The other one\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        reloaded.size = (32, 32)\n\n        assert im.mode == reloaded.mode\n        assert (32, 32) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        assert_image_equal(\n            reloaded, hopper().resize((32, 32), Image.Resampling.LANCZOS)\n        )\n\n\ndef test_getpixel(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n\n    im = hopper()\n    im.save(temp_file, \"ico\", sizes=[(32, 32), (64, 64)])\n\n    with Image.open(temp_file) as reloaded:\n        reloaded.load()\n        reloaded.size = (32, 32)\n\n        assert reloaded.getpixel((0, 0)) == (18, 20, 62)\n\n\ndef test_no_duplicates(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n    temp_file2 = str(tmp_path / \"temp2.ico\")\n\n    im = hopper()\n    sizes = [(32, 32), (64, 64)]\n    im.save(temp_file, \"ico\", sizes=sizes)\n\n    sizes.append(sizes[-1])\n    im.save(temp_file2, \"ico\", sizes=sizes)\n\n    assert os.path.getsize(temp_file) == os.path.getsize(temp_file2)\n\n\ndef test_different_bit_depths(tmp_path: Path) -> None:\n    temp_file = str(tmp_path / \"temp.ico\")\n    temp_file2 = str(tmp_path / \"temp2.ico\")\n\n    im = hopper()\n    im.save(temp_file, \"ico\", bitmap_format=\"bmp\", sizes=[(128, 128)])\n\n    hopper(\"1\").save(\n        temp_file2,\n        \"ico\",\n        bitmap_format=\"bmp\",\n        sizes=[(128, 128)],\n        append_images=[im],\n    )\n\n    assert os.path.getsize(temp_file) != os.path.getsize(temp_file2)\n\n    # Test that only matching sizes of different bit depths are saved\n    temp_file3 = str(tmp_path / \"temp3.ico\")\n    temp_file4 = str(tmp_path / \"temp4.ico\")\n\n    im.save(temp_file3, \"ico\", bitmap_format=\"bmp\", sizes=[(128, 128)])\n    im.save(\n        temp_file4,\n        \"ico\",\n        bitmap_format=\"bmp\",\n        sizes=[(128, 128)],\n        append_images=[Image.new(\"P\", (64, 64))],\n    )\n\n    assert os.path.getsize(temp_file3) == os.path.getsize(temp_file4)\n\n\n@pytest.mark.parametrize(\"mode\", (\"1\", \"L\", \"P\", \"RGB\", \"RGBA\"))\ndef test_save_to_bytes_bmp(mode: str) -> None:\n    output = io.BytesIO()\n    im = hopper(mode)\n    im.save(output, \"ico\", bitmap_format=\"bmp\", sizes=[(32, 32), (64, 64)])\n\n    # The default image\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert reloaded.info[\"sizes\"] == {(32, 32), (64, 64)}\n\n        assert \"RGBA\" == reloaded.mode\n        assert (64, 64) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        im = hopper(mode).resize((64, 64), Image.Resampling.LANCZOS).convert(\"RGBA\")\n        assert_image_equal(reloaded, im)\n\n    # The other one\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        reloaded.size = (32, 32)\n\n        assert \"RGBA\" == reloaded.mode\n        assert (32, 32) == reloaded.size\n        assert reloaded.format == \"ICO\"\n        im = hopper(mode).resize((32, 32), Image.Resampling.LANCZOS).convert(\"RGBA\")\n        assert_image_equal(reloaded, im)\n\n\ndef test_incorrect_size() -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        with pytest.raises(ValueError):\n            im.size = (1, 1)\n\n\ndef test_save_256x256(tmp_path: Path) -> None:\n    \"\"\"Issue #2264 https://github.com/python-pillow/Pillow/issues/2264\"\"\"\n    # Arrange\n    with Image.open(\"Tests/images/hopper_256x256.ico\") as im:\n        outfile = str(tmp_path / \"temp_saved_hopper_256x256.ico\")\n\n        # Act\n        im.save(outfile)\n    with Image.open(outfile) as im_saved:\n        # Assert\n        assert im_saved.size == (256, 256)\n\n\ndef test_only_save_relevant_sizes(tmp_path: Path) -> None:\n    \"\"\"Issue #2266 https://github.com/python-pillow/Pillow/issues/2266\n    Should save in 16x16, 24x24, 32x32, 48x48 sizes\n    and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes\n    \"\"\"\n    # Arrange\n    with Image.open(\"Tests/images/python.ico\") as im:  # 16x16, 32x32, 48x48\n        outfile = str(tmp_path / \"temp_saved_python.ico\")\n        # Act\n        im.save(outfile)\n\n    with Image.open(outfile) as im_saved:\n        # Assert\n        assert im_saved.info[\"sizes\"] == {(16, 16), (24, 24), (32, 32), (48, 48)}\n\n\ndef test_save_append_images(tmp_path: Path) -> None:\n    # append_images should be used for scaled down versions of the image\n    im = hopper(\"RGBA\")\n    provided_im = Image.new(\"RGBA\", (32, 32), (255, 0, 0))\n    outfile = str(tmp_path / \"temp_saved_multi_icon.ico\")\n    im.save(outfile, sizes=[(32, 32), (128, 128)], append_images=[provided_im])\n\n    with Image.open(outfile) as reread:\n        assert_image_equal(reread, hopper(\"RGBA\"))\n\n        reread.size = (32, 32)\n        assert_image_equal(reread, provided_im)\n\n\ndef test_unexpected_size() -> None:\n    # This image has been manually hexedited to state that it is 16x32\n    # while the image within is still 16x16\n    with pytest.warns(UserWarning):\n        with Image.open(\"Tests/images/hopper_unexpected.ico\") as im:\n            assert im.size == (16, 16)\n\n\ndef test_draw_reloaded(tmp_path: Path) -> None:\n    with Image.open(TEST_ICO_FILE) as im:\n        outfile = str(tmp_path / \"temp_saved_hopper_draw.ico\")\n\n        draw = ImageDraw.Draw(im)\n        draw.line((0, 0) + im.size, \"#f00\")\n        im.save(outfile)\n\n    with Image.open(outfile) as im:\n        assert_image_equal_tofile(im, \"Tests/images/hopper_draw.ico\")\n\n\ndef test_truncated_mask() -> None:\n    # 1 bpp\n    with open(\"Tests/images/hopper_mask.ico\", \"rb\") as fp:\n        data = fp.read()\n\n    ImageFile.LOAD_TRUNCATED_IMAGES = True\n    data = data[:-3]\n\n    try:\n        with Image.open(io.BytesIO(data)) as im:\n            with Image.open(\"Tests/images/hopper_mask.png\") as expected:\n                assert im.mode == \"1\"\n\n        # 32 bpp\n        output = io.BytesIO()\n        expected = hopper(\"RGBA\")\n        expected.save(output, \"ico\", bitmap_format=\"bmp\")\n\n        data = output.getvalue()[:-1]\n\n        with Image.open(io.BytesIO(data)) as im:\n            assert im.mode == \"RGB\"\n    finally:\n        ImageFile.LOAD_TRUNCATED_IMAGES = False\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": "warning: `VIRTUAL_ENV=/home/gcpuser/r2e-edits-internal/.venv` does not match the project environment path `.venv` and will be ignored\nUsing CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 12 packages in 11ms\nInstalled 12 packages in 259ms\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 1.21s\nPrepared 1 package in 34.38s\nInstalled 1 package in 1ms\n + pillow==11.0.0.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_d3f343bb9b658ff97cbac826f9d1d4763a352e85)\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_d3f343bb9b658ff97cbac826f9d1d4763a352e85\nconfigfile: pyproject.toml\nplugins: cov-6.0.0\ncollected 22 items\n\nr2e_tests/test_1.py \u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\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_load\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_mask\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_black_and_white\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_palette\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_invalid_file\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_getpixel\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_no_duplicates\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_different_bit_depths\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[1]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[L]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[P]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[RGB]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[RGBA]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_incorrect_size\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_256x256\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_only_save_relevant_sizes\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_append_images\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_unexpected_size\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_draw_reloaded\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_truncated_mask\u001b[0m\n\u001b[32m============================== \u001b[32m\u001b[1m22 passed\u001b[0m\u001b[32m in 0.22s\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_d3f343bb9b658ff97cbac826f9d1d4763a352e85\nconfigfile: pyproject.toml\nplugins: cov-6.0.0\ncollected 22 items\n\nr2e_tests/test_1.py \u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[31mF\u001b[0m\u001b[31m                               [100%]\u001b[0m\n\n=================================== FAILURES ===================================\n\u001b[31m\u001b[1m_____________________________ test_truncated_mask ______________________________\u001b[0m\n\n    def test_truncated_mask() -> None:\n        # 1 bpp\n        with open(\"Tests/images/hopper_mask.ico\", \"rb\") as fp:\n            data = fp.read()\n    \n        ImageFile.LOAD_TRUNCATED_IMAGES = True\n        data = data[:-3]\n    \n        try:\n>           with Image.open(io.BytesIO(data)) as im:\n\n\u001b[1m\u001b[31mr2e_tests/test_1.py\u001b[0m:255: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\u001b[1m\u001b[31msrc/PIL/Image.py\u001b[0m:3452: in open\n    im = _open_core(fp, filename, prefix, formats)\n\u001b[1m\u001b[31msrc/PIL/Image.py\u001b[0m:3440: in _open_core\n    im = factory(fp, filename)\n\u001b[1m\u001b[31msrc/PIL/ImageFile.py\u001b[0m:138: in __init__\n    self._open()\n\u001b[1m\u001b[31msrc/PIL/IcoImagePlugin.py\u001b[0m:311: in _open\n    self.load()\n\u001b[1m\u001b[31msrc/PIL/IcoImagePlugin.py\u001b[0m:328: in load\n    im = self.ico.getimage(self.size)\n\u001b[1m\u001b[31msrc/PIL/IcoImagePlugin.py\u001b[0m:196: in getimage\n    return self.frame(self.getentryindex(size, bpp))\n\u001b[1m\u001b[31msrc/PIL/IcoImagePlugin.py\u001b[0m:262: in frame\n    mask = Image.frombuffer(\n\u001b[1m\u001b[31msrc/PIL/Image.py\u001b[0m:3181: in frombuffer\n    return frombytes(mode, size, data, decoder_name, args)\n\u001b[1m\u001b[31msrc/PIL/Image.py\u001b[0m:3122: in frombytes\n    im.frombytes(data, decoder_name, decoder_args)\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <PIL.Image.Image image mode=1 size=16x16 at 0x77CF28C13760>\ndata = b'\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff...00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff\\x00\\x00\\x00\\xff'\ndecoder_name = 'raw', args = (('1;I', 4, -1),), decoder_args = ('1;I', 4, -1)\nd = <ImagingDecoder object at 0x77cf28c01030>, s = (60, 0)\n\n    def frombytes(\n        self, data: bytes | bytearray, decoder_name: str = \"raw\", *args: Any\n    ) -> None:\n        \"\"\"\n        Loads this image with pixel data from a bytes object.\n    \n        This method is similar to the :py:func:`~PIL.Image.frombytes` function,\n        but loads data into this image instead of creating a new image object.\n        \"\"\"\n    \n        if self.width == 0 or self.height == 0:\n            return\n    \n        decoder_args: Any = args\n        if len(decoder_args) == 1 and isinstance(decoder_args[0], tuple):\n            # may pass tuple instead of argument list\n            decoder_args = decoder_args[0]\n    \n        # default format\n        if decoder_name == \"raw\" and decoder_args == ():\n            decoder_args = self.mode\n    \n        # unpack data\n        d = _getdecoder(self.mode, decoder_name, decoder_args)\n        d.setimage(self.im)\n        s = d.decode(data)\n    \n        if s[0] >= 0:\n            msg = \"not enough image data\"\n>           raise ValueError(msg)\n\u001b[1m\u001b[31mE           ValueError: not enough image data\u001b[0m\n\n\u001b[1m\u001b[31msrc/PIL/Image.py\u001b[0m:870: ValueError\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_load\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_mask\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_black_and_white\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_palette\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_invalid_file\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_getpixel\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_no_duplicates\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_different_bit_depths\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[1]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[L]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[P]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[RGB]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_to_bytes_bmp[RGBA]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_incorrect_size\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_256x256\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_only_save_relevant_sizes\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_append_images\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_unexpected_size\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_draw_reloaded\u001b[0m\n\u001b[31mFAILED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_truncated_mask\u001b[0m - ValueError: not enough image data\n\u001b[31m========================= \u001b[31m\u001b[1m1 failed\u001b[0m, \u001b[32m21 passed\u001b[0m\u001b[31m in 0.36s\u001b[0m\u001b[31m =========================\u001b[0m\n",
    "old_commit_res_stderr": ""
}
Tests/test_file_ico.py src/PIL/IcoImagePlugin.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title**: Error Loading Truncated ICO Images with LOAD_TRUNCATED_IMAGES Enabled

**Description**:
When `ImageFile.LOAD_TRUNCATED_IMAGES` is enabled, attempting to load an ICO image with a truncated mask results in a `ValueError`. Instead of gracefully handling the truncated mask, the process fails with an error.

**Example Code**:
```python
from PIL import Image, ImageFile
import io

ImageFile.LOAD_TRUNCATED_IMAGES = True
with open("Tests/images/hopper_mask.ico", "rb") as fp:
    data = fp.read()

truncated_data = data[:-3]  # Simulate truncated mask data

with Image.open(io.BytesIO(truncated_data)) as im:
    im.load()
```

**Expected Behavior**:
The ICO image should load successfully without raising an error, potentially skipping the incomplete mask and loading the image without an alpha channel.

**Actual Behavior**:
A `ValueError` is raised with the message "not enough image data".

[/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/IcoImagePlugin.py
#
# The Python Imaging Library.
# $Id$
#
# Windows Icon support for PIL
#
# History:
#       96-05-27 fl     Created
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996.
#
# See the README file for information on usage and redistribution.
#

# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
# <casadebender@gmail.com>.
# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki
#
# Icon format references:
#   * https://en.wikipedia.org/wiki/ICO_(file_format)
#   * https://msdn.microsoft.com/en-us/library/ms997538.aspx
from __future__ import annotations

import warnings
from io import BytesIO
from math import ceil, log
from typing import IO

from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin
from ._binary import i16le as i16
from ._binary import i32le as i32
from ._binary import o8
from ._binary import o16le as o16
from ._binary import o32le as o32

#
# --------------------------------------------------------------------

_MAGIC = b"\0\0\1\0"


def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
    fp.write(_MAGIC)  # (2+2)
    bmp = im.encoderinfo.get("bitmap_format") == "bmp"
    sizes = im.encoderinfo.get(
        "sizes",
        [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],
    )
    frames = []
    provided_ims = [im] + im.encoderinfo.get("append_images", [])
    width, height = im.size
    for size in sorted(set(sizes)):
        if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256:
            continue

        for provided_im in provided_ims:
            if provided_im.size != size:
                continue
            frames.append(provided_im)
            if bmp:
                bits = BmpImagePlugin.SAVE[provided_im.mode][1]
                bits_used = [bits]
                for other_im in provided_ims:
                    if other_im.size != size:
                        continue
                    bits = BmpImagePlugin.SAVE[other_im.mode][1]
                    if bits not in bits_used:
                        # Another image has been supplied for this size
                        # with a different bit depth
                        frames.append(other_im)
                        bits_used.append(bits)
            break
        else:
            # TODO: invent a more convenient method for proportional scalings
            frame = provided_im.copy()
            frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None)
            frames.append(frame)
    fp.write(o16(len(frames)))  # idCount(2)
    offset = fp.tell() + len(frames) * 16
    for frame in frames:
        width, height = frame.size
        # 0 means 256
        fp.write(o8(width if width < 256 else 0))  # bWidth(1)
        fp.write(o8(height if height < 256 else 0))  # bHeight(1)

        bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0)
        fp.write(o8(colors))  # bColorCount(1)
        fp.write(b"\0")  # bReserved(1)
        fp.write(b"\0\0")  # wPlanes(2)
        fp.write(o16(bits))  # wBitCount(2)

        image_io = BytesIO()
        if bmp:
            frame.save(image_io, "dib")

            if bits != 32:
                and_mask = Image.new("1", size)
                ImageFile._save(
                    and_mask, image_io, [("raw", (0, 0) + size, 0, ("1", 0, -1))]
                )
        else:
            frame.save(image_io, "png")
        image_io.seek(0)
        image_bytes = image_io.read()
        if bmp:
            image_bytes = image_bytes[:8] + o32(height * 2) + image_bytes[12:]
        bytes_len = len(image_bytes)
        fp.write(o32(bytes_len))  # dwBytesInRes(4)
        fp.write(o32(offset))  # dwImageOffset(4)
        current = fp.tell()
        fp.seek(offset)
        fp.write(image_bytes)
        offset = offset + bytes_len
        fp.seek(current)


def _accept(prefix: bytes) -> bool:
    return prefix[:4] == _MAGIC


class IcoFile:
    def __init__(self, buf):
        """
        Parse image from file-like object containing ico file data
        """

        # check magic
        s = buf.read(6)
        if not _accept(s):
            msg = "not an ICO file"
            raise SyntaxError(msg)

        self.buf = buf
        self.entry = []

        # Number of items in file
        self.nb_items = i16(s, 4)

        # Get headers for each item
        for i in range(self.nb_items):
            s = buf.read(16)

            icon_header = {
                "width": s[0],
                "height": s[1],
                "nb_color": s[2],  # No. of colors in image (0 if >=8bpp)
                "reserved": s[3],
                "planes": i16(s, 4),
                "bpp": i16(s, 6),
                "size": i32(s, 8),
                "offset": i32(s, 12),
            }

            # See Wikipedia
            for j in ("width", "height"):
                if not icon_header[j]:
                    icon_header[j] = 256

            # See Wikipedia notes about color depth.
            # We need this just to differ images with equal sizes
            icon_header["color_depth"] = (
                icon_header["bpp"]
                or (
                    icon_header["nb_color"] != 0
                    and ceil(log(icon_header["nb_color"], 2))
                )
                or 256
            )

            icon_header["dim"] = (icon_header["width"], icon_header["height"])
            icon_header["square"] = icon_header["width"] * icon_header["height"]

            self.entry.append(icon_header)

        self.entry = sorted(self.entry, key=lambda x: x["color_depth"])
        # ICO images are usually squares
        self.entry = sorted(self.entry, key=lambda x: x["square"], reverse=True)

    def sizes(self):
        """
        Get a list of all available icon sizes and color depths.
        """
        return {(h["width"], h["height"]) for h in self.entry}

    def getentryindex(self, size, bpp=False):
        for i, h in enumerate(self.entry):
            if size == h["dim"] and (bpp is False or bpp == h["color_depth"]):
                return i
        return 0

    def getimage(self, size, bpp=False):
        """
        Get an image from the icon
        """
        return self.frame(self.getentryindex(size, bpp))

    def frame(self, idx: int) -> Image.Image:
        """
        Get an image from frame idx
        """

        header = self.entry[idx]

        self.buf.seek(header["offset"])
        data = self.buf.read(8)
        self.buf.seek(header["offset"])

        im: Image.Image
        if data[:8] == PngImagePlugin._MAGIC:
            # png frame
            im = PngImagePlugin.PngImageFile(self.buf)
            Image._decompression_bomb_check(im.size)
        else:
            # XOR + AND mask bmp frame
            im = BmpImagePlugin.DibImageFile(self.buf)
            Image._decompression_bomb_check(im.size)

            # change tile dimension to only encompass XOR image
            im._size = (im.size[0], int(im.size[1] / 2))
            d, e, o, a = im.tile[0]
            im.tile[0] = d, (0, 0) + im.size, o, a

            # figure out where AND mask image starts
            bpp = header["bpp"]
            if 32 == bpp:
                # 32-bit color depth icon image allows semitransparent areas
                # PIL's DIB format ignores transparency bits, recover them.
                # The DIB is packed in BGRX byte order where X is the alpha
                # channel.

                # Back up to start of bmp data
                self.buf.seek(o)
                # extract every 4th byte (eg. 3,7,11,15,...)
                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]

                # convert to an 8bpp grayscale image
                mask = Image.frombuffer(
                    "L",  # 8bpp
                    im.size,  # (w, h)
                    alpha_bytes,  # source chars
                    "raw",  # raw decoder
                    ("L", 0, -1),  # 8bpp inverted, unpadded, reversed
                )
            else:
                # get AND image from end of bitmap
                w = im.size[0]
                if (w % 32) > 0:
                    # bitmap row data is aligned to word boundaries
                    w += 32 - (im.size[0] % 32)

                # the total mask data is
                # padded row size * height / bits per char

                total_bytes = int((w * im.size[1]) / 8)
                and_mask_offset = header["offset"] + header["size"] - total_bytes

                self.buf.seek(and_mask_offset)
                mask_data = self.buf.read(total_bytes)

                # convert raw data to image
                mask = Image.frombuffer(
                    "1",  # 1 bpp
                    im.size,  # (w, h)
                    mask_data,  # source chars
                    "raw",  # raw decoder
                    ("1;I", int(w / 8), -1),  # 1bpp inverted, padded, reversed
                )

                # now we have two images, im is XOR image and mask is AND image

            # apply mask image as alpha channel
            im = im.convert("RGBA")
            im.putalpha(mask)

        return im


##
# Image plugin for Windows Icon files.


class IcoImageFile(ImageFile.ImageFile):
    """
    PIL read-only image support for Microsoft Windows .ico files.

    By default the largest resolution image in the file will be loaded. This
    can be changed by altering the 'size' attribute before calling 'load'.

    The info dictionary has a key 'sizes' that is a list of the sizes available
    in the icon file.

    Handles classic, XP and Vista icon formats.

    When saving, PNG compression is used. Support for this was only added in
    Windows Vista. If you are unable to view the icon in Windows, convert the
    image to "RGBA" mode before saving.

    This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
    <casadebender@gmail.com>.
    https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki
    """

    format = "ICO"
    format_description = "Windows Icon"

    def _open(self) -> None:
        self.ico = IcoFile(self.fp)
        self.info["sizes"] = self.ico.sizes()
        self.size = self.ico.entry[0]["dim"]
        self.load()

    @property
    def size(self):
        return self._size

    @size.setter
    def size(self, value):
        if value not in self.info["sizes"]:
            msg = "This is not one of the allowed sizes of this image"
            raise ValueError(msg)
        self._size = value

    def load(self):
        if self.im is not None and self.im.size == self.size:
            # Already loaded
            return Image.Image.load(self)
        im = self.ico.getimage(self.size)
        # if tile is PNG, it won't really be loaded yet
        im.load()
        self.im = im.im
        self._mode = im.mode
        if im.palette:
            self.palette = im.palette
        if im.size != self.size:
            warnings.warn("Image was not the expected size")

            index = self.ico.getentryindex(self.size)
            sizes = list(self.info["sizes"])
            sizes[index] = im.size
            self.info["sizes"] = set(sizes)

            self.size = im.size

    def load_seek(self, pos: int) -> None:
        # Flag the ImageFile.Parser so that it
        # just does all the decode at the end.
        pass


#
# --------------------------------------------------------------------


Image.register_open(IcoImageFile.format, IcoImageFile, _accept)
Image.register_save(IcoImageFile.format, _save)
Image.register_extension(IcoImageFile.format, ".ico")

Image.register_mime(IcoImageFile.format, "image/x-icon")

```
--- 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**: Error Loading Truncated ICO Images with LOAD_TRUNCATED_IMAGES Enabled

**Description**:
When `ImageFile.LOAD_TRUNCATED_IMAGES` is enabled, attempting to load an ICO image with a truncated mask results in a `ValueError`. Instead of gracefully handling the truncated mask, the process fails with an error.

**Example Code**:
```python
from PIL import Image, ImageFile
import io

ImageFile.LOAD_TRUNCATED_IMAGES = True
with open("Tests/images/hopper_mask.ico", "rb") as fp:
    data = fp.read()

truncated_data = data[:-3]  # Simulate truncated mask data

with Image.open(io.BytesIO(truncated_data)) as im:
    im.load()
```

**Expected Behavior**:
The ICO image should load successfully without raising an error, potentially skipping the incomplete mask and loading the image without an alpha channel.

**Actual Behavior**:
A `ValueError` is raised with the message "not enough image data".

[/ISSUE]
{
    "\u001b[1mtest_sanity\u001b[0m": "PASSED",
    "\u001b[1mtest_load\u001b[0m": "PASSED",
    "\u001b[1mtest_mask\u001b[0m": "PASSED",
    "\u001b[1mtest_black_and_white\u001b[0m": "PASSED",
    "\u001b[1mtest_palette\u001b[0m": "PASSED",
    "\u001b[1mtest_invalid_file\u001b[0m": "PASSED",
    "\u001b[1mtest_save_to_bytes\u001b[0m": "PASSED",
    "\u001b[1mtest_getpixel\u001b[0m": "PASSED",
    "\u001b[1mtest_no_duplicates\u001b[0m": "PASSED",
    "\u001b[1mtest_different_bit_depths\u001b[0m": "PASSED",
    "\u001b[1mtest_save_to_bytes_bmp[1]\u001b[0m": "PASSED",
    "\u001b[1mtest_save_to_bytes_bmp[L]\u001b[0m": "PASSED",
    "\u001b[1mtest_save_to_bytes_bmp[P]\u001b[0m": "PASSED",
    "\u001b[1mtest_save_to_bytes_bmp[RGB]\u001b[0m": "PASSED",
    "\u001b[1mtest_save_to_bytes_bmp[RGBA]\u001b[0m": "PASSED",
    "\u001b[1mtest_incorrect_size\u001b[0m": "PASSED",
    "\u001b[1mtest_save_256x256\u001b[0m": "PASSED",
    "\u001b[1mtest_only_save_relevant_sizes\u001b[0m": "PASSED",
    "\u001b[1mtest_save_append_images\u001b[0m": "PASSED",
    "\u001b[1mtest_unexpected_size\u001b[0m": "PASSED",
    "\u001b[1mtest_draw_reloaded\u001b[0m": "PASSED",
    "\u001b[1mtest_truncated_mask\u001b[0m": "PASSED"
}
diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py
index 086c87b1a..f331caad7 100644
--- a/src/PIL/IcoImagePlugin.py
+++ b/src/PIL/IcoImagePlugin.py
@@ -235,13 +235,19 @@ class IcoFile:
                 alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]
 
                 # convert to an 8bpp grayscale image
-                mask = Image.frombuffer(
-                    "L",  # 8bpp
-                    im.size,  # (w, h)
-                    alpha_bytes,  # source chars
-                    "raw",  # raw decoder
-                    ("L", 0, -1),  # 8bpp inverted, unpadded, reversed
-                )
+                try:
+                    mask = Image.frombuffer(
+                        "L",  # 8bpp
+                        im.size,  # (w, h)
+                        alpha_bytes,  # source chars
+                        "raw",  # raw decoder
+                        ("L", 0, -1),  # 8bpp inverted, unpadded, reversed
+                    )
+                except ValueError:
+                    if ImageFile.LOAD_TRUNCATED_IMAGES:
+                        mask = None
+                    else:
+                        raise
             else:
                 # get AND image from end of bitmap
                 w = im.size[0]
@@ -259,19 +265,26 @@ class IcoFile:
                 mask_data = self.buf.read(total_bytes)
 
                 # convert raw data to image
-                mask = Image.frombuffer(
-                    "1",  # 1 bpp
-                    im.size,  # (w, h)
-                    mask_data,  # source chars
-                    "raw",  # raw decoder
-                    ("1;I", int(w / 8), -1),  # 1bpp inverted, padded, reversed
-                )
+                try:
+                    mask = Image.frombuffer(
+                        "1",  # 1 bpp
+                        im.size,  # (w, h)
+                        mask_data,  # source chars
+                        "raw",  # raw decoder
+                        ("1;I", int(w / 8), -1),  # 1bpp inverted, padded, reversed
+                    )
+                except ValueError:
+                    if ImageFile.LOAD_TRUNCATED_IMAGES:
+                        mask = None
+                    else:
+                        raise
 
                 # now we have two images, im is XOR image and mask is AND image
 
             # apply mask image as alpha channel
-            im = im.convert("RGBA")
-            im.putalpha(mask)
+            if mask:
+                im = im.convert("RGBA")
+                im.putalpha(mask)
 
         return im
 
 

{"src/PIL/IcoImagePlugin.py": "#\n# The Python Imaging Library.\n# $Id$\n#\n# Windows Icon support for PIL\n#\n# History:\n#       96-05-27 fl     Created\n#\n# Copyright (c) Secret Labs AB 1997.\n# Copyright (c) Fredrik Lundh 1996.\n#\n# See the README file for information on usage and redistribution.\n#\n\n# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis\n# <casadebender@gmail.com>.\n# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki\n#\n# Icon format references:\n#   * https://en.wikipedia.org/wiki/ICO_(file_format)\n#   * https://msdn.microsoft.com/en-us/library/ms997538.aspx\nfrom __future__ import annotations\n\nimport warnings\nfrom io import BytesIO\nfrom math import ceil, log\nfrom typing import IO\n\nfrom . import BmpImagePlugin, Image, ImageFile, PngImagePlugin\nfrom ._binary import i16le as i16\nfrom ._binary import i32le as i32\nfrom ._binary import o8\nfrom ._binary import o16le as o16\nfrom ._binary import o32le as o32\n\n#\n# --------------------------------------------------------------------\n\n_MAGIC = b\"\\0\\0\\1\\0\"\n\n\ndef _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:\n    fp.write(_MAGIC)  # (2+2)\n    bmp = im.encoderinfo.get(\"bitmap_format\") == \"bmp\"\n    sizes = im.encoderinfo.get(\n        \"sizes\",\n        [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],\n    )\n    frames = []\n    provided_ims = [im] + im.encoderinfo.get(\"append_images\", [])\n    width, height = im.size\n    for size in sorted(set(sizes)):\n        if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256:\n            continue\n\n        for provided_im in provided_ims:\n            if provided_im.size != size:\n                continue\n            frames.append(provided_im)\n            if bmp:\n                bits = BmpImagePlugin.SAVE[provided_im.mode][1]\n                bits_used = [bits]\n                for other_im in provided_ims:\n                    if other_im.size != size:\n                        continue\n                    bits = BmpImagePlugin.SAVE[other_im.mode][1]\n                    if bits not in bits_used:\n                        # Another image has been supplied for this size\n                        # with a different bit depth\n                        frames.append(other_im)\n                        bits_used.append(bits)\n            break\n        else:\n            # TODO: invent a more convenient method for proportional scalings\n            frame = provided_im.copy()\n            frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None)\n            frames.append(frame)\n    fp.write(o16(len(frames)))  # idCount(2)\n    offset = fp.tell() + len(frames) * 16\n    for frame in frames:\n        width, height = frame.size\n        # 0 means 256\n        fp.write(o8(width if width < 256 else 0))  # bWidth(1)\n        fp.write(o8(height if height < 256 else 0))  # bHeight(1)\n\n        bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0)\n        fp.write(o8(colors))  # bColorCount(1)\n        fp.write(b\"\\0\")  # bReserved(1)\n        fp.write(b\"\\0\\0\")  # wPlanes(2)\n        fp.write(o16(bits))  # wBitCount(2)\n\n        image_io = BytesIO()\n        if bmp:\n            frame.save(image_io, \"dib\")\n\n            if bits != 32:\n                and_mask = Image.new(\"1\", size)\n                ImageFile._save(\n                    and_mask, image_io, [(\"raw\", (0, 0) + size, 0, (\"1\", 0, -1))]\n                )\n        else:\n            frame.save(image_io, \"png\")\n        image_io.seek(0)\n        image_bytes = image_io.read()\n        if bmp:\n            image_bytes = image_bytes[:8] + o32(height * 2) + image_bytes[12:]\n        bytes_len = len(image_bytes)\n        fp.write(o32(bytes_len))  # dwBytesInRes(4)\n        fp.write(o32(offset))  # dwImageOffset(4)\n        current = fp.tell()\n        fp.seek(offset)\n        fp.write(image_bytes)\n        offset = offset + bytes_len\n        fp.seek(current)\n\n\ndef _accept(prefix: bytes) -> bool:\n    return prefix[:4] == _MAGIC\n\n\nclass IcoFile:\n    def __init__(self, buf):\n        \"\"\"\n        Parse image from file-like object containing ico file data\n        \"\"\"\n\n        # check magic\n        s = buf.read(6)\n        if not _accept(s):\n            msg = \"not an ICO file\"\n            raise SyntaxError(msg)\n\n        self.buf = buf\n        self.entry = []\n\n        # Number of items in file\n        self.nb_items = i16(s, 4)\n\n        # Get headers for each item\n        for i in range(self.nb_items):\n            s = buf.read(16)\n\n            icon_header = {\n                \"width\": s[0],\n                \"height\": s[1],\n                \"nb_color\": s[2],  # No. of colors in image (0 if >=8bpp)\n                \"reserved\": s[3],\n                \"planes\": i16(s, 4),\n                \"bpp\": i16(s, 6),\n                \"size\": i32(s, 8),\n                \"offset\": i32(s, 12),\n            }\n\n            # See Wikipedia\n            for j in (\"width\", \"height\"):\n                if not icon_header[j]:\n                    icon_header[j] = 256\n\n            # See Wikipedia notes about color depth.\n            # We need this just to differ images with equal sizes\n            icon_header[\"color_depth\"] = (\n                icon_header[\"bpp\"]\n                or (\n                    icon_header[\"nb_color\"] != 0\n                    and ceil(log(icon_header[\"nb_color\"], 2))\n                )\n                or 256\n            )\n\n            icon_header[\"dim\"] = (icon_header[\"width\"], icon_header[\"height\"])\n            icon_header[\"square\"] = icon_header[\"width\"] * icon_header[\"height\"]\n\n            self.entry.append(icon_header)\n\n        self.entry = sorted(self.entry, key=lambda x: x[\"color_depth\"])\n        # ICO images are usually squares\n        self.entry = sorted(self.entry, key=lambda x: x[\"square\"], reverse=True)\n\n    def sizes(self):\n        \"\"\"\n        Get a list of all available icon sizes and color depths.\n        \"\"\"\n        return {(h[\"width\"], h[\"height\"]) for h in self.entry}\n\n    def getentryindex(self, size, bpp=False):\n        for i, h in enumerate(self.entry):\n            if size == h[\"dim\"] and (bpp is False or bpp == h[\"color_depth\"]):\n                return i\n        return 0\n\n    def getimage(self, size, bpp=False):\n        \"\"\"\n        Get an image from the icon\n        \"\"\"\n        return self.frame(self.getentryindex(size, bpp))\n\n    def frame(self, idx: int) -> Image.Image:\n        \"\"\"\n        Get an image from frame idx\n        \"\"\"\n\n        header = self.entry[idx]\n\n        self.buf.seek(header[\"offset\"])\n        data = self.buf.read(8)\n        self.buf.seek(header[\"offset\"])\n\n        im: Image.Image\n        if data[:8] == PngImagePlugin._MAGIC:\n            # png frame\n            im = PngImagePlugin.PngImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n        else:\n            # XOR + AND mask bmp frame\n            im = BmpImagePlugin.DibImageFile(self.buf)\n            Image._decompression_bomb_check(im.size)\n\n            # change tile dimension to only encompass XOR image\n            im._size = (im.size[0], int(im.size[1] / 2))\n            d, e, o, a = im.tile[0]\n            im.tile[0] = d, (0, 0) + im.size, o, a\n\n            # figure out where AND mask image starts\n            bpp = header[\"bpp\"]\n            if 32 == bpp:\n                # 32-bit color depth icon image allows semitransparent areas\n                # PIL's DIB format ignores transparency bits, recover them.\n                # The DIB is packed in BGRX byte order where X is the alpha\n                # channel.\n\n                # Back up to start of bmp data\n                self.buf.seek(o)\n                # extract every 4th byte (eg. 3,7,11,15,...)\n                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]\n\n                # convert to an 8bpp grayscale image\n                mask = Image.frombuffer(\n                    \"L\",  # 8bpp\n                    im.size,  # (w, h)\n                    alpha_bytes,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"L\", 0, -1),  # 8bpp inverted, unpadded, reversed\n                )\n            else:\n                # get AND image from end of bitmap\n                w = im.size[0]\n                if (w % 32) > 0:\n                    # bitmap row data is aligned to word boundaries\n                    w += 32 - (im.size[0] % 32)\n\n                # the total mask data is\n                # padded row size * height / bits per char\n\n                total_bytes = int((w * im.size[1]) / 8)\n                and_mask_offset = header[\"offset\"] + header[\"size\"] - total_bytes\n\n                self.buf.seek(and_mask_offset)\n                mask_data = self.buf.read(total_bytes)\n\n                # convert raw data to image\n                mask = Image.frombuffer(\n                    \"1\",  # 1 bpp\n                    im.size,  # (w, h)\n                    mask_data,  # source chars\n                    \"raw\",  # raw decoder\n                    (\"1;I\", int(w / 8), -1),  # 1bpp inverted, padded, reversed\n                )\n\n                # now we have two images, im is XOR image and mask is AND image\n\n            # apply mask image as alpha channel\n            im = im.convert(\"RGBA\")\n            im.putalpha(mask)\n\n        return im\n\n\n##\n# Image plugin for Windows Icon files.\n\n\nclass IcoImageFile(ImageFile.ImageFile):\n    \"\"\"\n    PIL read-only image support for Microsoft Windows .ico files.\n\n    By default the largest resolution image in the file will be loaded. This\n    can be changed by altering the 'size' attribute before calling 'load'.\n\n    The info dictionary has a key 'sizes' that is a list of the sizes available\n    in the icon file.\n\n    Handles classic, XP and Vista icon formats.\n\n    When saving, PNG compression is used. Support for this was only added in\n    Windows Vista. If you are unable to view the icon in Windows, convert the\n    image to \"RGBA\" mode before saving.\n\n    This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis\n    <casadebender@gmail.com>.\n    https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki\n    \"\"\"\n\n    format = \"ICO\"\n    format_description = \"Windows Icon\"\n\n    def _open(self) -> None:\n        self.ico = IcoFile(self.fp)\n        self.info[\"sizes\"] = self.ico.sizes()\n        self.size = self.ico.entry[0][\"dim\"]\n        self.load()\n\n    @property\n    def size(self):\n        return self._size\n\n    @size.setter\n    def size(self, value):\n        if value not in self.info[\"sizes\"]:\n            msg = \"This is not one of the allowed sizes of this image\"\n            raise ValueError(msg)\n        self._size = value\n\n    def load(self):\n        if self.im is not None and self.im.size == self.size:\n            # Already loaded\n            return Image.Image.load(self)\n        im = self.ico.getimage(self.size)\n        # if tile is PNG, it won't really be loaded yet\n        im.load()\n        self.im = im.im\n        self._mode = im.mode\n        if im.palette:\n            self.palette = im.palette\n        if im.size != self.size:\n            warnings.warn(\"Image was not the expected size\")\n\n            index = self.ico.getentryindex(self.size)\n            sizes = list(self.info[\"sizes\"])\n            sizes[index] = im.size\n            self.info[\"sizes\"] = set(sizes)\n\n            self.size = im.size\n\n    def load_seek(self, pos: int) -> None:\n        # Flag the ImageFile.Parser so that it\n        # just does all the decode at the end.\n        pass\n\n\n#\n# --------------------------------------------------------------------\n\n\nImage.register_open(IcoImageFile.format, IcoImageFile, _accept)\nImage.register_save(IcoImageFile.format, _save)\nImage.register_extension(IcoImageFile.format, \".ico\")\n\nImage.register_mime(IcoImageFile.format, \"image/x-icon\")\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**: Error Loading Truncated ICO Images with LOAD_TRUNCATED_IMAGES Enabled

**Description**:
When `ImageFile.LOAD_TRUNCATED_IMAGES` is enabled, attempting to load an ICO image with a truncated mask results in a `ValueError`. Instead of gracefully handling the truncated mask, the process fails with an error.

**Example Code**:
```python
from PIL import Image, ImageFile
import io

ImageFile.LOAD_TRUNCATED_IMAGES = True
with open("Tests/images/hopper_mask.ico", "rb") as fp:
    data = fp.read()

truncated_data = data[:-3]  # Simulate truncated mask data

with Image.open(io.BytesIO(truncated_data)) as im:
    im.load()
```

**Expected Behavior**:
The ICO image should load successfully without raising an error, potentially skipping the incomplete mask and loading the image without an alpha channel.

**Actual Behavior**:
A `ValueError` is raised with the message "not enough image data".

[/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/IcoImagePlugin.py
#
# The Python Imaging Library.
# $Id$
#
# Windows Icon support for PIL
#
# History:
#       96-05-27 fl     Created
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996.
#
# See the README file for information on usage and redistribution.
#

# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
# <casadebender@gmail.com>.
# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki
#
# Icon format references:
#   * https://en.wikipedia.org/wiki/ICO_(file_format)
#   * https://msdn.microsoft.com/en-us/library/ms997538.aspx
from __future__ import annotations

import warnings
from io import BytesIO
from math import ceil, log
from typing import IO

from . import BmpImagePlugin, Image, ImageFile, PngImagePlugin
from ._binary import i16le as i16
from ._binary import i32le as i32
from ._binary import o8
from ._binary import o16le as o16
from ._binary import o32le as o32

#
# --------------------------------------------------------------------

_MAGIC = b"\0\0\1\0"


def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
    fp.write(_MAGIC)  # (2+2)
    bmp = im.encoderinfo.get("bitmap_format") == "bmp"
    sizes = im.encoderinfo.get(
        "sizes",
        [(16, 16), (24, 24), (32, 32), (48, 48), (64, 64), (128, 128), (256, 256)],
    )
    frames = []
    provided_ims = [im] + im.encoderinfo.get("append_images", [])
    width, height = im.size
    for size in sorted(set(sizes)):
        if size[0] > width or size[1] > height or size[0] > 256 or size[1] > 256:
            continue

        for provided_im in provided_ims:
            if provided_im.size != size:
                continue
            frames.append(provided_im)
            if bmp:
                bits = BmpImagePlugin.SAVE[provided_im.mode][1]
                bits_used = [bits]
                for other_im in provided_ims:
                    if other_im.size != size:
                        continue
                    bits = BmpImagePlugin.SAVE[other_im.mode][1]
                    if bits not in bits_used:
                        # Another image has been supplied for this size
                        # with a different bit depth
                        frames.append(other_im)
                        bits_used.append(bits)
            break
        else:
            # TODO: invent a more convenient method for proportional scalings
            frame = provided_im.copy()
            frame.thumbnail(size, Image.Resampling.LANCZOS, reducing_gap=None)
            frames.append(frame)
    fp.write(o16(len(frames)))  # idCount(2)
    offset = fp.tell() + len(frames) * 16
    for frame in frames:
        width, height = frame.size
        # 0 means 256
        fp.write(o8(width if width < 256 else 0))  # bWidth(1)
        fp.write(o8(height if height < 256 else 0))  # bHeight(1)

        bits, colors = BmpImagePlugin.SAVE[frame.mode][1:] if bmp else (32, 0)
        fp.write(o8(colors))  # bColorCount(1)
        fp.write(b"\0")  # bReserved(1)
        fp.write(b"\0\0")  # wPlanes(2)
        fp.write(o16(bits))  # wBitCount(2)

        image_io = BytesIO()
        if bmp:
            frame.save(image_io, "dib")

            if bits != 32:
                and_mask = Image.new("1", size)
                ImageFile._save(
                    and_mask, image_io, [("raw", (0, 0) + size, 0, ("1", 0, -1))]
                )
        else:
            frame.save(image_io, "png")
        image_io.seek(0)
        image_bytes = image_io.read()
        if bmp:
            image_bytes = image_bytes[:8] + o32(height * 2) + image_bytes[12:]
        bytes_len = len(image_bytes)
        fp.write(o32(bytes_len))  # dwBytesInRes(4)
        fp.write(o32(offset))  # dwImageOffset(4)
        current = fp.tell()
        fp.seek(offset)
        fp.write(image_bytes)
        offset = offset + bytes_len
        fp.seek(current)


def _accept(prefix: bytes) -> bool:
    return prefix[:4] == _MAGIC


class IcoFile:
    def __init__(self, buf):
        """
        Parse image from file-like object containing ico file data
        """

        # check magic
        s = buf.read(6)
        if not _accept(s):
            msg = "not an ICO file"
            raise SyntaxError(msg)

        self.buf = buf
        self.entry = []

        # Number of items in file
        self.nb_items = i16(s, 4)

        # Get headers for each item
        for i in range(self.nb_items):
            s = buf.read(16)

            icon_header = {
                "width": s[0],
                "height": s[1],
                "nb_color": s[2],  # No. of colors in image (0 if >=8bpp)
                "reserved": s[3],
                "planes": i16(s, 4),
                "bpp": i16(s, 6),
                "size": i32(s, 8),
                "offset": i32(s, 12),
            }

            # See Wikipedia
            for j in ("width", "height"):
                if not icon_header[j]:
                    icon_header[j] = 256

            # See Wikipedia notes about color depth.
            # We need this just to differ images with equal sizes
            icon_header["color_depth"] = (
                icon_header["bpp"]
                or (
                    icon_header["nb_color"] != 0
                    and ceil(log(icon_header["nb_color"], 2))
                )
                or 256
            )

            icon_header["dim"] = (icon_header["width"], icon_header["height"])
            icon_header["square"] = icon_header["width"] * icon_header["height"]

            self.entry.append(icon_header)

        self.entry = sorted(self.entry, key=lambda x: x["color_depth"])
        # ICO images are usually squares
        self.entry = sorted(self.entry, key=lambda x: x["square"], reverse=True)

    def sizes(self):
        """
        Get a list of all available icon sizes and color depths.
        """
        return {(h["width"], h["height"]) for h in self.entry}

    def getentryindex(self, size, bpp=False):
        for i, h in enumerate(self.entry):
            if size == h["dim"] and (bpp is False or bpp == h["color_depth"]):
                return i
        return 0

    def getimage(self, size, bpp=False):
        """
        Get an image from the icon
        """
        return self.frame(self.getentryindex(size, bpp))

    def frame(self, idx: int) -> Image.Image:
        """
        Get an image from frame idx
        """

        header = self.entry[idx]

        self.buf.seek(header["offset"])
        data = self.buf.read(8)
        self.buf.seek(header["offset"])

        im: Image.Image
        if data[:8] == PngImagePlugin._MAGIC:
            # png frame
            im = PngImagePlugin.PngImageFile(self.buf)
            Image._decompression_bomb_check(im.size)
        else:
            # XOR + AND mask bmp frame
            im = BmpImagePlugin.DibImageFile(self.buf)
            Image._decompression_bomb_check(im.size)

            # change tile dimension to only encompass XOR image
            im._size = (im.size[0], int(im.size[1] / 2))
            d, e, o, a = im.tile[0]
            im.tile[0] = d, (0, 0) + im.size, o, a

            # figure out where AND mask image starts
            bpp = header["bpp"]
            if 32 == bpp:
                # 32-bit color depth icon image allows semitransparent areas
                # PIL's DIB format ignores transparency bits, recover them.
                # The DIB is packed in BGRX byte order where X is the alpha
                # channel.

                # Back up to start of bmp data
                self.buf.seek(o)
                # extract every 4th byte (eg. 3,7,11,15,...)
                alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4]

                # convert to an 8bpp grayscale image
                mask = Image.frombuffer(
                    "L",  # 8bpp
                    im.size,  # (w, h)
                    alpha_bytes,  # source chars
                    "raw",  # raw decoder
                    ("L", 0, -1),  # 8bpp inverted, unpadded, reversed
                )
            else:
                # get AND image from end of bitmap
                w = im.size[0]
                if (w % 32) > 0:
                    # bitmap row data is aligned to word boundaries
                    w += 32 - (im.size[0] % 32)

                # the total mask data is
                # padded row size * height / bits per char

                total_bytes = int((w * im.size[1]) / 8)
                and_mask_offset = header["offset"] + header["size"] - total_bytes

                self.buf.seek(and_mask_offset)
                mask_data = self.buf.read(total_bytes)

                # convert raw data to image
                mask = Image.frombuffer(
                    "1",  # 1 bpp
                    im.size,  # (w, h)
                    mask_data,  # source chars
                    "raw",  # raw decoder
                    ("1;I", int(w / 8), -1),  # 1bpp inverted, padded, reversed
                )

                # now we have two images, im is XOR image and mask is AND image

            # apply mask image as alpha channel
            im = im.convert("RGBA")
            im.putalpha(mask)

        return im


##
# Image plugin for Windows Icon files.


class IcoImageFile(ImageFile.ImageFile):
    """
    PIL read-only image support for Microsoft Windows .ico files.

    By default the largest resolution image in the file will be loaded. This
    can be changed by altering the 'size' attribute before calling 'load'.

    The info dictionary has a key 'sizes' that is a list of the sizes available
    in the icon file.

    Handles classic, XP and Vista icon formats.

    When saving, PNG compression is used. Support for this was only added in
    Windows Vista. If you are unable to view the icon in Windows, convert the
    image to "RGBA" mode before saving.

    This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
    <casadebender@gmail.com>.
    https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki
    """

    format = "ICO"
    format_description = "Windows Icon"

    def _open(self) -> None:
        self.ico = IcoFile(self.fp)
        self.info["sizes"] = self.ico.sizes()
        self.size = self.ico.entry[0]["dim"]
        self.load()

    @property
    def size(self):
        return self._size

    @size.setter
    def size(self, value):
        if value not in self.info["sizes"]:
            msg = "This is not one of the allowed sizes of this image"
            raise ValueError(msg)
        self._size = value

    def load(self):
        if self.im is not None and self.im.size == self.size:
            # Already loaded
            return Image.Image.load(self)
        im = self.ico.getimage(self.size)
        # if tile is PNG, it won't really be loaded yet
        im.load()
        self.im = im.im
        self._mode = im.mode
        if im.palette:
            self.palette = im.palette
        if im.size != self.size:
            warnings.warn("Image was not the expected size")

            index = self.ico.getentryindex(self.size)
            sizes = list(self.info["sizes"])
            sizes[index] = im.size
            self.info["sizes"] = set(sizes)

            self.size = im.size

    def load_seek(self, pos: int) -> None:
        # Flag the ImageFile.Parser so that it
        # just does all the decode at the end.
        pass


#
# --------------------------------------------------------------------


Image.register_open(IcoImageFile.format, IcoImageFile, _accept)
Image.register_save(IcoImageFile.format, _save)
Image.register_extension(IcoImageFile.format, ".ico")

Image.register_mime(IcoImageFile.format, "image/x-icon")

```
--- 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