mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-13 10:46:16 +03:00
Merge pull request #6674 from npjg/main
Added support for reading BMP images with RLE4 compression
This commit is contained in:
commit
5c9bc6517f
|
@ -176,6 +176,11 @@ def test_rle8():
|
|||
im.load()
|
||||
|
||||
|
||||
def test_rle4():
|
||||
with Image.open("Tests/images/bmp/g/pal4rle.bmp") as im:
|
||||
assert_image_similar_tofile(im, "Tests/images/bmp/g/pal4.bmp", 12)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"file_name,length",
|
||||
(
|
||||
|
|
|
@ -45,9 +45,9 @@ BMP
|
|||
^^^
|
||||
|
||||
Pillow reads and writes Windows and OS/2 BMP files containing ``1``, ``L``, ``P``,
|
||||
or ``RGB`` data. 16-colour images are read as ``P`` images. 4-bit run-length encoding
|
||||
is not supported. Support for reading 8-bit run-length encoding was added in Pillow
|
||||
9.1.0.
|
||||
or ``RGB`` data. 16-colour images are read as ``P`` images.
|
||||
Support for reading 8-bit run-length encoding was added in Pillow 9.1.0.
|
||||
Support for reading 4-bit run-length encoding was added in Pillow 9.3.0.
|
||||
|
||||
Opening
|
||||
~~~~~~~
|
||||
|
@ -56,7 +56,8 @@ The :py:meth:`~PIL.Image.open` method sets the following
|
|||
:py:attr:`~PIL.Image.Image.info` properties:
|
||||
|
||||
**compression**
|
||||
Set to ``bmp_rle`` if the file is run-length encoded.
|
||||
Set to 1 if the file is a 256-color run-length encoded image.
|
||||
Set to 2 if the file is a 16-color run-length encoded image.
|
||||
|
||||
DDS
|
||||
^^^
|
||||
|
|
|
@ -211,7 +211,7 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
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:
|
||||
elif file_info["compression"] in (self.RLE8, self.RLE4):
|
||||
decoder_name = "bmp_rle"
|
||||
else:
|
||||
raise OSError(f"Unsupported BMP compression ({file_info['compression']})")
|
||||
|
@ -250,16 +250,18 @@ class BmpImageFile(ImageFile.ImageFile):
|
|||
|
||||
# ---------------------------- Finally set the tile data for the plugin
|
||||
self.info["compression"] = file_info["compression"]
|
||||
args = [raw_mode]
|
||||
if decoder_name == "bmp_rle":
|
||||
args.append(file_info["compression"] == self.RLE4)
|
||||
else:
|
||||
args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3))
|
||||
args.append(file_info["direction"])
|
||||
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"],
|
||||
),
|
||||
tuple(args),
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -280,6 +282,7 @@ class BmpRleDecoder(ImageFile.PyDecoder):
|
|||
_pulls_fd = True
|
||||
|
||||
def decode(self, buffer):
|
||||
rle4 = self.args[1]
|
||||
data = bytearray()
|
||||
x = 0
|
||||
while len(data) < self.state.xsize * self.state.ysize:
|
||||
|
@ -293,7 +296,16 @@ class BmpRleDecoder(ImageFile.PyDecoder):
|
|||
if x + num_pixels > self.state.xsize:
|
||||
# Too much data for row
|
||||
num_pixels = max(0, self.state.xsize - x)
|
||||
data += byte * num_pixels
|
||||
if rle4:
|
||||
first_pixel = o8(byte[0] >> 4)
|
||||
second_pixel = o8(byte[0] & 0x0F)
|
||||
for index in range(num_pixels):
|
||||
if index % 2 == 0:
|
||||
data += first_pixel
|
||||
else:
|
||||
data += second_pixel
|
||||
else:
|
||||
data += byte * num_pixels
|
||||
x += num_pixels
|
||||
else:
|
||||
if byte[0] == 0:
|
||||
|
@ -314,9 +326,18 @@ class BmpRleDecoder(ImageFile.PyDecoder):
|
|||
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]:
|
||||
if rle4:
|
||||
# 2 pixels per byte
|
||||
byte_count = byte[0] // 2
|
||||
bytes_read = self.fd.read(byte_count)
|
||||
for byte_read in bytes_read:
|
||||
data += o8(byte_read >> 4)
|
||||
data += o8(byte_read & 0x0F)
|
||||
else:
|
||||
byte_count = byte[0]
|
||||
bytes_read = self.fd.read(byte_count)
|
||||
data += bytes_read
|
||||
if len(bytes_read) < byte_count:
|
||||
break
|
||||
x += byte[0]
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user