Electrify/icns_info.py
2016-02-09 08:12:59 -07:00

1433 lines
48 KiB
Python

import struct
import image_utils
import png
from PIL import Image
import os
from io import BytesIO
#---------------------CONSTANTS-----------------------------------------------#
ICNS_TABLE_OF_CONTENTS = 0x544F4320 # "TOC "
ICNS_ICON_VERSION = 0x69636E56 # "icnV"
ICNS_1024x1024_32BIT_ARGB_DATA = 0x69633130 # "ic10"
ICNS_512x512_32BIT_ARGB_DATA = 0x69633039 # "ic09"
ICNS_256x256_32BIT_ARGB_DATA = 0x69633038 # "ic08"
ICNS_128x128_32BIT_DATA = 0x69743332 # "it32"
ICNS_128x128_8BIT_MASK = 0x74386D6B # "t8mk"
ICNS_48x48_1BIT_DATA = 0x69636823 # "ich#"
ICNS_48x48_4BIT_DATA = 0x69636834 # "ich4"
ICNS_48x48_8BIT_DATA = 0x69636838 # "ich8"
ICNS_48x48_32BIT_DATA = 0x69683332 # "ih32"
ICNS_48x48_1BIT_MASK = 0x69636823 # "ich#"
ICNS_48x48_8BIT_MASK = 0x68386D6B # "h8mk"
ICNS_32x32_1BIT_DATA = 0x49434E23 # "ICN#"
ICNS_32x32_4BIT_DATA = 0x69636C34 # "icl4"
ICNS_32x32_8BIT_DATA = 0x69636C38 # "icl8"
ICNS_32x32_32BIT_DATA = 0x696C3332 # "il32"
ICNS_32x32_1BIT_MASK = 0x49434E23 # "ICN#"
ICNS_32x32_8BIT_MASK = 0x6C386D6B # "l8mk"
ICNS_16x16_1BIT_DATA = 0x69637323 # "ics#"
ICNS_16x16_4BIT_DATA = 0x69637334 # "ics4"
ICNS_16x16_8BIT_DATA = 0x69637338 # "ics8"
ICNS_16x16_32BIT_DATA = 0x69733332 # "is32"
ICNS_16x16_1BIT_MASK = 0x69637323 # "ics#"
ICNS_16x16_8BIT_MASK = 0x73386D6B # "s8mk"
ICNS_16x12_1BIT_DATA = 0x69636D23 # "icm#"
ICNS_16x12_4BIT_DATA = 0x69636D34 # "icm4"
ICNS_16x12_1BIT_MASK = 0x69636D23 # "icm#"
ICNS_16x12_8BIT_DATA = 0x69636D38 # "icm8"
ICNS_32x32_1BIT_ICON = 0x49434F4E # "ICON"
ICNS_TILE_VARIANT = 0x74696C65 # "tile"
ICNS_ROLLOVER_VARIANT = 0x6F766572 # "over"
ICNS_DROP_VARIANT = 0x64726F70 # "drop"
ICNS_OPEN_VARIANT = 0x6F70656E # "open"
ICNS_OPEN_DROP_VARIANT = 0x6F647270 # "odrp"
ICNS_NULL_DATA = 0x00000000
ICNS_NULL_MASK = 0x00000000
# icns file / resource type constants
ICNS_FAMILY_TYPE = 0x69636E73 # "icns"
ICNS_MACBINARY_TYPE = 0x6D42494E # "mBIN"
ICNS_NULL_TYPE = 0x00000000
ICNS_BYTE_BITS = 8
# icns error return values
ICNS_STATUS_OK = 0
ICNS_STATUS_NULL_PARAM = -1
ICNS_STATUS_NO_MEMORY = -2
ICNS_STATUS_INVALID_DATA = -3
ICNS_STATUS_IO_READ_ERR = 1
ICNS_STATUS_IO_WRITE_ERR = 2
ICNS_STATUS_DATA_NOT_FOUND = 3
ICNS_STATUS_UNSUPPORTED = 4
#---------------------------SYMBOL DICTS-------------------------------------#
struct_symbols = {1: 'B', # byte
2: 'H', # word
4: 'I', # unsigned int/double word
8: 'Q' # quad word
}
#used to easily get the icon type from dimensions
type_dict = {'mask': {128: {8: ICNS_128x128_8BIT_MASK},
48: {1: ICNS_48x48_1BIT_MASK,
8: ICNS_48x48_8BIT_MASK},
32: {1: ICNS_32x32_1BIT_MASK,
8: ICNS_32x32_8BIT_MASK},
16: {1: ICNS_16x16_1BIT_MASK,
8: ICNS_16x16_8BIT_MASK},
},
'data': {1024: {32: ICNS_1024x1024_32BIT_ARGB_DATA},
512: {32: ICNS_512x512_32BIT_ARGB_DATA},
256: {32: ICNS_256x256_32BIT_ARGB_DATA},
128: {32: ICNS_128x128_32BIT_DATA},
48: {1: ICNS_48x48_1BIT_DATA,
4: ICNS_48x48_4BIT_DATA,
8: ICNS_48x48_8BIT_DATA,
32: ICNS_48x48_32BIT_DATA},
32: {1: ICNS_32x32_1BIT_DATA,
4: ICNS_32x32_4BIT_DATA,
8: ICNS_32x32_8BIT_DATA,
32: ICNS_32x32_32BIT_DATA},
16: {1: ICNS_16x16_1BIT_DATA,
4: ICNS_16x16_4BIT_DATA,
8: ICNS_16x16_8BIT_DATA,
32: ICNS_16x16_32BIT_DATA},
}}
icns_colormap_4 = [
[0xFF, 0xFF, 0xFF],
[0xFC, 0xF3, 0x05],
[0xFF, 0x64, 0x02],
[0xDD, 0x08, 0x06],
[0xF2, 0x08, 0x84],
[0x46, 0x00, 0xA5],
[0x00, 0x00, 0xD4],
[0x02, 0xAB, 0xEA],
[0x1F, 0xB7, 0x14],
[0x00, 0x64, 0x11],
[0x56, 0x2C, 0x05],
[0x90, 0x71, 0x3A],
[0xC0, 0xC0, 0xC0],
[0x80, 0x80, 0x80],
[0x40, 0x40, 0x40],
[0x00, 0x00, 0x00]
]
icns_colormap_8 =[
[0xFF, 0xFF, 0xFF],
[0xFF, 0xFF, 0xCC],
[0xFF, 0xFF, 0x99],
[0xFF, 0xFF, 0x66],
[0xFF, 0xFF, 0x33],
[0xFF, 0xFF, 0x00],
[0xFF, 0xCC, 0xFF],
[0xFF, 0xCC, 0xCC],
[0xFF, 0xCC, 0x99],
[0xFF, 0xCC, 0x66],
[0xFF, 0xCC, 0x33],
[0xFF, 0xCC, 0x00],
[0xFF, 0x99, 0xFF],
[0xFF, 0x99, 0xCC],
[0xFF, 0x99, 0x99],
[0xFF, 0x99, 0x66],
[0xFF, 0x99, 0x33],
[0xFF, 0x99, 0x00],
[0xFF, 0x66, 0xFF],
[0xFF, 0x66, 0xCC],
[0xFF, 0x66, 0x99],
[0xFF, 0x66, 0x66],
[0xFF, 0x66, 0x33],
[0xFF, 0x66, 0x00],
[0xFF, 0x33, 0xFF],
[0xFF, 0x33, 0xCC],
[0xFF, 0x33, 0x99],
[0xFF, 0x33, 0x66],
[0xFF, 0x33, 0x33],
[0xFF, 0x33, 0x00],
[0xFF, 0x00, 0xFF],
[0xFF, 0x00, 0xCC],
[0xFF, 0x00, 0x99],
[0xFF, 0x00, 0x66],
[0xFF, 0x00, 0x33],
[0xFF, 0x00, 0x00],
[0xCC, 0xFF, 0xFF],
[0xCC, 0xFF, 0xCC],
[0xCC, 0xFF, 0x99],
[0xCC, 0xFF, 0x66],
[0xCC, 0xFF, 0x33],
[0xCC, 0xFF, 0x00],
[0xCC, 0xCC, 0xFF],
[0xCC, 0xCC, 0xCC],
[0xCC, 0xCC, 0x99],
[0xCC, 0xCC, 0x66],
[0xCC, 0xCC, 0x33],
[0xCC, 0xCC, 0x00],
[0xCC, 0x99, 0xFF],
[0xCC, 0x99, 0xCC],
[0xCC, 0x99, 0x99],
[0xCC, 0x99, 0x66],
[0xCC, 0x99, 0x33],
[0xCC, 0x99, 0x00],
[0xCC, 0x66, 0xFF],
[0xCC, 0x66, 0xCC],
[0xCC, 0x66, 0x99],
[0xCC, 0x66, 0x66],
[0xCC, 0x66, 0x33],
[0xCC, 0x66, 0x00],
[0xCC, 0x33, 0xFF],
[0xCC, 0x33, 0xCC],
[0xCC, 0x33, 0x99],
[0xCC, 0x33, 0x66],
[0xCC, 0x33, 0x33],
[0xCC, 0x33, 0x00],
[0xCC, 0x00, 0xFF],
[0xCC, 0x00, 0xCC],
[0xCC, 0x00, 0x99],
[0xCC, 0x00, 0x66],
[0xCC, 0x00, 0x33],
[0xCC, 0x00, 0x00],
[0x99, 0xFF, 0xFF],
[0x99, 0xFF, 0xCC],
[0x99, 0xFF, 0x99],
[0x99, 0xFF, 0x66],
[0x99, 0xFF, 0x33],
[0x99, 0xFF, 0x00],
[0x99, 0xCC, 0xFF],
[0x99, 0xCC, 0xCC],
[0x99, 0xCC, 0x99],
[0x99, 0xCC, 0x66],
[0x99, 0xCC, 0x33],
[0x99, 0xCC, 0x00],
[0x99, 0x99, 0xFF],
[0x99, 0x99, 0xCC],
[0x99, 0x99, 0x99],
[0x99, 0x99, 0x66],
[0x99, 0x99, 0x33],
[0x99, 0x99, 0x00],
[0x99, 0x66, 0xFF],
[0x99, 0x66, 0xCC],
[0x99, 0x66, 0x99],
[0x99, 0x66, 0x66],
[0x99, 0x66, 0x33],
[0x99, 0x66, 0x00],
[0x99, 0x33, 0xFF],
[0x99, 0x33, 0xCC],
[0x99, 0x33, 0x99],
[0x99, 0x33, 0x66],
[0x99, 0x33, 0x33],
[0x99, 0x33, 0x00],
[0x99, 0x00, 0xFF],
[0x99, 0x00, 0xCC],
[0x99, 0x00, 0x99],
[0x99, 0x00, 0x66],
[0x99, 0x00, 0x33],
[0x99, 0x00, 0x00],
[0x66, 0xFF, 0xFF],
[0x66, 0xFF, 0xCC],
[0x66, 0xFF, 0x99],
[0x66, 0xFF, 0x66],
[0x66, 0xFF, 0x33],
[0x66, 0xFF, 0x00],
[0x66, 0xCC, 0xFF],
[0x66, 0xCC, 0xCC],
[0x66, 0xCC, 0x99],
[0x66, 0xCC, 0x66],
[0x66, 0xCC, 0x33],
[0x66, 0xCC, 0x00],
[0x66, 0x99, 0xFF],
[0x66, 0x99, 0xCC],
[0x66, 0x99, 0x99],
[0x66, 0x99, 0x66],
[0x66, 0x99, 0x33],
[0x66, 0x99, 0x00],
[0x66, 0x66, 0xFF],
[0x66, 0x66, 0xCC],
[0x66, 0x66, 0x99],
[0x66, 0x66, 0x66],
[0x66, 0x66, 0x33],
[0x66, 0x66, 0x00],
[0x66, 0x33, 0xFF],
[0x66, 0x33, 0xCC],
[0x66, 0x33, 0x99],
[0x66, 0x33, 0x66],
[0x66, 0x33, 0x33],
[0x66, 0x33, 0x00],
[0x66, 0x00, 0xFF],
[0x66, 0x00, 0xCC],
[0x66, 0x00, 0x99],
[0x66, 0x00, 0x66],
[0x66, 0x00, 0x33],
[0x66, 0x00, 0x00],
[0x33, 0xFF, 0xFF],
[0x33, 0xFF, 0xCC],
[0x33, 0xFF, 0x99],
[0x33, 0xFF, 0x66],
[0x33, 0xFF, 0x33],
[0x33, 0xFF, 0x00],
[0x33, 0xCC, 0xFF],
[0x33, 0xCC, 0xCC],
[0x33, 0xCC, 0x99],
[0x33, 0xCC, 0x66],
[0x33, 0xCC, 0x33],
[0x33, 0xCC, 0x00],
[0x33, 0x99, 0xFF],
[0x33, 0x99, 0xCC],
[0x33, 0x99, 0x99],
[0x33, 0x99, 0x66],
[0x33, 0x99, 0x33],
[0x33, 0x99, 0x00],
[0x33, 0x66, 0xFF],
[0x33, 0x66, 0xCC],
[0x33, 0x66, 0x99],
[0x33, 0x66, 0x66],
[0x33, 0x66, 0x33],
[0x33, 0x66, 0x00],
[0x33, 0x33, 0xFF],
[0x33, 0x33, 0xCC],
[0x33, 0x33, 0x99],
[0x33, 0x33, 0x66],
[0x33, 0x33, 0x33],
[0x33, 0x33, 0x00],
[0x33, 0x00, 0xFF],
[0x33, 0x00, 0xCC],
[0x33, 0x00, 0x99],
[0x33, 0x00, 0x66],
[0x33, 0x00, 0x33],
[0x33, 0x00, 0x00],
[0x00, 0xFF, 0xFF],
[0x00, 0xFF, 0xCC],
[0x00, 0xFF, 0x99],
[0x00, 0xFF, 0x66],
[0x00, 0xFF, 0x33],
[0x00, 0xFF, 0x00],
[0x00, 0xCC, 0xFF],
[0x00, 0xCC, 0xCC],
[0x00, 0xCC, 0x99],
[0x00, 0xCC, 0x66],
[0x00, 0xCC, 0x33],
[0x00, 0xCC, 0x00],
[0x00, 0x99, 0xFF],
[0x00, 0x99, 0xCC],
[0x00, 0x99, 0x99],
[0x00, 0x99, 0x66],
[0x00, 0x99, 0x33],
[0x00, 0x99, 0x00],
[0x00, 0x66, 0xFF],
[0x00, 0x66, 0xCC],
[0x00, 0x66, 0x99],
[0x00, 0x66, 0x66],
[0x00, 0x66, 0x33],
[0x00, 0x66, 0x00],
[0x00, 0x33, 0xFF],
[0x00, 0x33, 0xCC],
[0x00, 0x33, 0x99],
[0x00, 0x33, 0x66],
[0x00, 0x33, 0x33],
[0x00, 0x33, 0x00],
[0x00, 0x00, 0xFF],
[0x00, 0x00, 0xCC],
[0x00, 0x00, 0x99],
[0x00, 0x00, 0x66],
[0x00, 0x00, 0x33],
[0xEE, 0x00, 0x00],
[0xDD, 0x00, 0x00],
[0xBB, 0x00, 0x00],
[0xAA, 0x00, 0x00],
[0x88, 0x00, 0x00],
[0x77, 0x00, 0x00],
[0x55, 0x00, 0x00],
[0x44, 0x00, 0x00],
[0x22, 0x00, 0x00],
[0x11, 0x00, 0x00],
[0x00, 0xEE, 0x00],
[0x00, 0xDD, 0x00],
[0x00, 0xBB, 0x00],
[0x00, 0xAA, 0x00],
[0x00, 0x88, 0x00],
[0x00, 0x77, 0x00],
[0x00, 0x55, 0x00],
[0x00, 0x44, 0x00],
[0x00, 0x22, 0x00],
[0x00, 0x11, 0x00],
[0x00, 0x00, 0xEE],
[0x00, 0x00, 0xDD],
[0x00, 0x00, 0xBB],
[0x00, 0x00, 0xAA],
[0x00, 0x00, 0x88],
[0x00, 0x00, 0x77],
[0x00, 0x00, 0x55],
[0x00, 0x00, 0x44],
[0x00, 0x00, 0x22],
[0x00, 0x00, 0x11],
[0xEE, 0xEE, 0xEE],
[0xDD, 0xDD, 0xDD],
[0xBB, 0xBB, 0xBB],
[0xAA, 0xAA, 0xAA],
[0x88, 0x88, 0x88],
[0x77, 0x77, 0x77],
[0x55, 0x55, 0x55],
[0x44, 0x44, 0x44],
[0x22, 0x22, 0x22],
[0x11, 0x11, 0x11],
[0x00, 0x00, 0x00]
]
#---------------------------UTILITY FUNCTIONS---------------------------------#
def encode_rle24(data):
dataRun = bytearray(130)
dataInChanSize = len(data)/4
dataTempCount = 0
dataTemp = bytearray(len(data) + len(data)/4)
if len(data) >= 65536:
dataTempCount = 4
for colorOffset in range(3):
runCount = 0
runLength = 1
runType = 0
dataRun[0] = data[colorOffset]
for dataInCount in range(1, dataInChanSize):
dataByte = data[colorOffset+(dataInCount*4)]
if runLength < 2:
dataRun[runLength] = dataByte
runLength += 1
elif runLength == 2:
if dataByte == dataRun[runLength-1] and dataByte == dataRun[runLength-2]:
runType = 1
else:
runType = 0
dataRun[runLength] = dataByte
runLength += 1
else:
if runType == 0 and runLength < 128:
if dataByte == dataRun[runLength-1] and dataByte == dataRun[runLength -2]:
dataTemp[dataTempCount] = runLength - 3
dataTempCount += 1
dataTemp[dataTempCount:dataTempCount+runLength-2] = dataRun[:runLength-2]
dataTempCount += runLength - 2
runCount += 1
dataRun[0] = dataRun[runLength - 2]
dataRun[1] = dataRun[runLength - 1]
dataRun[2] = dataByte
runLength = 3
runType = 1
else:
dataRun[runLength] = dataByte
runLength += 1
elif runType == 1 and runLength < 130:
if dataByte == dataRun[runLength - 1] and dataByte == dataRun[runLength - 2]:
dataRun[runLength] = dataByte
runLength += 1
else:
dataTemp[dataTempCount] = runLength + 125
dataTempCount += 1
dataTemp[dataTempCount] = dataRun[0]
dataTempCount += 1
runCount += 1
dataRun[0] = dataByte
runLength = 1
runType = 0
else:
if runType == 0:
dataTemp[dataTempCount] = runLength - 1
dataTempCount += 1
dataTemp[dataTempCount:dataTempCount+runLength] = dataRun[:runLength]
dataTempCount = dataTempCount + runLength
elif runType == 1:
dataTemp[dataTempCount] = runLength + 125
dataTempCount += 1
dataTemp[dataTempCount] = dataRun[0]
dataTempCount += 1
runCount += 1
dataRun[0] = dataByte
runLength = 1
runType = 0
if runLength > 0:
if runType == 0:
dataTemp[dataTempCount] = runLength - 1
dataTempCount += 1
dataTemp[dataTempCount:dataTempCount+runLength] = dataRun[:runLength]
dataTempCount = dataTempCount + runLength
elif runType == 1:
dataTemp[dataTempCount] = runLength + 125
dataTempCount += 1
dataTemp[dataTempCount] = dataRun[0]
dataTempCount += 1
runCount += 1
return dataTemp[:dataTempCount]
def decode_rle24(data, pixel_count):
color_value = 0
run_length = 0
data_offset = 0
pixel_offset = 0
i = 0
dest_icon_data = bytearray(pixel_count*4)
if from_bytes(data[:4]) == 0:
data_offset = 4
color_offset = 0
while color_offset < 3:
pixel_offset = 0
while (pixel_offset < pixel_count) and (data_offset < len(data)):
if ((data[data_offset] & 0x80) == 0):
run_length = (0xFF & data[data_offset]) + 1
data_offset += 1
i = 0
while (i < run_length) and (pixel_offset < pixel_count) and (data_offset < len(data)):
dest_icon_data[(pixel_offset*4)+color_offset] = data[data_offset]
pixel_offset += 1
data_offset += 1
i += 1
else:
run_length = (0xFF & data[data_offset]) - 125
data_offset += 1
color_value = data[data_offset]
data_offset += 1
i = 0
while (i < run_length) and (pixel_offset < pixel_count):
dest_icon_data[(pixel_offset*4)+color_offset] = color_value
pixel_offset += 1
i += 1
color_offset += 1
return dest_icon_data
def get_mask_type_for_icon_type(icon_type):
if icon_type == ICNS_TABLE_OF_CONTENTS or\
icon_type == ICNS_ICON_VERSION or\
icon_type == ICNS_1024x1024_32BIT_ARGB_DATA or\
icon_type == ICNS_512x512_32BIT_ARGB_DATA or\
icon_type == ICNS_256x256_32BIT_ARGB_DATA:
return ICNS_NULL_MASK
if icon_type == ICNS_128x128_32BIT_DATA:
return ICNS_128x128_8BIT_MASK
if icon_type == ICNS_48x48_32BIT_DATA:
return ICNS_48x48_8BIT_MASK
if icon_type == ICNS_32x32_32BIT_DATA:
return ICNS_32x32_8BIT_MASK
if icon_type == ICNS_16x16_32BIT_DATA:
return ICNS_16x16_8BIT_MASK
# 8-bit image types - 1-bit mask types
if icon_type == ICNS_48x48_8BIT_DATA:
return ICNS_48x48_1BIT_MASK
if icon_type == ICNS_32x32_8BIT_DATA:
return ICNS_32x32_1BIT_MASK
if icon_type == ICNS_16x16_8BIT_DATA:
return ICNS_16x16_1BIT_MASK
if icon_type == ICNS_16x12_8BIT_DATA:
return ICNS_16x12_1BIT_MASK
# 4 bit image types - 1-bit mask types
if icon_type == ICNS_48x48_4BIT_DATA:
return ICNS_48x48_1BIT_MASK
if icon_type == ICNS_32x32_4BIT_DATA:
return ICNS_32x32_1BIT_MASK
if icon_type == ICNS_16x16_4BIT_DATA:
return ICNS_16x16_1BIT_MASK
if icon_type == ICNS_16x12_4BIT_DATA:
return ICNS_16x12_1BIT_MASK
# 1 bit image types - 1-bit mask types
if icon_type == ICNS_48x48_1BIT_DATA:
return ICNS_48x48_1BIT_MASK
if icon_type == ICNS_32x32_1BIT_DATA:
return ICNS_32x32_1BIT_MASK
if icon_type == ICNS_16x16_1BIT_DATA:
return ICNS_16x16_1BIT_MASK
if icon_type == ICNS_16x12_1BIT_DATA:
return ICNS_16x12_1BIT_MASK
return ICNS_NULL_MASK
def to_bytes(n, length, endianess='big'):
h = '%x' % n
s = bytes.fromhex(('0'*(len(h) % 2) + h).zfill(length*2))
return s if endianess == 'big' else s[::-1]
def from_bytes(byte_str):
bits = (len(byte_str)-1)*8
result = 0
for i in range(len(byte_str)):
result = (byte_str[i] << bits) + result
bits -= 8
return result
def type_to_str(type):
s = bytearray()
s.append(type >> 24 & 0xff)
s.append(type >> 16 & 0xff)
s.append(type >> 8 & 0xff)
s.append(type & 0xff)
s.append(0)
return bytes(s)
class Printable(object):
def _attrs(self):
a = []
for attr in dir(self):
if not attr.startswith('_') and not callable(getattr(self, attr)):
a.append(attr)
return a
def _dict_items(self):
for a in reversed(self._attrs()):
yield a, getattr(self,a)
def _dict_string(self):
vals = []
for key, val in self._dict_items():
try:
vals.append(u'{}={}'.format(key, val))
except UnicodeDecodeError:
vals.append(u'{}=<not printable>'.format(key))
return u', '.join(vals)
def __repr__(self):
return str(self)
def __str__(self):
return u'{} [{}]'.format(self.__class__.__name__, self._dict_string())
#---------------------------CLASSES-------------------------------------------#
class Field(object):
"""This is a field object that will describe a field on the
class it is a part of.
"""
name = ''
size = 0
default_value = None
def __init__(self, name='', size=0, default_value=None):
self.name = name
self.size = size
self.default_value = default_value
class Structure(Printable):
"""A structure is composed of fields denoted by the _fields attribute."""
_fields = None
def __init__(self, *args, **kwargs):
for field in self._fields:
setattr(self, field.name, field.default_value)
for k,v in kwargs:
setattr(self, k, v)
@property
def size(self):
sum_ = 0
for f in self._fields:
sum_ += f.size
return sum_
def dump(self):
s = '>'
values = []
for field in self._fields:
val = getattr(self, field.name)
if not isinstance(val, (str, bytes)):
values.append(val)
s += struct_symbols[field.size]
else:
s += 'B'*len(val)
values.extend(struct.unpack('>'+'B'*len(val), bytearray(val, encoding='utf-8')))
return struct.pack(s, *values)
class Size(Printable):
def __init__(self, width=0, height=0):
self.width = width
self.height = height
class ICNSInfo(Printable):
def __init__(self):
self.iconType = None
self.isImage = False
self.isMask = False
self.iconSize = Size()
self.iconChannels = 0
self.iconPixelDepth = 0
self.iconBitDepth = 0
self.iconRawDataSize = 0
data = None
def get_image_type(self):
if not self.isImage and not self.isMask:
return ICNS_NULL_TYPE
if self.iconSize.width == 0 or self.iconSize.height == 0:
if self.iconRawDataSize == 24:
if self.isImage and self.isMask:
return ICNS_NULL_TYPE
if self.isImage:
return ICNS_16x12_1BIT_DATA
if self.isMask:
return ICNS_16x12_1BIT_MASK
if self.iconRawDataSize == 32:
if self.isImage and self.isMask:
return ICNS_NULL_TYPE
if self.isImage:
return ICNS_16x16_1BIT_DATA
if self.isMask:
return ICNS_16x16_1BIT_MASK
return ICNS_NULL_TYPE
if self.iconBitDepth == 0 and (self.iconSize.width < 128 or self.iconSize.height < 128):
if self.iconPixelDepth == 0 or self.iconChannels == 0:
return ICNS_NULL_TYPE
else:
self.iconBitDepth = self.iconPixelDepth * self.iconChannels
if self.iconSize.width == 16 and self.iconSize.height == 12:
if self.iconBitDepth == 1:
if self.isImage:
return ICNS_16x12_1BIT_DATA
if self.isMask:
return ICNS_16x12_1BIT_MASK
if self.iconBitDepth == 4:
return ICNS_16x12_4BIT_DATA
if self.iconBitDepth == 8:
return ICNS_16x12_8BIT_DATA
return ICNS_NULL_TYPE
if self.iconSize.width != self.iconSize.height:
return ICNS_NULL_TYPE
data_type = ''
if self.isImage:
data_type = 'data'
elif self.isMask:
data_type = 'mask'
try:
return type_dict[data_type][self.iconSize.width][self.iconBitDepth]
except KeyError:
return ICNS_NULL_TYPE
@classmethod
def from_type(cls, type):
icon_info = cls()
icon_info.iconType = type
if type == ICNS_TABLE_OF_CONTENTS or type == ICNS_ICON_VERSION:
return icon_info
if type == ICNS_1024x1024_32BIT_ARGB_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 1024
icon_info.iconSize.height = 1024
icon_info.iconChannels = 4
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 32
elif type == ICNS_512x512_32BIT_ARGB_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 512
icon_info.iconSize.height = 512
icon_info.iconChannels = 4
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 32
elif type == ICNS_256x256_32BIT_ARGB_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 256
icon_info.iconSize.height = 256
icon_info.iconChannels = 4
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 32
elif type == ICNS_128x128_32BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 128
icon_info.iconSize.height = 128
icon_info.iconChannels = 4
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 32
elif type == ICNS_48x48_32BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 48
icon_info.iconSize.height = 48
icon_info.iconChannels = 4
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 32
elif type == ICNS_32x32_32BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 32
icon_info.iconSize.height = 32
icon_info.iconChannels = 4
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 32
elif type == ICNS_16x16_32BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 16
icon_info.iconSize.height = 16
icon_info.iconChannels = 4
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 32
elif type == ICNS_48x48_8BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 48
icon_info.iconSize.height = 48
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 8
elif type == ICNS_32x32_8BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 32
icon_info.iconSize.height = 32
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 8
elif type == ICNS_16x16_8BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 16
icon_info.iconSize.height = 16
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 8
elif type == ICNS_16x12_8BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 16
icon_info.iconSize.height = 12
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 8
elif type == ICNS_128x128_8BIT_MASK:
icon_info.isImage = False
icon_info.isMask = True
icon_info.iconSize.width = 128
icon_info.iconSize.height = 128
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 8
elif type == ICNS_48x48_8BIT_MASK:
icon_info.isImage = False
icon_info.isMask = True
icon_info.iconSize.width = 48
icon_info.iconSize.height = 48
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 8
elif type == ICNS_32x32_8BIT_MASK:
icon_info.isImage = False
icon_info.isMask = True
icon_info.iconSize.width = 32
icon_info.iconSize.height = 32
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 8
elif type == ICNS_16x16_8BIT_MASK:
icon_info.isImage = False
icon_info.isMask = True
icon_info.iconSize.width = 16
icon_info.iconSize.height = 16
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 8
icon_info.iconBitDepth = 8
elif type == ICNS_48x48_4BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 48
icon_info.iconSize.height = 48
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 4
icon_info.iconBitDepth = 4
elif type == ICNS_32x32_4BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 32
icon_info.iconSize.height = 32
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 4
icon_info.iconBitDepth = 4
elif type == ICNS_16x16_4BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 16
icon_info.iconSize.height = 16
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 4
icon_info.iconBitDepth = 4
elif type == ICNS_16x12_4BIT_DATA:
icon_info.isImage = True
icon_info.isMask = False
icon_info.iconSize.width = 16
icon_info.iconSize.height = 12
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 4
icon_info.iconBitDepth = 4
elif type == ICNS_48x48_1BIT_DATA:
icon_info.isImage = True
icon_info.isMask = True
icon_info.iconSize.width = 48
icon_info.iconSize.height = 48
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 1
icon_info.iconBitDepth = 1
elif type == ICNS_32x32_1BIT_DATA:
icon_info.isImage = True
icon_info.isMask = True
icon_info.iconSize.width = 32
icon_info.iconSize.height = 32
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 1
icon_info.iconBitDepth = 1
elif type == ICNS_16x16_1BIT_DATA:
icon_info.isImage = True
icon_info.isMask = True
icon_info.iconSize.width = 16
icon_info.iconSize.height = 16
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 1
icon_info.iconBitDepth = 1
elif type == ICNS_16x12_1BIT_DATA:
icon_info.isImage = True
icon_info.isMask = True
icon_info.iconSize.width = 16
icon_info.iconSize.height = 12
icon_info.iconChannels = 1
icon_info.iconPixelDepth = 1
icon_info.iconBitDepth = 1
else:
print('Unable to parse icon type {}'.format(type_to_str(type)))
icon_info.iconType = ICNS_NULL_TYPE
icon_info.iconRawDataSize = int(icon_info.iconSize.height * icon_info.iconSize.width * icon_info.iconBitDepth/ICNS_BYTE_BITS)
icon_info.data = bytearray(int(icon_info.iconRawDataSize))
return icon_info
class ICNSHeader(Structure):
_fields = [Field('TypeID', 4, 'icns'),
Field('Size', 4)]
elements = None
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
self.elements = []
def parse_image(self, image):
if not image_utils.IMAGE_UTILS_AVAILABLE:
return None
icon_size = image_utils.nearest_icon_size(image.size[0], image.size[1])
icon_sizes = [icon_size]
self.Size = self.size
f_data = bytearray()
for icon_s in icon_sizes:
icns_element = ICNSElement()
im_data = image_utils.resize(image, (int(icon_s), int(icon_s)))
png_file = png.Reader(bytes=im_data)
width, height, data, stats_dict = png_file.read_flat()
if icon_size >= 256:
encoded_data = im_data
else:
encoded_data = encode_rle24(data)
bpp = stats_dict['bitdepth'] * 4
icns_info = ICNSInfo()
icns_info.isImage = 1
icns_info.iconSize.width = int(icon_size)
icns_info.iconSize.height = int(icon_size)
icns_info.iconBitDepth = bpp
icns_info.iconChannels = 4 if bpp == 32 else 1
icns_info.iconPixelDepth = int(bpp / icns_info.iconChannels)
icns_info.iconRawDataSize = int(width * height * 4)
icns_info.data = bytearray(list(data))
icon_type = icns_info.get_image_type()
mask_type = get_mask_type_for_icon_type(icon_type)
if mask_type != ICNS_NULL_MASK:
icns_mask = ICNSInfo.from_type(mask_type)
iconDataOffset = 0
maskDataOffset = 0
while iconDataOffset < icns_info.iconRawDataSize and maskDataOffset < icns_mask.iconRawDataSize:
icns_mask.data[maskDataOffset] = icns_info.data[iconDataOffset+3]
iconDataOffset += 4
maskDataOffset += 1
mask_element = ICNSElement()
mask_element.TypeID = mask_type
mask_element.Size = len(icns_mask.data) + mask_element.size
mask_element.icns_image = icns_mask
icns_element.TypeID = icon_type
icns_element.Size = len(encoded_data) + icns_element.size
icns_element.icns_image = icns_info
self.Size += icns_element.Size + mask_element.Size
f_data += icns_element.dump()+encoded_data+mask_element.dump()+icns_mask.data
self.elements.append(icns_element)
self.elements.append(mask_element)
else: # just use the png data
icns_element.TypeID = icon_type
icns_element.Size = len(encoded_data) + icns_element.size
icns_element.icns_image = icns_info
self.Size += icns_element.Size
f_data += icns_element.dump()+encoded_data
self.elements.append(icns_element)
return self.dump()+f_data
class ICNSElement(Structure):
_fields = [Field('TypeID', 4),
Field('Size', 4)]
icns_image = None
data = None
@classmethod
def from_family(cls, icns_data, icon_type):
resource_size = len(icns_data)
offset = 8
found_data = False
icon_element = cls()
while not found_data and offset < resource_size:
icon_element.TypeID = from_bytes(icns_data[offset:offset+4])
icon_element.Size = from_bytes(icns_data[offset+4:offset+8])
size = from_bytes(icns_data[offset+4:offset+8])
icon_element.data = icns_data[offset+8:offset+size]
if icon_element.TypeID == icon_type:
found_data = True
else:
offset += icon_element.Size
return icon_element
def get_image(self):
icon_type = self.TypeID
raw_data_size = self.Size - 8
data = self.data
icns_info = ICNSInfo()
icns_info.isImage = 1
if icon_type in [ICNS_256x256_32BIT_ARGB_DATA,
ICNS_512x512_32BIT_ARGB_DATA,
ICNS_1024x1024_32BIT_ARGB_DATA]:
magic_png = bytearray([0x89, 0x50, 0x4E, 0x47,
0x0D, 0x0A, 0x1A, 0x0A])
magic_read = data[:8]
if magic_png == magic_read:
png_file = png.Reader(bytes=data)
width, height, png_data, stats_dict = png_file.read_flat()
im = Image.frombytes('RGBA', [width, height], bytes(png_data))
output = BytesIO()
im.save(output, format='PNG')
bpp = stats_dict['bitdepth'] * 4
icns_info = ICNSInfo()
icns_info.isImage = 1
icns_info.iconSize.width = int(width)
icns_info.iconSize.height = int(height)
icns_info.iconBitDepth = bpp
icns_info.iconChannels = 4 if bpp == 32 else 1
icns_info.iconPixelDepth = int(bpp / icns_info.iconChannels)
icns_info.iconRawDataSize = int(width * height * 4)
icns_info.data = bytes(output.getvalue())
else:
image = Image.open(BytesIO(data))
mode_to_bpp = {'1':1, 'L':8, 'P':8, 'RGB':24, 'RGBA':32, 'CMYK':32, 'YCbCr':24, 'I':32, 'F':32}
output = BytesIO()
image.save(output, format='PNG')
bpp = mode_to_bpp[image.mode]
png_data = bytes(output.getvalue())
icns_info = ICNSInfo()
icns_info.isImage = 1
icns_info.iconSize.width = int(image.size[0])
icns_info.iconSize.height = int(image.size[1])
icns_info.iconBitDepth = bpp
icns_info.iconChannels = 4 if bpp == 32 else 1
icns_info.iconPixelDepth = int(bpp / icns_info.iconChannels)
icns_info.iconRawDataSize = int(image.size[0] * image.size[1] * 4)
icns_info.data = png_data
else:
icns_info = ICNSInfo.from_type(icon_type)
if icon_type in [ICNS_128x128_32BIT_DATA,
ICNS_48x48_32BIT_DATA,
ICNS_32x32_32BIT_DATA,
ICNS_16x16_32BIT_DATA]:
icon_bit_depth = icns_info.iconPixelDepth * icns_info.iconChannels
icon_data_row_size = icns_info.iconSize.width * icon_bit_depth*ICNS_BYTE_BITS
if raw_data_size < icns_info.iconRawDataSize:
pixel_count = icns_info.iconSize.width*icns_info.iconSize.height
decoded_data = decode_rle24(data, pixel_count)
icns_info.data = decoded_data
else:
data_count = 0
pixel_count = 0
while data_count < icns_info.iconSize.width:
data_pos = data_count * icon_data_row_size
icns_info.data[data_pos:data_pos+icon_data_row_size] = data[data_pos:data_pos+icon_data_row_size]
data_count += 1
pixel_count = icns_info.iconSize.width*icns_info.iconSize.height
data_count = 0
while data_count < pixel_count:
argb = icns_info.data[data_count*4:data_count*4+4]
rgba = [argb[1], argb[2], argb[3], argb[0]]
icns_info.data[data_count*4:data_count*4+4] = rgba
data_count += 1
elif icon_type in [ICNS_48x48_8BIT_DATA,
ICNS_32x32_8BIT_DATA,
ICNS_16x16_8BIT_DATA,
ICNS_16x12_8BIT_DATA,
ICNS_48x48_4BIT_DATA,
ICNS_32x32_4BIT_DATA,
ICNS_16x16_4BIT_DATA,
ICNS_16x12_4BIT_DATA,
ICNS_48x48_1BIT_DATA,
ICNS_32x32_1BIT_DATA,
ICNS_16x16_1BIT_DATA,
ICNS_16x12_1BIT_DATA]:
icon_bit_depth = icns_info.iconPixelDepth * icns_info.iconChannels
icon_data_row_size = icns_info.iconSize.width * icon_bit_depth*ICNS_BYTE_BITS
data_count = 0
while data_count < icns_info.iconSize.width:
data_pos = data_count * icon_data_row_size
icns_info.data[data_pos:data_pos+icon_data_row_size] = data[data_pos:data_pos+icon_data_row_size]
data_count += 1
return icns_info
def get_mask(self):
element_type = self.TypeID
element_size = self.Size
mask_type = element_type
raw_data_size = element_size - 8
data = self.data
icns_info = ICNSInfo.from_type(mask_type)
mask_bit_depth = icns_info.iconSize.width * icns_info.iconSize.height
mask_data_size = icns_info.iconRawDataSize
mask_data_row_size = int(icns_info.iconSize.width * mask_bit_depth / ICNS_BYTE_BITS)
if mask_type in [ICNS_128x128_8BIT_MASK,
ICNS_48x48_8BIT_MASK,
ICNS_32x32_8BIT_MASK,
ICNS_16x16_8BIT_MASK]:
data_count = 0
while data_count < icns_info.iconSize.height:
data_pos = int(data_count * mask_data_row_size)
icns_info.data[data_pos:data_pos+mask_data_row_size] = data[data_pos:data_pos+mask_data_row_size]
data_count += 1
elif mask_type in [ICNS_48x48_1BIT_MASK,
ICNS_32x32_1BIT_MASK,
ICNS_16x16_1BIT_MASK,
ICNS_16x12_1BIT_MASK]:
if raw_data_size == mask_data_size*2:
data_count = 0
while data_count < icns_info.iconSize.height:
data_pos = data_count * mask_data_row_size
icns_info.data[data_pos:data_pos+mask_data_row_size] = data[data_pos+mask_data_size:data_pos+mask_data_row_size+mask_data_size]
data_count += 1
else:
data_count = 0
while data_count < icns_info.iconSize.height:
data_pos = data_count * mask_data_row_size
icns_info.data[data_pos:data_pos+mask_data_row_size] = data[data_pos:data_pos+mask_data_row_size]
data_count += 1
return icns_info
def icns_read_be(icns_data, size):
icns_bytes = icns_data[:size]
if size == 1:
return icns_bytes[0]
elif size == 2:
return icns_bytes[1] | icns_bytes[0] << 8
elif size == 3:
return (icns_bytes[2] & 0xffff | icns_bytes[1] & 0xffff << 8| icns_bytes[0] & 0xffff << 16) & 0x00FFFFFF
elif size == 4:
return icns_bytes[3] | icns_bytes[2] << 8 | icns_bytes[1] << 16 | icns_bytes[0] << 24
elif size == 8:
b = icns_bytes
return b[7] | b[6] << 8 | b[5] << 16 | b[4] << 24 | b[3] << 32 | b[2] << 40 | b[1] << 48 | b[0] << 56
def icns_header_check(icns_data):
resource_type = icns_read_be(icns_data, 4)
resource_size = icns_read_be(icns_data[4:], 4)
if resource_type != ICNS_FAMILY_TYPE:
raise Exception('File is not an ICNS file.')
if resource_size != len(icns_data):
raise Exception('Expected size {}, but got {}'.format(len(icns_data)))
def icns_parse_family_data(icns_data):
resource_type = icns_read_be(icns_data, 4)
resource_size = icns_read_be(icns_data[4:], 4)
if resource_type == ICNS_FAMILY_TYPE:
if len(icns_data) == resource_size:
icns_data[:4] = to_bytes(resource_type, 4)
icns_data[4:8] = to_bytes(resource_size, 4)
offset = 8
while (offset+8) < resource_size:
element_type = icns_read_be(icns_data[offset:], 4)
element_size = icns_read_be(icns_data[offset+4:], 4)
icns_data[offset:offset+4] = to_bytes(element_type, 4)
icns_data[offset+4:offset+8] = to_bytes(element_size, 4)
offset += element_size
return icns_data
def get_image_with_mask(icns_data, element_type):
element = ICNSElement.from_family(icns_data, element_type)
icns_image = element.get_image()
if element_type in [ICNS_256x256_32BIT_ARGB_DATA,
ICNS_512x512_32BIT_ARGB_DATA,
ICNS_1024x1024_32BIT_ARGB_DATA]:
return icns_image
mask_type = get_mask_type_for_icon_type(element_type)
mask_element = ICNSElement.from_family(icns_data, mask_type)
mask_image = mask_element.get_mask()
old_bit_depth = icns_image.iconPixelDepth * icns_image.iconChannels
if old_bit_depth < 32:
old_bit_depth = icns_image.iconPixelDepth * icns_image.iconChannels
pixel_count = icns_image.iconSize.width * icns_image.iconSize.height
new_block_size = icns_image.iconSize.width * 32
new_data_size = new_block_size * icns_image.iconSize.height
old_data = icns_image.data
new_data = bytearray(int(new_data_size))
data_count = 0
if element_type in [ICNS_48x48_8BIT_DATA,
ICNS_32x32_8BIT_DATA,
ICNS_16x16_8BIT_DATA,
ICNS_16x12_8BIT_DATA]:
for pixel_id in range(pixel_count):
color_index = old_data[data_count]
color_rgb = icns_colormap_8[color_index]
new_data[pixel_id*4+0] = color_rgb[0]
new_data[pixel_id*4+1] = color_rgb[1]
new_data[pixel_id*4+2] = color_rgb[2]
new_data[pixel_id*4+3] = 0xFF
data_count += 1
elif element_type in [ICNS_48x48_4BIT_DATA,
ICNS_32x32_4BIT_DATA,
ICNS_16x16_4BIT_DATA,
ICNS_16x12_4BIT_DATA]:
data_value = 0
for pixel_id in range(pixel_count):
if (pixel_id % 2) == 0:
data_value = old_data[data_count]
data_count += 1
color_index = (data_value & 0xF0) >> 4
color_rgb = icns_colormap_4[color_index]
new_data[pixel_id*4+0] = color_rgb[0]
new_data[pixel_id*4+1] = color_rgb[1]
new_data[pixel_id*4+2] = color_rgb[2]
new_data[pixel_id*4+3] = 0xFF
elif element_type in [ICNS_48x48_1BIT_DATA,
ICNS_32x32_1BIT_DATA,
ICNS_16x16_1BIT_DATA,
ICNS_16x12_1BIT_DATA]:
data_value = 0
for pixel_id in range(pixel_count):
if (pixel_id % 8) == 0:
data_value = old_data[data_count]
data_count += 1
color_index = 0x00 if (data_value & 0x80) else 0xFF
data_value = data_value << 1
new_data[pixel_id*4+0] = color_index
new_data[pixel_id*4+1] = color_index
new_data[pixel_id*4+2] = color_index
new_data[pixel_id*4+3] = 0xFF
icns_image.iconPixelDepth = 8
icns_image.iconChannels = 4
icns_image.iconRawDataSize = int(new_data_size)
icns_image.data = new_data
if mask_type in [ICNS_128x128_8BIT_MASK,
ICNS_48x48_8BIT_MASK,
ICNS_32x32_8BIT_MASK,
ICNS_16x16_8BIT_MASK]:
pixel_count = mask_image.iconSize.width * mask_image.iconSize.height
data_count = 0
for pixel_id in range(pixel_count):
icns_image.data[pixel_id*4+3] = mask_image.data[data_count]
data_count += 1
elif mask_type in [ICNS_48x48_1BIT_MASK,
ICNS_32x32_1BIT_MASK,
ICNS_16x16_1BIT_MASK,
ICNS_16x12_1BIT_MASK]:
pixel_count = mask_image.iconSize.width * mask_image.iconSize.height
data_count = 0
data_value = 0
for pixel_id in range(pixel_count):
if (pixel_id % 8) == 0:
data_value = mask_image.data[data_count]
data_count += 1
color_index = 0xFF if (data_value & 0x80) else 0x00
data_value = data_value << 1
icns_image.data[pixel_id*4+3] = color_index
im = Image.frombytes('RGBA', [icns_image.iconSize.width,icns_image.iconSize.height], bytes(icns_image.data))
#print(icns_image.data)
output = BytesIO()
im.save(output, format='PNG')
icns_image.data = bytes(output.getvalue())
return icns_image
def extract_icons(all_icns_data):
image_count = 0
stack = [all_icns_data]
data = []
while stack:
offset = 8
icns_data = stack.pop()
while ((offset+8) < len(icns_data)):
element = ICNSElement()
element.TypeID = from_bytes(icns_data[offset:offset+4])
element.Size = from_bytes(icns_data[offset+4:offset+8])
if element.TypeID == ICNS_TABLE_OF_CONTENTS:
pass
elif element.TypeID in [ICNS_TILE_VARIANT,
ICNS_ROLLOVER_VARIANT,
ICNS_DROP_VARIANT,
ICNS_OPEN_VARIANT,
ICNS_OPEN_DROP_VARIANT]:
variant_data = icns_data[offset:offset+element.Size]
variant_data[:4] = 'icns'
variant_data[4:8] = to_bytes(element.Size, 4)
variant_data = icns_parse_family_data(variant_data)
stack.append(variant_data)
else:
icon_info = ICNSInfo.from_type(element.TypeID)
if icon_info.isImage:
image_count += 1
image_data = get_image_with_mask(icns_data, element.TypeID)
image_data.data = bytes(image_data.data)
data.append(image_data)
offset += element.Size
return data
def icns_to_png(icns_file, out_file=None):
if not os.path.exists(icns_file):
return
icns_fp = open(icns_file, 'rb')
icns_data = bytearray(icns_fp.read())
icns_fp.close()
icns_header_check(icns_data)
new_data = icns_parse_family_data(icns_data)
icons = extract_icons(new_data)
#for image in icons:
# fname = 'test{}.png'.format(image.iconSize.width)
# print fname
# f = open(fname, 'wb+')
# f.write(image.data)
# f.close()
return icons