Repository navigation
BmpImagePlugin - Unsupported 32bpp DIBs with Alpha #8594
Description
Activity
Sorry to bother you all with such a niche problem. 😅
For context, the attached bitmap (DIB) comes from one of the themes in Windows XP - I am trying to look into writing a general tool for converting .MSSTYLES based themes to various Linux toolkits.
I pinpointed this line of interest in
BmpImagePlugin: https://github.com/python-pillow/Pillow/blob/main/src/PIL/BmpImagePlugin.py#L245Removing this check for 22 byte header does resolve the issue for me, however it breaks the 32bpp bitmap test case. Unfortunately it seems like the alpha channel behaviour is undocumented, at least I cannot find anything. Both images use the 40-byte BITMAPINFOHEADER, and the only real difference is
biSizeImagebeing set to 0 in the broken bitmap. Not something I think can be relied on to change how the data should be interpreted.I don't think this is something easily solvable via parsing the format alone, so I have a fork of Pillow to include
BMPAandDIBAas additional formats for explicitly hinting that the alpha channel is to be read.I'll submit a corresponding PR, though my solution might be rubbish - I don't use Python all that much. 😝
For context, #1125 is the PR that introduced that line of code.
Can I ask - how do you know the image should be RGBA? Googling, I found https://jumpshare.com/viewer/bmp, and it showed no transparency when given the file.
I found https://learn.microsoft.com/en-us/windows/win32/wmdm/-bitmapinfoheader, which describes 32 bitcount as
The bitmap has a maximum of 2^32 colors. If the biCompression member is BI_RGB, the bmiColors member is NULL. Each DWORD in the bitmap array represents the relative intensities of blue, green, and red, respectively, for a pixel. The high byte in each DWORD is not used.
So it sounds like RGB is the intention.
Can I ask - how do you know the image should be RGBA? Googling, I found https://jumpshare.com/viewer/bmp, and it showed no transparency when given the file.
Because this DIB comes directly from the resource file
luna.msstyles- the theme engine does actually use the alpha channel (they moved to using PNGs in Windows Vista). All themes on Windows XP make use of RGBA bitmaps in the same way.Here's a comparison between the current read and what is intended:

I know the documentation does not say anything about it, I'm not sure if it was ever formally documented or whether it's just modern MSDN missing stuff. From searching around there's old forum posts and pages on CodeProject (RIP) about these 32bpp bitmaps. I think potentially it is possible to create them in Photoshop, but I'm not sure if that's still the case in newer versions.
The question I was trying to ask is - what program are you using to view this file that shows transparency?
Should I understand your response to mean that you're not viewing them in a program, and your expectation is based on the fact that Windows loads this data and then you see the result in the OS? Not just the transparent corners, but also a transparent inside under some circumstances?
You caught me just before I finished writing this update. 😛
In that post I compared between
DIBin Pillow, andDIBAin my branch - so it was based off the expectation of how Windows handles the data. I have just had a look out of curiosity to see if what I read about Photoshop is true, and it does appear to be the case that Photoshop (CS2 here) also recognises the alpha channel:Hmm. I'm concerned about interpreting data that the documentation says to ignore, because that's what third-party software would follow when creating images, and it's possible that what they write to this fourth channel may not actually work as an alpha channel. So it might fix your image, but it might also break others.
What do you think of this code, that will work with Pillow as-is to read your image as RGBA?
from PIL import Image, _binary, BmpImagePlugin filename = "BLUE_RADIOBUTTON25_BMP.bmp" im = Image.open(filename) if im.format == "DIB" and im.info["compression"] == BmpImagePlugin.BmpImageFile.COMPRESSIONS["RAW"]: bits = None with open(filename, "rb") as fp: header_size = _binary.i32le(fp.read(4)) if header_size == 40: header_data = fp.read(header_size - 4) bits = _binary.i16le(header_data, 10) if bits == 32: im._mode = "RGBA" args = list(im.tile[0].args) args[0] = "BGRA" im.tile = [im.tile[0]._replace(args=tuple(args))] im.save("out.png")
Hmm. I'm concerned about interpreting data that the documentation says to ignore, because that's what third-party software would follow when creating images, and it's possible that what they write to this fourth channel may not actually work as an alpha channel. So it might fix your image, but it might also break others.
This was why I opted to add the extra formats to act as 'hints', rather than trying to guess what the interpretation should be - so it cannot break existing images: rozniak@79683d3
What do you think of this code, that will work with Pillow as-is to read your image as RGBA?
It works perfectly. 😄 I mean it's up to you really, I suppose it might not be a very common type of bitmap for people to read so I don't mind working with this snippet in my own project. I did try to keep the change minimal and to avoid breaking any existing images according to spec, depends whether you think it's worth having or not. I suppose there is always this issue thread for future reference otherwise. 😛
I've created #8602 as a suggestion - it will retain the current Pillow behaviour by default, but if you run
from PIL import BmpImagePlugin BmpImagePlugin.USE_RAW_ALPHA = True
first, then the fourth channel will be read.
Reacted by Rory FewellReally appreciate you being so helpful with this problem, I didn't expect much at all considering it's an undocumented use of a niche variant of the file format so I wanted to express my thanks. 😁

What did you do?
Attempted to read the attached 32bpp DIB with
Image.open()(I've plopped it in a ZIP because GitHub doesn't like me just chucking the DIB itself in here)
sampledib.zip
What did you expect to happen?
The image reads as
RGBAWhat actually happened?
The image reads as
RGBWhat are your OS, Python and Pillow versions?
Locally built Pillow from d66c51a