420 lines
8.3 KiB
Haxe
420 lines
8.3 KiB
Haxe
package helpers;
|
|
|
|
|
|
//import openfl.display.Bitmap;
|
|
//import openfl.display.BitmapData;
|
|
//import openfl.display.Shape;
|
|
//import openfl.geom.Rectangle;
|
|
//import openfl.utils.ByteArray;
|
|
//import format.SVG;
|
|
import haxe.io.Bytes;
|
|
import haxe.io.BytesOutput;
|
|
import haxe.io.Path;
|
|
import helpers.FileHelper;
|
|
import helpers.ImageHelper;
|
|
import helpers.LogHelper;
|
|
import helpers.PathHelper;
|
|
import lime.graphics.Image;
|
|
import lime.math.Rectangle;
|
|
import lime.utils.ByteArray;
|
|
import project.Icon;
|
|
import sys.io.File;
|
|
import sys.FileSystem;
|
|
|
|
|
|
class IconHelper {
|
|
|
|
|
|
public static function createIcon (icons:Array <Icon>, width:Int, height:Int, targetPath:String):Bool {
|
|
|
|
var icon = findMatch (icons, width, height);
|
|
|
|
if (icon != null && icon.size > 0 && Path.extension (icon.path) == "png") {
|
|
|
|
PathHelper.mkdir (Path.directory (targetPath));
|
|
FileHelper.copyFile (icon.path, targetPath);
|
|
return true;
|
|
|
|
} else {
|
|
|
|
var image = getIconImage (icons, width, height);
|
|
|
|
if (image != null) {
|
|
|
|
PathHelper.mkdir (Path.directory (targetPath));
|
|
File.saveBytes (targetPath, image.encode ("png"));
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
public static function createMacIcon (icons:Array <Icon>, targetPath:String):Bool {
|
|
|
|
var out = new BytesOutput ();
|
|
out.bigEndian = true;
|
|
|
|
// Not sure why the 128x128 icon is not saving properly. Disabling for now
|
|
|
|
for (i in 0...3) {
|
|
|
|
var s = ([ 16, 32, 48, 128 ])[i];
|
|
var code = ([ "is32", "il32", "ih32", "it32" ])[i];
|
|
var image = getIconImage (icons, s, s);
|
|
|
|
if (image != null) {
|
|
|
|
for (c in 0...4) out.writeByte (code.charCodeAt(c));
|
|
|
|
var n = s * s;
|
|
var pixels = image.getPixels (new Rectangle (0, 0, s, s));
|
|
|
|
var bytes_r = packBits (pixels, 1, s * s);
|
|
var bytes_g = packBits (pixels, 2, s * s);
|
|
var bytes_b = packBits (pixels, 3, s * s);
|
|
|
|
out.writeInt32 (bytes_r.length + bytes_g.length + bytes_b.length + 8);
|
|
out.writeBytes (bytes_r, 0, bytes_r.length);
|
|
out.writeBytes (bytes_g, 0, bytes_g.length);
|
|
out.writeBytes (bytes_b, 0, bytes_b.length);
|
|
|
|
var code = ([ "s8mk", "l8mk", "h8mk", "t8mk" ])[i];
|
|
|
|
for (c in 0...4) out.writeByte (code.charCodeAt (c));
|
|
|
|
var bytes_a = extractBits (pixels, 0, s * s);
|
|
out.writeInt32 (bytes_a.length + 8);
|
|
out.writeBytes (bytes_a, 0, bytes_a.length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i in 0...5) {
|
|
|
|
var s = ([ 32, 64, 256, 512, 1024 ])[i];
|
|
var code = ([ "ic11", "ic12", "ic08", "ic09", "ic10" ])[i];
|
|
var image = getIconImage (icons, s, s);
|
|
|
|
if (image != null) {
|
|
|
|
for (c in 0...4) out.writeByte (code.charCodeAt(c));
|
|
|
|
var bytes = image.encode ("png");
|
|
|
|
out.writeInt32 (bytes.length + 8);
|
|
out.writeBytes (bytes, 0, bytes.length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var bytes = out.getBytes ();
|
|
|
|
if (bytes.length > 0) {
|
|
|
|
var file = File.write (targetPath, true);
|
|
file.bigEndian = true;
|
|
|
|
for (c in 0...4) file.writeByte ("icns".charCodeAt (c));
|
|
|
|
file.writeInt32 (bytes.length + 8);
|
|
file.writeBytes (bytes, 0, bytes.length);
|
|
file.close ();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
public static function createWindowsIcon (icons:Array <Icon>, targetPath:String):Bool {
|
|
|
|
var sizes = [ 16, 24, 32, 40, 48, 64, 96, 128, 256 ];
|
|
var images = new Array <Image> ();
|
|
|
|
var data_pos = 6;
|
|
|
|
for (size in sizes) {
|
|
|
|
var image = getIconImage (icons, size, size);
|
|
|
|
if (image != null) {
|
|
|
|
images.push (image);
|
|
data_pos += 16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var ico = new ByteArray ();
|
|
ico.bigEndian = false;
|
|
ico.writeShort (0);
|
|
ico.writeShort (1);
|
|
ico.writeShort (images.length);
|
|
|
|
for (image in images) {
|
|
|
|
var size = image.width;
|
|
var xor_size = size * size * 4;
|
|
var and_size = size * size >> 3;
|
|
ico.writeByte (size);
|
|
ico.writeByte (size);
|
|
ico.writeByte (0); // palette
|
|
ico.writeByte (0); // reserved
|
|
ico.writeShort (1); // planes
|
|
ico.writeShort (32); // bits per pixel
|
|
ico.writeInt (40 + xor_size + and_size); // Data size
|
|
ico.writeInt (data_pos); // Data offset
|
|
data_pos += 40 + xor_size + and_size;
|
|
|
|
}
|
|
|
|
for (image in images) {
|
|
|
|
var size = image.width;
|
|
var xor_size = size * size * 4;
|
|
var and_size = size * size >> 3;
|
|
|
|
ico.writeInt (40); // size (bytes)
|
|
ico.writeInt (size);
|
|
ico.writeInt (size * 2);
|
|
ico.writeShort (1);
|
|
ico.writeShort (32);
|
|
ico.writeInt (0); // Bit fields...
|
|
ico.writeInt (xor_size + and_size); // Size...
|
|
ico.writeInt (0); // res-x
|
|
ico.writeInt (0); // res-y
|
|
ico.writeInt (0); // cols
|
|
ico.writeInt (0); // important
|
|
|
|
var bits = image.getPixels (new Rectangle (0, 0, size, size));
|
|
var and_mask = new ByteArray ();
|
|
|
|
for (y in 0...size) {
|
|
|
|
var mask = 0;
|
|
var bit = 128;
|
|
bits.position = (size-1 - y) * 4 * size;
|
|
|
|
for (i in 0...size) {
|
|
|
|
var r = bits.readByte ();
|
|
var g = bits.readByte ();
|
|
var b = bits.readByte ();
|
|
var a = bits.readByte ();
|
|
ico.writeByte (b);
|
|
ico.writeByte (g);
|
|
ico.writeByte (r);
|
|
ico.writeByte (a);
|
|
|
|
if (a < 128)
|
|
mask |= bit;
|
|
|
|
bit = bit >> 1;
|
|
|
|
if (bit == 0) {
|
|
|
|
and_mask.writeByte (mask);
|
|
bit = 128;
|
|
mask = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ico.writeBytes (and_mask, 0, and_mask.length);
|
|
|
|
}
|
|
|
|
if (images.length > 0) {
|
|
|
|
var file = File.write (targetPath, true);
|
|
file.writeBytes (ico, 0, ico.length);
|
|
file.close ();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
private static function extractBits (data:ByteArray, offset:Int, len:Int):Bytes {
|
|
|
|
var out = new BytesOutput ();
|
|
|
|
for (i in 0...len) {
|
|
|
|
out.writeByte (data[i * 4 + offset]);
|
|
|
|
}
|
|
|
|
return out.getBytes ();
|
|
|
|
}
|
|
|
|
|
|
public static function findMatch (icons:Array <Icon>, width:Int, height:Int):Icon {
|
|
|
|
var match = null;
|
|
|
|
for (icon in icons) {
|
|
|
|
if ((icon.width == 0 && icon.height == 0) || (icon.width == width && icon.height == height)) {
|
|
|
|
match = icon;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
public static function findNearestMatch (icons:Array <Icon>, width:Int, height:Int):Icon {
|
|
|
|
var match = null;
|
|
|
|
for (icon in icons) {
|
|
|
|
if (icon.width > width / 2 && icon.height > height / 2) {
|
|
|
|
if (match == null) {
|
|
|
|
match = icon;
|
|
|
|
} else {
|
|
|
|
if (icon.width > match.width && icon.height > match.height) {
|
|
|
|
if (match.width < width || match.height < height) {
|
|
|
|
match = icon;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (icon.width > width && icon.height > height) {
|
|
|
|
match = icon;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
private static function getIconImage (icons:Array <Icon>, width:Int, height:Int, backgroundColor:Int = null):Image {
|
|
|
|
var icon = findMatch (icons, width, height);
|
|
|
|
if (icon == null) {
|
|
|
|
icon = findNearestMatch (icons, width, height);
|
|
|
|
}
|
|
|
|
if (icon == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (!FileSystem.exists (icon.path)) {
|
|
|
|
LogHelper.warn ("Could not find icon path: " + icon.path);
|
|
return null;
|
|
|
|
}
|
|
|
|
var extension = Path.extension (icon.path);
|
|
var image = null;
|
|
|
|
switch (extension) {
|
|
|
|
case "png", "jpg", "jpeg":
|
|
|
|
image = ImageHelper.resizeImage (Image.fromFile (icon.path), width, height);
|
|
|
|
case "svg":
|
|
|
|
//image = ImageHelper.rasterizeSVG (null /*new SVG (File.getContent (icon.path))*/, width, height, backgroundColor);
|
|
image = ImageHelper.rasterizeSVG (icon.path, width, height, backgroundColor);
|
|
|
|
}
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
|
private static function packBits (data:ByteArray, offset:Int, len:Int):Bytes {
|
|
|
|
var out = new BytesOutput ();
|
|
var idx = 0;
|
|
|
|
while (idx < len) {
|
|
|
|
var val = data[idx * 4 + offset];
|
|
var same = 1;
|
|
|
|
/*
|
|
Hmmmm...
|
|
while( ((idx+same) < len) && (data[ (idx+same)*4 + offset ]==val) && (same < 2) )
|
|
same++;
|
|
*/
|
|
|
|
if (same == 1) {
|
|
|
|
var raw = idx + 120 < len ? 120 : len - idx;
|
|
out.writeByte (raw - 1);
|
|
|
|
for (i in 0...raw) {
|
|
|
|
out.writeByte (data[idx * 4 + offset]);
|
|
idx++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
out.writeByte (257 - same);
|
|
out.writeByte (val);
|
|
idx += same;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out.getBytes ();
|
|
|
|
}
|
|
|
|
|
|
} |