I'm trying to extract and save the 32x32 icon from a .ico file that contains multiple icons with multiple sizes and color depths using Pillow. The 32x32 icon is available in the following color depths: 32-bit, 8-bit and 4-bit.
I tried opening the icon file using Image.open()
, then set its size to 32x32, and then save it as a .png file. I expect to get the icon with the highest color depth possible, but I'm getting the one with the lowest color depth possible.
Here's a minimal, reproducible example:
from PIL import Image
icon = Image.open("icon.ico")
icon.size = (32, 32)
icon.save("icon.png")
You can get the .ico file here: https://www.mediafire.com/file/uls693wvjn3njqa/icon.ico/file
I also tried looking for similar questions on the internet, but I didn't find anything.
Is there a way I can tell Pillow to extract the 32x32 icon from a .ico file at the highest color depth possible without modifying my .ico file to exclude icons with lower color depths?
WHile an ICO image loaded in PIL provide access to the individual images through the img.ico.frame
call, there is one problem: upon loading, PIL will convert all loaded individual frames which are indexed into the RGBA format.
Fortunately, it will preserve a bpp
attribute listing the original bitcount for each frame in a headers list stored in the img.ico.entry
attribute.
Which means a small function which iterates the frame headers can find the needed information:
from PIL import Image
def get_max_bpp(ico, size=(32,32)):
frames = [(index, header, ico.ico.frame(index))
for index, header in enumerate(icon.ico.entry)
if header.width==size[0] and header.height == size[1]
]
frames.sort(key=lambda frame_info: frame_info[1].bpp, reverse=True)
return frames[0][2]
icon = Image.open("icon.ico")
max_32 = get_max_bpp(icon)
max_32.save("icon.png")
So, a straight look at the .mode
attribute of each frame won't help - all will show up as RGBA (at least for an ICO file which contains at least one 24 or 32bit frame. I had not tested with a file with indexed-only frames).