From 1749a18ab426e54b883a99bd6c6f9aa0fbec0705 Mon Sep 17 00:00:00 2001 From: Joshua Granick Date: Fri, 1 Aug 2014 20:22:39 -0700 Subject: [PATCH] Refactor into Image and ImageBuffer --- lime/Assets.hx | 30 +- lime/graphics/Font.hx | 13 +- lime/media/Image.hx | 389 ++++++++++++++++---------- lime/media/ImageBuffer.hx | 110 +++++--- samples/SimpleImage/Source/Main.hx | 14 +- templates/haxe/DefaultAssetLibrary.hx | 20 +- 6 files changed, 349 insertions(+), 227 deletions(-) diff --git a/lime/Assets.hx b/lime/Assets.hx index 70d2a9bd2..f56250948 100644 --- a/lime/Assets.hx +++ b/lime/Assets.hx @@ -5,7 +5,7 @@ package lime; import haxe.Unserializer; import lime.graphics.Font; import lime.media.AudioBuffer; -import lime.media.Image; +import lime.media.ImageBuffer; import lime.utils.ByteArray; @@ -189,7 +189,7 @@ class Assets { * @param useCache (Optional) Whether to use BitmapData from the cache(Default: true) * @return A new BitmapData object */ - public static function getImage (id:String, useCache:Bool = true):Image { + public static function getImageBuffer (id:String, useCache:Bool = true):ImageBuffer { initialize (); @@ -217,7 +217,7 @@ class Assets { if (library.isLocal (symbolName, cast AssetType.IMAGE)) { - var image = library.getImage (symbolName); + var image = library.getImageBuffer (symbolName); if (useCache && cache.enabled) { @@ -500,12 +500,12 @@ class Assets { } - private static function isValidImage (image:Image):Bool { + private static function isValidImage (buffer:ImageBuffer):Bool { #if (tools && !display) #if (cpp || neko) - return true; + return (buffer != null); //return (bitmapData.__handle != null); #elseif flash @@ -519,7 +519,7 @@ class Assets { return false; }*/ - return true; + return (buffer != null); #end #end @@ -651,7 +651,7 @@ class Assets { } - public static function loadImage (id:String, handler:Image -> Void, useCache:Bool = true):Void { + public static function loadImageBuffer (id:String, handler:ImageBuffer -> Void, useCache:Bool = true):Void { initialize (); @@ -680,7 +680,7 @@ class Assets { if (useCache && cache.enabled) { - library.loadImage (symbolName, function (image:Image):Void { + library.loadImageBuffer (symbolName, function (image:ImageBuffer):Void { cache.image.set (id, image); handler (image); @@ -689,7 +689,7 @@ class Assets { } else { - library.loadImage (symbolName, handler); + library.loadImageBuffer (symbolName, handler); } @@ -965,7 +965,7 @@ class AssetLibrary { } - public function getImage (id:String):Image { + public function getImageBuffer (id:String):ImageBuffer { return null; @@ -1046,9 +1046,9 @@ class AssetLibrary { } - public function loadImage (id:String, handler:Image -> Void):Void { + public function loadImageBuffer (id:String, handler:ImageBuffer -> Void):Void { - handler (getImage (id)); + handler (getImageBuffer (id)); } @@ -1097,7 +1097,7 @@ class AssetCache { public var audio:Map; public var enabled:Bool = true; - public var image:Map; + public var image:Map; public var font:Map; @@ -1105,7 +1105,7 @@ class AssetCache { audio = new Map (); font = new Map (); - image = new Map (); + image = new Map (); } @@ -1116,7 +1116,7 @@ class AssetCache { audio = new Map (); font = new Map (); - image = new Map (); + image = new Map (); } else { diff --git a/lime/graphics/Font.hx b/lime/graphics/Font.hx index 97a3912a4..91beef149 100644 --- a/lime/graphics/Font.hx +++ b/lime/graphics/Font.hx @@ -2,6 +2,7 @@ package lime.graphics; import haxe.ds.StringMap; import lime.media.Image; +import lime.media.ImageBuffer; import lime.utils.UInt8Array; import lime.system.System; #if js @@ -20,7 +21,7 @@ typedef GlyphRect = { } typedef GlyphData = { - var image:Image; + var image:ImageBuffer; var glyphs:StringMap; }; @@ -134,7 +135,7 @@ class Font { image.src = __canvas.toDataURL(); return { glyphs: glyphRects, - image: new lime.media.Image (image, __canvas.width, __canvas.height) + image: ImageBuffer.fromImage (image) } #elseif flash @@ -203,10 +204,10 @@ class Font { } } - + return { glyphs: glyphRects, - image: new Image (bd, bd.width, bd.height) + image: ImageBuffer.fromBitmapData (bd) } #elseif (cpp || neko) @@ -232,10 +233,10 @@ class Font { }); } - + return { glyphs: glyphRects, - image: new Image (new UInt8Array (data.image.data), data.image.width, data.image.height, data.image.bpp) + image: new ImageBuffer (new UInt8Array (data.image.data), data.image.width, data.image.height, data.image.bpp) }; } diff --git a/lime/media/Image.hx b/lime/media/Image.hx index d866d7a87..7c5e5430c 100644 --- a/lime/media/Image.hx +++ b/lime/media/Image.hx @@ -1,6 +1,8 @@ package lime.media; +import lime.app.Application; +import lime.graphics.Renderer; import lime.utils.ByteArray; import lime.utils.UInt8Array; import lime.system.System; @@ -13,6 +15,8 @@ import js.Browser; import flash.display.BitmapData; #end +@:access(lime.app.Application) + class Image { @@ -22,204 +26,287 @@ class Image { private static var __context:CanvasRenderingContext2D; #end - public var bpp:Int; - public var data (get, set):ImageBuffer; + public var buffer:ImageBuffer; public var height:Int; public var offsetX:Int; public var offsetY:Int; - public var powerOfTwo (get, null):Bool; - public var premultiplied:Bool; - public var src:ImageSource; - public var textureHeight:Int; - public var textureWidth:Int; + public var powerOfTwo (get, set):Bool; + public var premultiplied (get, set):Bool; public var width:Int; - private var __data:ImageBuffer; + private var __renderer:Renderer; - public function new (src:ImageSource = null, width:Int = 0, height:Int = 0, bpp:Int = 4) { + public function new (buffer:ImageBuffer, renderer:Renderer = null) { - this.src = src; - #if (!js && !flash) - this.__data = src; - #end + this.buffer = buffer; - this.width = textureWidth = width; - this.height = textureHeight = height; - this.bpp = bpp; - - } - - - public function forcePowerOfTwo () { - - if (powerOfTwo) return; - - textureWidth = 1; - textureHeight = 1; - - while (textureWidth < width) { + if (renderer == null) { - textureWidth <<= 1; + __renderer = Application.__instance.window.currentRenderer; + + } else { + + __renderer = renderer; } - while (textureHeight < height) { + width = buffer.width; + height = buffer.height; + offsetX = 0; + offsetY = 0; + + switch (__renderer.context) { - textureHeight <<= 1; - - } - - #if js - - if (__canvas == null) { - - __canvas = cast Browser.document.createElement ("canvas"); - __context = cast __canvas.getContext ("2d"); - - } - - __canvas.width = textureWidth; - __canvas.height = textureHeight; - __context.clearRect (0, 0, textureWidth, textureHeight); - __context.drawImage (src, 0, 0, width, height); - - var pixels = __context.getImageData (0, 0, textureWidth, textureHeight); - __data = new ImageBuffer (pixels.data); - - #elseif flash - - var bitmapData = new BitmapData (textureWidth, textureHeight, true, 0x000000); - bitmapData.draw (src, null, null, null, true); - - var pixels = bitmapData.getPixels (bitmapData.rect); - __data = new ImageBuffer (pixels); - - #else - - var newData = new ImageBuffer (textureWidth * textureHeight * 4); - - for (y in 0...height) { - - for (x in 0...width) { + case OPENGL (_): - newData.setPixel (y * textureWidth + x, newData.getPixel (y * width + x)); - - } + #if js + if (buffer.data == null && buffer.src != null && buffer.width > 0 && buffer.height > 0) { + + if (__canvas == null) { + + __canvas = cast Browser.document.createElement ("canvas"); + __context = cast __canvas.getContext ("2d"); + + } + + __canvas.width = buffer.width; + __canvas.height = buffer.height; + __context.drawImage (buffer.src, 0, 0); + + var pixels = __context.getImageData (0, 0, buffer.width, buffer.height); + buffer.data = pixels.data; + + } + #end + + default: } - __data = newData; - - #end - } - public static function fromBytes (bytes:ByteArray):Image { + public function getPixel (x:Int, y:Int):Int { - #if (cpp || neko) + // TODO: cache context type? - var imageData = lime_image_load (bytes); - return (imageData == null ? null : new Image (new ImageBuffer (imageData.data), imageData.width, imageData.height, imageData.bpp)); + x += offsetX; + y += offsetY; - #else - - throw "Image.loadFromFile not supported on this target"; - - #end - - } - - - public static function fromFile (path:String) { - - #if (cpp || neko) - - var imageData = lime_image_load (path); - return (imageData == null ? null : new Image (new ImageBuffer (imageData.data), imageData.width, imageData.height, imageData.bpp)); - - #else - - throw "Image.loadFromFile not supported on this target"; - - #end - - } - - - public function premultiplyAlpha ():Void { - - if (premultiplied) return; - - var data = this.data; - data.premultiply (); - - premultiplied = true; - - } - - - private function get_data ():ImageBuffer { - - if (__data == null && src != null && width > 0 && height > 0) { + switch (__renderer.context) { - #if js - - if (__canvas == null) { + case OPENGL (_): - __canvas = cast Browser.document.createElement ("canvas"); - __context = cast __canvas.getContext ("2d"); + // TODO: handle premultiplied - } + var data = buffer.data; + var index = (y * buffer.width + x) * 4; + + return ((data[index + 3] << 24) | (data[index] << 16 ) | (data[index + 1] << 8) | data[index + 2]); - __canvas.width = width; - __canvas.height = height; - __context.drawImage (src, 0, 0); + case FLASH (_): + + #if flash + return buffer.src.getPixel (x, y); + #else + return 0; + #end - var pixels = __context.getImageData (0, 0, width, height); - __data = new ImageBuffer (pixels.data); + case DOM (_): + + // TODO + + return 0; - #elseif flash - - var pixels = src.getPixels (src.rect); - __data = new ImageBuffer (pixels); - - #end + case CANVAS (_): + + // TODO + + return 0; + + default: + + return 0; } - return __data; } - private function set_data (value:ImageBuffer):ImageBuffer { + public function setPixel (x:Int, y:Int, value:Int):Void { - return __data = value; + x += offsetX; + y += offsetY; + + switch (__renderer.context) { + + case OPENGL (_): + + // TODO: handle premultiplied + + var data = buffer.data; + var index = (y * buffer.width + x) * 4; + + data[index + 3] = (value >> 24) & 0xFF; + data[index] = (value >> 16) & 0xFF; + data[index + 1] = (value >> 8) & 0xFF; + data[index + 2] = value & 0xFF; + + case FLASH (_): + + #if flash + buffer.src.setPixel (x, y, value); + #end + + case DOM (_): + + // TODO + + case CANVAS (_): + + // TODO + + default: + + } } + + + // Get & Set Methods + + + + private function get_powerOfTwo ():Bool { - return ((width != 0) && ((width & (~width + 1)) == width)) && ((height != 0) && ((height & (~height + 1)) == height)); + return ((buffer.width != 0) && ((buffer.width & (~buffer.width + 1)) == buffer.width)) && ((buffer.height != 0) && ((buffer.height & (~buffer.height + 1)) == buffer.height)); } - #if (cpp || neko) - private static var lime_image_load:Dynamic = System.load ("lime", "lime_image_load", 1); - #end + private function set_powerOfTwo (value:Bool):Bool { + + if (value != powerOfTwo) { + + var newWidth = 1; + var newHeight = 1; + + while (newWidth < buffer.width) { + + newWidth <<= 1; + + } + + while (newHeight < buffer.height) { + + newHeight <<= 1; + + } + + switch (__renderer.context) { + + case OPENGL (_): + + var data = buffer.data; + var newData = new UInt8Array (newWidth * newHeight * 4); + var sourceIndex:Int, index:Int; + + for (y in 0...buffer.height) { + + for (x in 0...buffer.width) { + + sourceIndex = y * buffer.width + x; + index = y * newWidth + x * 4; + + newData[index] = data[sourceIndex]; + newData[index + 1] = data[sourceIndex + 1]; + newData[index + 2] = data[sourceIndex + 2]; + newData[index + 3] = data[sourceIndex + 3]; + + } + + } + + buffer.data = newData; + buffer.width = newWidth; + buffer.height = newHeight; + + case FLASH (_): + + #if flash + var bitmapData = new BitmapData (newWidth, newHeight, true, 0x000000); + bitmapData.draw (buffer.src, null, null, null, true); + + buffer.src = bitmapData; + buffer.width = newWidth; + buffer.height = newHeight; + #end + + default: + + // TODO + + } + + } + + return value; + + } -} - - -#if js -private typedef ImageSource = js.html.Image; -#elseif flash -private typedef ImageSource = flash.display.BitmapData; -#else -private typedef ImageSource = UInt8Array; -#end + private function get_premultiplied ():Bool { + + return buffer.premultiplied; + + } + + + private function set_premultiplied (value:Bool):Bool { + + if (value && !buffer.premultiplied) { + + switch (__renderer.context) { + + case OPENGL (_): + + var data = buffer.data; + var index, a; + var length = Std.int (data.length / 4); + + for (i in 0...length) { + + index = i * 4; + + a = data[index + 3]; + data[index] = (data[index] * a) >> 8; + data[index + 1] = (data[index + 1] * a) >> 8; + data[index + 2] = (data[index + 2] * a) >> 8; + + } + + buffer.premultiplied = true; + + default: + + // TODO + + } + + } else { + + // TODO, unmultiply + + } + + return value; + + } + + +} \ No newline at end of file diff --git a/lime/media/ImageBuffer.hx b/lime/media/ImageBuffer.hx index df6100239..ad3775329 100644 --- a/lime/media/ImageBuffer.hx +++ b/lime/media/ImageBuffer.hx @@ -1,82 +1,120 @@ package lime.media; +import lime.system.System; +import lime.utils.ByteArray; import lime.utils.UInt8Array; +#if js +import js.html.Image in HTMLImage; +#elseif flash +import flash.display.BitmapData; +#end -abstract ImageBuffer(UInt8Array) from UInt8Array to UInt8Array { + +class ImageBuffer { - public var length (get, never):Int; + public var bitsPerPixel:Int; + public var data:UInt8Array; + public var height:Int; + public var premultiplied:Bool; + public var width:Int; + + #if js + public var src:HTMLImage; + #elseif flash + public var src:BitmapData; + #else + public var src:Dynamic; + #end - public function new (data:Dynamic = null) { + public function new (data:UInt8Array = null, width:Int = 0, height:Int = 0, bitsPerPixel:Int = 4) { - this = new UInt8Array (data); + this.data = data; + this.width = width; + this.height = height; + this.bitsPerPixel = bitsPerPixel; } - public function getComponent (pos:Int):Int { + #if flash + public static function fromBitmapData (bitmapData:BitmapData):ImageBuffer { - return this[pos]; + var buffer = new ImageBuffer (null, bitmapData.width, bitmapData.height); + buffer.src = bitmapData; + return buffer; } + #end - public function getPixel (pos:Int):Int { + public static function fromBytes (bytes:ByteArray):ImageBuffer { - var index = pos * 4; + #if (cpp || neko) - return ((this[index + 3] << 24) | (this[index] << 16 ) | (this[index + 1] << 8) | this[index + 2]); + var data = lime_image_load (bytes); - } - - - public function premultiply ():Void { - - var index, a; - var length = Std.int (this.length / 4); - - for (i in 0...length) { + if (data != null) { - index = i * 4; + return new ImageBuffer (new UInt8Array (data.data), data.width, data.height, data.bpp); - a = this[index + 3]; - this[index] = (this[index] * a) >> 8; - this[index + 1] = (this[index + 1] * a) >> 8; - this[index + 2] = (this[index + 2] * a) >> 8; + } else { + + return null; } - } - - - public function setComponent (pos:Int, value:Int):Void { + #else - this[pos] = value; + throw "ImageBuffer.loadFromFile not supported on this target"; + + #end } - public function setPixel (pos:Int, value:Int):Void { + public static function fromFile (path:String):ImageBuffer { - var index = pos * 4; + #if (cpp || neko) - this[index + 3] = (value >> 24) & 0xFF; - this[index] = (value >> 16) & 0xFF; - this[index + 1] = (value >> 8) & 0xFF; - this[index + 2] = value & 0xFF; + var data = lime_image_load (path); + + if (data != null) { + + return new ImageBuffer (new UInt8Array (data.data), data.width, data.height, data.bpp); + + } else { + + return null; + + } + + #else + + throw "ImageBuffer.loadFromFile not supported on this target"; + + #end } - private function get_length ():Int { + #if js + public static function fromImage (image:HTMLImage):ImageBuffer { - return this.length; + var buffer = new ImageBuffer (null, image.width, image.height); + buffer.src = image; + return buffer; } + #end + #if (cpp || neko) + private static var lime_image_load:Dynamic = System.load ("lime", "lime_image_load", 1); + #end + } \ No newline at end of file diff --git a/samples/SimpleImage/Source/Main.hx b/samples/SimpleImage/Source/Main.hx index 68877eb22..89d3afe4d 100644 --- a/samples/SimpleImage/Source/Main.hx +++ b/samples/SimpleImage/Source/Main.hx @@ -4,6 +4,7 @@ package; import lime.app.Application; import lime.graphics.opengl.*; import lime.graphics.RenderContext; +import lime.media.ImageBuffer; import lime.utils.Float32Array; import lime.utils.GLUtils; import lime.utils.Matrix4; @@ -14,6 +15,7 @@ class Main extends Application { private var buffer:GLBuffer; + private var image:ImageBuffer; private var matrixUniform:GLUniformLocation; private var program:GLProgram; private var texture:GLTexture; @@ -30,11 +32,12 @@ class Main extends Application { public override function init (context:RenderContext):Void { + image = Assets.getImageBuffer ("assets/lime.png"); + switch (context) { case CANVAS (context): - var image = Assets.getImage ("assets/lime.png"); context.fillStyle = "#" + StringTools.hex (config.background, 6); context.fillRect (0, 0, window.width, window.height); context.drawImage (image.src, 0, 0, image.width, image.height); @@ -42,16 +45,13 @@ class Main extends Application { case DOM (element): #if js - var image = new js.html.Image (); - image.src = Assets.getPath ("assets/lime.png"); element.style.backgroundColor = "#" + StringTools.hex (config.background, 6); - element.appendChild (image); + element.appendChild (image.src); #end case FLASH (sprite): #if flash - var image = Assets.getImage ("assets/lime.png"); var bitmap = new flash.display.Bitmap (image.src); sprite.addChild (bitmap); #end @@ -98,8 +98,6 @@ class Main extends Application { gl.enableVertexAttribArray (textureAttribute); gl.uniform1i (imageUniform, 0); - var image = Assets.getImage ("assets/lime.png"); - var data = [ image.width, image.height, 0, 1, 1, @@ -121,7 +119,7 @@ class Main extends Application { #if js gl.texImage2D (gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image.src); #else - gl.texImage2D (gl.TEXTURE_2D, 0, gl.RGBA, image.textureWidth, image.textureHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, image.data); + gl.texImage2D (gl.TEXTURE_2D, 0, gl.RGBA, image.width, image.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, image.data); #end gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); diff --git a/templates/haxe/DefaultAssetLibrary.hx b/templates/haxe/DefaultAssetLibrary.hx index 7fb857b62..6f4d77474 100644 --- a/templates/haxe/DefaultAssetLibrary.hx +++ b/templates/haxe/DefaultAssetLibrary.hx @@ -5,7 +5,7 @@ import haxe.Timer; import haxe.Unserializer; import lime.app.Preloader; import lime.media.AudioBuffer; -import lime.media.Image; +import lime.media.ImageBuffer; import lime.media.openal.AL; import lime.utils.ByteArray; import lime.utils.UInt8Array; @@ -224,21 +224,19 @@ class DefaultAssetLibrary extends AssetLibrary { } - public override function getImage (id:String):Image { + public override function getImageBuffer (id:String):ImageBuffer { #if flash - var bitmapData = cast (Type.createInstance (className.get (id), []), BitmapData); - return new Image (bitmapData, bitmapData.width, bitmapData.height); + return ImageBuffer.fromBitmapData (cast (Type.createInstance (className.get (id), []), BitmapData)); #elseif js - var image = Preloader.images.get (path.get (id)); - return new Image (image, image.width, image.height); + return ImageBuffer.fromImage (Preloader.images.get (path.get (id))); #else - return Image.fromFile (path.get (id)); + return ImageBuffer.fromFile (path.get (id)); #end @@ -441,7 +439,7 @@ class DefaultAssetLibrary extends AssetLibrary { } - public override function loadImage (id:String, handler:Image -> Void):Void { + public override function loadImageBuffer (id:String, handler:ImageBuffer -> Void):Void { #if flash @@ -451,20 +449,20 @@ class DefaultAssetLibrary extends AssetLibrary { loader.contentLoaderInfo.addEventListener (Event.COMPLETE, function (event:Event) { var bitmapData = cast (event.currentTarget.content, Bitmap).bitmapData; - handler (new Image (bitmapData, bitmapData.width, bitmapData.height)); + handler (ImageBuffer.fromBitmapData (bitmapData)); }); loader.load (new URLRequest (path.get (id))); } else { - handler (getImage (id)); + handler (getImageBuffer (id)); } #else - handler (getImage (id)); + handler (getImageBuffer (id)); #end