mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-12 10:16:17 +03:00
Merge pull request #7956 from Cirras/obscure-bitmap-headers
Add support for reading `BITMAPV2INFOHEADER` and `BITMAPV3INFOHEADER`
This commit is contained in:
commit
22705d3da5
BIN
Tests/images/bmp/q/rgb32h52.bmp
Normal file
BIN
Tests/images/bmp/q/rgb32h52.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
BIN
Tests/images/bmp/q/rgba32h56.bmp
Normal file
BIN
Tests/images/bmp/q/rgba32h56.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
|
@ -44,6 +44,9 @@ def test_questionable() -> None:
|
|||
"pal8os2sp.bmp",
|
||||
"pal8rletrns.bmp",
|
||||
"rgb32bf-xbgr.bmp",
|
||||
"rgba32.bmp",
|
||||
"rgb32h52.bmp",
|
||||
"rgba32h56.bmp",
|
||||
]
|
||||
for f in get_files("q"):
|
||||
try:
|
||||
|
|
|
@ -5,7 +5,7 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
|
||||
from PIL import BmpImagePlugin, Image
|
||||
from PIL import BmpImagePlugin, Image, _binary
|
||||
|
||||
from .helper import (
|
||||
assert_image_equal,
|
||||
|
@ -128,6 +128,29 @@ def test_load_dib() -> None:
|
|||
assert_image_equal_tofile(im, "Tests/images/clipboard_target.png")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"header_size, path",
|
||||
(
|
||||
(12, "g/pal8os2.bmp"),
|
||||
(40, "g/pal1.bmp"),
|
||||
(52, "q/rgb32h52.bmp"),
|
||||
(56, "q/rgba32h56.bmp"),
|
||||
(64, "q/pal8os2v2.bmp"),
|
||||
(108, "g/pal8v4.bmp"),
|
||||
(124, "g/pal8v5.bmp"),
|
||||
),
|
||||
)
|
||||
def test_dib_header_size(header_size, path):
|
||||
image_path = "Tests/images/bmp/" + path
|
||||
with open(image_path, "rb") as fp:
|
||||
data = fp.read()[14:]
|
||||
assert _binary.i32le(data) == header_size
|
||||
|
||||
dib = io.BytesIO(data)
|
||||
with Image.open(dib) as im:
|
||||
im.load()
|
||||
|
||||
|
||||
def test_save_dib(tmp_path: Path) -> None:
|
||||
outfile = str(tmp_path / "temp.dib")
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ def _accept(prefix: bytes) -> bool:
|
|||
|
||||
|
||||
def _dib_accept(prefix):
|
||||
return i32(prefix) in [12, 40, 64, 108, 124]
|
||||
return i32(prefix) in [12, 40, 52, 56, 64, 108, 124]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
@ -83,8 +83,9 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
# 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
|
||||
# ------------------------------- Windows Bitmap v2, IBM OS/2 Bitmap v1
|
||||
# ----- This format has different offsets because of width/height types
|
||||
# 12: BITMAPCOREHEADER/OS21XBITMAPHEADER
|
||||
if file_info["header_size"] == 12:
|
||||
file_info["width"] = i16(header_data, 0)
|
||||
file_info["height"] = i16(header_data, 2)
|
||||
|
@ -93,9 +94,14 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
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):
|
||||
# --------------------------------------------- Windows Bitmap v3 to v5
|
||||
# 40: BITMAPINFOHEADER
|
||||
# 52: BITMAPV2HEADER
|
||||
# 56: BITMAPV3HEADER
|
||||
# 64: BITMAPCOREHEADER2/OS22XBITMAPHEADER
|
||||
# 108: BITMAPV4HEADER
|
||||
# 124: BITMAPV5HEADER
|
||||
elif file_info["header_size"] in (40, 52, 56, 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)
|
||||
|
@ -117,10 +123,13 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
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"]
|
||||
):
|
||||
masks = ["r_mask", "g_mask", "b_mask"]
|
||||
if len(header_data) >= 48:
|
||||
if len(header_data) >= 52:
|
||||
masks.append("a_mask")
|
||||
else:
|
||||
file_info["a_mask"] = 0x0
|
||||
for idx, mask in enumerate(masks):
|
||||
file_info[mask] = i32(header_data, 36 + idx * 4)
|
||||
else:
|
||||
# 40 byte headers only have the three components in the
|
||||
|
@ -132,7 +141,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
# 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"]:
|
||||
for mask in masks:
|
||||
file_info[mask] = i32(read(4))
|
||||
file_info["rgb_mask"] = (
|
||||
file_info["r_mask"],
|
||||
|
@ -175,9 +184,11 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
32: [
|
||||
(0xFF0000, 0xFF00, 0xFF, 0x0),
|
||||
(0xFF000000, 0xFF0000, 0xFF00, 0x0),
|
||||
(0xFF000000, 0xFF00, 0xFF, 0x0),
|
||||
(0xFF000000, 0xFF0000, 0xFF00, 0xFF),
|
||||
(0xFF, 0xFF00, 0xFF0000, 0xFF000000),
|
||||
(0xFF0000, 0xFF00, 0xFF, 0xFF000000),
|
||||
(0xFF000000, 0xFF00, 0xFF, 0xFF0000),
|
||||
(0x0, 0x0, 0x0, 0x0),
|
||||
],
|
||||
24: [(0xFF0000, 0xFF00, 0xFF)],
|
||||
|
@ -186,9 +197,11 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
MASK_MODES = {
|
||||
(32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX",
|
||||
(32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR",
|
||||
(32, (0xFF000000, 0xFF00, 0xFF, 0x0)): "BGXR",
|
||||
(32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR",
|
||||
(32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA",
|
||||
(32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
|
||||
(32, (0xFF000000, 0xFF00, 0xFF, 0xFF0000)): "BGAR",
|
||||
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
|
||||
(24, (0xFF0000, 0xFF00, 0xFF)): "BGR",
|
||||
(16, (0xF800, 0x7E0, 0x1F)): "BGR;16",
|
||||
|
|
|
@ -790,6 +790,17 @@ ImagingUnpackBGRX(UINT8 *_out, const UINT8 *in, int pixels) {
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ImagingUnpackBGXR(UINT8 *_out, const UINT8 *in, int pixels) {
|
||||
int i;
|
||||
for (i = 0; i < pixels; i++) {
|
||||
UINT32 iv = MAKE_UINT32(in[3], in[1], in[0], 255);
|
||||
memcpy(_out, &iv, sizeof(iv));
|
||||
in += 4;
|
||||
_out += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ImagingUnpackXRGB(UINT8 *_out, const UINT8 *in, int pixels) {
|
||||
int i;
|
||||
|
@ -1090,6 +1101,17 @@ unpackBGRA16B(UINT8 *_out, const UINT8 *in, int pixels) {
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
unpackBGAR(UINT8 *_out, const UINT8 *in, int pixels) {
|
||||
int i;
|
||||
for (i = 0; i < pixels; i++) {
|
||||
UINT32 iv = MAKE_UINT32(in[3], in[1], in[0], in[2]);
|
||||
memcpy(_out, &iv, sizeof(iv));
|
||||
in += 4;
|
||||
_out += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unpack to "CMYK" image */
|
||||
|
||||
static void
|
||||
|
@ -1584,6 +1606,7 @@ static struct {
|
|||
{"RGB", "RGBA;L", 32, unpackRGBAL},
|
||||
{"RGB", "RGBA;15", 16, ImagingUnpackRGBA15},
|
||||
{"RGB", "BGRX", 32, ImagingUnpackBGRX},
|
||||
{"RGB", "BGXR", 32, ImagingUnpackBGXR},
|
||||
{"RGB", "XRGB", 32, ImagingUnpackXRGB},
|
||||
{"RGB", "XBGR", 32, ImagingUnpackXBGR},
|
||||
{"RGB", "YCC;P", 24, ImagingUnpackYCC},
|
||||
|
@ -1624,6 +1647,7 @@ static struct {
|
|||
{"RGBA", "BGRA", 32, unpackBGRA},
|
||||
{"RGBA", "BGRA;16L", 64, unpackBGRA16L},
|
||||
{"RGBA", "BGRA;16B", 64, unpackBGRA16B},
|
||||
{"RGBA", "BGAR", 32, unpackBGAR},
|
||||
{"RGBA", "ARGB", 32, unpackARGB},
|
||||
{"RGBA", "ABGR", 32, unpackABGR},
|
||||
{"RGBA", "YCCA;P", 32, ImagingUnpackYCCA},
|
||||
|
|
Loading…
Reference in New Issue
Block a user