From 003170b782d2d0633f976662fcb1f718022a34e3 Mon Sep 17 00:00:00 2001 From: Joshua Granick Date: Wed, 7 Jan 2015 22:18:07 -0800 Subject: [PATCH] Fix the Windows icon generation, add Bitmap encoding support, break JPG/PNG support into separate lime.graphics.format.* class files --- lime/graphics/Image.hx | 52 ++-------- lime/graphics/format/BMP.hx | 185 +++++++++++++++++++++++++++++++++++ lime/graphics/format/JPEG.hx | 39 ++++++++ lime/graphics/format/PNG.hx | 70 +++++++++++++ tools/helpers/IconHelper.hx | 124 ++++++++--------------- 5 files changed, 342 insertions(+), 128 deletions(-) create mode 100644 lime/graphics/format/BMP.hx create mode 100644 lime/graphics/format/JPEG.hx create mode 100644 lime/graphics/format/PNG.hx diff --git a/lime/graphics/Image.hx b/lime/graphics/Image.hx index 57a8e9e01..03cd443bd 100644 --- a/lime/graphics/Image.hx +++ b/lime/graphics/Image.hx @@ -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); + + case "bmp": - #elseif (sys && (!disable_cffi || !format)) - - return lime_image_encode (buffer, 1, quality); - - #end + return BMP.encode (this); default: diff --git a/lime/graphics/format/BMP.hx b/lime/graphics/format/BMP.hx new file mode 100644 index 000000000..61794482a --- /dev/null +++ b/lime/graphics/format/BMP.hx @@ -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; + +} \ No newline at end of file diff --git a/lime/graphics/format/JPEG.hx b/lime/graphics/format/JPEG.hx new file mode 100644 index 000000000..1b52fdaa0 --- /dev/null +++ b/lime/graphics/format/JPEG.hx @@ -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 + + +} \ No newline at end of file diff --git a/lime/graphics/format/PNG.hx b/lime/graphics/format/PNG.hx new file mode 100644 index 000000000..d36bbd385 --- /dev/null +++ b/lime/graphics/format/PNG.hx @@ -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 + + +} \ No newline at end of file diff --git a/tools/helpers/IconHelper.hx b/tools/helpers/IconHelper.hx index 947c12e07..0474ec0ae 100644 --- a/tools/helpers/IconHelper.hx +++ b/tools/helpers/IconHelper.hx @@ -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 , targetPath:String):Bool { var sizes = [ 16, 24, 32, 40, 48, 64, 96, 128, 256 ]; - var images = new Array (); - var data_pos = 6; + var images = new Array (); + var imageData = new Array (); for (size in sizes) { @@ -149,101 +150,56 @@ class IconHelper { 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) { + if (size < 256) { - 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); + imageData.push (BMP.encode (image, ICO)); - if (a < 128) - mask |= bit; + } else { - 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; }