-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added support for reading BMP images with RLE4 compression #6674
Changes from 5 commits
70e3e4f
c2e9c66
455ffff
f2dfd0b
cc45886
78430b9
6c8234b
4b31a7b
d092bb7
cf46156
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -212,7 +212,9 @@ def _bitmap(self, header=0, offset=0): | |
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" | ||
decoder_name = "bmp_rle8" | ||
elif file_info["compression"] == self.RLE4: | ||
decoder_name = "bmp_rle4" | ||
else: | ||
raise OSError(f"Unsupported BMP compression ({file_info['compression']})") | ||
|
||
|
@@ -276,7 +278,7 @@ def _open(self): | |
self._bitmap(offset=offset) | ||
|
||
|
||
class BmpRleDecoder(ImageFile.PyDecoder): | ||
class BmpRle8Decoder(ImageFile.PyDecoder): | ||
_pulls_fd = True | ||
|
||
def decode(self, buffer): | ||
|
@@ -328,6 +330,69 @@ def decode(self, buffer): | |
return -1, 0 | ||
|
||
|
||
class BmpRle4Decoder(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) | ||
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 | ||
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 (2 pixels per byte) | ||
total_bytes_to_read = byte[0] // 2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this "2" comment mean? If it means this value is always 2, that won't work, because this is already checked for on line 367. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not a comment. Commenting in Python is #. This is floor division. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, right. There's even a real comment right above that and I didn't notice... |
||
bytes_read = self.fd.read(total_bytes_to_read) | ||
for byte_read in bytes_read: | ||
first_pixel = o8(byte_read >> 4) | ||
data += first_pixel | ||
second_pixel = o8(byte_read & 0x0F) | ||
data += second_pixel | ||
if len(bytes_read) < total_bytes_to_read: | ||
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) | ||
# ============================================================================= | ||
|
@@ -433,7 +498,8 @@ def _save(im, fp, filename, bitmap_header=True): | |
|
||
Image.register_mime(BmpImageFile.format, "image/bmp") | ||
|
||
Image.register_decoder("bmp_rle", BmpRleDecoder) | ||
Image.register_decoder("bmp_rle8", BmpRle8Decoder) | ||
Image.register_decoder("bmp_rle4", BmpRle4Decoder) | ||
|
||
Image.register_open(DibImageFile.format, DibImageFile, _dib_accept) | ||
Image.register_save(DibImageFile.format, _dib_save) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any significant difference between hopper_rle4.bmp and pal4rle.bmp?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've removed hopper_rle4.bmp, since the other test image seems sufficient.