{
    "file_diffs": [
        {
            "old_file_content": "import io\n\nimport pytest\n\nfrom PIL import BmpImagePlugin, Image\n\nfrom .helper import (\n    assert_image_equal,\n    assert_image_equal_tofile,\n    assert_image_similar_tofile,\n    hopper,\n)\n\n\ndef test_sanity(tmp_path):\n    def roundtrip(im):\n        outfile = str(tmp_path / \"temp.bmp\")\n\n        im.save(outfile, \"BMP\")\n\n        with Image.open(outfile) as reloaded:\n            reloaded.load()\n            assert im.mode == reloaded.mode\n            assert im.size == reloaded.size\n            assert reloaded.format == \"BMP\"\n            assert reloaded.get_format_mimetype() == \"image/bmp\"\n\n    roundtrip(hopper())\n\n    roundtrip(hopper(\"1\"))\n    roundtrip(hopper(\"L\"))\n    roundtrip(hopper(\"P\"))\n    roundtrip(hopper(\"RGB\"))\n\n\ndef test_invalid_file():\n    with open(\"Tests/images/flower.jpg\", \"rb\") as fp:\n        with pytest.raises(SyntaxError):\n            BmpImagePlugin.BmpImageFile(fp)\n\n\ndef test_save_to_bytes():\n    output = io.BytesIO()\n    im = hopper()\n    im.save(output, \"BMP\")\n\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert im.mode == reloaded.mode\n        assert im.size == reloaded.size\n        assert reloaded.format == \"BMP\"\n\n\ndef test_save_too_large(tmp_path):\n    outfile = str(tmp_path / \"temp.bmp\")\n    with Image.new(\"RGB\", (1, 1)) as im:\n        im._size = (37838, 37838)\n        with pytest.raises(ValueError):\n            im.save(outfile)\n\n\ndef test_dpi():\n    dpi = (72, 72)\n\n    output = io.BytesIO()\n    with hopper() as im:\n        im.save(output, \"BMP\", dpi=dpi)\n\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert reloaded.info[\"dpi\"] == (72.008961115161, 72.008961115161)\n\n\ndef test_save_bmp_with_dpi(tmp_path):\n    # Test for #1301\n    # Arrange\n    outfile = str(tmp_path / \"temp.jpg\")\n    with Image.open(\"Tests/images/hopper.bmp\") as im:\n        assert im.info[\"dpi\"] == (95.98654816726399, 95.98654816726399)\n\n        # Act\n        im.save(outfile, \"JPEG\", dpi=im.info[\"dpi\"])\n\n        # Assert\n        with Image.open(outfile) as reloaded:\n            reloaded.load()\n            assert reloaded.info[\"dpi\"] == (96, 96)\n            assert reloaded.size == im.size\n            assert reloaded.format == \"JPEG\"\n\n\ndef test_save_float_dpi(tmp_path):\n    outfile = str(tmp_path / \"temp.bmp\")\n    with Image.open(\"Tests/images/hopper.bmp\") as im:\n        im.save(outfile, dpi=(72.21216100543306, 72.21216100543306))\n        with Image.open(outfile) as reloaded:\n            assert reloaded.info[\"dpi\"] == (72.21216100543306, 72.21216100543306)\n\n\ndef test_load_dib():\n    # test for #1293, Imagegrab returning Unsupported Bitfields Format\n    with Image.open(\"Tests/images/clipboard.dib\") as im:\n        assert im.format == \"DIB\"\n        assert im.get_format_mimetype() == \"image/bmp\"\n\n        assert_image_equal_tofile(im, \"Tests/images/clipboard_target.png\")\n\n\ndef test_save_dib(tmp_path):\n    outfile = str(tmp_path / \"temp.dib\")\n\n    with Image.open(\"Tests/images/clipboard.dib\") as im:\n        im.save(outfile)\n\n        with Image.open(outfile) as reloaded:\n            assert reloaded.format == \"DIB\"\n            assert reloaded.get_format_mimetype() == \"image/bmp\"\n            assert_image_equal(im, reloaded)\n\n\ndef test_rgba_bitfields():\n    # This test image has been manually hexedited\n    # to change the bitfield compression in the header from XBGR to RGBA\n    with Image.open(\"Tests/images/rgb32bf-rgba.bmp\") as im:\n\n        # So before the comparing the image, swap the channels\n        b, g, r = im.split()[1:]\n        im = Image.merge(\"RGB\", (r, g, b))\n\n    assert_image_equal_tofile(im, \"Tests/images/bmp/q/rgb32bf-xbgr.bmp\")\n\n    # This test image has been manually hexedited\n    # to change the bitfield compression in the header from XBGR to ABGR\n    with Image.open(\"Tests/images/rgb32bf-abgr.bmp\") as im:\n        assert_image_equal_tofile(\n            im.convert(\"RGB\"), \"Tests/images/bmp/q/rgb32bf-xbgr.bmp\"\n        )\n\n\ndef test_rle8():\n    with Image.open(\"Tests/images/hopper_rle8.bmp\") as im:\n        assert_image_similar_tofile(im.convert(\"RGB\"), \"Tests/images/hopper.bmp\", 12)\n\n    with Image.open(\"Tests/images/hopper_rle8_greyscale.bmp\") as im:\n        assert_image_equal_tofile(im, \"Tests/images/bw_gradient.png\")\n\n    # This test image has been manually hexedited\n    # to have rows with too much data\n    with Image.open(\"Tests/images/hopper_rle8_row_overflow.bmp\") as im:\n        assert_image_similar_tofile(im.convert(\"RGB\"), \"Tests/images/hopper.bmp\", 12)\n\n    # Signal end of bitmap before the image is finished\n    with open(\"Tests/images/bmp/g/pal8rle.bmp\", \"rb\") as fp:\n        data = fp.read(1063) + b\"\\x01\"\n        with Image.open(io.BytesIO(data)) as im:\n            with pytest.raises(ValueError):\n                im.load()\n\n\n@pytest.mark.parametrize(\n    \"file_name,length\",\n    (\n        # EOF immediately after the header\n        (\"Tests/images/hopper_rle8.bmp\", 1078),\n        # EOF during delta\n        (\"Tests/images/bmp/q/pal8rletrns.bmp\", 3670),\n        # EOF when reading data in absolute mode\n        (\"Tests/images/bmp/g/pal8rle.bmp\", 1064),\n    ),\n)\ndef test_rle8_eof(file_name, length):\n    with open(file_name, \"rb\") as fp:\n        data = fp.read(length)\n        with Image.open(io.BytesIO(data)) as im:\n            with pytest.raises(ValueError):\n                im.load()\n\n\ndef test_offset():\n    # This image has been hexedited\n    # to exclude the palette size from the pixel data offset\n    with Image.open(\"Tests/images/pal8_offset.bmp\") as im:\n        assert_image_equal_tofile(im, \"Tests/images/bmp/g/pal8.bmp\")\n",
            "new_file_content": "import io\n\nimport pytest\n\nfrom PIL import BmpImagePlugin, Image\n\nfrom .helper import (\n    assert_image_equal,\n    assert_image_equal_tofile,\n    assert_image_similar_tofile,\n    hopper,\n)\n\n\ndef test_sanity(tmp_path):\n    def roundtrip(im):\n        outfile = str(tmp_path / \"temp.bmp\")\n\n        im.save(outfile, \"BMP\")\n\n        with Image.open(outfile) as reloaded:\n            reloaded.load()\n            assert im.mode == reloaded.mode\n            assert im.size == reloaded.size\n            assert reloaded.format == \"BMP\"\n            assert reloaded.get_format_mimetype() == \"image/bmp\"\n\n    roundtrip(hopper())\n\n    roundtrip(hopper(\"1\"))\n    roundtrip(hopper(\"L\"))\n    roundtrip(hopper(\"P\"))\n    roundtrip(hopper(\"RGB\"))\n\n\ndef test_invalid_file():\n    with open(\"Tests/images/flower.jpg\", \"rb\") as fp:\n        with pytest.raises(SyntaxError):\n            BmpImagePlugin.BmpImageFile(fp)\n\n\ndef test_save_to_bytes():\n    output = io.BytesIO()\n    im = hopper()\n    im.save(output, \"BMP\")\n\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert im.mode == reloaded.mode\n        assert im.size == reloaded.size\n        assert reloaded.format == \"BMP\"\n\n\ndef test_small_palette(tmp_path):\n    im = Image.new(\"P\", (1, 1))\n    colors = [0, 0, 0, 125, 125, 125, 255, 255, 255]\n    im.putpalette(colors)\n\n    out = str(tmp_path / \"temp.bmp\")\n    im.save(out)\n\n    with Image.open(out) as reloaded:\n        assert reloaded.getpalette() == colors\n\n\ndef test_save_too_large(tmp_path):\n    outfile = str(tmp_path / \"temp.bmp\")\n    with Image.new(\"RGB\", (1, 1)) as im:\n        im._size = (37838, 37838)\n        with pytest.raises(ValueError):\n            im.save(outfile)\n\n\ndef test_dpi():\n    dpi = (72, 72)\n\n    output = io.BytesIO()\n    with hopper() as im:\n        im.save(output, \"BMP\", dpi=dpi)\n\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert reloaded.info[\"dpi\"] == (72.008961115161, 72.008961115161)\n\n\ndef test_save_bmp_with_dpi(tmp_path):\n    # Test for #1301\n    # Arrange\n    outfile = str(tmp_path / \"temp.jpg\")\n    with Image.open(\"Tests/images/hopper.bmp\") as im:\n        assert im.info[\"dpi\"] == (95.98654816726399, 95.98654816726399)\n\n        # Act\n        im.save(outfile, \"JPEG\", dpi=im.info[\"dpi\"])\n\n        # Assert\n        with Image.open(outfile) as reloaded:\n            reloaded.load()\n            assert reloaded.info[\"dpi\"] == (96, 96)\n            assert reloaded.size == im.size\n            assert reloaded.format == \"JPEG\"\n\n\ndef test_save_float_dpi(tmp_path):\n    outfile = str(tmp_path / \"temp.bmp\")\n    with Image.open(\"Tests/images/hopper.bmp\") as im:\n        im.save(outfile, dpi=(72.21216100543306, 72.21216100543306))\n        with Image.open(outfile) as reloaded:\n            assert reloaded.info[\"dpi\"] == (72.21216100543306, 72.21216100543306)\n\n\ndef test_load_dib():\n    # test for #1293, Imagegrab returning Unsupported Bitfields Format\n    with Image.open(\"Tests/images/clipboard.dib\") as im:\n        assert im.format == \"DIB\"\n        assert im.get_format_mimetype() == \"image/bmp\"\n\n        assert_image_equal_tofile(im, \"Tests/images/clipboard_target.png\")\n\n\ndef test_save_dib(tmp_path):\n    outfile = str(tmp_path / \"temp.dib\")\n\n    with Image.open(\"Tests/images/clipboard.dib\") as im:\n        im.save(outfile)\n\n        with Image.open(outfile) as reloaded:\n            assert reloaded.format == \"DIB\"\n            assert reloaded.get_format_mimetype() == \"image/bmp\"\n            assert_image_equal(im, reloaded)\n\n\ndef test_rgba_bitfields():\n    # This test image has been manually hexedited\n    # to change the bitfield compression in the header from XBGR to RGBA\n    with Image.open(\"Tests/images/rgb32bf-rgba.bmp\") as im:\n\n        # So before the comparing the image, swap the channels\n        b, g, r = im.split()[1:]\n        im = Image.merge(\"RGB\", (r, g, b))\n\n    assert_image_equal_tofile(im, \"Tests/images/bmp/q/rgb32bf-xbgr.bmp\")\n\n    # This test image has been manually hexedited\n    # to change the bitfield compression in the header from XBGR to ABGR\n    with Image.open(\"Tests/images/rgb32bf-abgr.bmp\") as im:\n        assert_image_equal_tofile(\n            im.convert(\"RGB\"), \"Tests/images/bmp/q/rgb32bf-xbgr.bmp\"\n        )\n\n\ndef test_rle8():\n    with Image.open(\"Tests/images/hopper_rle8.bmp\") as im:\n        assert_image_similar_tofile(im.convert(\"RGB\"), \"Tests/images/hopper.bmp\", 12)\n\n    with Image.open(\"Tests/images/hopper_rle8_greyscale.bmp\") as im:\n        assert_image_equal_tofile(im, \"Tests/images/bw_gradient.png\")\n\n    # This test image has been manually hexedited\n    # to have rows with too much data\n    with Image.open(\"Tests/images/hopper_rle8_row_overflow.bmp\") as im:\n        assert_image_similar_tofile(im.convert(\"RGB\"), \"Tests/images/hopper.bmp\", 12)\n\n    # Signal end of bitmap before the image is finished\n    with open(\"Tests/images/bmp/g/pal8rle.bmp\", \"rb\") as fp:\n        data = fp.read(1063) + b\"\\x01\"\n        with Image.open(io.BytesIO(data)) as im:\n            with pytest.raises(ValueError):\n                im.load()\n\n\n@pytest.mark.parametrize(\n    \"file_name,length\",\n    (\n        # EOF immediately after the header\n        (\"Tests/images/hopper_rle8.bmp\", 1078),\n        # EOF during delta\n        (\"Tests/images/bmp/q/pal8rletrns.bmp\", 3670),\n        # EOF when reading data in absolute mode\n        (\"Tests/images/bmp/g/pal8rle.bmp\", 1064),\n    ),\n)\ndef test_rle8_eof(file_name, length):\n    with open(file_name, \"rb\") as fp:\n        data = fp.read(length)\n        with Image.open(io.BytesIO(data)) as im:\n            with pytest.raises(ValueError):\n                im.load()\n\n\ndef test_offset():\n    # This image has been hexedited\n    # to exclude the palette size from the pixel data offset\n    with Image.open(\"Tests/images/pal8_offset.bmp\") as im:\n        assert_image_equal_tofile(im, \"Tests/images/bmp/g/pal8.bmp\")\n",
            "header": {
                "file": {
                    "path": "Tests/test_file_bmp.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "d58666b44",
                "new_commit_hash": "4c964fbea",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/Tests/test_file_bmp.py"
            },
            "plus_file": {
                "path": "b/Tests/test_file_bmp.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 51,
                            "length": 6
                        },
                        "new_range": {
                            "start": 51,
                            "length": 18
                        },
                        "section": "def test_save_to_bytes():"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        assert reloaded.format == \"BMP\"",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "def test_small_palette(tmp_path):",
                                "type": "added"
                            },
                            {
                                "content": "    im = Image.new(\"P\", (1, 1))",
                                "type": "added"
                            },
                            {
                                "content": "    colors = [0, 0, 0, 125, 125, 125, 255, 255, 255]",
                                "type": "added"
                            },
                            {
                                "content": "    im.putpalette(colors)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    out = str(tmp_path / \"temp.bmp\")",
                                "type": "added"
                            },
                            {
                                "content": "    im.save(out)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    with Image.open(out) as reloaded:",
                                "type": "added"
                            },
                            {
                                "content": "        assert reloaded.getpalette() == colors",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "def test_save_too_large(tmp_path):",
                                "type": "context"
                            },
                            {
                                "content": "    outfile = str(tmp_path / \"temp.bmp\")",
                                "type": "context"
                            },
                            {
                                "content": "    with Image.new(\"RGB\", (1, 1)) as im:",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "Tests/test_file_bmp.py",
                            "type": "function",
                            "name": "test_small_palette",
                            "content": "def test_small_palette(tmp_path):\n    im = Image.new(\"P\", (1, 1))\n    colors = [0, 0, 0, 125, 125, 125, 255, 255, 255]\n    im.putpalette(colors)\n\n    out = str(tmp_path / \"temp.bmp\")\n    im.save(out)\n\n    with Image.open(out) as reloaded:\n        assert reloaded.getpalette() == colors",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 54,
                            "end_lineno": 63,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "#\n# The Python Imaging Library.\n# $Id$\n#\n# BMP file handler\n#\n# Windows (and OS/2) native bitmap storage format.\n#\n# history:\n# 1995-09-01 fl   Created\n# 1996-04-30 fl   Added save\n# 1997-08-27 fl   Fixed save of 1-bit images\n# 1998-03-06 fl   Load P images as L where possible\n# 1998-07-03 fl   Load P images as 1 where possible\n# 1998-12-29 fl   Handle small palettes\n# 2002-12-30 fl   Fixed load of 1-bit palette images\n# 2003-04-21 fl   Fixed load of 1-bit monochrome images\n# 2003-04-23 fl   Added limited support for BI_BITFIELDS compression\n#\n# Copyright (c) 1997-2003 by Secret Labs AB\n# Copyright (c) 1995-2003 by Fredrik Lundh\n#\n# See the README file for information on usage and redistribution.\n#\n\n\nimport os\n\nfrom . import Image, ImageFile, ImagePalette\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# Read BMP file\n\nBIT2MODE = {\n    # bits => mode, rawmode\n    1: (\"P\", \"P;1\"),\n    4: (\"P\", \"P;4\"),\n    8: (\"P\", \"P\"),\n    16: (\"RGB\", \"BGR;15\"),\n    24: (\"RGB\", \"BGR\"),\n    32: (\"RGB\", \"BGRX\"),\n}\n\n\ndef _accept(prefix):\n    return prefix[:2] == b\"BM\"\n\n\ndef _dib_accept(prefix):\n    return i32(prefix) in [12, 40, 64, 108, 124]\n\n\n# =============================================================================\n# Image plugin for the Windows BMP format.\n# =============================================================================\nclass BmpImageFile(ImageFile.ImageFile):\n    \"\"\"Image plugin for the Windows Bitmap format (BMP)\"\"\"\n\n    # ------------------------------------------------------------- Description\n    format_description = \"Windows Bitmap\"\n    format = \"BMP\"\n\n    # -------------------------------------------------- BMP Compression values\n    COMPRESSIONS = {\"RAW\": 0, \"RLE8\": 1, \"RLE4\": 2, \"BITFIELDS\": 3, \"JPEG\": 4, \"PNG\": 5}\n    for k, v in COMPRESSIONS.items():\n        vars()[k] = v\n\n    def _bitmap(self, header=0, offset=0):\n        \"\"\"Read relevant info about the BMP\"\"\"\n        read, seek = self.fp.read, self.fp.seek\n        if header:\n            seek(header)\n        # read bmp header size @offset 14 (this is part of the header size)\n        file_info = {\"header_size\": i32(read(4)), \"direction\": -1}\n\n        # -------------------- If requested, read header at a specific position\n        # read the rest of the bmp header, without its size\n        header_data = ImageFile._safe_read(self.fp, file_info[\"header_size\"] - 4)\n\n        # -------------------------------------------------- IBM OS/2 Bitmap v1\n        # ----- This format has different offsets because of width/height types\n        if file_info[\"header_size\"] == 12:\n            file_info[\"width\"] = i16(header_data, 0)\n            file_info[\"height\"] = i16(header_data, 2)\n            file_info[\"planes\"] = i16(header_data, 4)\n            file_info[\"bits\"] = i16(header_data, 6)\n            file_info[\"compression\"] = self.RAW\n            file_info[\"palette_padding\"] = 3\n\n        # --------------------------------------------- Windows Bitmap v2 to v5\n        # v3, OS/2 v2, v4, v5\n        elif file_info[\"header_size\"] in (40, 64, 108, 124):\n            file_info[\"y_flip\"] = header_data[7] == 0xFF\n            file_info[\"direction\"] = 1 if file_info[\"y_flip\"] else -1\n            file_info[\"width\"] = i32(header_data, 0)\n            file_info[\"height\"] = (\n                i32(header_data, 4)\n                if not file_info[\"y_flip\"]\n                else 2**32 - i32(header_data, 4)\n            )\n            file_info[\"planes\"] = i16(header_data, 8)\n            file_info[\"bits\"] = i16(header_data, 10)\n            file_info[\"compression\"] = i32(header_data, 12)\n            # byte size of pixel data\n            file_info[\"data_size\"] = i32(header_data, 16)\n            file_info[\"pixels_per_meter\"] = (\n                i32(header_data, 20),\n                i32(header_data, 24),\n            )\n            file_info[\"colors\"] = i32(header_data, 28)\n            file_info[\"palette_padding\"] = 4\n            self.info[\"dpi\"] = tuple(x / 39.3701 for x in file_info[\"pixels_per_meter\"])\n            if file_info[\"compression\"] == self.BITFIELDS:\n                if len(header_data) >= 52:\n                    for idx, mask in enumerate(\n                        [\"r_mask\", \"g_mask\", \"b_mask\", \"a_mask\"]\n                    ):\n                        file_info[mask] = i32(header_data, 36 + idx * 4)\n                else:\n                    # 40 byte headers only have the three components in the\n                    # bitfields masks, ref:\n                    # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx\n                    # See also\n                    # https://github.com/python-pillow/Pillow/issues/1293\n                    # There is a 4th component in the RGBQuad, in the alpha\n                    # location, but it is listed as a reserved component,\n                    # and it is not generally an alpha channel\n                    file_info[\"a_mask\"] = 0x0\n                    for mask in [\"r_mask\", \"g_mask\", \"b_mask\"]:\n                        file_info[mask] = i32(read(4))\n                file_info[\"rgb_mask\"] = (\n                    file_info[\"r_mask\"],\n                    file_info[\"g_mask\"],\n                    file_info[\"b_mask\"],\n                )\n                file_info[\"rgba_mask\"] = (\n                    file_info[\"r_mask\"],\n                    file_info[\"g_mask\"],\n                    file_info[\"b_mask\"],\n                    file_info[\"a_mask\"],\n                )\n        else:\n            raise OSError(f\"Unsupported BMP header type ({file_info['header_size']})\")\n\n        # ------------------ Special case : header is reported 40, which\n        # ---------------------- is shorter than real size for bpp >= 16\n        self._size = file_info[\"width\"], file_info[\"height\"]\n\n        # ------- If color count was not found in the header, compute from bits\n        file_info[\"colors\"] = (\n            file_info[\"colors\"]\n            if file_info.get(\"colors\", 0)\n            else (1 << file_info[\"bits\"])\n        )\n        if offset == 14 + file_info[\"header_size\"] and file_info[\"bits\"] <= 8:\n            offset += 4 * file_info[\"colors\"]\n\n        # ---------------------- Check bit depth for unusual unsupported values\n        self.mode, raw_mode = BIT2MODE.get(file_info[\"bits\"], (None, None))\n        if self.mode is None:\n            raise OSError(f\"Unsupported BMP pixel depth ({file_info['bits']})\")\n\n        # ---------------- Process BMP with Bitfields compression (not palette)\n        decoder_name = \"raw\"\n        if file_info[\"compression\"] == self.BITFIELDS:\n            SUPPORTED = {\n                32: [\n                    (0xFF0000, 0xFF00, 0xFF, 0x0),\n                    (0xFF000000, 0xFF0000, 0xFF00, 0x0),\n                    (0xFF000000, 0xFF0000, 0xFF00, 0xFF),\n                    (0xFF, 0xFF00, 0xFF0000, 0xFF000000),\n                    (0xFF0000, 0xFF00, 0xFF, 0xFF000000),\n                    (0x0, 0x0, 0x0, 0x0),\n                ],\n                24: [(0xFF0000, 0xFF00, 0xFF)],\n                16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],\n            }\n            MASK_MODES = {\n                (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): \"BGRX\",\n                (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): \"XBGR\",\n                (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): \"ABGR\",\n                (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): \"RGBA\",\n                (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): \"BGRA\",\n                (32, (0x0, 0x0, 0x0, 0x0)): \"BGRA\",\n                (24, (0xFF0000, 0xFF00, 0xFF)): \"BGR\",\n                (16, (0xF800, 0x7E0, 0x1F)): \"BGR;16\",\n                (16, (0x7C00, 0x3E0, 0x1F)): \"BGR;15\",\n            }\n            if file_info[\"bits\"] in SUPPORTED:\n                if (\n                    file_info[\"bits\"] == 32\n                    and file_info[\"rgba_mask\"] in SUPPORTED[file_info[\"bits\"]]\n                ):\n                    raw_mode = MASK_MODES[(file_info[\"bits\"], file_info[\"rgba_mask\"])]\n                    self.mode = \"RGBA\" if \"A\" in raw_mode else self.mode\n                elif (\n                    file_info[\"bits\"] in (24, 16)\n                    and file_info[\"rgb_mask\"] in SUPPORTED[file_info[\"bits\"]]\n                ):\n                    raw_mode = MASK_MODES[(file_info[\"bits\"], file_info[\"rgb_mask\"])]\n                else:\n                    raise OSError(\"Unsupported BMP bitfields layout\")\n            else:\n                raise OSError(\"Unsupported BMP bitfields layout\")\n        elif file_info[\"compression\"] == self.RAW:\n            if file_info[\"bits\"] == 32 and header == 22:  # 32-bit .cur offset\n                raw_mode, self.mode = \"BGRA\", \"RGBA\"\n        elif file_info[\"compression\"] == self.RLE8:\n            decoder_name = \"bmp_rle\"\n        else:\n            raise OSError(f\"Unsupported BMP compression ({file_info['compression']})\")\n\n        # --------------- Once the header is processed, process the palette/LUT\n        if self.mode == \"P\":  # Paletted for 1, 4 and 8 bit images\n\n            # ---------------------------------------------------- 1-bit images\n            if not (0 < file_info[\"colors\"] <= 65536):\n                raise OSError(f\"Unsupported BMP Palette size ({file_info['colors']})\")\n            else:\n                padding = file_info[\"palette_padding\"]\n                palette = read(padding * file_info[\"colors\"])\n                greyscale = True\n                indices = (\n                    (0, 255)\n                    if file_info[\"colors\"] == 2\n                    else list(range(file_info[\"colors\"]))\n                )\n\n                # ----------------- Check if greyscale and ignore palette if so\n                for ind, val in enumerate(indices):\n                    rgb = palette[ind * padding : ind * padding + 3]\n                    if rgb != o8(val) * 3:\n                        greyscale = False\n\n                # ------- If all colors are grey, white or black, ditch palette\n                if greyscale:\n                    self.mode = \"1\" if file_info[\"colors\"] == 2 else \"L\"\n                    raw_mode = self.mode\n                else:\n                    self.mode = \"P\"\n                    self.palette = ImagePalette.raw(\n                        \"BGRX\" if padding == 4 else \"BGR\", palette\n                    )\n\n        # ---------------------------- Finally set the tile data for the plugin\n        self.info[\"compression\"] = file_info[\"compression\"]\n        self.tile = [\n            (\n                decoder_name,\n                (0, 0, file_info[\"width\"], file_info[\"height\"]),\n                offset or self.fp.tell(),\n                (\n                    raw_mode,\n                    ((file_info[\"width\"] * file_info[\"bits\"] + 31) >> 3) & (~3),\n                    file_info[\"direction\"],\n                ),\n            )\n        ]\n\n    def _open(self):\n        \"\"\"Open file, check magic number and read header\"\"\"\n        # read 14 bytes: magic number, filesize, reserved, header final offset\n        head_data = self.fp.read(14)\n        # choke if the file does not have the required magic bytes\n        if not _accept(head_data):\n            raise SyntaxError(\"Not a BMP file\")\n        # read the start position of the BMP image data (u32)\n        offset = i32(head_data, 10)\n        # load bitmap information (offset=raster info)\n        self._bitmap(offset=offset)\n\n\nclass BmpRleDecoder(ImageFile.PyDecoder):\n    _pulls_fd = True\n\n    def decode(self, buffer):\n        data = bytearray()\n        x = 0\n        while len(data) < self.state.xsize * self.state.ysize:\n            pixels = self.fd.read(1)\n            byte = self.fd.read(1)\n            if not pixels or not byte:\n                break\n            num_pixels = pixels[0]\n            if num_pixels:\n                # encoded mode\n                if x + num_pixels > self.state.xsize:\n                    # Too much data for row\n                    num_pixels = max(0, self.state.xsize - x)\n                data += byte * num_pixels\n                x += num_pixels\n            else:\n                if byte[0] == 0:\n                    # end of line\n                    while len(data) % self.state.xsize != 0:\n                        data += b\"\\x00\"\n                    x = 0\n                elif byte[0] == 1:\n                    # end of bitmap\n                    break\n                elif byte[0] == 2:\n                    # delta\n                    bytes_read = self.fd.read(2)\n                    if len(bytes_read) < 2:\n                        break\n                    right, up = self.fd.read(2)\n                    data += b\"\\x00\" * (right + up * self.state.xsize)\n                    x = len(data) % self.state.xsize\n                else:\n                    # absolute mode\n                    bytes_read = self.fd.read(byte[0])\n                    data += bytes_read\n                    if len(bytes_read) < byte[0]:\n                        break\n                    x += byte[0]\n\n                    # align to 16-bit word boundary\n                    if self.fd.tell() % 2 != 0:\n                        self.fd.seek(1, os.SEEK_CUR)\n        rawmode = \"L\" if self.mode == \"L\" else \"P\"\n        self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1]))\n        return -1, 0\n\n\n# =============================================================================\n# Image plugin for the DIB format (BMP alias)\n# =============================================================================\nclass DibImageFile(BmpImageFile):\n\n    format = \"DIB\"\n    format_description = \"Windows Bitmap\"\n\n    def _open(self):\n        self._bitmap()\n\n\n#\n# --------------------------------------------------------------------\n# Write BMP file\n\n\nSAVE = {\n    \"1\": (\"1\", 1, 2),\n    \"L\": (\"L\", 8, 256),\n    \"P\": (\"P\", 8, 256),\n    \"RGB\": (\"BGR\", 24, 0),\n    \"RGBA\": (\"BGRA\", 32, 0),\n}\n\n\ndef _dib_save(im, fp, filename):\n    _save(im, fp, filename, False)\n\n\ndef _save(im, fp, filename, bitmap_header=True):\n    try:\n        rawmode, bits, colors = SAVE[im.mode]\n    except KeyError as e:\n        raise OSError(f\"cannot write mode {im.mode} as BMP\") from e\n\n    info = im.encoderinfo\n\n    dpi = info.get(\"dpi\", (96, 96))\n\n    # 1 meter == 39.3701 inches\n    ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))\n\n    stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)\n    header = 40  # or 64 for OS/2 version 2\n    image = stride * im.size[1]\n\n    # bitmap header\n    if bitmap_header:\n        offset = 14 + header + colors * 4\n        file_size = offset + image\n        if file_size > 2**32 - 1:\n            raise ValueError(\"File size is too large for the BMP format\")\n        fp.write(\n            b\"BM\"  # file type (magic)\n            + o32(file_size)  # file size\n            + o32(0)  # reserved\n            + o32(offset)  # image data offset\n        )\n\n    # bitmap info header\n    fp.write(\n        o32(header)  # info header size\n        + o32(im.size[0])  # width\n        + o32(im.size[1])  # height\n        + o16(1)  # planes\n        + o16(bits)  # depth\n        + o32(0)  # compression (0=uncompressed)\n        + o32(image)  # size of bitmap\n        + o32(ppm[0])  # resolution\n        + o32(ppm[1])  # resolution\n        + o32(colors)  # colors used\n        + o32(colors)  # colors important\n    )\n\n    fp.write(b\"\\0\" * (header - 40))  # padding (for OS/2 format)\n\n    if im.mode == \"1\":\n        for i in (0, 255):\n            fp.write(o8(i) * 4)\n    elif im.mode == \"L\":\n        for i in range(256):\n            fp.write(o8(i) * 4)\n    elif im.mode == \"P\":\n        fp.write(im.im.getpalette(\"RGB\", \"BGRX\"))\n\n    ImageFile._save(im, fp, [(\"raw\", (0, 0) + im.size, 0, (rawmode, stride, -1))])\n\n\n#\n# --------------------------------------------------------------------\n# Registry\n\n\nImage.register_open(BmpImageFile.format, BmpImageFile, _accept)\nImage.register_save(BmpImageFile.format, _save)\n\nImage.register_extension(BmpImageFile.format, \".bmp\")\n\nImage.register_mime(BmpImageFile.format, \"image/bmp\")\n\nImage.register_decoder(\"bmp_rle\", BmpRleDecoder)\n\nImage.register_open(DibImageFile.format, DibImageFile, _dib_accept)\nImage.register_save(DibImageFile.format, _dib_save)\n\nImage.register_extension(DibImageFile.format, \".dib\")\n\nImage.register_mime(DibImageFile.format, \"image/bmp\")\n",
            "new_file_content": "#\n# The Python Imaging Library.\n# $Id$\n#\n# BMP file handler\n#\n# Windows (and OS/2) native bitmap storage format.\n#\n# history:\n# 1995-09-01 fl   Created\n# 1996-04-30 fl   Added save\n# 1997-08-27 fl   Fixed save of 1-bit images\n# 1998-03-06 fl   Load P images as L where possible\n# 1998-07-03 fl   Load P images as 1 where possible\n# 1998-12-29 fl   Handle small palettes\n# 2002-12-30 fl   Fixed load of 1-bit palette images\n# 2003-04-21 fl   Fixed load of 1-bit monochrome images\n# 2003-04-23 fl   Added limited support for BI_BITFIELDS compression\n#\n# Copyright (c) 1997-2003 by Secret Labs AB\n# Copyright (c) 1995-2003 by Fredrik Lundh\n#\n# See the README file for information on usage and redistribution.\n#\n\n\nimport os\n\nfrom . import Image, ImageFile, ImagePalette\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# Read BMP file\n\nBIT2MODE = {\n    # bits => mode, rawmode\n    1: (\"P\", \"P;1\"),\n    4: (\"P\", \"P;4\"),\n    8: (\"P\", \"P\"),\n    16: (\"RGB\", \"BGR;15\"),\n    24: (\"RGB\", \"BGR\"),\n    32: (\"RGB\", \"BGRX\"),\n}\n\n\ndef _accept(prefix):\n    return prefix[:2] == b\"BM\"\n\n\ndef _dib_accept(prefix):\n    return i32(prefix) in [12, 40, 64, 108, 124]\n\n\n# =============================================================================\n# Image plugin for the Windows BMP format.\n# =============================================================================\nclass BmpImageFile(ImageFile.ImageFile):\n    \"\"\"Image plugin for the Windows Bitmap format (BMP)\"\"\"\n\n    # ------------------------------------------------------------- Description\n    format_description = \"Windows Bitmap\"\n    format = \"BMP\"\n\n    # -------------------------------------------------- BMP Compression values\n    COMPRESSIONS = {\"RAW\": 0, \"RLE8\": 1, \"RLE4\": 2, \"BITFIELDS\": 3, \"JPEG\": 4, \"PNG\": 5}\n    for k, v in COMPRESSIONS.items():\n        vars()[k] = v\n\n    def _bitmap(self, header=0, offset=0):\n        \"\"\"Read relevant info about the BMP\"\"\"\n        read, seek = self.fp.read, self.fp.seek\n        if header:\n            seek(header)\n        # read bmp header size @offset 14 (this is part of the header size)\n        file_info = {\"header_size\": i32(read(4)), \"direction\": -1}\n\n        # -------------------- If requested, read header at a specific position\n        # read the rest of the bmp header, without its size\n        header_data = ImageFile._safe_read(self.fp, file_info[\"header_size\"] - 4)\n\n        # -------------------------------------------------- IBM OS/2 Bitmap v1\n        # ----- This format has different offsets because of width/height types\n        if file_info[\"header_size\"] == 12:\n            file_info[\"width\"] = i16(header_data, 0)\n            file_info[\"height\"] = i16(header_data, 2)\n            file_info[\"planes\"] = i16(header_data, 4)\n            file_info[\"bits\"] = i16(header_data, 6)\n            file_info[\"compression\"] = self.RAW\n            file_info[\"palette_padding\"] = 3\n\n        # --------------------------------------------- Windows Bitmap v2 to v5\n        # v3, OS/2 v2, v4, v5\n        elif file_info[\"header_size\"] in (40, 64, 108, 124):\n            file_info[\"y_flip\"] = header_data[7] == 0xFF\n            file_info[\"direction\"] = 1 if file_info[\"y_flip\"] else -1\n            file_info[\"width\"] = i32(header_data, 0)\n            file_info[\"height\"] = (\n                i32(header_data, 4)\n                if not file_info[\"y_flip\"]\n                else 2**32 - i32(header_data, 4)\n            )\n            file_info[\"planes\"] = i16(header_data, 8)\n            file_info[\"bits\"] = i16(header_data, 10)\n            file_info[\"compression\"] = i32(header_data, 12)\n            # byte size of pixel data\n            file_info[\"data_size\"] = i32(header_data, 16)\n            file_info[\"pixels_per_meter\"] = (\n                i32(header_data, 20),\n                i32(header_data, 24),\n            )\n            file_info[\"colors\"] = i32(header_data, 28)\n            file_info[\"palette_padding\"] = 4\n            self.info[\"dpi\"] = tuple(x / 39.3701 for x in file_info[\"pixels_per_meter\"])\n            if file_info[\"compression\"] == self.BITFIELDS:\n                if len(header_data) >= 52:\n                    for idx, mask in enumerate(\n                        [\"r_mask\", \"g_mask\", \"b_mask\", \"a_mask\"]\n                    ):\n                        file_info[mask] = i32(header_data, 36 + idx * 4)\n                else:\n                    # 40 byte headers only have the three components in the\n                    # bitfields masks, ref:\n                    # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx\n                    # See also\n                    # https://github.com/python-pillow/Pillow/issues/1293\n                    # There is a 4th component in the RGBQuad, in the alpha\n                    # location, but it is listed as a reserved component,\n                    # and it is not generally an alpha channel\n                    file_info[\"a_mask\"] = 0x0\n                    for mask in [\"r_mask\", \"g_mask\", \"b_mask\"]:\n                        file_info[mask] = i32(read(4))\n                file_info[\"rgb_mask\"] = (\n                    file_info[\"r_mask\"],\n                    file_info[\"g_mask\"],\n                    file_info[\"b_mask\"],\n                )\n                file_info[\"rgba_mask\"] = (\n                    file_info[\"r_mask\"],\n                    file_info[\"g_mask\"],\n                    file_info[\"b_mask\"],\n                    file_info[\"a_mask\"],\n                )\n        else:\n            raise OSError(f\"Unsupported BMP header type ({file_info['header_size']})\")\n\n        # ------------------ Special case : header is reported 40, which\n        # ---------------------- is shorter than real size for bpp >= 16\n        self._size = file_info[\"width\"], file_info[\"height\"]\n\n        # ------- If color count was not found in the header, compute from bits\n        file_info[\"colors\"] = (\n            file_info[\"colors\"]\n            if file_info.get(\"colors\", 0)\n            else (1 << file_info[\"bits\"])\n        )\n        if offset == 14 + file_info[\"header_size\"] and file_info[\"bits\"] <= 8:\n            offset += 4 * file_info[\"colors\"]\n\n        # ---------------------- Check bit depth for unusual unsupported values\n        self.mode, raw_mode = BIT2MODE.get(file_info[\"bits\"], (None, None))\n        if self.mode is None:\n            raise OSError(f\"Unsupported BMP pixel depth ({file_info['bits']})\")\n\n        # ---------------- Process BMP with Bitfields compression (not palette)\n        decoder_name = \"raw\"\n        if file_info[\"compression\"] == self.BITFIELDS:\n            SUPPORTED = {\n                32: [\n                    (0xFF0000, 0xFF00, 0xFF, 0x0),\n                    (0xFF000000, 0xFF0000, 0xFF00, 0x0),\n                    (0xFF000000, 0xFF0000, 0xFF00, 0xFF),\n                    (0xFF, 0xFF00, 0xFF0000, 0xFF000000),\n                    (0xFF0000, 0xFF00, 0xFF, 0xFF000000),\n                    (0x0, 0x0, 0x0, 0x0),\n                ],\n                24: [(0xFF0000, 0xFF00, 0xFF)],\n                16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],\n            }\n            MASK_MODES = {\n                (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): \"BGRX\",\n                (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): \"XBGR\",\n                (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): \"ABGR\",\n                (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): \"RGBA\",\n                (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): \"BGRA\",\n                (32, (0x0, 0x0, 0x0, 0x0)): \"BGRA\",\n                (24, (0xFF0000, 0xFF00, 0xFF)): \"BGR\",\n                (16, (0xF800, 0x7E0, 0x1F)): \"BGR;16\",\n                (16, (0x7C00, 0x3E0, 0x1F)): \"BGR;15\",\n            }\n            if file_info[\"bits\"] in SUPPORTED:\n                if (\n                    file_info[\"bits\"] == 32\n                    and file_info[\"rgba_mask\"] in SUPPORTED[file_info[\"bits\"]]\n                ):\n                    raw_mode = MASK_MODES[(file_info[\"bits\"], file_info[\"rgba_mask\"])]\n                    self.mode = \"RGBA\" if \"A\" in raw_mode else self.mode\n                elif (\n                    file_info[\"bits\"] in (24, 16)\n                    and file_info[\"rgb_mask\"] in SUPPORTED[file_info[\"bits\"]]\n                ):\n                    raw_mode = MASK_MODES[(file_info[\"bits\"], file_info[\"rgb_mask\"])]\n                else:\n                    raise OSError(\"Unsupported BMP bitfields layout\")\n            else:\n                raise OSError(\"Unsupported BMP bitfields layout\")\n        elif file_info[\"compression\"] == self.RAW:\n            if file_info[\"bits\"] == 32 and header == 22:  # 32-bit .cur offset\n                raw_mode, self.mode = \"BGRA\", \"RGBA\"\n        elif file_info[\"compression\"] == self.RLE8:\n            decoder_name = \"bmp_rle\"\n        else:\n            raise OSError(f\"Unsupported BMP compression ({file_info['compression']})\")\n\n        # --------------- Once the header is processed, process the palette/LUT\n        if self.mode == \"P\":  # Paletted for 1, 4 and 8 bit images\n\n            # ---------------------------------------------------- 1-bit images\n            if not (0 < file_info[\"colors\"] <= 65536):\n                raise OSError(f\"Unsupported BMP Palette size ({file_info['colors']})\")\n            else:\n                padding = file_info[\"palette_padding\"]\n                palette = read(padding * file_info[\"colors\"])\n                greyscale = True\n                indices = (\n                    (0, 255)\n                    if file_info[\"colors\"] == 2\n                    else list(range(file_info[\"colors\"]))\n                )\n\n                # ----------------- Check if greyscale and ignore palette if so\n                for ind, val in enumerate(indices):\n                    rgb = palette[ind * padding : ind * padding + 3]\n                    if rgb != o8(val) * 3:\n                        greyscale = False\n\n                # ------- If all colors are grey, white or black, ditch palette\n                if greyscale:\n                    self.mode = \"1\" if file_info[\"colors\"] == 2 else \"L\"\n                    raw_mode = self.mode\n                else:\n                    self.mode = \"P\"\n                    self.palette = ImagePalette.raw(\n                        \"BGRX\" if padding == 4 else \"BGR\", palette\n                    )\n\n        # ---------------------------- Finally set the tile data for the plugin\n        self.info[\"compression\"] = file_info[\"compression\"]\n        self.tile = [\n            (\n                decoder_name,\n                (0, 0, file_info[\"width\"], file_info[\"height\"]),\n                offset or self.fp.tell(),\n                (\n                    raw_mode,\n                    ((file_info[\"width\"] * file_info[\"bits\"] + 31) >> 3) & (~3),\n                    file_info[\"direction\"],\n                ),\n            )\n        ]\n\n    def _open(self):\n        \"\"\"Open file, check magic number and read header\"\"\"\n        # read 14 bytes: magic number, filesize, reserved, header final offset\n        head_data = self.fp.read(14)\n        # choke if the file does not have the required magic bytes\n        if not _accept(head_data):\n            raise SyntaxError(\"Not a BMP file\")\n        # read the start position of the BMP image data (u32)\n        offset = i32(head_data, 10)\n        # load bitmap information (offset=raster info)\n        self._bitmap(offset=offset)\n\n\nclass BmpRleDecoder(ImageFile.PyDecoder):\n    _pulls_fd = True\n\n    def decode(self, buffer):\n        data = bytearray()\n        x = 0\n        while len(data) < self.state.xsize * self.state.ysize:\n            pixels = self.fd.read(1)\n            byte = self.fd.read(1)\n            if not pixels or not byte:\n                break\n            num_pixels = pixels[0]\n            if num_pixels:\n                # encoded mode\n                if x + num_pixels > self.state.xsize:\n                    # Too much data for row\n                    num_pixels = max(0, self.state.xsize - x)\n                data += byte * num_pixels\n                x += num_pixels\n            else:\n                if byte[0] == 0:\n                    # end of line\n                    while len(data) % self.state.xsize != 0:\n                        data += b\"\\x00\"\n                    x = 0\n                elif byte[0] == 1:\n                    # end of bitmap\n                    break\n                elif byte[0] == 2:\n                    # delta\n                    bytes_read = self.fd.read(2)\n                    if len(bytes_read) < 2:\n                        break\n                    right, up = self.fd.read(2)\n                    data += b\"\\x00\" * (right + up * self.state.xsize)\n                    x = len(data) % self.state.xsize\n                else:\n                    # absolute mode\n                    bytes_read = self.fd.read(byte[0])\n                    data += bytes_read\n                    if len(bytes_read) < byte[0]:\n                        break\n                    x += byte[0]\n\n                    # align to 16-bit word boundary\n                    if self.fd.tell() % 2 != 0:\n                        self.fd.seek(1, os.SEEK_CUR)\n        rawmode = \"L\" if self.mode == \"L\" else \"P\"\n        self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1]))\n        return -1, 0\n\n\n# =============================================================================\n# Image plugin for the DIB format (BMP alias)\n# =============================================================================\nclass DibImageFile(BmpImageFile):\n\n    format = \"DIB\"\n    format_description = \"Windows Bitmap\"\n\n    def _open(self):\n        self._bitmap()\n\n\n#\n# --------------------------------------------------------------------\n# Write BMP file\n\n\nSAVE = {\n    \"1\": (\"1\", 1, 2),\n    \"L\": (\"L\", 8, 256),\n    \"P\": (\"P\", 8, 256),\n    \"RGB\": (\"BGR\", 24, 0),\n    \"RGBA\": (\"BGRA\", 32, 0),\n}\n\n\ndef _dib_save(im, fp, filename):\n    _save(im, fp, filename, False)\n\n\ndef _save(im, fp, filename, bitmap_header=True):\n    try:\n        rawmode, bits, colors = SAVE[im.mode]\n    except KeyError as e:\n        raise OSError(f\"cannot write mode {im.mode} as BMP\") from e\n\n    info = im.encoderinfo\n\n    dpi = info.get(\"dpi\", (96, 96))\n\n    # 1 meter == 39.3701 inches\n    ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))\n\n    stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)\n    header = 40  # or 64 for OS/2 version 2\n    image = stride * im.size[1]\n\n    if im.mode == \"1\":\n        palette = b\"\".join(o8(i) * 4 for i in (0, 255))\n    elif im.mode == \"L\":\n        palette = b\"\".join(o8(i) * 4 for i in range(256))\n    elif im.mode == \"P\":\n        palette = im.im.getpalette(\"RGB\", \"BGRX\")\n        colors = len(palette) // 4\n    else:\n        palette = None\n\n    # bitmap header\n    if bitmap_header:\n        offset = 14 + header + colors * 4\n        file_size = offset + image\n        if file_size > 2**32 - 1:\n            raise ValueError(\"File size is too large for the BMP format\")\n        fp.write(\n            b\"BM\"  # file type (magic)\n            + o32(file_size)  # file size\n            + o32(0)  # reserved\n            + o32(offset)  # image data offset\n        )\n\n    # bitmap info header\n    fp.write(\n        o32(header)  # info header size\n        + o32(im.size[0])  # width\n        + o32(im.size[1])  # height\n        + o16(1)  # planes\n        + o16(bits)  # depth\n        + o32(0)  # compression (0=uncompressed)\n        + o32(image)  # size of bitmap\n        + o32(ppm[0])  # resolution\n        + o32(ppm[1])  # resolution\n        + o32(colors)  # colors used\n        + o32(colors)  # colors important\n    )\n\n    fp.write(b\"\\0\" * (header - 40))  # padding (for OS/2 format)\n\n    if palette:\n        fp.write(palette)\n\n    ImageFile._save(im, fp, [(\"raw\", (0, 0) + im.size, 0, (rawmode, stride, -1))])\n\n\n#\n# --------------------------------------------------------------------\n# Registry\n\n\nImage.register_open(BmpImageFile.format, BmpImageFile, _accept)\nImage.register_save(BmpImageFile.format, _save)\n\nImage.register_extension(BmpImageFile.format, \".bmp\")\n\nImage.register_mime(BmpImageFile.format, \"image/bmp\")\n\nImage.register_decoder(\"bmp_rle\", BmpRleDecoder)\n\nImage.register_open(DibImageFile.format, DibImageFile, _dib_accept)\nImage.register_save(DibImageFile.format, _dib_save)\n\nImage.register_extension(DibImageFile.format, \".dib\")\n\nImage.register_mime(DibImageFile.format, \"image/bmp\")\n",
            "header": {
                "file": {
                    "path": "src/PIL/BmpImagePlugin.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "7bb73fc93",
                "new_commit_hash": "1041ab763",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/src/PIL/BmpImagePlugin.py"
            },
            "plus_file": {
                "path": "b/src/PIL/BmpImagePlugin.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 375,
                            "length": 6
                        },
                        "new_range": {
                            "start": 375,
                            "length": 16
                        },
                        "section": "def _save(im, fp, filename, bitmap_header=True):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    header = 40  # or 64 for OS/2 version 2",
                                "type": "context"
                            },
                            {
                                "content": "    image = stride * im.size[1]",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    if im.mode == \"1\":",
                                "type": "added"
                            },
                            {
                                "content": "        palette = b\"\".join(o8(i) * 4 for i in (0, 255))",
                                "type": "added"
                            },
                            {
                                "content": "    elif im.mode == \"L\":",
                                "type": "added"
                            },
                            {
                                "content": "        palette = b\"\".join(o8(i) * 4 for i in range(256))",
                                "type": "added"
                            },
                            {
                                "content": "    elif im.mode == \"P\":",
                                "type": "added"
                            },
                            {
                                "content": "        palette = im.im.getpalette(\"RGB\", \"BGRX\")",
                                "type": "added"
                            },
                            {
                                "content": "        colors = len(palette) // 4",
                                "type": "added"
                            },
                            {
                                "content": "    else:",
                                "type": "added"
                            },
                            {
                                "content": "        palette = None",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    # bitmap header",
                                "type": "context"
                            },
                            {
                                "content": "    if bitmap_header:",
                                "type": "context"
                            },
                            {
                                "content": "        offset = 14 + header + colors * 4",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "src/PIL/BmpImagePlugin.py",
                            "type": "function",
                            "name": "_save",
                            "content": "def _save(im, fp, filename, bitmap_header=True):\n    try:\n        rawmode, bits, colors = SAVE[im.mode]\n    except KeyError as e:\n        raise OSError(f\"cannot write mode {im.mode} as BMP\") from e\n\n    info = im.encoderinfo\n\n    dpi = info.get(\"dpi\", (96, 96))\n\n    # 1 meter == 39.3701 inches\n    ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))\n\n    stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)\n    header = 40  # or 64 for OS/2 version 2\n    image = stride * im.size[1]\n\n    if im.mode == \"1\":\n        palette = b\"\".join(o8(i) * 4 for i in (0, 255))\n    elif im.mode == \"L\":\n        palette = b\"\".join(o8(i) * 4 for i in range(256))\n    elif im.mode == \"P\":\n        palette = im.im.getpalette(\"RGB\", \"BGRX\")\n        colors = len(palette) // 4\n    else:\n        palette = None\n\n    # bitmap header\n    if bitmap_header:\n        offset = 14 + header + colors * 4\n        file_size = offset + image\n        if file_size > 2**32 - 1:\n            raise ValueError(\"File size is too large for the BMP format\")\n        fp.write(\n            b\"BM\"  # file type (magic)\n            + o32(file_size)  # file size\n            + o32(0)  # reserved\n            + o32(offset)  # image data offset\n        )\n\n    # bitmap info header\n    fp.write(\n        o32(header)  # info header size\n        + o32(im.size[0])  # width\n        + o32(im.size[1])  # height\n        + o16(1)  # planes\n        + o16(bits)  # depth\n        + o32(0)  # compression (0=uncompressed)\n        + o32(image)  # size of bitmap\n        + o32(ppm[0])  # resolution\n        + o32(ppm[1])  # resolution\n        + o32(colors)  # colors used\n        + o32(colors)  # colors important\n    )\n\n    fp.write(b\"\\0\" * (header - 40))  # padding (for OS/2 format)\n\n    if palette:\n        fp.write(palette)\n\n    ImageFile._save(im, fp, [(\"raw\", (0, 0) + im.size, 0, (rawmode, stride, -1))])",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 361,
                            "end_lineno": 421,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 405,
                            "length": 14
                        },
                        "new_range": {
                            "start": 415,
                            "length": 8
                        },
                        "section": "def _save(im, fp, filename, bitmap_header=True):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    fp.write(b\"\\0\" * (header - 40))  # padding (for OS/2 format)",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    if im.mode == \"1\":",
                                "type": "deleted"
                            },
                            {
                                "content": "        for i in (0, 255):",
                                "type": "deleted"
                            },
                            {
                                "content": "            fp.write(o8(i) * 4)",
                                "type": "deleted"
                            },
                            {
                                "content": "    elif im.mode == \"L\":",
                                "type": "deleted"
                            },
                            {
                                "content": "        for i in range(256):",
                                "type": "deleted"
                            },
                            {
                                "content": "            fp.write(o8(i) * 4)",
                                "type": "deleted"
                            },
                            {
                                "content": "    elif im.mode == \"P\":",
                                "type": "deleted"
                            },
                            {
                                "content": "        fp.write(im.im.getpalette(\"RGB\", \"BGRX\"))",
                                "type": "deleted"
                            },
                            {
                                "content": "    if palette:",
                                "type": "added"
                            },
                            {
                                "content": "        fp.write(palette)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    ImageFile._save(im, fp, [(\"raw\", (0, 0) + im.size, 0, (rawmode, stride, -1))])",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "src/PIL/BmpImagePlugin.py",
                            "type": "function",
                            "name": "_save",
                            "content": "def _save(im, fp, filename, bitmap_header=True):\n    try:\n        rawmode, bits, colors = SAVE[im.mode]\n    except KeyError as e:\n        raise OSError(f\"cannot write mode {im.mode} as BMP\") from e\n\n    info = im.encoderinfo\n\n    dpi = info.get(\"dpi\", (96, 96))\n\n    # 1 meter == 39.3701 inches\n    ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))\n\n    stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)\n    header = 40  # or 64 for OS/2 version 2\n    image = stride * im.size[1]\n\n    # bitmap header\n    if bitmap_header:\n        offset = 14 + header + colors * 4\n        file_size = offset + image\n        if file_size > 2**32 - 1:\n            raise ValueError(\"File size is too large for the BMP format\")\n        fp.write(\n            b\"BM\"  # file type (magic)\n            + o32(file_size)  # file size\n            + o32(0)  # reserved\n            + o32(offset)  # image data offset\n        )\n\n    # bitmap info header\n    fp.write(\n        o32(header)  # info header size\n        + o32(im.size[0])  # width\n        + o32(im.size[1])  # height\n        + o16(1)  # planes\n        + o16(bits)  # depth\n        + o32(0)  # compression (0=uncompressed)\n        + o32(image)  # size of bitmap\n        + o32(ppm[0])  # resolution\n        + o32(ppm[1])  # resolution\n        + o32(colors)  # colors used\n        + o32(colors)  # colors important\n    )\n\n    fp.write(b\"\\0\" * (header - 40))  # padding (for OS/2 format)\n\n    if im.mode == \"1\":\n        for i in (0, 255):\n            fp.write(o8(i) * 4)\n    elif im.mode == \"L\":\n        for i in range(256):\n            fp.write(o8(i) * 4)\n    elif im.mode == \"P\":\n        fp.write(im.im.getpalette(\"RGB\", \"BGRX\"))\n\n    ImageFile._save(im, fp, [(\"raw\", (0, 0) + im.size, 0, (rawmode, stride, -1))])",
                            "ast_type_str": "FunctionDef",
                            "start_lineno": 361,
                            "end_lineno": 417,
                            "parent": null
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "8b2d70d17a4791d68df6c10d8337d769290c6528^",
    "new_commit_hash": "8b2d70d17a4791d68df6c10d8337d769290c6528",
    "commit_message": "Corrected BMP palette size when saving",
    "commit_date": "2022-09-14T19:54:36+10:00",
    "metadata": {}
}
{
    "repo_name": "pillow",
    "new_commit_hash": "8b2d70d17a4791d68df6c10d8337d769290c6528",
    "test_file_codes": [
        "import io\n\nimport pytest\n\nfrom PIL import BmpImagePlugin, Image\n\nfrom .helper import (\n    assert_image_equal,\n    assert_image_equal_tofile,\n    assert_image_similar_tofile,\n    hopper,\n)\n\n\ndef test_sanity(tmp_path):\n    def roundtrip(im):\n        outfile = str(tmp_path / \"temp.bmp\")\n\n        im.save(outfile, \"BMP\")\n\n        with Image.open(outfile) as reloaded:\n            reloaded.load()\n            assert im.mode == reloaded.mode\n            assert im.size == reloaded.size\n            assert reloaded.format == \"BMP\"\n            assert reloaded.get_format_mimetype() == \"image/bmp\"\n\n    roundtrip(hopper())\n\n    roundtrip(hopper(\"1\"))\n    roundtrip(hopper(\"L\"))\n    roundtrip(hopper(\"P\"))\n    roundtrip(hopper(\"RGB\"))\n\n\ndef test_invalid_file():\n    with open(\"Tests/images/flower.jpg\", \"rb\") as fp:\n        with pytest.raises(SyntaxError):\n            BmpImagePlugin.BmpImageFile(fp)\n\n\ndef test_save_to_bytes():\n    output = io.BytesIO()\n    im = hopper()\n    im.save(output, \"BMP\")\n\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert im.mode == reloaded.mode\n        assert im.size == reloaded.size\n        assert reloaded.format == \"BMP\"\n\n\ndef test_small_palette(tmp_path):\n    im = Image.new(\"P\", (1, 1))\n    colors = [0, 0, 0, 125, 125, 125, 255, 255, 255]\n    im.putpalette(colors)\n\n    out = str(tmp_path / \"temp.bmp\")\n    im.save(out)\n\n    with Image.open(out) as reloaded:\n        assert reloaded.getpalette() == colors\n\n\ndef test_save_too_large(tmp_path):\n    outfile = str(tmp_path / \"temp.bmp\")\n    with Image.new(\"RGB\", (1, 1)) as im:\n        im._size = (37838, 37838)\n        with pytest.raises(ValueError):\n            im.save(outfile)\n\n\ndef test_dpi():\n    dpi = (72, 72)\n\n    output = io.BytesIO()\n    with hopper() as im:\n        im.save(output, \"BMP\", dpi=dpi)\n\n    output.seek(0)\n    with Image.open(output) as reloaded:\n        assert reloaded.info[\"dpi\"] == (72.008961115161, 72.008961115161)\n\n\ndef test_save_bmp_with_dpi(tmp_path):\n    # Test for #1301\n    # Arrange\n    outfile = str(tmp_path / \"temp.jpg\")\n    with Image.open(\"Tests/images/hopper.bmp\") as im:\n        assert im.info[\"dpi\"] == (95.98654816726399, 95.98654816726399)\n\n        # Act\n        im.save(outfile, \"JPEG\", dpi=im.info[\"dpi\"])\n\n        # Assert\n        with Image.open(outfile) as reloaded:\n            reloaded.load()\n            assert reloaded.info[\"dpi\"] == (96, 96)\n            assert reloaded.size == im.size\n            assert reloaded.format == \"JPEG\"\n\n\ndef test_save_float_dpi(tmp_path):\n    outfile = str(tmp_path / \"temp.bmp\")\n    with Image.open(\"Tests/images/hopper.bmp\") as im:\n        im.save(outfile, dpi=(72.21216100543306, 72.21216100543306))\n        with Image.open(outfile) as reloaded:\n            assert reloaded.info[\"dpi\"] == (72.21216100543306, 72.21216100543306)\n\n\ndef test_load_dib():\n    # test for #1293, Imagegrab returning Unsupported Bitfields Format\n    with Image.open(\"Tests/images/clipboard.dib\") as im:\n        assert im.format == \"DIB\"\n        assert im.get_format_mimetype() == \"image/bmp\"\n\n        assert_image_equal_tofile(im, \"Tests/images/clipboard_target.png\")\n\n\ndef test_save_dib(tmp_path):\n    outfile = str(tmp_path / \"temp.dib\")\n\n    with Image.open(\"Tests/images/clipboard.dib\") as im:\n        im.save(outfile)\n\n        with Image.open(outfile) as reloaded:\n            assert reloaded.format == \"DIB\"\n            assert reloaded.get_format_mimetype() == \"image/bmp\"\n            assert_image_equal(im, reloaded)\n\n\ndef test_rgba_bitfields():\n    # This test image has been manually hexedited\n    # to change the bitfield compression in the header from XBGR to RGBA\n    with Image.open(\"Tests/images/rgb32bf-rgba.bmp\") as im:\n\n        # So before the comparing the image, swap the channels\n        b, g, r = im.split()[1:]\n        im = Image.merge(\"RGB\", (r, g, b))\n\n    assert_image_equal_tofile(im, \"Tests/images/bmp/q/rgb32bf-xbgr.bmp\")\n\n    # This test image has been manually hexedited\n    # to change the bitfield compression in the header from XBGR to ABGR\n    with Image.open(\"Tests/images/rgb32bf-abgr.bmp\") as im:\n        assert_image_equal_tofile(\n            im.convert(\"RGB\"), \"Tests/images/bmp/q/rgb32bf-xbgr.bmp\"\n        )\n\n\ndef test_rle8():\n    with Image.open(\"Tests/images/hopper_rle8.bmp\") as im:\n        assert_image_similar_tofile(im.convert(\"RGB\"), \"Tests/images/hopper.bmp\", 12)\n\n    with Image.open(\"Tests/images/hopper_rle8_greyscale.bmp\") as im:\n        assert_image_equal_tofile(im, \"Tests/images/bw_gradient.png\")\n\n    # This test image has been manually hexedited\n    # to have rows with too much data\n    with Image.open(\"Tests/images/hopper_rle8_row_overflow.bmp\") as im:\n        assert_image_similar_tofile(im.convert(\"RGB\"), \"Tests/images/hopper.bmp\", 12)\n\n    # Signal end of bitmap before the image is finished\n    with open(\"Tests/images/bmp/g/pal8rle.bmp\", \"rb\") as fp:\n        data = fp.read(1063) + b\"\\x01\"\n        with Image.open(io.BytesIO(data)) as im:\n            with pytest.raises(ValueError):\n                im.load()\n\n\n@pytest.mark.parametrize(\n    \"file_name,length\",\n    (\n        # EOF immediately after the header\n        (\"Tests/images/hopper_rle8.bmp\", 1078),\n        # EOF during delta\n        (\"Tests/images/bmp/q/pal8rletrns.bmp\", 3670),\n        # EOF when reading data in absolute mode\n        (\"Tests/images/bmp/g/pal8rle.bmp\", 1064),\n    ),\n)\ndef test_rle8_eof(file_name, length):\n    with open(file_name, \"rb\") as fp:\n        data = fp.read(length)\n        with Image.open(io.BytesIO(data)) as im:\n            with pytest.raises(ValueError):\n                im.load()\n\n\ndef test_offset():\n    # This image has been hexedited\n    # to exclude the palette size from the pixel data offset\n    with Image.open(\"Tests/images/pal8_offset.bmp\") as im:\n        assert_image_equal_tofile(im, \"Tests/images/bmp/g/pal8.bmp\")\n"
    ],
    "test_file_names": [
        "test_1.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "Starting Pillow installation attempts...\nVerifying Pillow installation...\n✅ Pillow installation successful!\nSuccessfully installed Pillow\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 12 packages in 16ms\nInstalled 12 packages in 133ms\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 800ms\nPrepared 1 package in 32.28s\nInstalled 1 package in 1ms\n + pillow==9.3.0.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/pillow_8b2d70d17a4791d68df6c10d8337d769290c6528)\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_8b2d70d17a4791d68df6c10d8337d769290c6528\nconfigfile: setup.cfg\nplugins: cov-6.0.0\ncollected 16 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                                     [100%]\u001b[0m\n\n==================================== PASSES ====================================\n\u001b[36m\u001b[1m=========================== short test summary info ============================\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_sanity\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_invalid_file\u001b[0m\n\u001b[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_small_palette\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_too_large\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_dpi\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_bmp_with_dpi\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_float_dpi\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_load_dib\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_dib\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rgba_bitfields\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rle8\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rle8_eof[Tests/images/hopper_rle8.bmp-1078]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rle8_eof[Tests/images/bmp/q/pal8rletrns.bmp-3670]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rle8_eof[Tests/images/bmp/g/pal8rle.bmp-1064]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_offset\u001b[0m\n\u001b[32m============================== \u001b[32m\u001b[1m16 passed\u001b[0m\u001b[32m in 0.16s\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_8b2d70d17a4791d68df6c10d8337d769290c6528\nconfigfile: setup.cfg\nplugins: cov-6.0.0\ncollected 16 items\n\nr2e_tests/test_1.py \u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[32m.\u001b[0m\u001b[31mF\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[31m                                     [100%]\u001b[0m\n\n=================================== FAILURES ===================================\n\u001b[31m\u001b[1m______________________________ test_small_palette ______________________________\u001b[0m\n\ntmp_path = PosixPath('/tmp/pytest-of-gcpuser/pytest-415/test_small_palette0')\n\n    def test_small_palette(tmp_path):\n        im = Image.new(\"P\", (1, 1))\n        colors = [0, 0, 0, 125, 125, 125, 255, 255, 255]\n        im.putpalette(colors)\n    \n        out = str(tmp_path / \"temp.bmp\")\n        im.save(out)\n    \n        with Image.open(out) as reloaded:\n>           assert reloaded.getpalette() == colors\n\n\u001b[1m\u001b[31mr2e_tests/test_1.py\u001b[0m:63: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\u001b[1m\u001b[31msrc/PIL/Image.py\u001b[0m:1442: in getpalette\n    self.load()\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <PIL.BmpImagePlugin.BmpImageFile image mode=P size=1x1 at 0x74E1AAC41C10>\n\n    def load(self):\n        \"\"\"Load image data based on tile list\"\"\"\n    \n        if self.tile is None:\n            raise OSError(\"cannot load this image\")\n    \n        pixel = Image.Image.load(self)\n        if not self.tile:\n            return pixel\n    \n        self.map = None\n        use_mmap = self.filename and len(self.tile) == 1\n        # As of pypy 2.1.0, memory mapping was failing here.\n        use_mmap = use_mmap and not hasattr(sys, \"pypy_version_info\")\n    \n        readonly = 0\n    \n        # look for read/seek overrides\n        try:\n            read = self.load_read\n            # don't use mmap if there are custom read/seek functions\n            use_mmap = False\n        except AttributeError:\n            read = self.fp.read\n    \n        try:\n            seek = self.load_seek\n            use_mmap = False\n        except AttributeError:\n            seek = self.fp.seek\n    \n        if use_mmap:\n            # try memory mapping\n            decoder_name, extents, offset, args = self.tile[0]\n            if (\n                decoder_name == \"raw\"\n                and len(args) >= 3\n                and args[0] == self.mode\n                and args[0] in Image._MAPMODES\n            ):\n                try:\n                    # use mmap, if possible\n                    import mmap\n    \n                    with open(self.filename) as fp:\n                        self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)\n>                   self.im = Image.core.map_buffer(\n                        self.map, self.size, decoder_name, offset, args\n                    )\n\u001b[1m\u001b[31mE                   ValueError: buffer is not large enough\u001b[0m\n\n\u001b[1m\u001b[31msrc/PIL/ImageFile.py\u001b[0m:195: 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_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_save_too_large\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_dpi\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_bmp_with_dpi\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_float_dpi\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_load_dib\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_save_dib\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rgba_bitfields\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rle8\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rle8_eof[Tests/images/hopper_rle8.bmp-1078]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rle8_eof[Tests/images/bmp/q/pal8rletrns.bmp-3670]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_rle8_eof[Tests/images/bmp/g/pal8rle.bmp-1064]\u001b[0m\n\u001b[32mPASSED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_offset\u001b[0m\n\u001b[31mFAILED\u001b[0m r2e_tests/test_1.py::\u001b[1mtest_small_palette\u001b[0m - ValueError: buffer is not large enough\n\u001b[31m========================= \u001b[31m\u001b[1m1 failed\u001b[0m, \u001b[32m15 passed\u001b[0m\u001b[31m in 0.18s\u001b[0m\u001b[31m =========================\u001b[0m\n",
    "old_commit_res_stderr": ""
}
Tests/test_file_bmp.py src/PIL/BmpImagePlugin.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
**Analysis:**

- **Key Parts:**
  - **Commit Message:** "Corrected BMP palette size when saving."
  - **Test Results:** The `test_small_palette` was failing with `ValueError: buffer is not large enough` in the old commit and passes in the new commit.
  
- **Main Problem:** Saving a BMP image in "P" mode with a small palette (e.g., 3 colors) previously caused a buffer size error, preventing the image from being saved and loaded correctly.
  
- **Expected Behavior:** Saving a BMP image with a small palette should work without errors, and the palette should be accurately preserved upon loading the image.
  
- **Actual Behavior:** Attempting to save a BMP image with a small palette resulted in a `ValueError` indicating that the buffer is not large enough.

**Drafted GitHub Issue:**

[ISSUE]
**Title:** Saving BMP Images with Small Palettes Raises Buffer Size Error

**Description:**
When attempting to save a BMP image in "P" mode with a limited palette, an error occurs during the loading of the saved image. For example, creating an image with a small set of palette colors and saving it as BMP leads to a buffer size issue.

**Example Code:**
```python
from PIL import Image

# Create a palette-based image with a small palette
im = Image.new("P", (1, 1))
colors = [0, 0, 0, 125, 125, 125, 255, 255, 255]
im.putpalette(colors)

# Save the image
im.save("temp.bmp")

# Attempt to load the saved image
with Image.open("temp.bmp") as reloaded:
    palette = reloaded.getpalette()
```

**Expected Behavior:**
The BMP image should be saved successfully, and upon loading, the palette should match the original colors without any errors.

**Actual Behavior:**
A `ValueError` is raised with the message "buffer is not large enough" when attempting to load the saved BMP image with the small palette.

[/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/BmpImagePlugin.py
#
# The Python Imaging Library.
# $Id$
#
# BMP file handler
#
# Windows (and OS/2) native bitmap storage format.
#
# history:
# 1995-09-01 fl   Created
# 1996-04-30 fl   Added save
# 1997-08-27 fl   Fixed save of 1-bit images
# 1998-03-06 fl   Load P images as L where possible
# 1998-07-03 fl   Load P images as 1 where possible
# 1998-12-29 fl   Handle small palettes
# 2002-12-30 fl   Fixed load of 1-bit palette images
# 2003-04-21 fl   Fixed load of 1-bit monochrome images
# 2003-04-23 fl   Added limited support for BI_BITFIELDS compression
#
# Copyright (c) 1997-2003 by Secret Labs AB
# Copyright (c) 1995-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#


import os

from . import Image, ImageFile, ImagePalette
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

#
# --------------------------------------------------------------------
# Read BMP file

BIT2MODE = {
    # bits => mode, rawmode
    1: ("P", "P;1"),
    4: ("P", "P;4"),
    8: ("P", "P"),
    16: ("RGB", "BGR;15"),
    24: ("RGB", "BGR"),
    32: ("RGB", "BGRX"),
}


def _accept(prefix):
    return prefix[:2] == b"BM"


def _dib_accept(prefix):
    return i32(prefix) in [12, 40, 64, 108, 124]


# =============================================================================
# Image plugin for the Windows BMP format.
# =============================================================================
class BmpImageFile(ImageFile.ImageFile):
    """Image plugin for the Windows Bitmap format (BMP)"""

    # ------------------------------------------------------------- Description
    format_description = "Windows Bitmap"
    format = "BMP"

    # -------------------------------------------------- BMP Compression values
    COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5}
    for k, v in COMPRESSIONS.items():
        vars()[k] = v

    def _bitmap(self, header=0, offset=0):
        """Read relevant info about the BMP"""
        read, seek = self.fp.read, self.fp.seek
        if header:
            seek(header)
        # read bmp header size @offset 14 (this is part of the header size)
        file_info = {"header_size": i32(read(4)), "direction": -1}

        # -------------------- If requested, read header at a specific position
        # read the rest of the bmp header, without its size
        header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4)

        # -------------------------------------------------- IBM OS/2 Bitmap v1
        # ----- This format has different offsets because of width/height types
        if file_info["header_size"] == 12:
            file_info["width"] = i16(header_data, 0)
            file_info["height"] = i16(header_data, 2)
            file_info["planes"] = i16(header_data, 4)
            file_info["bits"] = i16(header_data, 6)
            file_info["compression"] = self.RAW
            file_info["palette_padding"] = 3

        # --------------------------------------------- Windows Bitmap v2 to v5
        # v3, OS/2 v2, v4, v5
        elif file_info["header_size"] in (40, 64, 108, 124):
            file_info["y_flip"] = header_data[7] == 0xFF
            file_info["direction"] = 1 if file_info["y_flip"] else -1
            file_info["width"] = i32(header_data, 0)
            file_info["height"] = (
                i32(header_data, 4)
                if not file_info["y_flip"]
                else 2**32 - i32(header_data, 4)
            )
            file_info["planes"] = i16(header_data, 8)
            file_info["bits"] = i16(header_data, 10)
            file_info["compression"] = i32(header_data, 12)
            # byte size of pixel data
            file_info["data_size"] = i32(header_data, 16)
            file_info["pixels_per_meter"] = (
                i32(header_data, 20),
                i32(header_data, 24),
            )
            file_info["colors"] = i32(header_data, 28)
            file_info["palette_padding"] = 4
            self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"])
            if file_info["compression"] == self.BITFIELDS:
                if len(header_data) >= 52:
                    for idx, mask in enumerate(
                        ["r_mask", "g_mask", "b_mask", "a_mask"]
                    ):
                        file_info[mask] = i32(header_data, 36 + idx * 4)
                else:
                    # 40 byte headers only have the three components in the
                    # bitfields masks, ref:
                    # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
                    # See also
                    # https://github.com/python-pillow/Pillow/issues/1293
                    # There is a 4th component in the RGBQuad, in the alpha
                    # location, but it is listed as a reserved component,
                    # and it is not generally an alpha channel
                    file_info["a_mask"] = 0x0
                    for mask in ["r_mask", "g_mask", "b_mask"]:
                        file_info[mask] = i32(read(4))
                file_info["rgb_mask"] = (
                    file_info["r_mask"],
                    file_info["g_mask"],
                    file_info["b_mask"],
                )
                file_info["rgba_mask"] = (
                    file_info["r_mask"],
                    file_info["g_mask"],
                    file_info["b_mask"],
                    file_info["a_mask"],
                )
        else:
            raise OSError(f"Unsupported BMP header type ({file_info['header_size']})")

        # ------------------ Special case : header is reported 40, which
        # ---------------------- is shorter than real size for bpp >= 16
        self._size = file_info["width"], file_info["height"]

        # ------- If color count was not found in the header, compute from bits
        file_info["colors"] = (
            file_info["colors"]
            if file_info.get("colors", 0)
            else (1 << file_info["bits"])
        )
        if offset == 14 + file_info["header_size"] and file_info["bits"] <= 8:
            offset += 4 * file_info["colors"]

        # ---------------------- Check bit depth for unusual unsupported values
        self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
        if self.mode is None:
            raise OSError(f"Unsupported BMP pixel depth ({file_info['bits']})")

        # ---------------- Process BMP with Bitfields compression (not palette)
        decoder_name = "raw"
        if file_info["compression"] == self.BITFIELDS:
            SUPPORTED = {
                32: [
                    (0xFF0000, 0xFF00, 0xFF, 0x0),
                    (0xFF000000, 0xFF0000, 0xFF00, 0x0),
                    (0xFF000000, 0xFF0000, 0xFF00, 0xFF),
                    (0xFF, 0xFF00, 0xFF0000, 0xFF000000),
                    (0xFF0000, 0xFF00, 0xFF, 0xFF000000),
                    (0x0, 0x0, 0x0, 0x0),
                ],
                24: [(0xFF0000, 0xFF00, 0xFF)],
                16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],
            }
            MASK_MODES = {
                (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX",
                (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR",
                (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR",
                (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA",
                (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
                (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
                (24, (0xFF0000, 0xFF00, 0xFF)): "BGR",
                (16, (0xF800, 0x7E0, 0x1F)): "BGR;16",
                (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15",
            }
            if file_info["bits"] in SUPPORTED:
                if (
                    file_info["bits"] == 32
                    and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]
                ):
                    raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])]
                    self.mode = "RGBA" if "A" in raw_mode else self.mode
                elif (
                    file_info["bits"] in (24, 16)
                    and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]
                ):
                    raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
                else:
                    raise OSError("Unsupported BMP bitfields layout")
            else:
                raise OSError("Unsupported BMP bitfields layout")
        elif file_info["compression"] == self.RAW:
            if file_info["bits"] == 32 and header == 22:  # 32-bit .cur offset
                raw_mode, self.mode = "BGRA", "RGBA"
        elif file_info["compression"] == self.RLE8:
            decoder_name = "bmp_rle"
        else:
            raise OSError(f"Unsupported BMP compression ({file_info['compression']})")

        # --------------- Once the header is processed, process the palette/LUT
        if self.mode == "P":  # Paletted for 1, 4 and 8 bit images

            # ---------------------------------------------------- 1-bit images
            if not (0 < file_info["colors"] <= 65536):
                raise OSError(f"Unsupported BMP Palette size ({file_info['colors']})")
            else:
                padding = file_info["palette_padding"]
                palette = read(padding * file_info["colors"])
                greyscale = True
                indices = (
                    (0, 255)
                    if file_info["colors"] == 2
                    else list(range(file_info["colors"]))
                )

                # ----------------- Check if greyscale and ignore palette if so
                for ind, val in enumerate(indices):
                    rgb = palette[ind * padding : ind * padding + 3]
                    if rgb != o8(val) * 3:
                        greyscale = False

                # ------- If all colors are grey, white or black, ditch palette
                if greyscale:
                    self.mode = "1" if file_info["colors"] == 2 else "L"
                    raw_mode = self.mode
                else:
                    self.mode = "P"
                    self.palette = ImagePalette.raw(
                        "BGRX" if padding == 4 else "BGR", palette
                    )

        # ---------------------------- Finally set the tile data for the plugin
        self.info["compression"] = file_info["compression"]
        self.tile = [
            (
                decoder_name,
                (0, 0, file_info["width"], file_info["height"]),
                offset or self.fp.tell(),
                (
                    raw_mode,
                    ((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3),
                    file_info["direction"],
                ),
            )
        ]

    def _open(self):
        """Open file, check magic number and read header"""
        # read 14 bytes: magic number, filesize, reserved, header final offset
        head_data = self.fp.read(14)
        # choke if the file does not have the required magic bytes
        if not _accept(head_data):
            raise SyntaxError("Not a BMP file")
        # read the start position of the BMP image data (u32)
        offset = i32(head_data, 10)
        # load bitmap information (offset=raster info)
        self._bitmap(offset=offset)


class BmpRleDecoder(ImageFile.PyDecoder):
    _pulls_fd = True

    def decode(self, buffer):
        data = bytearray()
        x = 0
        while len(data) < self.state.xsize * self.state.ysize:
            pixels = self.fd.read(1)
            byte = self.fd.read(1)
            if not pixels or not byte:
                break
            num_pixels = pixels[0]
            if num_pixels:
                # encoded mode
                if x + num_pixels > self.state.xsize:
                    # Too much data for row
                    num_pixels = max(0, self.state.xsize - x)
                data += byte * num_pixels
                x += num_pixels
            else:
                if byte[0] == 0:
                    # end of line
                    while len(data) % self.state.xsize != 0:
                        data += b"\x00"
                    x = 0
                elif byte[0] == 1:
                    # end of bitmap
                    break
                elif byte[0] == 2:
                    # delta
                    bytes_read = self.fd.read(2)
                    if len(bytes_read) < 2:
                        break
                    right, up = self.fd.read(2)
                    data += b"\x00" * (right + up * self.state.xsize)
                    x = len(data) % self.state.xsize
                else:
                    # absolute mode
                    bytes_read = self.fd.read(byte[0])
                    data += bytes_read
                    if len(bytes_read) < byte[0]:
                        break
                    x += byte[0]

                    # align to 16-bit word boundary
                    if self.fd.tell() % 2 != 0:
                        self.fd.seek(1, os.SEEK_CUR)
        rawmode = "L" if self.mode == "L" else "P"
        self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1]))
        return -1, 0


# =============================================================================
# Image plugin for the DIB format (BMP alias)
# =============================================================================
class DibImageFile(BmpImageFile):

    format = "DIB"
    format_description = "Windows Bitmap"

    def _open(self):
        self._bitmap()


#
# --------------------------------------------------------------------
# Write BMP file


SAVE = {
    "1": ("1", 1, 2),
    "L": ("L", 8, 256),
    "P": ("P", 8, 256),
    "RGB": ("BGR", 24, 0),
    "RGBA": ("BGRA", 32, 0),
}


def _dib_save(im, fp, filename):
    _save(im, fp, filename, False)


def _save(im, fp, filename, bitmap_header=True):
    try:
        rawmode, bits, colors = SAVE[im.mode]
    except KeyError as e:
        raise OSError(f"cannot write mode {im.mode} as BMP") from e

    info = im.encoderinfo

    dpi = info.get("dpi", (96, 96))

    # 1 meter == 39.3701 inches
    ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))

    stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)
    header = 40  # or 64 for OS/2 version 2
    image = stride * im.size[1]

    # bitmap header
    if bitmap_header:
        offset = 14 + header + colors * 4
        file_size = offset + image
        if file_size > 2**32 - 1:
            raise ValueError("File size is too large for the BMP format")
        fp.write(
            b"BM"  # file type (magic)
            + o32(file_size)  # file size
            + o32(0)  # reserved
            + o32(offset)  # image data offset
        )

    # bitmap info header
    fp.write(
        o32(header)  # info header size
        + o32(im.size[0])  # width
        + o32(im.size[1])  # height
        + o16(1)  # planes
        + o16(bits)  # depth
        + o32(0)  # compression (0=uncompressed)
        + o32(image)  # size of bitmap
        + o32(ppm[0])  # resolution
        + o32(ppm[1])  # resolution
        + o32(colors)  # colors used
        + o32(colors)  # colors important
    )

    fp.write(b"\0" * (header - 40))  # padding (for OS/2 format)

    if im.mode == "1":
        for i in (0, 255):
            fp.write(o8(i) * 4)
    elif im.mode == "L":
        for i in range(256):
            fp.write(o8(i) * 4)
    elif im.mode == "P":
        fp.write(im.im.getpalette("RGB", "BGRX"))

    ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))])


#
# --------------------------------------------------------------------
# Registry


Image.register_open(BmpImageFile.format, BmpImageFile, _accept)
Image.register_save(BmpImageFile.format, _save)

Image.register_extension(BmpImageFile.format, ".bmp")

Image.register_mime(BmpImageFile.format, "image/bmp")

Image.register_decoder("bmp_rle", BmpRleDecoder)

Image.register_open(DibImageFile.format, DibImageFile, _dib_accept)
Image.register_save(DibImageFile.format, _dib_save)

Image.register_extension(DibImageFile.format, ".dib")

Image.register_mime(DibImageFile.format, "image/bmp")

```
--- 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.
**Analysis:**

- **Key Parts:**
  - **Commit Message:** "Corrected BMP palette size when saving."
  - **Test Results:** The `test_small_palette` was failing with `ValueError: buffer is not large enough` in the old commit and passes in the new commit.
  
- **Main Problem:** Saving a BMP image in "P" mode with a small palette (e.g., 3 colors) previously caused a buffer size error, preventing the image from being saved and loaded correctly.
  
- **Expected Behavior:** Saving a BMP image with a small palette should work without errors, and the palette should be accurately preserved upon loading the image.
  
- **Actual Behavior:** Attempting to save a BMP image with a small palette resulted in a `ValueError` indicating that the buffer is not large enough.

**Drafted GitHub Issue:**

[ISSUE]
**Title:** Saving BMP Images with Small Palettes Raises Buffer Size Error

**Description:**
When attempting to save a BMP image in "P" mode with a limited palette, an error occurs during the loading of the saved image. For example, creating an image with a small set of palette colors and saving it as BMP leads to a buffer size issue.

**Example Code:**
```python
from PIL import Image

# Create a palette-based image with a small palette
im = Image.new("P", (1, 1))
colors = [0, 0, 0, 125, 125, 125, 255, 255, 255]
im.putpalette(colors)

# Save the image
im.save("temp.bmp")

# Attempt to load the saved image
with Image.open("temp.bmp") as reloaded:
    palette = reloaded.getpalette()
```

**Expected Behavior:**
The BMP image should be saved successfully, and upon loading, the palette should match the original colors without any errors.

**Actual Behavior:**
A `ValueError` is raised with the message "buffer is not large enough" when attempting to load the saved BMP image with the small palette.

[/ISSUE]
{
    "\u001b[1mtest_sanity\u001b[0m": "PASSED",
    "\u001b[1mtest_invalid_file\u001b[0m": "PASSED",
    "\u001b[1mtest_save_to_bytes\u001b[0m": "PASSED",
    "\u001b[1mtest_small_palette\u001b[0m": "PASSED",
    "\u001b[1mtest_save_too_large\u001b[0m": "PASSED",
    "\u001b[1mtest_dpi\u001b[0m": "PASSED",
    "\u001b[1mtest_save_bmp_with_dpi\u001b[0m": "PASSED",
    "\u001b[1mtest_save_float_dpi\u001b[0m": "PASSED",
    "\u001b[1mtest_load_dib\u001b[0m": "PASSED",
    "\u001b[1mtest_save_dib\u001b[0m": "PASSED",
    "\u001b[1mtest_rgba_bitfields\u001b[0m": "PASSED",
    "\u001b[1mtest_rle8\u001b[0m": "PASSED",
    "\u001b[1mtest_rle8_eof[Tests/images/hopper_rle8.bmp-1078]\u001b[0m": "PASSED",
    "\u001b[1mtest_rle8_eof[Tests/images/bmp/q/pal8rletrns.bmp-3670]\u001b[0m": "PASSED",
    "\u001b[1mtest_rle8_eof[Tests/images/bmp/g/pal8rle.bmp-1064]\u001b[0m": "PASSED",
    "\u001b[1mtest_offset\u001b[0m": "PASSED"
}
diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py
index 7bb73fc93..1041ab763 100644
--- a/src/PIL/BmpImagePlugin.py
+++ b/src/PIL/BmpImagePlugin.py
@@ -375,6 +375,16 @@ def _save(im, fp, filename, bitmap_header=True):
     header = 40  # or 64 for OS/2 version 2
     image = stride * im.size[1]
 
+    if im.mode == "1":
+        palette = b"".join(o8(i) * 4 for i in (0, 255))
+    elif im.mode == "L":
+        palette = b"".join(o8(i) * 4 for i in range(256))
+    elif im.mode == "P":
+        palette = im.im.getpalette("RGB", "BGRX")
+        colors = len(palette) // 4
+    else:
+        palette = None
+
     # bitmap header
     if bitmap_header:
         offset = 14 + header + colors * 4
@@ -405,14 +415,8 @@ def _save(im, fp, filename, bitmap_header=True):
 
     fp.write(b"\0" * (header - 40))  # padding (for OS/2 format)
 
-    if im.mode == "1":
-        for i in (0, 255):
-            fp.write(o8(i) * 4)
-    elif im.mode == "L":
-        for i in range(256):
-            fp.write(o8(i) * 4)
-    elif im.mode == "P":
-        fp.write(im.im.getpalette("RGB", "BGRX"))
+    if palette:
+        fp.write(palette)
 
     ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))])
 
 

{"src/PIL/BmpImagePlugin.py": "#\n# The Python Imaging Library.\n# $Id$\n#\n# BMP file handler\n#\n# Windows (and OS/2) native bitmap storage format.\n#\n# history:\n# 1995-09-01 fl   Created\n# 1996-04-30 fl   Added save\n# 1997-08-27 fl   Fixed save of 1-bit images\n# 1998-03-06 fl   Load P images as L where possible\n# 1998-07-03 fl   Load P images as 1 where possible\n# 1998-12-29 fl   Handle small palettes\n# 2002-12-30 fl   Fixed load of 1-bit palette images\n# 2003-04-21 fl   Fixed load of 1-bit monochrome images\n# 2003-04-23 fl   Added limited support for BI_BITFIELDS compression\n#\n# Copyright (c) 1997-2003 by Secret Labs AB\n# Copyright (c) 1995-2003 by Fredrik Lundh\n#\n# See the README file for information on usage and redistribution.\n#\n\n\nimport os\n\nfrom . import Image, ImageFile, ImagePalette\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# Read BMP file\n\nBIT2MODE = {\n    # bits => mode, rawmode\n    1: (\"P\", \"P;1\"),\n    4: (\"P\", \"P;4\"),\n    8: (\"P\", \"P\"),\n    16: (\"RGB\", \"BGR;15\"),\n    24: (\"RGB\", \"BGR\"),\n    32: (\"RGB\", \"BGRX\"),\n}\n\n\ndef _accept(prefix):\n    return prefix[:2] == b\"BM\"\n\n\ndef _dib_accept(prefix):\n    return i32(prefix) in [12, 40, 64, 108, 124]\n\n\n# =============================================================================\n# Image plugin for the Windows BMP format.\n# =============================================================================\nclass BmpImageFile(ImageFile.ImageFile):\n    \"\"\"Image plugin for the Windows Bitmap format (BMP)\"\"\"\n\n    # ------------------------------------------------------------- Description\n    format_description = \"Windows Bitmap\"\n    format = \"BMP\"\n\n    # -------------------------------------------------- BMP Compression values\n    COMPRESSIONS = {\"RAW\": 0, \"RLE8\": 1, \"RLE4\": 2, \"BITFIELDS\": 3, \"JPEG\": 4, \"PNG\": 5}\n    for k, v in COMPRESSIONS.items():\n        vars()[k] = v\n\n    def _bitmap(self, header=0, offset=0):\n        \"\"\"Read relevant info about the BMP\"\"\"\n        read, seek = self.fp.read, self.fp.seek\n        if header:\n            seek(header)\n        # read bmp header size @offset 14 (this is part of the header size)\n        file_info = {\"header_size\": i32(read(4)), \"direction\": -1}\n\n        # -------------------- If requested, read header at a specific position\n        # read the rest of the bmp header, without its size\n        header_data = ImageFile._safe_read(self.fp, file_info[\"header_size\"] - 4)\n\n        # -------------------------------------------------- IBM OS/2 Bitmap v1\n        # ----- This format has different offsets because of width/height types\n        if file_info[\"header_size\"] == 12:\n            file_info[\"width\"] = i16(header_data, 0)\n            file_info[\"height\"] = i16(header_data, 2)\n            file_info[\"planes\"] = i16(header_data, 4)\n            file_info[\"bits\"] = i16(header_data, 6)\n            file_info[\"compression\"] = self.RAW\n            file_info[\"palette_padding\"] = 3\n\n        # --------------------------------------------- Windows Bitmap v2 to v5\n        # v3, OS/2 v2, v4, v5\n        elif file_info[\"header_size\"] in (40, 64, 108, 124):\n            file_info[\"y_flip\"] = header_data[7] == 0xFF\n            file_info[\"direction\"] = 1 if file_info[\"y_flip\"] else -1\n            file_info[\"width\"] = i32(header_data, 0)\n            file_info[\"height\"] = (\n                i32(header_data, 4)\n                if not file_info[\"y_flip\"]\n                else 2**32 - i32(header_data, 4)\n            )\n            file_info[\"planes\"] = i16(header_data, 8)\n            file_info[\"bits\"] = i16(header_data, 10)\n            file_info[\"compression\"] = i32(header_data, 12)\n            # byte size of pixel data\n            file_info[\"data_size\"] = i32(header_data, 16)\n            file_info[\"pixels_per_meter\"] = (\n                i32(header_data, 20),\n                i32(header_data, 24),\n            )\n            file_info[\"colors\"] = i32(header_data, 28)\n            file_info[\"palette_padding\"] = 4\n            self.info[\"dpi\"] = tuple(x / 39.3701 for x in file_info[\"pixels_per_meter\"])\n            if file_info[\"compression\"] == self.BITFIELDS:\n                if len(header_data) >= 52:\n                    for idx, mask in enumerate(\n                        [\"r_mask\", \"g_mask\", \"b_mask\", \"a_mask\"]\n                    ):\n                        file_info[mask] = i32(header_data, 36 + idx * 4)\n                else:\n                    # 40 byte headers only have the three components in the\n                    # bitfields masks, ref:\n                    # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx\n                    # See also\n                    # https://github.com/python-pillow/Pillow/issues/1293\n                    # There is a 4th component in the RGBQuad, in the alpha\n                    # location, but it is listed as a reserved component,\n                    # and it is not generally an alpha channel\n                    file_info[\"a_mask\"] = 0x0\n                    for mask in [\"r_mask\", \"g_mask\", \"b_mask\"]:\n                        file_info[mask] = i32(read(4))\n                file_info[\"rgb_mask\"] = (\n                    file_info[\"r_mask\"],\n                    file_info[\"g_mask\"],\n                    file_info[\"b_mask\"],\n                )\n                file_info[\"rgba_mask\"] = (\n                    file_info[\"r_mask\"],\n                    file_info[\"g_mask\"],\n                    file_info[\"b_mask\"],\n                    file_info[\"a_mask\"],\n                )\n        else:\n            raise OSError(f\"Unsupported BMP header type ({file_info['header_size']})\")\n\n        # ------------------ Special case : header is reported 40, which\n        # ---------------------- is shorter than real size for bpp >= 16\n        self._size = file_info[\"width\"], file_info[\"height\"]\n\n        # ------- If color count was not found in the header, compute from bits\n        file_info[\"colors\"] = (\n            file_info[\"colors\"]\n            if file_info.get(\"colors\", 0)\n            else (1 << file_info[\"bits\"])\n        )\n        if offset == 14 + file_info[\"header_size\"] and file_info[\"bits\"] <= 8:\n            offset += 4 * file_info[\"colors\"]\n\n        # ---------------------- Check bit depth for unusual unsupported values\n        self.mode, raw_mode = BIT2MODE.get(file_info[\"bits\"], (None, None))\n        if self.mode is None:\n            raise OSError(f\"Unsupported BMP pixel depth ({file_info['bits']})\")\n\n        # ---------------- Process BMP with Bitfields compression (not palette)\n        decoder_name = \"raw\"\n        if file_info[\"compression\"] == self.BITFIELDS:\n            SUPPORTED = {\n                32: [\n                    (0xFF0000, 0xFF00, 0xFF, 0x0),\n                    (0xFF000000, 0xFF0000, 0xFF00, 0x0),\n                    (0xFF000000, 0xFF0000, 0xFF00, 0xFF),\n                    (0xFF, 0xFF00, 0xFF0000, 0xFF000000),\n                    (0xFF0000, 0xFF00, 0xFF, 0xFF000000),\n                    (0x0, 0x0, 0x0, 0x0),\n                ],\n                24: [(0xFF0000, 0xFF00, 0xFF)],\n                16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],\n            }\n            MASK_MODES = {\n                (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): \"BGRX\",\n                (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): \"XBGR\",\n                (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): \"ABGR\",\n                (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): \"RGBA\",\n                (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): \"BGRA\",\n                (32, (0x0, 0x0, 0x0, 0x0)): \"BGRA\",\n                (24, (0xFF0000, 0xFF00, 0xFF)): \"BGR\",\n                (16, (0xF800, 0x7E0, 0x1F)): \"BGR;16\",\n                (16, (0x7C00, 0x3E0, 0x1F)): \"BGR;15\",\n            }\n            if file_info[\"bits\"] in SUPPORTED:\n                if (\n                    file_info[\"bits\"] == 32\n                    and file_info[\"rgba_mask\"] in SUPPORTED[file_info[\"bits\"]]\n                ):\n                    raw_mode = MASK_MODES[(file_info[\"bits\"], file_info[\"rgba_mask\"])]\n                    self.mode = \"RGBA\" if \"A\" in raw_mode else self.mode\n                elif (\n                    file_info[\"bits\"] in (24, 16)\n                    and file_info[\"rgb_mask\"] in SUPPORTED[file_info[\"bits\"]]\n                ):\n                    raw_mode = MASK_MODES[(file_info[\"bits\"], file_info[\"rgb_mask\"])]\n                else:\n                    raise OSError(\"Unsupported BMP bitfields layout\")\n            else:\n                raise OSError(\"Unsupported BMP bitfields layout\")\n        elif file_info[\"compression\"] == self.RAW:\n            if file_info[\"bits\"] == 32 and header == 22:  # 32-bit .cur offset\n                raw_mode, self.mode = \"BGRA\", \"RGBA\"\n        elif file_info[\"compression\"] == self.RLE8:\n            decoder_name = \"bmp_rle\"\n        else:\n            raise OSError(f\"Unsupported BMP compression ({file_info['compression']})\")\n\n        # --------------- Once the header is processed, process the palette/LUT\n        if self.mode == \"P\":  # Paletted for 1, 4 and 8 bit images\n\n            # ---------------------------------------------------- 1-bit images\n            if not (0 < file_info[\"colors\"] <= 65536):\n                raise OSError(f\"Unsupported BMP Palette size ({file_info['colors']})\")\n            else:\n                padding = file_info[\"palette_padding\"]\n                palette = read(padding * file_info[\"colors\"])\n                greyscale = True\n                indices = (\n                    (0, 255)\n                    if file_info[\"colors\"] == 2\n                    else list(range(file_info[\"colors\"]))\n                )\n\n                # ----------------- Check if greyscale and ignore palette if so\n                for ind, val in enumerate(indices):\n                    rgb = palette[ind * padding : ind * padding + 3]\n                    if rgb != o8(val) * 3:\n                        greyscale = False\n\n                # ------- If all colors are grey, white or black, ditch palette\n                if greyscale:\n                    self.mode = \"1\" if file_info[\"colors\"] == 2 else \"L\"\n                    raw_mode = self.mode\n                else:\n                    self.mode = \"P\"\n                    self.palette = ImagePalette.raw(\n                        \"BGRX\" if padding == 4 else \"BGR\", palette\n                    )\n\n        # ---------------------------- Finally set the tile data for the plugin\n        self.info[\"compression\"] = file_info[\"compression\"]\n        self.tile = [\n            (\n                decoder_name,\n                (0, 0, file_info[\"width\"], file_info[\"height\"]),\n                offset or self.fp.tell(),\n                (\n                    raw_mode,\n                    ((file_info[\"width\"] * file_info[\"bits\"] + 31) >> 3) & (~3),\n                    file_info[\"direction\"],\n                ),\n            )\n        ]\n\n    def _open(self):\n        \"\"\"Open file, check magic number and read header\"\"\"\n        # read 14 bytes: magic number, filesize, reserved, header final offset\n        head_data = self.fp.read(14)\n        # choke if the file does not have the required magic bytes\n        if not _accept(head_data):\n            raise SyntaxError(\"Not a BMP file\")\n        # read the start position of the BMP image data (u32)\n        offset = i32(head_data, 10)\n        # load bitmap information (offset=raster info)\n        self._bitmap(offset=offset)\n\n\nclass BmpRleDecoder(ImageFile.PyDecoder):\n    _pulls_fd = True\n\n    def decode(self, buffer):\n        data = bytearray()\n        x = 0\n        while len(data) < self.state.xsize * self.state.ysize:\n            pixels = self.fd.read(1)\n            byte = self.fd.read(1)\n            if not pixels or not byte:\n                break\n            num_pixels = pixels[0]\n            if num_pixels:\n                # encoded mode\n                if x + num_pixels > self.state.xsize:\n                    # Too much data for row\n                    num_pixels = max(0, self.state.xsize - x)\n                data += byte * num_pixels\n                x += num_pixels\n            else:\n                if byte[0] == 0:\n                    # end of line\n                    while len(data) % self.state.xsize != 0:\n                        data += b\"\\x00\"\n                    x = 0\n                elif byte[0] == 1:\n                    # end of bitmap\n                    break\n                elif byte[0] == 2:\n                    # delta\n                    bytes_read = self.fd.read(2)\n                    if len(bytes_read) < 2:\n                        break\n                    right, up = self.fd.read(2)\n                    data += b\"\\x00\" * (right + up * self.state.xsize)\n                    x = len(data) % self.state.xsize\n                else:\n                    # absolute mode\n                    bytes_read = self.fd.read(byte[0])\n                    data += bytes_read\n                    if len(bytes_read) < byte[0]:\n                        break\n                    x += byte[0]\n\n                    # align to 16-bit word boundary\n                    if self.fd.tell() % 2 != 0:\n                        self.fd.seek(1, os.SEEK_CUR)\n        rawmode = \"L\" if self.mode == \"L\" else \"P\"\n        self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1]))\n        return -1, 0\n\n\n# =============================================================================\n# Image plugin for the DIB format (BMP alias)\n# =============================================================================\nclass DibImageFile(BmpImageFile):\n\n    format = \"DIB\"\n    format_description = \"Windows Bitmap\"\n\n    def _open(self):\n        self._bitmap()\n\n\n#\n# --------------------------------------------------------------------\n# Write BMP file\n\n\nSAVE = {\n    \"1\": (\"1\", 1, 2),\n    \"L\": (\"L\", 8, 256),\n    \"P\": (\"P\", 8, 256),\n    \"RGB\": (\"BGR\", 24, 0),\n    \"RGBA\": (\"BGRA\", 32, 0),\n}\n\n\ndef _dib_save(im, fp, filename):\n    _save(im, fp, filename, False)\n\n\ndef _save(im, fp, filename, bitmap_header=True):\n    try:\n        rawmode, bits, colors = SAVE[im.mode]\n    except KeyError as e:\n        raise OSError(f\"cannot write mode {im.mode} as BMP\") from e\n\n    info = im.encoderinfo\n\n    dpi = info.get(\"dpi\", (96, 96))\n\n    # 1 meter == 39.3701 inches\n    ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))\n\n    stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)\n    header = 40  # or 64 for OS/2 version 2\n    image = stride * im.size[1]\n\n    # bitmap header\n    if bitmap_header:\n        offset = 14 + header + colors * 4\n        file_size = offset + image\n        if file_size > 2**32 - 1:\n            raise ValueError(\"File size is too large for the BMP format\")\n        fp.write(\n            b\"BM\"  # file type (magic)\n            + o32(file_size)  # file size\n            + o32(0)  # reserved\n            + o32(offset)  # image data offset\n        )\n\n    # bitmap info header\n    fp.write(\n        o32(header)  # info header size\n        + o32(im.size[0])  # width\n        + o32(im.size[1])  # height\n        + o16(1)  # planes\n        + o16(bits)  # depth\n        + o32(0)  # compression (0=uncompressed)\n        + o32(image)  # size of bitmap\n        + o32(ppm[0])  # resolution\n        + o32(ppm[1])  # resolution\n        + o32(colors)  # colors used\n        + o32(colors)  # colors important\n    )\n\n    fp.write(b\"\\0\" * (header - 40))  # padding (for OS/2 format)\n\n    if im.mode == \"1\":\n        for i in (0, 255):\n            fp.write(o8(i) * 4)\n    elif im.mode == \"L\":\n        for i in range(256):\n            fp.write(o8(i) * 4)\n    elif im.mode == \"P\":\n        fp.write(im.im.getpalette(\"RGB\", \"BGRX\"))\n\n    ImageFile._save(im, fp, [(\"raw\", (0, 0) + im.size, 0, (rawmode, stride, -1))])\n\n\n#\n# --------------------------------------------------------------------\n# Registry\n\n\nImage.register_open(BmpImageFile.format, BmpImageFile, _accept)\nImage.register_save(BmpImageFile.format, _save)\n\nImage.register_extension(BmpImageFile.format, \".bmp\")\n\nImage.register_mime(BmpImageFile.format, \"image/bmp\")\n\nImage.register_decoder(\"bmp_rle\", BmpRleDecoder)\n\nImage.register_open(DibImageFile.format, DibImageFile, _dib_accept)\nImage.register_save(DibImageFile.format, _dib_save)\n\nImage.register_extension(DibImageFile.format, \".dib\")\n\nImage.register_mime(DibImageFile.format, \"image/bmp\")\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 ---
**Analysis:**

- **Key Parts:**
  - **Commit Message:** "Corrected BMP palette size when saving."
  - **Test Results:** The `test_small_palette` was failing with `ValueError: buffer is not large enough` in the old commit and passes in the new commit.
  
- **Main Problem:** Saving a BMP image in "P" mode with a small palette (e.g., 3 colors) previously caused a buffer size error, preventing the image from being saved and loaded correctly.
  
- **Expected Behavior:** Saving a BMP image with a small palette should work without errors, and the palette should be accurately preserved upon loading the image.
  
- **Actual Behavior:** Attempting to save a BMP image with a small palette resulted in a `ValueError` indicating that the buffer is not large enough.

**Drafted GitHub Issue:**

[ISSUE]
**Title:** Saving BMP Images with Small Palettes Raises Buffer Size Error

**Description:**
When attempting to save a BMP image in "P" mode with a limited palette, an error occurs during the loading of the saved image. For example, creating an image with a small set of palette colors and saving it as BMP leads to a buffer size issue.

**Example Code:**
```python
from PIL import Image

# Create a palette-based image with a small palette
im = Image.new("P", (1, 1))
colors = [0, 0, 0, 125, 125, 125, 255, 255, 255]
im.putpalette(colors)

# Save the image
im.save("temp.bmp")

# Attempt to load the saved image
with Image.open("temp.bmp") as reloaded:
    palette = reloaded.getpalette()
```

**Expected Behavior:**
The BMP image should be saved successfully, and upon loading, the palette should match the original colors without any errors.

**Actual Behavior:**
A `ValueError` is raised with the message "buffer is not large enough" when attempting to load the saved BMP image with the small palette.

[/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/BmpImagePlugin.py
#
# The Python Imaging Library.
# $Id$
#
# BMP file handler
#
# Windows (and OS/2) native bitmap storage format.
#
# history:
# 1995-09-01 fl   Created
# 1996-04-30 fl   Added save
# 1997-08-27 fl   Fixed save of 1-bit images
# 1998-03-06 fl   Load P images as L where possible
# 1998-07-03 fl   Load P images as 1 where possible
# 1998-12-29 fl   Handle small palettes
# 2002-12-30 fl   Fixed load of 1-bit palette images
# 2003-04-21 fl   Fixed load of 1-bit monochrome images
# 2003-04-23 fl   Added limited support for BI_BITFIELDS compression
#
# Copyright (c) 1997-2003 by Secret Labs AB
# Copyright (c) 1995-2003 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#


import os

from . import Image, ImageFile, ImagePalette
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

#
# --------------------------------------------------------------------
# Read BMP file

BIT2MODE = {
    # bits => mode, rawmode
    1: ("P", "P;1"),
    4: ("P", "P;4"),
    8: ("P", "P"),
    16: ("RGB", "BGR;15"),
    24: ("RGB", "BGR"),
    32: ("RGB", "BGRX"),
}


def _accept(prefix):
    return prefix[:2] == b"BM"


def _dib_accept(prefix):
    return i32(prefix) in [12, 40, 64, 108, 124]


# =============================================================================
# Image plugin for the Windows BMP format.
# =============================================================================
class BmpImageFile(ImageFile.ImageFile):
    """Image plugin for the Windows Bitmap format (BMP)"""

    # ------------------------------------------------------------- Description
    format_description = "Windows Bitmap"
    format = "BMP"

    # -------------------------------------------------- BMP Compression values
    COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5}
    for k, v in COMPRESSIONS.items():
        vars()[k] = v

    def _bitmap(self, header=0, offset=0):
        """Read relevant info about the BMP"""
        read, seek = self.fp.read, self.fp.seek
        if header:
            seek(header)
        # read bmp header size @offset 14 (this is part of the header size)
        file_info = {"header_size": i32(read(4)), "direction": -1}

        # -------------------- If requested, read header at a specific position
        # read the rest of the bmp header, without its size
        header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4)

        # -------------------------------------------------- IBM OS/2 Bitmap v1
        # ----- This format has different offsets because of width/height types
        if file_info["header_size"] == 12:
            file_info["width"] = i16(header_data, 0)
            file_info["height"] = i16(header_data, 2)
            file_info["planes"] = i16(header_data, 4)
            file_info["bits"] = i16(header_data, 6)
            file_info["compression"] = self.RAW
            file_info["palette_padding"] = 3

        # --------------------------------------------- Windows Bitmap v2 to v5
        # v3, OS/2 v2, v4, v5
        elif file_info["header_size"] in (40, 64, 108, 124):
            file_info["y_flip"] = header_data[7] == 0xFF
            file_info["direction"] = 1 if file_info["y_flip"] else -1
            file_info["width"] = i32(header_data, 0)
            file_info["height"] = (
                i32(header_data, 4)
                if not file_info["y_flip"]
                else 2**32 - i32(header_data, 4)
            )
            file_info["planes"] = i16(header_data, 8)
            file_info["bits"] = i16(header_data, 10)
            file_info["compression"] = i32(header_data, 12)
            # byte size of pixel data
            file_info["data_size"] = i32(header_data, 16)
            file_info["pixels_per_meter"] = (
                i32(header_data, 20),
                i32(header_data, 24),
            )
            file_info["colors"] = i32(header_data, 28)
            file_info["palette_padding"] = 4
            self.info["dpi"] = tuple(x / 39.3701 for x in file_info["pixels_per_meter"])
            if file_info["compression"] == self.BITFIELDS:
                if len(header_data) >= 52:
                    for idx, mask in enumerate(
                        ["r_mask", "g_mask", "b_mask", "a_mask"]
                    ):
                        file_info[mask] = i32(header_data, 36 + idx * 4)
                else:
                    # 40 byte headers only have the three components in the
                    # bitfields masks, ref:
                    # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
                    # See also
                    # https://github.com/python-pillow/Pillow/issues/1293
                    # There is a 4th component in the RGBQuad, in the alpha
                    # location, but it is listed as a reserved component,
                    # and it is not generally an alpha channel
                    file_info["a_mask"] = 0x0
                    for mask in ["r_mask", "g_mask", "b_mask"]:
                        file_info[mask] = i32(read(4))
                file_info["rgb_mask"] = (
                    file_info["r_mask"],
                    file_info["g_mask"],
                    file_info["b_mask"],
                )
                file_info["rgba_mask"] = (
                    file_info["r_mask"],
                    file_info["g_mask"],
                    file_info["b_mask"],
                    file_info["a_mask"],
                )
        else:
            raise OSError(f"Unsupported BMP header type ({file_info['header_size']})")

        # ------------------ Special case : header is reported 40, which
        # ---------------------- is shorter than real size for bpp >= 16
        self._size = file_info["width"], file_info["height"]

        # ------- If color count was not found in the header, compute from bits
        file_info["colors"] = (
            file_info["colors"]
            if file_info.get("colors", 0)
            else (1 << file_info["bits"])
        )
        if offset == 14 + file_info["header_size"] and file_info["bits"] <= 8:
            offset += 4 * file_info["colors"]

        # ---------------------- Check bit depth for unusual unsupported values
        self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
        if self.mode is None:
            raise OSError(f"Unsupported BMP pixel depth ({file_info['bits']})")

        # ---------------- Process BMP with Bitfields compression (not palette)
        decoder_name = "raw"
        if file_info["compression"] == self.BITFIELDS:
            SUPPORTED = {
                32: [
                    (0xFF0000, 0xFF00, 0xFF, 0x0),
                    (0xFF000000, 0xFF0000, 0xFF00, 0x0),
                    (0xFF000000, 0xFF0000, 0xFF00, 0xFF),
                    (0xFF, 0xFF00, 0xFF0000, 0xFF000000),
                    (0xFF0000, 0xFF00, 0xFF, 0xFF000000),
                    (0x0, 0x0, 0x0, 0x0),
                ],
                24: [(0xFF0000, 0xFF00, 0xFF)],
                16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],
            }
            MASK_MODES = {
                (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX",
                (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR",
                (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR",
                (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA",
                (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
                (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
                (24, (0xFF0000, 0xFF00, 0xFF)): "BGR",
                (16, (0xF800, 0x7E0, 0x1F)): "BGR;16",
                (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15",
            }
            if file_info["bits"] in SUPPORTED:
                if (
                    file_info["bits"] == 32
                    and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]
                ):
                    raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])]
                    self.mode = "RGBA" if "A" in raw_mode else self.mode
                elif (
                    file_info["bits"] in (24, 16)
                    and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]
                ):
                    raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
                else:
                    raise OSError("Unsupported BMP bitfields layout")
            else:
                raise OSError("Unsupported BMP bitfields layout")
        elif file_info["compression"] == self.RAW:
            if file_info["bits"] == 32 and header == 22:  # 32-bit .cur offset
                raw_mode, self.mode = "BGRA", "RGBA"
        elif file_info["compression"] == self.RLE8:
            decoder_name = "bmp_rle"
        else:
            raise OSError(f"Unsupported BMP compression ({file_info['compression']})")

        # --------------- Once the header is processed, process the palette/LUT
        if self.mode == "P":  # Paletted for 1, 4 and 8 bit images

            # ---------------------------------------------------- 1-bit images
            if not (0 < file_info["colors"] <= 65536):
                raise OSError(f"Unsupported BMP Palette size ({file_info['colors']})")
            else:
                padding = file_info["palette_padding"]
                palette = read(padding * file_info["colors"])
                greyscale = True
                indices = (
                    (0, 255)
                    if file_info["colors"] == 2
                    else list(range(file_info["colors"]))
                )

                # ----------------- Check if greyscale and ignore palette if so
                for ind, val in enumerate(indices):
                    rgb = palette[ind * padding : ind * padding + 3]
                    if rgb != o8(val) * 3:
                        greyscale = False

                # ------- If all colors are grey, white or black, ditch palette
                if greyscale:
                    self.mode = "1" if file_info["colors"] == 2 else "L"
                    raw_mode = self.mode
                else:
                    self.mode = "P"
                    self.palette = ImagePalette.raw(
                        "BGRX" if padding == 4 else "BGR", palette
                    )

        # ---------------------------- Finally set the tile data for the plugin
        self.info["compression"] = file_info["compression"]
        self.tile = [
            (
                decoder_name,
                (0, 0, file_info["width"], file_info["height"]),
                offset or self.fp.tell(),
                (
                    raw_mode,
                    ((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3),
                    file_info["direction"],
                ),
            )
        ]

    def _open(self):
        """Open file, check magic number and read header"""
        # read 14 bytes: magic number, filesize, reserved, header final offset
        head_data = self.fp.read(14)
        # choke if the file does not have the required magic bytes
        if not _accept(head_data):
            raise SyntaxError("Not a BMP file")
        # read the start position of the BMP image data (u32)
        offset = i32(head_data, 10)
        # load bitmap information (offset=raster info)
        self._bitmap(offset=offset)


class BmpRleDecoder(ImageFile.PyDecoder):
    _pulls_fd = True

    def decode(self, buffer):
        data = bytearray()
        x = 0
        while len(data) < self.state.xsize * self.state.ysize:
            pixels = self.fd.read(1)
            byte = self.fd.read(1)
            if not pixels or not byte:
                break
            num_pixels = pixels[0]
            if num_pixels:
                # encoded mode
                if x + num_pixels > self.state.xsize:
                    # Too much data for row
                    num_pixels = max(0, self.state.xsize - x)
                data += byte * num_pixels
                x += num_pixels
            else:
                if byte[0] == 0:
                    # end of line
                    while len(data) % self.state.xsize != 0:
                        data += b"\x00"
                    x = 0
                elif byte[0] == 1:
                    # end of bitmap
                    break
                elif byte[0] == 2:
                    # delta
                    bytes_read = self.fd.read(2)
                    if len(bytes_read) < 2:
                        break
                    right, up = self.fd.read(2)
                    data += b"\x00" * (right + up * self.state.xsize)
                    x = len(data) % self.state.xsize
                else:
                    # absolute mode
                    bytes_read = self.fd.read(byte[0])
                    data += bytes_read
                    if len(bytes_read) < byte[0]:
                        break
                    x += byte[0]

                    # align to 16-bit word boundary
                    if self.fd.tell() % 2 != 0:
                        self.fd.seek(1, os.SEEK_CUR)
        rawmode = "L" if self.mode == "L" else "P"
        self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1]))
        return -1, 0


# =============================================================================
# Image plugin for the DIB format (BMP alias)
# =============================================================================
class DibImageFile(BmpImageFile):

    format = "DIB"
    format_description = "Windows Bitmap"

    def _open(self):
        self._bitmap()


#
# --------------------------------------------------------------------
# Write BMP file


SAVE = {
    "1": ("1", 1, 2),
    "L": ("L", 8, 256),
    "P": ("P", 8, 256),
    "RGB": ("BGR", 24, 0),
    "RGBA": ("BGRA", 32, 0),
}


def _dib_save(im, fp, filename):
    _save(im, fp, filename, False)


def _save(im, fp, filename, bitmap_header=True):
    try:
        rawmode, bits, colors = SAVE[im.mode]
    except KeyError as e:
        raise OSError(f"cannot write mode {im.mode} as BMP") from e

    info = im.encoderinfo

    dpi = info.get("dpi", (96, 96))

    # 1 meter == 39.3701 inches
    ppm = tuple(map(lambda x: int(x * 39.3701 + 0.5), dpi))

    stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)
    header = 40  # or 64 for OS/2 version 2
    image = stride * im.size[1]

    # bitmap header
    if bitmap_header:
        offset = 14 + header + colors * 4
        file_size = offset + image
        if file_size > 2**32 - 1:
            raise ValueError("File size is too large for the BMP format")
        fp.write(
            b"BM"  # file type (magic)
            + o32(file_size)  # file size
            + o32(0)  # reserved
            + o32(offset)  # image data offset
        )

    # bitmap info header
    fp.write(
        o32(header)  # info header size
        + o32(im.size[0])  # width
        + o32(im.size[1])  # height
        + o16(1)  # planes
        + o16(bits)  # depth
        + o32(0)  # compression (0=uncompressed)
        + o32(image)  # size of bitmap
        + o32(ppm[0])  # resolution
        + o32(ppm[1])  # resolution
        + o32(colors)  # colors used
        + o32(colors)  # colors important
    )

    fp.write(b"\0" * (header - 40))  # padding (for OS/2 format)

    if im.mode == "1":
        for i in (0, 255):
            fp.write(o8(i) * 4)
    elif im.mode == "L":
        for i in range(256):
            fp.write(o8(i) * 4)
    elif im.mode == "P":
        fp.write(im.im.getpalette("RGB", "BGRX"))

    ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, stride, -1))])


#
# --------------------------------------------------------------------
# Registry


Image.register_open(BmpImageFile.format, BmpImageFile, _accept)
Image.register_save(BmpImageFile.format, _save)

Image.register_extension(BmpImageFile.format, ".bmp")

Image.register_mime(BmpImageFile.format, "image/bmp")

Image.register_decoder("bmp_rle", BmpRleDecoder)

Image.register_open(DibImageFile.format, DibImageFile, _dib_accept)
Image.register_save(DibImageFile.format, _dib_save)

Image.register_extension(DibImageFile.format, ".dib")

Image.register_mime(DibImageFile.format, "image/bmp")

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