From e3c92ebd67b4fd24bfc10018c6eb525ef6e3e921 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Sat, 14 Sep 2019 10:44:17 -0600 Subject: [PATCH] Fix image replacement --- bmpimageplugin.py | 326 ------------------------------------ ico_plugin.py | 410 ---------------------------------------------- pe.py | 5 +- 3 files changed, 3 insertions(+), 738 deletions(-) delete mode 100644 bmpimageplugin.py delete mode 100644 ico_plugin.py diff --git a/bmpimageplugin.py b/bmpimageplugin.py deleted file mode 100644 index 4733b10..0000000 --- a/bmpimageplugin.py +++ /dev/null @@ -1,326 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# BMP file handler -# -# Windows (and OS/2) native bitmap storage format. -# -# history: -# 1995-09-01 fl Created -# 1996-04-30 fl Added save -# 1997-08-27 fl Fixed save of 1-bit images -# 1998-03-06 fl Load P images as L where possible -# 1998-07-03 fl Load P images as 1 where possible -# 1998-12-29 fl Handle small palettes -# 2002-12-30 fl Fixed load of 1-bit palette images -# 2003-04-21 fl Fixed load of 1-bit monochrome images -# 2003-04-23 fl Added limited support for BI_BITFIELDS compression -# -# Copyright (c) 1997-2003 by Secret Labs AB -# Copyright (c) 1995-2003 by Fredrik Lundh -# -# See the README file for information on usage and redistribution. -# - - -__version__ = "0.7" - - -from PIL import Image, ImageFile, ImagePalette, _binary -from PIL.BmpImagePlugin import * #This is a hack to override the default bmp plugin for PIL -from io import BytesIO -import math - -i8 = _binary.i8 -i16 = _binary.i16le -i32 = _binary.i32le -o8 = _binary.o8 -o16 = _binary.o16le -o32 = _binary.o32le - -# -# -------------------------------------------------------------------- -# Read BMP file - -BIT2MODE = { - # bits => mode, rawmode - 1: ("P", "P;1"), - 4: ("P", "P;4"), - 8: ("P", "P"), - 16: ("RGB", "BGR;15"), - 24: ("RGB", "BGR"), - 32: ("RGBA", "BGRA") -} - - -def _accept(prefix): - return prefix[:2] == b"BM" - - -## -# Image plugin for the Windows BMP format. - -class BmpImageFile(ImageFile.ImageFile): - - format = "BMP" - format_description = "Windows Bitmap" - - def _bitmap(self, header=0, offset=0): - if header: - self.fp.seek(header) - - read = self.fp.read - - if not offset: - offset = self.fp.tell() - # CORE/INFO - s = read(4) - s = s + ImageFile._safe_read(self.fp, i32(s)-4) - - if len(s) == 12: - - # OS/2 1.0 CORE - bits = i16(s[10:]) - self.size = i16(s[4:]), i16(s[6:]) - compression = 0 - lutsize = 3 - colors = 0 - direction = -1 - - elif len(s) in [40, 64, 108, 124]: - - # WIN 3.1 or OS/2 2.0 INFO - bits = i16(s[14:]) - self.size = i32(s[4:]), i32(s[8:]) - compression = i32(s[16:]) - pxperm = (i32(s[24:]), i32(s[28:])) # Pixels per meter - lutsize = 4 - colors = i32(s[32:]) - direction = -1 - if i8(s[11]) == 0xff: - # upside-down storage - self.size = self.size[0], 2**32 - self.size[1] - direction = 0 - - self.info["dpi"] = tuple(map(lambda x: math.ceil(x / 39.3701), - pxperm)) - - else: - raise IOError("Unsupported BMP header type (%d)" % len(s)) - - if (self.size[0]*self.size[1]) > 2**31: - # Prevent DOS for > 2gb images - raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) - - if not colors: - colors = 1 << bits - - # MODE - try: - self.mode, rawmode = BIT2MODE[bits] - except KeyError: - raise IOError("Unsupported BMP pixel depth (%d)" % bits) - - if compression == 3: - # BI_BITFIELDS compression - mask = i32(s[0x36-14:]), i32(s[0x3a-14:]), i32(s[0x3E-14:]), i32(s[0x42-14:]) - if bits == 32 and mask == (0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000): - rawmode = "BGRA" - elif bits == 16 and mask == (0x00f800, 0x0007e0, 0x00001f): - rawmode = "BGR;16" - elif bits == 16 and mask == (0x007c00, 0x0003e0, 0x00001f): - rawmode = "BGR;15" - else: - print(bits, map(hex, mask)) - raise IOError("Unsupported BMP bitfields layout") - elif compression != 0: - raise IOError("Unsupported BMP compression (%d)" % compression) - - # LUT - if self.mode == "P": - palette = [] - greyscale = 1 - if colors == 2: - indices = (0, 255) - elif colors > 2**16 or colors <= 0: # We're reading a i32. - raise IOError("Unsupported BMP Palette size (%d)" % colors) - else: - indices = list(range(colors)) - for i in indices: - rgb = read(lutsize)[:3] - if rgb != o8(i)*3: - greyscale = 0 - palette.append(rgb) - if greyscale: - if colors == 2: - self.mode = rawmode = "1" - else: - self.mode = rawmode = "L" - else: - self.mode = "P" - self.palette = ImagePalette.raw( - "BGR", b"".join(palette) - ) - - if not offset: - offset = self.fp.tell() - - self.tile = [("raw", - (0, 0) + self.size, - offset, - (rawmode, ((self.size[0]*bits+31) >> 3) & (~3), - direction))] - - self.info["compression"] = compression - - def _open(self): - - # HEAD - s = self.fp.read(14) - if s[:2] != b"BM": - raise SyntaxError("Not a BMP file") - offset = i32(s[10:]) - - self._bitmap(offset=offset) - -class DibImageFile(BmpImageFile): - - format = "DIB" - format_description = "Windows Bitmap" - - def __init__(self, buf, *args, **kwargs): - BmpImageFile.__init__(self, buf, *args, **kwargs) - buf.seek(0) - self.buf = buf.read() - buf.seek(0) - - def _open(self): - self._bitmap() - - def get_file_data(self): - return self.buf - - def to_bitmapimage(self, header): - self.fp.seek(header['offset']) - d = bytearray(self.fp.read(header['size'])) - print(self.info) - dpi = (96,96) - ppm = tuple(map(lambda x: int(x * 39.3701), dpi)) - - d[24:28] = o32(ppm[0]) - d[28:32] = o32(ppm[1]) - - dib_size = i32(str(d[:4])) - offset = 14 + dib_size - data = BytesIO() - data.write(b'BM'+ - o32(14+len(d)) + - o32(0) + - o32(offset)) - - data.write(d) - data.seek(0) - new_image = Image.open(data) - return new_image - -# -# -------------------------------------------------------------------- -# Write BMP file - -SAVE = { - "1": ("1", 1, 2), - "L": ("L", 8, 256), - "P": ("P", 8, 256), - "RGB": ("BGR", 24, 0), - "RGBA": ("BGRA", 32, 0), -} - - -def _save(im, fp, filename, check=0): - try: - rawmode, bits, colors = SAVE[im.mode] - except KeyError: - raise IOError("cannot write mode %s as BMP" % im.mode) - - if check: - return check - - info = im.encoderinfo - - dpi = info.get("dpi", (96, 96)) - - # 1 meter == 39.3701 inches - ppm = tuple(map(lambda x: int(x * 39.3701), dpi)) - - stride = ((im.size[0]*bits+7)//8+3) & (~3) - 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] - - red_mask = 0x00ff0000 - green_mask = 0x0000ff00 - blue_mask = 0x000000ff - alpha_mask = 0xff000000 - # bitmap header - fp.write(b"BM" + # file type (magic) - o32(offset+image+16) + # file size - o32(0) + # reserved - o32(offset+16)) # image data offset - - width,height = im.size - - # bitmap info header - fp.write(o32(header+16) + # info header size - o32(width) + # width - o32(height) + # height - o16(1) + # planes - o16(bits) + # depth - o32(3) + # compression (0=uncompressed) - 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) - ) - # 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 - b'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(16)) - - if im.mode == "1": - for i in (0, 255): - fp.write(o8(i) * 4) - elif im.mode == "L": - for i in range(256): - fp.write(o8(i) * 4) - elif im.mode == "P": - fp.write(im.im.getpalette("RGB", "BGRX")) - - #fp.write( - ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, - (rawmode, stride, -1))]) - -# -# -------------------------------------------------------------------- -# Registry - -Image.register_open(BmpImageFile.format, BmpImageFile, _accept) -Image.register_save(BmpImageFile.format, _save) - -Image.register_extension(BmpImageFile.format, ".bmp") diff --git a/ico_plugin.py b/ico_plugin.py deleted file mode 100644 index b7bcb27..0000000 --- a/ico_plugin.py +++ /dev/null @@ -1,410 +0,0 @@ -# -# The Python Imaging Library. -# $Id$ -# -# Windows Icon support for PIL -# -# History: -# 96-05-27 fl Created -# -# Copyright (c) Secret Labs AB 1997. -# Copyright (c) Fredrik Lundh 1996. -# -# See the README file for information on usage and redistribution. -# - -# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis -# . -# https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin -# -# Icon format references: -# * http://en.wikipedia.org/wiki/ICO_(file_format) -# * http://msdn.microsoft.com/en-us/library/ms997538.aspx - - -__version__ = "0.1" - -from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary -from .bmpimageplugin import * -from io import BytesIO, StringIO -from math import log, ceil - -i8 = _binary.i8 -i16 = _binary.i16le -i32 = _binary.i32le -o8 = _binary.o8 -o16 = _binary.o16le -o32 = _binary.o32le - -_MAGIC = b"\0\0\1\0" - - -def _accept(prefix): - return prefix[:4] == _MAGIC - - -class IcoFile: - format = 'ICO' - def __init__(self, buf): - """ - Parse image from file-like object containing ico file data - """ - - # check magic - s = buf.read(6) - if not _accept(s): - raise SyntaxError("not an ICO file") - - self.buf = buf - self.entries = [] - - # Number of items in file - self.nb_items = i16(s[4:]) - - # Get headers for each item - for i in range(self.nb_items): - s = buf.read(16) - - icon_header = { - 'width': i8(s[0]), - 'height': i8(s[1]), - 'nb_color': i8(s[2]), # No. of colors in image (0 if >=8bpp) - 'reserved': i8(s[3]), - 'planes': i16(s[4:]), - 'bpp': i16(s[6:]), - 'size': i32(s[8:]), - 'offset': i32(s[12:]) - } - - # See Wikipedia - for j in ('width', 'height'): - if not icon_header[j]: - icon_header[j] = 256 - - # See Wikipedia notes about color depth. - # We need this just to differ images with equal sizes - icon_header['color_depth'] = (icon_header['bpp'] or - (icon_header['nb_color'] != 0 and - ceil(log(icon_header['nb_color'], - 2))) or 256) - - icon_header['dim'] = (icon_header['width'], icon_header['height']) - icon_header['square'] = (icon_header['width'] * - icon_header['height']) - - self.entries.append(icon_header) - - self.entries = sorted(self.entries, key=lambda x: x['color_depth']) - # ICO images are usually squares - # self.entries = sorted(self.entries, key=lambda x: x['width']) - self.entries = sorted(self.entries, key=lambda x: x['square']) - self.entries.reverse() - - def sizes(self): - """ - Get a list of all available icon sizes and color depths. - """ - return set((h['width'], h['height']) for h in self.entries) - - def getimage(self, size, bpp=False): - """ - Get an image from the icon - """ - for (i, h) in enumerate(self.entries): - if size == h['dim'] and (bpp is False or bpp == h['color_depth']): - return self.frame(i) - return self.frame(0) - - def get_images(self): - images = [] - for i, header in enumerate(self.entries): - f = self.frame(i) - if f.format: - images.append(self.frame(i)) - return images - - def frame(self, idx): - """ - Get an image from frame idx - """ - - header = self.entries[idx] - - self.buf.seek(header['offset']) - data = self.buf.read(8) - self.buf.seek(header['offset']) - - if data[:8] == PngImagePlugin._MAGIC: - # png frame - s = BytesIO() - s.write(self.buf.read(header['size'])) - s.seek(0) - self.buf.seek(header['offset']) - im = Image.open(s) - else: - # XOR + AND mask bmp frame - s = BytesIO() - s.write(self.buf.read(header['size'])) - s.seek(0) - self.buf.seek(header['offset']) - im = DibImageFile(s) - #im = im.to_bitmapimage(header) - #print im.tile - # change tile dimension to only encompass XOR image - im.size = (im.size[0], int(im.size[1] / 2)) - #print im.size - d, e, o, a = im.tile[0] - im.tile[0] = d, (0, 0) + im.size, o, a - - # figure out where AND mask image starts - mode = a[0] - bpp = 8 - for k in BmpImagePlugin.BIT2MODE.keys(): - if mode == BmpImagePlugin.BIT2MODE[k][1]: - bpp = k - break - - if 32 == bpp: - # 32-bit color depth icon image allows semitransparent areas - # PIL's DIB format ignores transparency bits, recover them. - # The DIB is packed in BGRX byte order where X is the alpha - # channel. - - # Back up to start of bmp data - self.buf.seek(o) - # extract every 4th byte (eg. 3,7,11,15,...) - alpha_bytes = self.buf.read(im.size[0] * im.size[1] * 4)[3::4] - - # convert to an 8bpp grayscale image - mask = Image.frombuffer( - 'L', # 8bpp - im.size, # (w, h) - alpha_bytes, # source chars - 'raw', # raw decoder - ('L', 0, -1) # 8bpp inverted, unpadded, reversed - ) - else: - # get AND image from end of bitmap - w = im.size[0] - if (w % 32) > 0: - # bitmap row data is aligned to word boundaries - w += 32 - (im.size[0] % 32) - - # the total mask data is - # padded row size * height / bits per char - - and_mask_offset = o + int(im.size[0] * im.size[1] * - (bpp / 8.0)) - total_bytes = int((w * im.size[1]) / 8) - - self.buf.seek(and_mask_offset) - maskData = self.buf.read(total_bytes) - - # convert raw data to image - mask = Image.frombuffer( - '1', # 1 bpp - im.size, # (w, h) - maskData, # source chars - 'raw', # raw decoder - ('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed - ) - - # now we have two images, im is XOR image and mask is AND image - - if im.mode != 'RGBA': - im = im.convert('RGBA') - im.putalpha(mask) - - return im - - -## -# Image plugin for Windows Icon files. - -class IcoImageFile(ImageFile.ImageFile): - """ - PIL read-only image support for Microsoft Windows .ico files. - - By default the largest resolution image in the file will be loaded. This - can be changed by altering the 'size' attribute before calling 'load'. - - The info dictionary has a key 'sizes' that is a list of the sizes available - in the icon file. - - Handles classic, XP and Vista icon formats. - - This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis - . - https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin - """ - format = "ICO" - format_description = "Windows Icon" - - def _open(self): - self.ico = IcoFile(self.fp) - self.info['sizes'] = self.ico.sizes() - self.size = self.ico.entries[0]['dim'] - self.load() - - def load(self): - im = self.ico.getimage(self.size) - # if tile is PNG, it won't really be loaded yet - im.load() - self.im = im.im - self.mode = im.mode - self.size = im.size - - def get_images(self): - return self.ico.get_images() - - def load_seek(self): - # Flage the ImageFile.Parser so that it - # just does all the decode at the end. - pass - -SAVE = { - "1": ("1", 1, 2), - "L": ("L", 8, 256), - "P": ("P", 8, 256), - "RGB": ("BGR", 24, 0), - "RGBA": ("BGRA", 32, 0), -} - -def get_data(im): - s = BytesIO() - if im.format != 'DIB': - im.save(s, im.format) - else: - s.write(im.buf) - s.seek(0) - - if im.format == 'BMP': - bmp_f = s - bmp_f.seek(10) - offset = i32(bmp_f.read(4)) - dib_size = i32(bmp_f.read(4)) - dib = bytearray(o32(dib_size)+bytearray(bmp_f.read(36))) - dib[:4] = o32(40) - dib[8:12] = o32(i32(dib[8:12])*2) - dib[16:20] = o32(0) - dib = dib[:40] - bmp_f.seek(offset) - data = bytearray(bmp_f.read()) - data = dib+data - else: - data = bytearray(s.read()) - - return data - - - -def _save(image, fp, filename, check=0): - data = None - images = [] - if image.format != 'ICO': - bmp_f = BytesIO() - image.save(bmp_f, format='BMP') - bmp_f.seek(0) - im2 = Image.open(bmp_f) - if im2.format: - images.append(im2) - else: - images = image.get_images() - - number_of_images = len(images) - - header_size = 6+16*number_of_images - - # ico header - fp.write(o16(0) + # reserved - o16(1) + # file type - o16(number_of_images)) # number of images - - current_offset = header_size - for i, im in enumerate(images): - - data = get_data(im) - - try: - 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(range(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 range(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 range(scanline_pad): - matte_data+=o8(0) - - fp.write(o8(im.size[0]) + # width - o8(im.size[1]) + # height - o8(0) + # color palette - o8(0) + # reserved - o16(1) + # planes - o16(bits) + # depth - o32(len(data+matte_data)) + # size of image in bytes - o32(current_offset) # offset - - ) - 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(range(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 range(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 range(scanline_pad): - fp.write(o8(0)) - - - fp.flush() - - -# -# -------------------------------------------------------------------- - -Image.register_open(IcoFile.format, IcoImageFile, _accept) -Image.register_save(IcoFile.format, _save) - -Image.register_extension(IcoFile.format, '.ico') diff --git a/pe.py b/pe.py index 6172ee8..0a6cfcf 100644 --- a/pe.py +++ b/pe.py @@ -2,7 +2,7 @@ import os import struct from io import BytesIO -from .ico_plugin import * +from PIL import Image def resize(image, size, format=None): output = BytesIO() @@ -28,7 +28,7 @@ def resize(image, size, format=None): back.paste(image, tuple(offset)) format = format or image.format - back.save(output, format) + back.save(output, format, sizes=[size]) contents = output.getvalue() output.close() return contents @@ -1279,6 +1279,7 @@ class PEFile(Printable): #9662 is the exact length of the icon in nw.exe extra_size = icon_file_size-new_icon_size + if extra_size < 0: extra_size = 0 icon_data = bytearray(i_data) + bytearray(extra_size)