Load Windows ICO files

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import operator
import struct

from PIL import BmpImagePlugin, PngImagePlugin, Image


def load_icon(file, index=None):
    '''
    Load Windows ICO image.

    See http://en.wikipedia.org/w/index.php?oldid=264332061 for file format
    description.
    '''
    if isinstance(file, basestring):
        file = open(file, 'rb')

    header = struct.unpack('<3H', file.read(6))

    # Check magic
    if header[:2] != (0, 1):
        raise SyntaxError('Not an ICO file')

    # Collect icon directories
    directories = []
    for i in xrange(header[2]):
        directory = list(struct.unpack('<4B2H2I', file.read(16)))
        for j in xrange(3):
            if not directory[j]:
                directory[j] = 256

        directories.append(directory)

    if index is None:
        # Select best icon
        directory = max(directories, key=operator.itemgetter(slice(0, 3)))
    else:
        directory = directories[index]

    # Seek to the bitmap data
    file.seek(directory[7])

    prefix = file.read(16)
    file.seek(-16, 1)

    if PngImagePlugin._accept(prefix):
        # Windows Vista icon with PNG inside
        image = PngImagePlugin.PngImageFile(file)
    else:
        # Load XOR bitmap
        image = BmpImagePlugin.DibImageFile(file)
        if image.mode == 'RGBA':
            # Windows XP 32-bit color depth icon without AND bitmap
            pass
        else:
            # Patch up the bitmap height
            image.size = image.size[0], image.size[1] >> 1
            d, e, o, a = image.tile[0]
            image.tile[0] = d, (0, 0) + image.size, o, a

            # Calculate AND bitmap dimensions. See
            # http://en.wikipedia.org/w/index.php?oldid=264236948#Pixel_storage
            # for description
            offset = o + a[1] * image.size[1]
            stride = ((image.size[0] + 31) >> 5) << 2
            size = stride * image.size[1]

            # Load AND bitmap
            file.seek(offset)
            string = file.read(size)
            mask = Image.fromstring('1', image.size, string, 'raw',
                                    ('1;I', stride, -1))

            image = image.convert('RGBA')
            image.putalpha(mask)

    return image

More like this

  1. Download images as png or pdf by gkelly 6 years, 1 month ago
  2. Dynamic tabular inlines with optional drag-n-drop sorting by Aneon 4 years ago
  3. Cacheable resources by jbrisbin 4 years, 9 months ago
  4. Handling Image uploading and Delete Old Image in Django by mulianto 1 year ago
  5. Crop and scale image to a given size by rpw 6 years ago

Comments

jpwatts (on January 29, 2009):

You just made my life a lot easier. Trying to preserve ICO transparency was giving me a headache. Thank you.

#

bd808 (on June 16, 2009):

With my version of PIL this function doesn't handle 32bpp icon resources correctly. PIL's DIB loader discards the alpha channel in these images. It is possible to recover this alpha mask and add it back into the Image like you have done for the 1bpp AND mask.

I've written an enhanced PIL ICO plugin which seems to overcome this issue. (At least for the test cases I've used.)

#

Matt John (on September 17, 2009):

Good Coding.... Really help me in [HTML_REMOVED]mcsa training[HTML_REMOVED], there are many options there. [url=http://www.ccnaguide.com]ccna[/url]

#

(Forgotten your password?)