Compare commits
No commits in common. "master" and "python3" have entirely different histories.
3 changed files with 738 additions and 3 deletions
326
bmpimageplugin.py
Normal file
326
bmpimageplugin.py
Normal file
|
|
@ -0,0 +1,326 @@
|
||||||
|
#
|
||||||
|
# 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")
|
||||||
410
ico_plugin.py
Normal file
410
ico_plugin.py
Normal file
|
|
@ -0,0 +1,410 @@
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
# <casadebender@gmail.com>.
|
||||||
|
# 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
|
||||||
|
<casadebender@gmail.com>.
|
||||||
|
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')
|
||||||
5
pe.py
5
pe.py
|
|
@ -2,7 +2,7 @@ import os
|
||||||
import struct
|
import struct
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from .ico_plugin import *
|
||||||
|
|
||||||
def resize(image, size, format=None):
|
def resize(image, size, format=None):
|
||||||
output = BytesIO()
|
output = BytesIO()
|
||||||
|
|
@ -28,7 +28,7 @@ def resize(image, size, format=None):
|
||||||
|
|
||||||
back.paste(image, tuple(offset))
|
back.paste(image, tuple(offset))
|
||||||
format = format or image.format
|
format = format or image.format
|
||||||
back.save(output, format, sizes=[size])
|
back.save(output, format)
|
||||||
contents = output.getvalue()
|
contents = output.getvalue()
|
||||||
output.close()
|
output.close()
|
||||||
return contents
|
return contents
|
||||||
|
|
@ -1279,7 +1279,6 @@ class PEFile(Printable):
|
||||||
|
|
||||||
#9662 is the exact length of the icon in nw.exe
|
#9662 is the exact length of the icon in nw.exe
|
||||||
extra_size = icon_file_size-new_icon_size
|
extra_size = icon_file_size-new_icon_size
|
||||||
|
|
||||||
if extra_size < 0:
|
if extra_size < 0:
|
||||||
extra_size = 0
|
extra_size = 0
|
||||||
icon_data = bytearray(i_data) + bytearray(extra_size)
|
icon_data = bytearray(i_data) + bytearray(extra_size)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue