Skip to content

Commit 6c8234b

Browse files
committed
Combined BMP RLE decoders
1 parent 78430b9 commit 6c8234b

File tree

1 file changed

+34
-79
lines changed

1 file changed

+34
-79
lines changed

src/PIL/BmpImagePlugin.py

Lines changed: 34 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,8 @@ def _bitmap(self, header=0, offset=0):
211211
elif file_info["compression"] == self.RAW:
212212
if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset
213213
raw_mode, self.mode = "BGRA", "RGBA"
214-
elif file_info["compression"] == self.RLE8:
215-
decoder_name = "bmp_rle8"
216-
elif file_info["compression"] == self.RLE4:
217-
decoder_name = "bmp_rle4"
214+
elif file_info["compression"] in (self.RLE8, self.RLE4):
215+
decoder_name = "bmp_rle"
218216
else:
219217
raise OSError(f"Unsupported BMP compression ({file_info['compression']})")
220218

@@ -252,16 +250,18 @@ def _bitmap(self, header=0, offset=0):
252250

253251
# ---------------------------- Finally set the tile data for the plugin
254252
self.info["compression"] = file_info["compression"]
253+
args = [raw_mode]
254+
if decoder_name == "bmp_rle":
255+
args.append(file_info["compression"] == self.RLE4)
256+
else:
257+
args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3))
258+
args.append(file_info["direction"])
255259
self.tile = [
256260
(
257261
decoder_name,
258262
(0, 0, file_info["width"], file_info["height"]),
259263
offset or self.fp.tell(),
260-
(
261-
raw_mode,
262-
((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3),
263-
file_info["direction"],
264-
),
264+
tuple(args),
265265
)
266266
]
267267

@@ -278,10 +278,11 @@ def _open(self):
278278
self._bitmap(offset=offset)
279279

280280

281-
class BmpRle8Decoder(ImageFile.PyDecoder):
281+
class BmpRleDecoder(ImageFile.PyDecoder):
282282
_pulls_fd = True
283283

284284
def decode(self, buffer):
285+
rle4 = self.args[1]
285286
data = bytearray()
286287
x = 0
287288
while len(data) < self.state.xsize * self.state.ysize:
@@ -295,67 +296,19 @@ def decode(self, buffer):
295296
if x + num_pixels > self.state.xsize:
296297
# Too much data for row
297298
num_pixels = max(0, self.state.xsize - x)
298-
data += byte * num_pixels
299-
x += num_pixels
300-
else:
301-
if byte[0] == 0:
302-
# end of line
303-
while len(data) % self.state.xsize != 0:
304-
data += b"\x00"
305-
x = 0
306-
elif byte[0] == 1:
307-
# end of bitmap
308-
break
309-
elif byte[0] == 2:
310-
# delta
311-
bytes_read = self.fd.read(2)
312-
if len(bytes_read) < 2:
313-
break
314-
right, up = self.fd.read(2)
315-
data += b"\x00" * (right + up * self.state.xsize)
316-
x = len(data) % self.state.xsize
299+
if rle4:
300+
first_pixel = o8(byte[0] >> 4)
301+
second_pixel = o8(byte[0] & 0x0F)
302+
for index in range(num_pixels):
303+
if index % 2 == 0:
304+
data += first_pixel
305+
else:
306+
data += second_pixel
317307
else:
318-
# absolute mode
319-
bytes_read = self.fd.read(byte[0])
320-
data += bytes_read
321-
if len(bytes_read) < byte[0]:
322-
break
323-
x += byte[0]
324-
325-
# align to 16-bit word boundary
326-
if self.fd.tell() % 2 != 0:
327-
self.fd.seek(1, os.SEEK_CUR)
328-
rawmode = "L" if self.mode == "L" else "P"
329-
self.set_as_raw(bytes(data), (rawmode, 0, self.args[-1]))
330-
return -1, 0
331-
332-
333-
class BmpRle4Decoder(ImageFile.PyDecoder):
334-
_pulls_fd = True
335-
336-
def decode(self, buffer):
337-
data = bytearray()
338-
x = 0
339-
while len(data) < self.state.xsize * self.state.ysize:
340-
pixels = self.fd.read(1)
341-
byte = self.fd.read(1)
342-
if not pixels or not byte:
343-
break
344-
num_pixels = pixels[0]
345-
if num_pixels:
346-
# encoded mode
347-
if x + num_pixels > self.state.xsize:
348-
# Too much data for row
349-
num_pixels = max(0, self.state.xsize - x)
350-
first_pixel = o8(byte[0] >> 4)
351-
second_pixel = o8(byte[0] & 0x0F)
352-
for index in range(num_pixels):
353-
if index % 2 == 0:
354-
data += first_pixel
355-
else:
356-
data += second_pixel
308+
data += byte * num_pixels
357309
x += num_pixels
358310
else:
311+
# absolute mode
359312
if byte[0] == 0:
360313
# end of line
361314
while len(data) % self.state.xsize != 0:
@@ -373,15 +326,18 @@ def decode(self, buffer):
373326
data += b"\x00" * (right + up * self.state.xsize)
374327
x = len(data) % self.state.xsize
375328
else:
376-
# absolute mode (2 pixels per byte)
377-
total_bytes_to_read = byte[0] // 2
378-
bytes_read = self.fd.read(total_bytes_to_read)
379-
for byte_read in bytes_read:
380-
first_pixel = o8(byte_read >> 4)
381-
data += first_pixel
382-
second_pixel = o8(byte_read & 0x0F)
383-
data += second_pixel
384-
if len(bytes_read) < total_bytes_to_read:
329+
if rle4:
330+
# 2 pixels per byte
331+
byte_count = byte[0] // 2
332+
bytes_read = self.fd.read(byte_count)
333+
for byte_read in bytes_read:
334+
data += o8(byte_read >> 4)
335+
data += o8(byte_read & 0x0F)
336+
else:
337+
byte_count = byte[0]
338+
bytes_read = self.fd.read(byte_count)
339+
data += bytes_read
340+
if len(bytes_read) < byte_count:
385341
break
386342
x += byte[0]
387343

@@ -498,8 +454,7 @@ def _save(im, fp, filename, bitmap_header=True):
498454

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

501-
Image.register_decoder("bmp_rle8", BmpRle8Decoder)
502-
Image.register_decoder("bmp_rle4", BmpRle4Decoder)
457+
Image.register_decoder("bmp_rle", BmpRleDecoder)
503458

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

0 commit comments

Comments
 (0)