From 19c8f08c61e01ef107519476db95fbed1c0bfe9d Mon Sep 17 00:00:00 2001 From: Joey Payne Date: Mon, 3 Nov 2014 20:14:31 +1300 Subject: [PATCH] Fixed some issues with icon generation. --- bmpimageplugin.py | 42 +++++++++++++++++----------------- ico_plugin.py | 57 ++++++++++++++++++++++++++++++++++++++++++++--- pe.py | 21 ++++++++--------- 3 files changed, 84 insertions(+), 36 deletions(-) diff --git a/bmpimageplugin.py b/bmpimageplugin.py index 523e52b..968254a 100644 --- a/bmpimageplugin.py +++ b/bmpimageplugin.py @@ -254,7 +254,7 @@ def _save(im, fp, filename, check=0): ppm = tuple(map(lambda x: int(x * 39.3701), dpi)) stride = ((im.size[0]*bits+7)//8+3) & (~3) - header = 64 #108 if im.mode == 'RGBA' else 40 # or 64 for OS/2 version 2 + header = 108 if im.mode == 'RGBA' else 40 # or 64 for OS/2 version 2 offset = 14 + header + colors*4 image = stride * im.size[1] @@ -265,14 +265,14 @@ def _save(im, fp, filename, check=0): # bitmap header fp.write(b"BM" + # file type (magic) - o32(offset+image) + # file size + o32(offset+image+16) + # file size o32(0) + # reserved - o32(offset)) # image data offset + o32(offset+16)) # image data offset width,height = im.size # bitmap info header - fp.write(o32(header) + # info header size + fp.write(o32(header+16) + # info header size o32(width) + # width o32(height) + # height o16(1) + # planes @@ -281,28 +281,28 @@ def _save(im, fp, filename, check=0): o32(image) + # size of bitmap o32(ppm[0]) + o32(ppm[1]) + # resolution o32(colors) + # colors used - o32(colors) + # colors important - o32(red_mask) + # red channel ma - o32(green_mask) + # green channel mask - o32(blue_mask) + # blue channel mask - o32(alpha_mask) + o32(colors) # colors important + #o32(red_mask) + # red channel ma + #o32(green_mask) + # green channel mask + #o32(blue_mask) + # blue channel mask + #o32(alpha_mask) ) # This was commented out because although it works, some # decoders do not support images with a BI_BITFIELDS compression # - #if im.mode == 'RGBA': - # fp.write(o32(red_mask) + # red channel mask - # o32(green_mask) + # green channel mask - # o32(blue_mask) + # blue channel mask - # o32(alpha_mask) + # alpha channel mask - # 'BGRs' + # Color Space - # o8(0)*0x24 + # ciexyztriple color space endpoints - # o32(0) + # red gamma - # o32(0) + # green gamma - # o32(0) # blue gamma - # ) + if im.mode == 'RGBA': + fp.write(o32(red_mask) + # red channel mask + o32(green_mask) + # green channel mask + o32(blue_mask) + # blue channel mask + o32(alpha_mask) + # alpha channel mask + 'BGRs' + # Color Space + o8(0)*0x24 + # ciexyztriple color space endpoints + o32(0) + # red gamma + o32(0) + # green gamma + o32(0) # blue gamma + ) - fp.write(bytearray(header - 40 - 4)) + fp.write(bytearray(16)) if im.mode == "1": for i in (0, 255): diff --git a/ico_plugin.py b/ico_plugin.py index afe37fe..1aeed0f 100644 --- a/ico_plugin.py +++ b/ico_plugin.py @@ -291,7 +291,7 @@ def get_data(im): dib = dib[:40] bmp_f.seek(offset) data = bytearray(bmp_f.read()) - data = dib+data[24:] + bytearray(24) + data = dib+data else: data = bytearray(s.read()) @@ -330,6 +330,31 @@ def _save(image, fp, filename, check=0): rawmode, bits, colors = SAVE[im.mode] except KeyError: raise IOError("cannot write mode %s as BMP" % im.mode) + matte_data = bytearray() + if im.format == 'BMP': + + #write matte data. Taken from imagemagick + scanline_pad = (((im.size[0]+31) & ~31)-im.size[0]) >> 3 + row_len = im.size[0]*4 + for y in reversed(xrange(im.size[1])): + d = data[40:] + #BGRA + row_pixels = d[row_len*y:row_len*y+row_len] + bit=0 + byte=0 + for x in xrange(im.size[0]): + p=row_pixels[x] + + byte<<=1 + bit+=1 + if bit == 8: + matte_data+=o8(byte) + bit=0 + byte=0 + if not bit == 0: + matte_data+=o8(byte<<(8-bit)) + for i in xrange(scanline_pad): + matte_data+=o8(0) fp.write(o8(im.size[0]) + # width o8(im.size[1]) + # height @@ -337,17 +362,43 @@ def _save(image, fp, filename, check=0): o8(0) + # reserved o16(1) + # planes o16(bits) + # depth - o32(len(data)) + # size of image in bytes + o32(len(data+matte_data)) + # size of image in bytes o32(current_offset) # offset ) - current_offset += len(data) + current_offset += len(data+matte_data) for im in images: data = get_data(im) fp.write(data) + if im.format == 'BMP': + + #write matte data. Taken from imagemagick + scanline_pad = (((im.size[0]+31) & ~31)-im.size[0]) >> 3 + row_len = im.size[0]*4 + for y in reversed(xrange(im.size[1])): + d = data[40:] + #BGRA + row_pixels = d[row_len*y:row_len*y+row_len] + bit=0 + byte=0 + for x in xrange(im.size[0]): + p=row_pixels[x] + + byte<<=1 + bit+=1 + if bit == 8: + fp.write(o8(byte)) + bit=0 + byte=0 + if not bit == 0: + fp.write(o8(byte<<(8-bit))) + for i in xrange(scanline_pad): + fp.write(o8(0)) + + fp.flush() diff --git a/pe.py b/pe.py index da942a3..e74eaed 100644 --- a/pe.py +++ b/pe.py @@ -4,7 +4,7 @@ from cStringIO import StringIO from ico_plugin import * -def resize(image, size): +def resize(image, size, format=None): output = StringIO() back = Image.new('RGBA', size, (0,0,0,0)) image.thumbnail(size, Image.ANTIALIAS) @@ -14,7 +14,8 @@ def resize(image, size): else: offset[0] = back.size[0]/2-image.size[0]/2 back.paste(image, tuple(offset)) - back.save(output, image.format) + format = format or image.format + back.save(output, format) contents = output.getvalue() output.close() return contents @@ -1252,20 +1253,16 @@ class PEFile(Printable): g_entry = group_header.entries[0] icon = Image.open(icon_path) - i_data = resize(icon, (g_entry.Width.value, g_entry.Height.value)) - s = StringIO() - s.write(i_data) - s.seek(0) + i_data = resize(icon, (g_entry.Width.value, g_entry.Height.value), format='ico') - icon = Image.open(s) - s2 = StringIO() - icon.save(s2, 'ico') - new_icon_size = s2.tell() - s2.seek(0) + new_icon_size = len(i_data) icon_file_size = g_entry.DataSize.value+group_header.size+g_entry.size+2 #9662 is the exact length of the icon in nw.exe - icon_data = bytearray(s2.read()) + bytearray(icon_file_size-new_icon_size) + extra_size = icon_file_size-new_icon_size + if extra_size < 0: + extra_size = 0 + icon_data = bytearray(i_data) + bytearray(extra_size) icon_header = IconHeader.parse_from_data(icon_data, absolute_offset=0)