Fix the Windows icon generation, add Bitmap encoding support, break JPG/PNG support into separate lime.graphics.format.* class files

This commit is contained in:
Joshua Granick
2015-01-07 22:18:07 -08:00
parent ec2056842e
commit 003170b782
5 changed files with 342 additions and 128 deletions

View File

@@ -6,6 +6,9 @@ import haxe.io.Bytes;
import haxe.io.BytesInput;
import haxe.io.BytesOutput;
import lime.app.Application;
import lime.graphics.format.BMP;
import lime.graphics.format.JPEG;
import lime.graphics.format.PNG;
import lime.graphics.utils.ImageCanvasUtil;
import lime.graphics.utils.ImageDataUtil;
import lime.math.ColorMatrix;
@@ -292,54 +295,15 @@ class Image {
case "png":
#if java
#elseif (sys && (!disable_cffi || !format))
return lime_image_encode (buffer, 0, quality);
#elseif !js
try {
var bytes = Bytes.alloc (width * height * 4 + height);
var sourceBytes = buffer.data.getByteBuffer ();
var sourceIndex:Int, index:Int;
for (y in 0...height) {
sourceIndex = y * width * 4;
index = y * width * 4 + y;
bytes.set (index, 0);
bytes.blit (index + 1, sourceBytes, sourceIndex, width * 4);
}
var data = new List ();
data.add (CHeader ({ width: width, height: height, colbits: 8, color: ColTrue (true), interlaced: false }));
data.add (CData (Deflate.run (bytes)));
data.add (CEnd);
var output = new BytesOutput ();
var png = new Writer (output);
png.write (data);
return ByteArray.fromBytes (output.getBytes ());
} catch (e:Dynamic) {}
#end
return PNG.encode (this);
case "jpg", "jpeg":
#if java
return JPEG.encode (this, quality);
#elseif (sys && (!disable_cffi || !format))
case "bmp":
return lime_image_encode (buffer, 1, quality);
#end
return BMP.encode (this);
default:

185
lime/graphics/format/BMP.hx Normal file
View File

@@ -0,0 +1,185 @@
package lime.graphics.format;
import lime.graphics.Image;
import lime.math.Rectangle;
import lime.utils.ByteArray;
class BMP {
public static function encode (image:Image, type:BMPType = null):ByteArray {
if (type == null) {
type = RGB;
}
var fileHeaderLength = 14;
var infoHeaderLength = 40;
var pixelValuesLength = (image.width * image.height * 4);
switch (type) {
case BITFIELD:
infoHeaderLength = 108;
case ICO:
fileHeaderLength = 0;
pixelValuesLength += image.width * image.height;
default:
}
var data = new ByteArray (fileHeaderLength + infoHeaderLength + pixelValuesLength);
data.bigEndian = false;
if (fileHeaderLength > 0) {
data.writeByte (0x42);
data.writeByte (0x4D);
data.writeInt (data.length);
data.writeInt (0);
data.writeInt (fileHeaderLength + infoHeaderLength);
}
data.writeInt (infoHeaderLength);
data.writeInt (image.width);
if (type == ICO) {
data.writeInt (image.height * 2);
} else {
data.writeInt (image.height);
}
data.writeShort (1);
data.writeShort (32);
switch (type) {
case BITFIELD: data.writeInt (3);
default: data.writeInt (0);
}
data.writeInt (pixelValuesLength);
data.writeInt (0x2e30);
data.writeInt (0x2e30);
data.writeInt (0);
data.writeInt (0);
if (type == BITFIELD) {
data.writeInt (0x00FF0000);
data.writeInt (0x0000FF00);
data.writeInt (0x000000FF);
data.writeInt (0xFF000000);
data.writeByte (0x20);
data.writeByte (0x6E);
data.writeByte (0x69);
data.writeByte (0x57);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
data.writeInt (0);
}
var pixels = image.getPixels (new Rectangle (0, 0, image.width, image.height));
var a, r, g, b;
if (type != ICO) {
for (y in 0...image.height) {
pixels.position = (image.height - 1 - y) * 4 * image.width;
for (x in 0...image.width) {
a = pixels.readByte ();
r = pixels.readByte ();
g = pixels.readByte ();
b = pixels.readByte ();
data.writeByte (b);
data.writeByte (g);
data.writeByte (r);
data.writeByte (a);
}
}
} else {
var andMask = new ByteArray (image.width * image.height);
for (y in 0...image.height) {
pixels.position = (image.height - 1 - y) * 4 * image.width;
for (x in 0...image.width) {
a = pixels.readByte ();
r = pixels.readByte ();
g = pixels.readByte ();
b = pixels.readByte ();
data.writeByte (b);
data.writeByte (g);
data.writeByte (r);
data.writeByte (a);
// TODO: Fix the AND mask
//if (a < 128) {
//andMask.writeByte (1);
//} else {
andMask.writeByte (0);
//}
}
}
data.writeBytes (andMask);
}
return data;
}
}
enum BMPType {
RGB;
BITFIELD;
ICO;
}

View File

@@ -0,0 +1,39 @@
package lime.graphics.format;
import lime.graphics.Image;
import lime.system.System;
import lime.utils.ByteArray;
class JPEG {
public static function encode (image:Image, quality:Int):ByteArray {
#if java
#elseif (sys && (!disable_cffi || !format))
return lime_image_encode (image.buffer, 1, quality);
#end
return null;
}
// Native Methods
#if (cpp || neko || nodejs)
private static var lime_image_encode:ImageBuffer -> Int -> Int -> ByteArray = System.load ("lime", "lime_image_encode", 3);
#end
}

View File

@@ -0,0 +1,70 @@
package lime.graphics.format;
import lime.graphics.Image;
import lime.system.System;
import lime.utils.ByteArray;
class PNG {
public static function encode (image:Image):ByteArray {
#if java
#elseif (sys && (!disable_cffi || !format))
return lime_image_encode (image.buffer, 0, 0);
#elseif !js
try {
var bytes = Bytes.alloc (image.width * image.height * 4 + image.height);
var sourceBytes = image.buffer.data.getByteBuffer ();
var sourceIndex:Int, index:Int;
for (y in 0...image.height) {
sourceIndex = y * image.width * 4;
index = y * image.width * 4 + y;
bytes.set (index, 0);
bytes.blit (index + 1, sourceBytes, sourceIndex, image.width * 4);
}
var data = new List ();
data.add (CHeader ({ width: image.width, height: image.height, colbits: 8, color: ColTrue (true), interlaced: false }));
data.add (CData (Deflate.run (bytes)));
data.add (CEnd);
var output = new BytesOutput ();
var png = new Writer (output);
png.write (data);
return ByteArray.fromBytes (output.getBytes ());
} catch (e:Dynamic) {}
#end
return null;
}
// Native Methods
#if (cpp || neko || nodejs)
private static var lime_image_encode:ImageBuffer -> Int -> Int -> ByteArray = System.load ("lime", "lime_image_encode", 3);
#end
}

View File

@@ -14,6 +14,7 @@ import helpers.FileHelper;
import helpers.ImageHelper;
import helpers.LogHelper;
import helpers.PathHelper;
import lime.graphics.format.BMP;
import lime.graphics.Image;
import lime.math.Rectangle;
import lime.utils.ByteArray;
@@ -139,9 +140,9 @@ class IconHelper {
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;
var images = new Array <Image> ();
var imageData = new Array <ByteArray> ();
for (size in sizes) {
@@ -149,101 +150,56 @@ class IconHelper {
if (image != null) {
images.push (image);
data_pos += 16;
if (size < 256) {
}
imageData.push (BMP.encode (image, ICO));
}
} else {
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 a = bits.readByte ();
var r = bits.readByte ();
var g = bits.readByte ();
var b = 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;
}
imageData.push (image.encode ("png"));
}
images.push (image);
}
ico.writeBytes (and_mask, 0, and_mask.length);
}
var icon = new ByteArray ();
icon.bigEndian = false;
icon.writeShort (0);
icon.writeShort (1);
icon.writeShort (images.length);
var dataOffset = 6 + (16 * images.length);
for (i in 0...images.length) {
var size = images[i].width;
icon.writeByte (size > 255 ? 0 : size);
icon.writeByte (size > 255 ? 0 : size);
icon.writeByte (0);
icon.writeByte (0);
icon.writeShort (1);
icon.writeShort (32);
icon.writeInt (imageData[i].length);
icon.writeInt (dataOffset);
dataOffset += imageData[i].length;
}
for (data in imageData) {
icon.writeBytes (data);
}
if (images.length > 0) {
var file = File.write (targetPath, true);
file.writeBytes (ico, 0, ico.length);
file.close ();
File.saveBytes (targetPath, icon);
return true;
}