Skip to content
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

RLE8 compression support for bitmap images #3425

Closed
bbotezatu opened this issue Oct 22, 2018 · 2 comments · Fixed by #6102
Closed

RLE8 compression support for bitmap images #3425

bbotezatu opened this issue Oct 22, 2018 · 2 comments · Fixed by #6102

Comments

@bbotezatu
Copy link

bbotezatu commented Oct 22, 2018

What did you do?

Attempted to open an RLE8-compressed bitmap image.

What did you expect to happen?

The image to display correctly.

What actually happened?

An IOError("Unsupported BMP compression (%d)" % file_info['compression']) was thrown.

We try to open the image in our main program:

def load_image(self, path, canvas):
        try:
            image = Image.open(path)

This calls the BmpImageFile class from BmpImagePlugin.py, which attempts to display the file according to the compression types listed in the dictionary on line 63:

COMPRESSIONS = {
        'RAW': 0,
        'RLE8': 1,
        'RLE4': 2,
        'BITFIELDS': 3,
        'JPEG': 4,
        'PNG': 5
    }
    RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5

After reading header data, the program continues; on line 158, an if statement checks:

if file_info['compression'] == self.BITFIELDS:

which evaluates to false. So, the program then jumps to line 190, where it checks whether the file's compression type is RAW:

elif file_info['compression'] == self.RAW:

which also evaluates to false. This causes the program to jump the code block to line 193, where the exception is thrown, and causes our main program to fail.

raise IOError("Unsupported BMP compression (%d)" %

The code that ultimately throws this exception is found at line 194, in the BmpImagePlugin source.

What are your OS, Python and Pillow versions?

  • OS: Windows 10
  • Python: 3.6.3
  • Pillow: 5.2.0

testImages.zip

@JensRestemeier
Copy link

JensRestemeier commented Dec 10, 2021

Just had this problem, for a single BMP file in a directory of otherwise supported files... to avoid using another package just for this file I made a quick and dirty implementation:

def loadBmp(f):
    data = f.read()
    
    magic,size,_,_,pixel_ofs = struct.unpack_from("<2sIHHI", data, 0)
    # print (magic,size,pixel_ofs)
    
    if magic != b"BM" or size != len(data):
        raise Exception("not a BMP file")

    headersize,width,height,planes,bits,compression,img_size,ppm_hor,ppm_ver,num_colours,req_colours = struct.unpack_from("<IIIHHIIIIII", data, 14)
    # print (headersize,width,height,planes,bits,compression,img_size,ppm_hor,ppm_ver,num_colours,req_colours)
    if headersize != 40:
        raise Exception("only BITMAPINFOHEADER supported")

    if compression != 1:
        raise Exception("only RLE8 compression supported")
        
    pal = data[14+40:14+40+num_colours*4]
    bitmap = array.array("B", [0] * width * height)
    ofs = pixel_ofs
    x = 0
    y = 0
    while ofs < len(data) and y < height:
        cmd = data[ofs]
        ofs += 1
        if cmd == 0:
            op = data[ofs]
            ofs += 1
            if op == 0:
                # end of line
                x = 0
                y += 1
            elif op == 1:
                # end of bitmap
                ofs = len(data)
            elif op == 2:
                # delta
                delta = struct.unpack_from("<H", data, ofs)
                pos = x + y * width + delta
                x = pos % width
                y = pos // width
            else:
                for i in range(op):
                    bitmap[y*width+x] = data[ofs]
                    ofs += 1
                    x += 1
        else:
            val = data[ofs]
            ofs += 1
            for i in range(cmd):
                bitmap[y*width+x] = val
                x += 1
    im = Image.frombytes("P", (width, height), bytes(bitmap))
    im.putpalette(pal, "RGBX")
    return im

This is based on the official Microsoft documentation:
https://docs.microsoft.com/en-us/windows/win32/gdi/bitmap-compression

@radarhere
Copy link
Member

For the record, test2.bmp is actually BITFIELDS.

I've created PR #6102 to resolve this.

Pillow automation moved this from Icebox to Closed Mar 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Pillow
  
Closed
Development

Successfully merging a pull request may close this issue.

4 participants