Added BMP RLE8 decoder

This commit is contained in:
Andrew Murray 2022-03-09 22:35:48 +11:00
parent 397a940995
commit 0ace3fcd26
3 changed files with 53 additions and 2 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -4,7 +4,12 @@ import pytest
from PIL import BmpImagePlugin, Image
from .helper import assert_image_equal, assert_image_equal_tofile, hopper
from .helper import (
assert_image_equal,
assert_image_equal_tofile,
assert_image_similar_tofile,
hopper,
)
def test_sanity(tmp_path):
@ -125,6 +130,11 @@ def test_rgba_bitfields():
assert_image_equal_tofile(im, "Tests/images/bmp/q/rgb32bf-xbgr.bmp")
def test_rle8():
with Image.open("Tests/images/hopper_rle8.bmp") as im:
assert_image_similar_tofile(im.convert("RGB"), "Tests/images/hopper.bmp", 12)
def test_offset():
# This image has been hexedited
# to exclude the palette size from the pixel data offset

View File

@ -24,6 +24,8 @@
#
import os
from . import Image, ImageFile, ImagePalette
from ._binary import i16le as i16
from ._binary import i32le as i32
@ -167,6 +169,7 @@ class BmpImageFile(ImageFile.ImageFile):
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: [
@ -208,6 +211,8 @@ 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:
decoder_name = "bmp_rle"
else:
raise OSError(f"Unsupported BMP compression ({file_info['compression']})")
@ -247,7 +252,7 @@ class BmpImageFile(ImageFile.ImageFile):
self.info["compression"] = file_info["compression"]
self.tile = [
(
"raw",
decoder_name,
(0, 0, file_info["width"], file_info["height"]),
offset or self.fp.tell(),
(
@ -271,6 +276,40 @@ class BmpImageFile(ImageFile.ImageFile):
self._bitmap(offset=offset)
class BmpRleDecoder(ImageFile.PyDecoder):
_pulls_fd = True
def decode(self, buffer):
data = bytearray()
while True:
num_pixels = self.fd.read(1)[0]
byte = self.fd.read(1)
if num_pixels:
# encoded mode
data += byte * num_pixels
else:
if byte[0] == 0:
# end of line
while len(data) % self.state.xsize != 0:
data += b"\x00"
elif byte[0] == 1:
# end of bitmap
break
elif byte[0] == 2:
# delta
right, up = self.fd.read(2)
data += b"\x00" * (right + up * self.state.xsize)
else:
# absolute mode
data += self.fd.read(byte[0])
# align to 16-bit word boundary
if self.fd.tell() % 2 != 0:
self.fd.seek(1, os.SEEK_CUR)
self.set_as_raw(bytes(data), ("P", 0, self.args[-1]))
return -1, 0
# =============================================================================
# Image plugin for the DIB format (BMP alias)
# =============================================================================
@ -372,6 +411,8 @@ 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)