Przeglądaj źródła

Merge pull request #147 from motion-twin/lime

Lime/Openfl better integration
Nicolas Cannasse 9 lat temu
rodzic
commit
125eb62e00

+ 17 - 7
h2d/Text.hx

@@ -9,7 +9,7 @@ enum Align {
 class Text extends Drawable {
 
 	public var font(default, set) : Font;
-	public var text(default, set) : String;
+	public var text(default, set) : hxd.UString;
 	public var textColor(default, set) : Int;
 	public var maxWidth(default, set) : Null<Float>;
 	public var dropShadow : { dx : Float, dy : Float, color : Int, alpha : Float };
@@ -27,6 +27,10 @@ class Text extends Drawable {
 	var calcWidth:Int;
 	var calcHeight:Int;
 	var calcSizeHeight:Int;
+	
+	#if lime
+	var waShader : h3d.shader.WhiteAlpha;
+	#end
 
 	public function new( font : Font, ?parent ) {
 		super(parent);
@@ -41,6 +45,12 @@ class Text extends Drawable {
 	function set_font(font) {
 		if( this.font == font ) return font;
 		this.font = font;
+		#if lime
+		if( font.tile.getTexture().format == ALPHA )
+			if( waShader == null ) addShader( waShader = new h3d.shader.WhiteAlpha() );
+		else
+			if( waShader != null ) removeShader( waShader );
+		#end
 		if( glyphs != null ) glyphs.remove();
 		glyphs = new TileGroup(font == null ? null : font.tile, this);
 		glyphs.visible = false;
@@ -98,7 +108,7 @@ class Text extends Drawable {
 		glyphs.drawWith(ctx,this);
 	}
 
-	function set_text(t) {
+	function set_text(t : hxd.UString) {
 		var t = t == null ? "null" : t;
 		if( t == this.text ) return t;
 		this.text = t;
@@ -111,7 +121,7 @@ class Text extends Drawable {
 		if( allocated && text != null && font != null ) initGlyphs(text);
 	}
 
-	public function calcTextWidth( text : String ) {
+	public function calcTextWidth( text : hxd.UString ) {
 		if( calcDone ) {
 			var ow = calcWidth, oh = calcHeight, osh = calcSizeHeight, oy = calcYMin;
 			initGlyphs(text, false);
@@ -128,7 +138,7 @@ class Text extends Drawable {
 		}
 	}
 
-	public function splitText( text : String, leftMargin = 0 ) {
+	public function splitText( text : hxd.UString, leftMargin = 0 ) {
 		if( maxWidth == null )
 			return text;
 		var lines = [], rest = text, restPos = 0;
@@ -146,7 +156,7 @@ class Text extends Drawable {
 				var size = x + esize + letterSpacing;
 				var k = i + 1, max = text.length;
 				var prevChar = prevChar;
-				while( size <= maxWidth && k < text.length ) {
+				while( size <= maxWidth && k < max ) {
 					var cc = text.charCodeAt(k++);
 					if( font.charset.isSpace(cc) || cc == '\n'.code ) break;
 					var e = font.getChar(cc);
@@ -179,7 +189,7 @@ class Text extends Drawable {
 		return lines.join("\n");
 	}
 
-	function initGlyphs( text : String, rebuild = true, lines : Array<Int> = null ) : Void {
+	function initGlyphs( text : hxd.UString, rebuild = true, lines : Array<Int> = null ) : Void {
 		if( rebuild ) glyphs.clear();
 		var x = 0, y = 0, xMax = 0, prevChar = -1;
 		var align = rebuild ? textAlign : Left;
@@ -208,7 +218,7 @@ class Text extends Drawable {
 				var size = x + esize + letterSpacing;
 				var k = i + 1, max = text.length;
 				var prevChar = prevChar;
-				while( size <= maxWidth && k < text.length ) {
+				while( size <= maxWidth && k < max ) {
 					var cc = text.charCodeAt(k++);
 					if( font.charset.isSpace(cc) || cc == '\n'.code ) break;
 					var e = font.getChar(cc);

+ 27 - 18
h3d/impl/GlDriver.hx

@@ -86,18 +86,18 @@ class GlDriver extends Driver {
 	var curTarget : h3d.mat.Texture;
 
 	public function new() {
-		#if js
+		#if (nme || openfl || lime)
+		// check for a bug in HxCPP handling of sub buffers
+		var tmp = new Float32Array(8);
+		var sub = new Float32Array(tmp.buffer, 0, 4);
+		#if cpp fixMult = sub.length == 1; #end  // should be 4
+		#elseif js
 		canvas = @:privateAccess hxd.Stage.getCanvas();
 		if( canvas == null ) throw "Canvas #webgl not found";
 		gl = canvas.getContextWebGL({alpha:false});
 		if( gl == null ) throw "Could not acquire GL context";
 		// debug if webgl_debug.js is included
 		untyped if( __js__('typeof')(WebGLDebugUtils) != "undefined" ) gl = untyped WebGLDebugUtils.makeDebugContext(gl);
-		#elseif (nme || openfl)
-		// check for a bug in HxCPP handling of sub buffers
-		var tmp = new Float32Array(8);
-		var sub = new Float32Array(tmp.buffer, 0, 4);
-		fixMult = sub.length == 1; // should be 4
 		#end
 		programs = new Map();
 		curAttribs = 0;
@@ -374,13 +374,14 @@ class GlDriver extends Driver {
 	function getChannels( t : Texture ) {
 		return switch( t.internalFmt ) {
 		case GL.RGBA: GL.RGBA;
+		case GL.ALPHA: GL.ALPHA;
 		default: throw "Invalid format " + t.internalFmt;
 		}
 	}
 
 	override function isSupportedFormat( fmt : h3d.mat.Data.TextureFormat ) {
 		return switch( fmt ) {
-		case RGBA: true;
+		case RGBA, ALPHA: true;
 		case RGBA32F: hasFeature(FloatTextures);
 		default: false;
 		}
@@ -394,6 +395,8 @@ class GlDriver extends Driver {
 		switch( t.format ) {
 		case RGBA:
 			// default
+		case ALPHA:
+			tt.internalFmt = GL.ALPHA;
 		case RGBA32F if( hasFeature(FloatTextures) ):
 			tt.pixelFmt = GL.FLOAT;
 		default:
@@ -473,11 +476,11 @@ class GlDriver extends Driver {
 	}
 
 	override function uploadTextureBitmap( t : h3d.mat.Texture, bmp : hxd.BitmapData, mipLevel : Int, side : Int ) {
-		#if (nme || hxsdl)
+	#if (nme || hxsdl || openfl || lime)
 		var pixels = bmp.getPixels();
 		uploadTexturePixels(t, pixels, mipLevel, side);
 		pixels.dispose();
-		#else
+	#else
 		if( t.format != RGBA ) {
 			var pixels = bmp.getPixels();
 			uploadTexturePixels(t, pixels, mipLevel, side);
@@ -485,15 +488,19 @@ class GlDriver extends Driver {
 		} else {
 			var img = bmp.toNative();
 			gl.bindTexture(GL.TEXTURE_2D, t.t.t);
-			#if lime
-			gl.texImage2D(GL.TEXTURE_2D, mipLevel, t.t.internalFmt, bmp.width, bmp.height, 0, getChannels(t.t), t.pixelFmt, img.image.data);
-			#else
 			gl.texImage2D(GL.TEXTURE_2D, mipLevel, t.t.internalFmt, getChannels(t.t), t.t.pixelFmt, img.getImageData(0, 0, bmp.width, bmp.height));
-			#end
 			if( t.flags.has(MipMapped) ) gl.generateMipmap(GL.TEXTURE_2D);
 			gl.bindTexture(GL.TEXTURE_2D, null);
 			t.flags.set(WasCleared);
 		}
+	#end
+	}
+
+	inline static function bytesToUint8Array( b : haxe.io.Bytes ) : Uint8Array {
+		#if (lime && !js)
+		return new Uint8Array(b);
+		#else
+		return new Uint8Array(b.getData());
 		#end
 	}
 
@@ -503,9 +510,11 @@ class GlDriver extends Driver {
 		#if hxsdl
 		pixels.setFlip(true);
 		gl.texImage2D(GL.TEXTURE_2D, mipLevel, t.t.internalFmt, t.width, t.height, 0, getChannels(t.t), t.t.pixelFmt, pixels.bytes.getData());
+		#elseif lime
+		pixels.setFlip(true);
+		gl.texImage2D(GL.TEXTURE_2D, mipLevel, t.t.internalFmt, t.width, t.height, 0, getChannels(t.t), t.t.pixelFmt, bytesToUint8Array(pixels.bytes));
 		#else
-		var pixels = new Uint8Array(pixels.bytes.getData());
-		gl.texImage2D(GL.TEXTURE_2D, mipLevel, t.t.internalFmt, t.width, t.height, 0, getChannels(t.t), t.t.pixelFmt, pixels);
+		gl.texImage2D(GL.TEXTURE_2D, mipLevel, t.t.internalFmt, t.width, t.height, 0, getChannels(t.t), t.t.pixelFmt, bytesToUint8Array(pixels.bytes));
 		#end
 		if( t.flags.has(MipMapped) ) gl.generateMipmap(GL.TEXTURE_2D);
 		gl.bindTexture(GL.TEXTURE_2D, null);
@@ -531,7 +540,7 @@ class GlDriver extends Driver {
 		#if hxsdl
 		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride * 4, buf.getData(), bufPos, vertexCount * stride * 4);
 		#else
-		var buf = new Uint8Array(buf.getData());
+		var buf = bytesToUint8Array(buf);
 		var sub = new Uint8Array(buf.buffer, bufPos, vertexCount * stride * 4);
 		gl.bufferSubData(GL.ARRAY_BUFFER, startVertex * stride * 4, sub);
 		#end
@@ -555,7 +564,7 @@ class GlDriver extends Driver {
 		#if hxsdl
 		gl.bufferSubData(GL.ELEMENT_ARRAY_BUFFER, startIndice * 2, buf.getData(), bufPos, indiceCount * 2);
 		#else
-		var buf = new Uint8Array(buf.getData());
+		var buf = bytesToUint8Array(buf);
 		var sub = new Uint8Array(buf.buffer, bufPos, indiceCount * 2);
 		gl.bufferSubData(GL.ELEMENT_ARRAY_BUFFER, startIndice * 2, sub);
 		#end
@@ -626,7 +635,7 @@ class GlDriver extends Driver {
 	}
 
 	override function isDisposed() {
-		#if (nme || openfl)
+		#if (nme || openfl) //lime ??
 		return false;
 		#else
 		return gl.isContextLost();

+ 4 - 1
h3d/mat/Texture.hx

@@ -19,7 +19,10 @@ class Texture {
 	/**
 		Tells if the Driver requires y-flipping the texture pixels before uploading.
 	**/
-	public static inline var nativeFlip = #if hxsdl true #else false #end;
+	public static inline var nativeFlip = 	#if (hxsdl) true
+											#elseif (openfl) false
+											#elseif (lime && (cpp || neko || nodejs)) true
+											#else false #end;
 
 	var t : h3d.impl.Driver.Texture;
 	var mem : h3d.impl.MemoryManager;

+ 16 - 0
h3d/shader/WhiteAlpha.hx

@@ -0,0 +1,16 @@
+package h3d.shader;
+
+class WhiteAlpha extends hxsl.Shader {
+
+	static var SRC = {
+
+		var textureColor : Vec4;
+
+		function fragment() {
+			textureColor.rgb = vec3(1.0);
+		}
+
+	};
+
+
+}

+ 27 - 1
hxd/BitmapData.hx

@@ -5,6 +5,8 @@ typedef BitmapInnerData =
 	flash.display.BitmapData;
 #elseif js
 	js.html.CanvasRenderingContext2D;
+#elseif lime
+	lime.graphics.Image;
 #else
 	BitmapInnerDataImpl;
 
@@ -49,6 +51,8 @@ class BitmapData {
 			canvas.width = width;
 			canvas.height = height;
 			ctx = canvas.getContext2d();
+			#elseif lime
+			data = new lime.graphics.Image( null, 0, 0, width, height );
 			#else
 			data = new BitmapInnerData();
 			data.pixels = new haxe.ds.Vector(width * height);
@@ -97,7 +101,11 @@ class BitmapData {
 		for( dy in 0...height ) {
 			var p = x + (y + dy) * data.width;
 			for( dx in 0...width )
+				#if lime
+				data.buffer.data[p++] = color;
+				#else
 				data.pixels[p++] = color;
+				#end
 		}
 		#end
 	}
@@ -292,6 +300,8 @@ class BitmapData {
 			i = ctx.getImageData(x, y, 1, 1);
 		}
 		return (i.data[a] << 16) | (i.data[a|1] << 8) | i.data[a|2] | (i.data[a|3] << 24);
+		#elseif lime
+		return if( x >= 0 && y >= 0 && x < data.width && y < data.height ) data.buffer.data[x + y * data.width] else 0;
 		#else
 		return if( x >= 0 && y >= 0 && x < data.width && y < data.height ) data.pixels[x + y * data.width] else 0;
 		#end
@@ -323,6 +333,8 @@ class BitmapData {
 		i.data[2] = c & 0xFF;
 		i.data[3] = (c >>> 24) & 0xFF;
 		ctx.putImageData(i, x, y);
+		#elseif lime
+		if( x >= 0 && y >= 0 && x < data.width && y < data.height ) data.buffer.data[x + y * data.width] = c;
 		#else
 		if( x >= 0 && y >= 0 && x < data.width && y < data.height ) data.pixels[x + y * data.width] = c;
 		#end
@@ -350,7 +362,7 @@ class BitmapData {
 
 	public function getPixels() : Pixels {
 		#if (flash || nme || openfl)
-		var p = new Pixels(width, height, haxe.io.Bytes.ofData(bmp.getPixels(bmp.rect)#if openfl .getData() #end), ARGB);
+		var p = new Pixels(width, height, haxe.io.Bytes.ofData(bmp.getPixels(bmp.rect)), ARGB);
 		p.flags.set(AlphaPremultiplied);
 		return p;
 		#elseif js
@@ -359,6 +371,9 @@ class BitmapData {
 		var data = ctx.getImageData(0, 0, w, h).data;
 		var pixels = data.buffer;
 		return new Pixels(w, h, haxe.io.Bytes.ofData(pixels), RGBA);
+		#elseif lime
+		var p = new Pixels(width, height, this.data.data.buffer, RGBA);
+		return p;
 		#else
 		var out = hxd.impl.Tmp.getBytes(data.width * data.height * 4);
 		for( i in 0...data.width*data.height )
@@ -392,6 +407,17 @@ class BitmapData {
 		#elseif (nme || openfl)
 		pixels.convert(BGRA);
 		bmp.setPixels(bmp.rect, flash.utils.ByteArray.fromBytes(pixels.bytes));
+		#elseif lime
+		// TODO format
+		pixels.convert(BGRA);
+		var src = pixels.bytes;
+		var i = 0;
+		for( y in 0...height ){
+			for( x in 0...width  ){
+				data.setPixel32( x, y, src.getInt32(i<<2) );
+				i++;
+			}
+		}
 		#else
 		pixels.convert(BGRA);
 		var src = pixels.bytes;

+ 1 - 0
hxd/PixelFormat.hx

@@ -6,4 +6,5 @@ enum PixelFormat {
 	RGBA;
 	RGBA16F;
 	RGBA32F;
+	ALPHA;
 }

+ 1 - 0
hxd/Pixels.hx

@@ -336,6 +336,7 @@ class Pixels {
 
 	public static function bytesPerPixel( format : PixelFormat ) {
 		return switch( format ) {
+		case ALPHA: 1;
 		case ARGB, BGRA, RGBA: 4;
 		case RGBA16F: 8;
 		case RGBA32F: 16;

+ 6 - 0
hxd/Res.hx

@@ -14,6 +14,12 @@ class Res {
 	public static macro function initEmbed(?options:haxe.macro.Expr.ExprOf<hxd.res.EmbedOptions>) {
 		return macro hxd.Res.loader = new hxd.res.Loader(hxd.fs.EmbedFileSystem.create(null,$options));
 	}
+	
+	#if lime
+	public static macro function initLime() {
+		return macro hxd.Res.loader = new hxd.res.Loader(new hxd.fs.LimeFileSystem());
+	}
+	#end
 
 	public static macro function initLocal() {
 		var dir = haxe.macro.Context.definedValue("resourcesPath");

+ 35 - 3
hxd/Stage.hx

@@ -11,11 +11,13 @@ class Stage {
 	#if (flash || openfl || nme)
 	var stage : flash.display.Stage;
 	var fsDelayed : Bool;
+	#elseif lime
+	var limeStage : hxd.impl.LimeStage;
 	#end
 	var resizeEvents : List<Void -> Void>;
 	var eventTargets : List<Event -> Void>;
 
-	#if js
+	#if (js && !lime)
 	@:allow(hxd)
 	static function getCanvas() {
 		var canvas : js.html.CanvasElement = cast js.Browser.document.getElementById("webgl");
@@ -40,6 +42,9 @@ class Stage {
 		stage.scaleMode = flash.display.StageScaleMode.NO_SCALE;
 		stage.addEventListener(flash.events.Event.RESIZE, onResize);
 		initGesture(false);
+		#elseif lime
+		limeStage = new hxd.impl.LimeStage( this );
+		lime.app.Application.current.addModule( limeStage );
 		#elseif js
 		canvas = getCanvas();
 		canvasPos = canvas.getBoundingClientRect();
@@ -331,7 +336,7 @@ class Stage {
 		event(ev);
 	}
 
-#elseif js
+#elseif (js && !lime)
 
 	var curMouseX : Float = 0.;
 	var curMouseY : Float = 0.;
@@ -422,6 +427,33 @@ class Stage {
 		return b;
 	}
 
+#elseif lime
+
+	inline function get_mouseX() {
+		return limeStage.mouseX;
+	}
+
+	inline function get_mouseY() {
+		return limeStage.mouseY;
+	}
+
+	inline function get_width() {
+		return limeStage.width;
+	}
+
+	inline function get_height() {
+		return limeStage.height;
+	}
+
+	function get_mouseLock() {
+		return false;
+	}
+
+	function set_mouseLock(b) {
+		if( b ) throw "Not implemented";
+		return b;
+	}
+
 #else
 
 	function get_mouseX() {
@@ -451,4 +483,4 @@ class Stage {
 
 #end
 
-}
+}

+ 100 - 63
hxd/System.hx

@@ -96,12 +96,14 @@ class System {
 		}
 		VIEW.render = function(_) if ( f != null ) f();
 		#elseif openfl
-		if( VIEW == null ) {
-			VIEW = new openfl.display.OpenGLView();
-			VIEW.name = "glView";
-			flash.Lib.current.addChildAt(VIEW,0);
+		if( openfl.display.OpenGLView.isSupported ){
+			if( VIEW == null ) {
+				VIEW = new openfl.display.OpenGLView();
+				VIEW.name = "glView";
+				flash.Lib.current.addChildAt(VIEW, 0);
+			}
+			VIEW.render = function(_) if ( f != null ) f();
 		}
-		VIEW.render = function(_) if ( f != null ) f();
 		#else
 		if( loop != null )
 			flash.Lib.current.removeEventListener(flash.events.Event.ENTER_FRAME, loop);
@@ -150,64 +152,6 @@ class System {
          (0 == 2 ? nme.Lib.HW_AA : 0),
          "Heaps Application"
 		);
-		#elseif openfl
-		var windowSize = haxe.macro.Compiler.getDefine("window");
-		if( windowSize == null ) windowSize = "800x600";
-		var windowSize = windowSize.split("x");
-		var width = Std.parseInt(windowSize[0]), height = Std.parseInt(windowSize[1]);
-		if( width < 100 ) width = 100;
-		if ( height < 100 ) height = 100;
-
-		var config = {
-
-			build: "1",
-			company: "Heaps",
-			fps: 60,
-			orientation: "",
-			packageName: "heaps.application",
-			version: "0.0.1",
-			windows: [
-
-				{
-					antialiasing: 0,
-					background: 16777215,
-					borderless: false,
-					depthBuffer: true,
-					display: 0,
-					fullscreen: false,
-					hardware: true,
-					height: height,
-					parameters: "{}",
-					resizable: true,
-					stencilBuffer: true,
-					title: "Heaps Application",
-					vsync: true,
-					width: width,
-					x: null,
-					y: null
-				},
-			]
-
-		};
-
-		var app = new openfl.display.Application ();
-		app.create (config);
-
-		try {
-			callb();
-		} catch( e : Dynamic ) {
-			Sys.println(e);
-			#if debug
-			Sys.println(haxe.CallStack.toString(haxe.CallStack.exceptionStack()));
-			#end
-		}
-
-		var result = app.exec ();
-
-		#if sys
-		Sys.exit(result);
-		#end
-
 		#else
 		callb();
 		#end
@@ -311,6 +255,99 @@ class System {
 		return flash.system.Capabilities.language;
 	}
 
+	#elseif lime
+
+	static function get_isWindowed() {
+		return true;
+	}
+
+	static function get_isTouch() {
+		#if desktop
+		return false;
+		#else
+		return true;
+		#end
+	}
+
+	static function get_width() {
+		var win = lime.app.Application.current.window;
+		return Std.int(win.width * win.scale);
+	}
+
+	static function get_height() {
+		var win = lime.app.Application.current.window;
+		return Std.int(win.height * win.scale);
+	}
+
+	static function get_isAndroid() {
+		return #if android true #else false #end;
+	}
+
+	static function get_isIOS() {
+		return #if ios true #else false #end;
+	}
+
+	static function get_screenDPI() {
+		return 0; // TODO
+	}
+
+	@:allow(hxd.impl.LimeStage)
+	static var loopFunc = null;
+
+	public static function getCurrentLoop() {
+		return loopFunc;
+	}
+
+	public static function setLoop( f : Void -> Void ) {
+		loopFunc = f;
+	}
+
+	public static function start(callb) {
+		callb();
+	}
+
+	public static function getClipboard() : String {
+		return lime.system.Clipboard.text;
+	}
+
+	public static function exit() {
+		return lime.system.System.exit( 0 );
+	}
+
+	public static function setNativeCursor( c : Cursor ) {
+		lime.ui.Mouse.cursor = switch( c ){
+		case Default: DEFAULT;
+		case Button: POINTER;
+		case Move: MOVE;
+		case TextInput: TEXT;
+		case Hide: DEFAULT;
+		case Custom(_,_,_,_):
+			throw "not supported";
+		}
+		if( c == Hide ) lime.ui.Mouse.hide() else lime.ui.Mouse.show();
+	}
+
+
+	/**
+		Returns the device name:
+			"PC" for a desktop computer
+			Or the android device name
+			(will add iPad/iPhone/iPod soon)
+	**/
+	static var CACHED_NAME = null;
+	public static function getDeviceName() {
+		if( CACHED_NAME != null )
+			return CACHED_NAME;
+		var name;
+		name = "Unknown"; // TODO
+		CACHED_NAME = name;
+		return name;
+	}
+
+	static function get_lang() {
+		return null; // TODO
+	}
+
 	#elseif js
 
 	static var LOOP = null;

+ 39 - 0
hxd/UString.hx

@@ -0,0 +1,39 @@
+package hxd;
+
+abstract UString(String) from String to String {
+
+	public var length(get,never) : Int;
+
+	inline function get_length() : Int {
+		#if (flash || js)
+		return this.length;
+		#else
+		return haxe.Utf8.length( this );
+		#end
+	}
+
+	public inline function charCodeAt( pos : Int ) : Int {
+		#if (flash || js)
+		return this.charCodeAt( pos );
+		#else
+		return haxe.Utf8.charCodeAt( this, pos );
+		#end
+	}
+
+	public inline function substr( pos : Int, ?len : Int ) : UString {
+		#if (flash  || js)
+		return this.substr( pos, len );
+		#else
+		return haxe.Utf8.sub( this, pos, len );
+		#end
+	}
+
+	public inline function charAt( pos : Int ) : UString {
+		#if (flash || js)
+		return this.charAt( pos );
+		#else
+		return haxe.Utf8.sub( this, pos, 1 );
+		#end
+	}
+
+}

+ 5 - 2
hxd/fs/EmbedFileSystem.hx

@@ -109,7 +109,7 @@ private class EmbedEntry extends FileEntry {
 	}
 
 	override function loadBitmap( onLoaded : LoadedBitmap -> Void ) : Void {
-		#if flash
+		#if (flash || openfl)
 		var loader = new flash.display.Loader();
 		loader.contentLoaderInfo.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e:flash.events.IOErrorEvent) {
 			throw Std.string(e) + " while loading " + relPath;
@@ -140,6 +140,9 @@ private class EmbedEntry extends FileEntry {
 		for( i in 0...(3-(bytes*4)%3)%3 )
 			extra += "=";
 		image.src = "data:image/" + extension + ";base64," + rawData + extra;
+		#elseif lime
+		open();
+		onLoaded( new LoadedBitmap(lime.graphics.Image.fromBytes(bytes)) );
 		#else
 		throw "TODO";
 		#end
@@ -272,4 +275,4 @@ class EmbedFileSystem #if !macro implements FileSystem #end {
 	public function dispose() {
 	}
 
-}
+}

+ 219 - 0
hxd/fs/LimeFileSystem.hx

@@ -0,0 +1,219 @@
+package hxd.fs;
+
+#if !macro
+
+@:allow(hxd.fs.LimeFileSystem)
+@:access(hxd.fs.LimeFileSystem)
+private class LimeEntry extends FileEntry {
+
+	var fs : LimeFileSystem;
+	var relPath : String;
+	
+	#if flash
+	var bytes : flash.utils.ByteArray;
+	#else
+	var bytes : haxe.io.Bytes;
+	var readPos : Int;
+	#end
+
+	var isReady:Bool = false;
+	override function get_isAvailable() return isReady;
+	
+	function new(fs, name, relPath) {
+		this.fs = fs;
+		this.name = name;
+		this.relPath = relPath;
+	}
+
+	override function getSign() : Int {
+		#if flash
+		var old = bytes == null ? 0 : bytes.position;
+		open();
+		bytes.endian = flash.utils.Endian.LITTLE_ENDIAN;
+		var v = bytes.readUnsignedInt();
+		bytes.position = old;
+		return v;
+		#else
+		var old = readPos;
+		open();
+		readPos = old;
+		return bytes.get(0) | (bytes.get(1) << 8) | (bytes.get(2) << 16) | (bytes.get(3) << 24);
+		#end
+	}
+
+	override function getBytes() : haxe.io.Bytes {
+		#if flash
+		if( bytes == null )
+			open();
+		return haxe.io.Bytes.ofData(bytes);
+		#else
+		if( bytes == null )
+			open();
+		return bytes;
+		#end
+	}
+	
+	override function open() {
+		if(lime.Assets.isLocal(name)) {
+			var inBytes = lime.Assets.getBytes(name);
+			if( inBytes == null ) throw "Missing resource " + name;
+			#if flash
+			bytes = inBytes.getData();
+			bytes.position = 0;
+			#else
+			bytes = inBytes;
+			readPos = 0;
+			#end
+			isReady = true;
+		} else {
+			//#if flash
+			//	throw "Non embeded files are not supported on flash platform with Lime";
+			//#else
+			lime.Assets.loadBytes(name).onComplete(function(inBytes) {
+				#if flash
+				bytes = inBytes.getData();
+				bytes.position = 0;
+				#else
+				bytes = inBytes;
+				readPos = 0;
+				#end
+				isReady = true;
+			});
+			//#end
+		}
+	}
+	
+	override function skip( nbytes : Int ) {
+		#if flash
+		bytes.position += nbytes;
+		#else
+		readPos += nbytes;
+		#end
+	}
+	
+	override function readByte() : Int {
+		#if flash
+		return bytes.readUnsignedByte();
+		#else
+		return bytes.get(readPos++);
+		#end
+	}
+	
+	override function read( out : haxe.io.Bytes, pos : Int, size : Int ) : Void {
+		#if flash
+		bytes.readBytes(out.getData(), pos, size);
+		#else
+		out.blit(pos, bytes, readPos, size);
+		readPos += size;
+		#end
+	}
+	
+	override function close() {
+		bytes = null;
+		#if !flash
+		readPos = 0;
+		#end
+	}
+
+	override function load( ?onReady : Void -> Void ) : Void {
+		#if (flash || js)
+		if( onReady != null ) haxe.Timer.delay(onReady, 1);
+		#end
+	}
+
+	override function loadBitmap( onLoaded : LoadedBitmap -> Void ) : Void {
+		#if flash
+		var loader = new flash.display.Loader();
+		loader.contentLoaderInfo.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e:flash.events.IOErrorEvent) {
+			throw Std.string(e) + " while loading " + relPath;
+		});
+		loader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE, function(_) {
+			var content : flash.display.Bitmap = cast loader.content;
+			onLoaded(new LoadedBitmap(content.bitmapData));
+			loader.unload();
+		});
+		open();
+		loader.loadBytes(bytes);
+		close(); // flash will copy bytes content in loadBytes() !
+		#else
+		
+		lime.graphics.Image.fromBytes(bytes, function(img) {
+			onLoaded(new hxd.fs.LoadedBitmap(img));
+		});
+		close();
+		#end
+	}
+	
+	override function get_isDirectory() {
+		return fs.isDirectory(relPath);
+	}
+
+	override function get_path() {
+		return relPath == "." ? "<root>" : relPath;
+	}
+
+	override function exists( name : String ) {
+		return fs.exists(relPath == "." ? name : relPath + "/" + name);
+	}
+
+	override function get( name : String ) {
+		return fs.get(relPath == "." ? name : relPath + "/" + name);
+	}
+
+	override function get_size() {
+		open();
+		return bytes.length;
+	}
+
+	override function iterator() {
+		return new hxd.impl.ArrayIterator(fs.subFiles(relPath));
+	}
+}
+
+#end
+
+class LimeFileSystem #if !macro implements FileSystem #end {
+
+	#if !macro
+	
+	public function new() {
+	}
+
+	public function getRoot() : FileEntry {
+		return new LimeEntry(this,"root",".");
+	}
+	
+	function splitPath( path : String ) {
+		return path == "." ? [] : path.split("/");
+	}
+
+	function subFiles( path : String ) : Array<FileEntry> {
+		var out:Array<FileEntry> = [];
+		var all = lime.Assets.list();
+		for( f in all )
+		{
+			if( f != path && StringTools.startsWith(f, path) )
+				out.push(get(f));
+		}
+		return out;
+	}
+
+	function isDirectory( path : String ) {
+		return subFiles(path).length > 0;
+	}
+
+	public function exists( path : String ) {
+		return lime.Assets.exists(path);
+	}
+
+	public function get( path : String ) {
+		if( !exists(path) )
+			throw new NotFound(path);
+		return new LimeEntry(this, path.split("/").pop(), path);
+	}
+	#end
+	
+	public function dispose() {
+	}
+
+}

+ 17 - 2
hxd/fs/LoadedBitmap.hx

@@ -1,6 +1,14 @@
 package hxd.fs;
 
-typedef LoadedBitmapData = #if flash flash.display.BitmapData #elseif js js.html.Image #else Dynamic #end
+#if (flash || openfl)
+typedef LoadedBitmapData = flash.display.BitmapData;
+#elseif lime
+typedef LoadedBitmapData = lime.graphics.Image;
+#elseif js 
+typedef LoadedBitmapData = js.html.Image;
+#else 
+typedef LoadedBitmapData = Dynamic;
+#end
 
 abstract LoadedBitmap(LoadedBitmapData) {
 
@@ -9,8 +17,15 @@ abstract LoadedBitmap(LoadedBitmapData) {
 	}
 
 	public function toBitmap() : hxd.BitmapData {
-		#if flash
+		#if (flash || openfl)
 		return hxd.BitmapData.fromNative(this);
+		#elseif lime
+		var bmp = new hxd.BitmapData(this.width, this.height);
+			//TODO  test this
+			#if js @:privateAccess bmp.pixel = this.src;
+			#else @:privateAccess bmp.data = this;
+			#end
+		return bmp;
 		#elseif js
 		var bmp = new hxd.BitmapData(this.width, this.height);
 		@:privateAccess bmp.ctx.drawImage(this, 0, 0);

+ 477 - 0
hxd/impl/LimeStage.hx

@@ -0,0 +1,477 @@
+package hxd.impl;
+import hxd.Event;
+
+@:allow(hxd.Stage)
+@:access(hxd.Stage)
+class LimeStage implements lime.app.IModule {
+	
+	var stage : hxd.Stage;
+
+	var width : Int;
+	var height : Int;
+	
+	var enableMouse : Bool;
+
+	var mouseX : Int = 0;
+	var mouseY : Int = 0;
+
+	public function new( s : hxd.Stage ){
+		stage = s;
+		enableMouse = !hxd.System.isTouch;
+		width = hxd.System.width;
+		height = hxd.System.height;
+	}
+
+	public function render(renderer){
+		if( hxd.System.loopFunc != null )
+			hxd.System.loopFunc();
+	}
+
+	public function onWindowResize(win:lime.ui.Window, w, h){
+		width = Std.int(w * win.scale);
+		height = Std.int(h * win.scale);
+		stage.onResize(null);
+	}
+
+	public function onMouseMove(win, x : Float, y : Float){
+		if( !enableMouse ) return;
+		mouseX = Std.int(x);
+		mouseY = Std.int(y);
+		stage.event(new Event(EMove, mouseX, mouseY));
+	}
+
+	public function onMouseDown(win, x : Float, y : Float, button : Int ){
+		if( !enableMouse ) return;
+		var e = new Event(EPush, x, y);
+		e.button = button;
+		stage.event(e);
+	}
+
+	public function onMouseUp(win, x : Float, y : Float, button : Int){
+		if( !enableMouse ) return;
+		var e = new Event(ERelease, x, y);
+		e.button = button;
+		stage.event(e);
+	}
+	
+	public function onMouseWheel( win, dx : Float, dy : Float ){
+		if( dy != 0 ){
+			var ev = new Event(EWheel, mouseX, mouseY);
+			ev.wheelDelta = dy;
+			stage.event(ev);
+		}
+	}
+
+	public function onTouchEnd( touch : lime.ui.Touch ){
+		if( !hxd.Stage.ENABLE_TOUCH ) return;
+		var e = new Event( ERelease, touch.x*width, touch.y*height );
+		e.touchId = touch.id;
+		stage.event(e);
+	}
+
+	public function onTouchMove( touch : lime.ui.Touch ){
+		if( !hxd.Stage.ENABLE_TOUCH ) return;
+		var e = new Event( EMove, touch.x*width, touch.y*height );
+		e.touchId = touch.id;
+		stage.event(e);
+	}
+
+	public function onTouchStart( touch : lime.ui.Touch ){
+		if( !hxd.Stage.ENABLE_TOUCH ) return;
+		var e = new Event( EPush, touch.x*width, touch.y*height );
+		e.touchId = touch.id;
+		stage.event(e);
+	}
+
+	public function onKeyDown( window, keyCode:lime.ui.KeyCode, modifier:lime.ui.KeyModifier ){
+		var ev = new Event(EKeyDown, mouseX, mouseY);
+		ev.keyCode = Keyboard.convertKeyCode(keyCode);
+		ev.charCode = Keyboard.getCharCode(ev.keyCode, modifier.shiftKey);
+		stage.event(ev);
+	}
+
+	public function onKeyUp( window, keyCode:lime.ui.KeyCode, modifier:lime.ui.KeyModifier ){
+		var ev = new Event(EKeyUp, mouseX, mouseY);
+		ev.keyCode = Keyboard.convertKeyCode(keyCode);
+		ev.charCode = Keyboard.getCharCode(ev.keyCode, modifier.shiftKey);
+		stage.event(ev);
+	}
+
+	public function onGamepadAxisMove( gamepad, axis, value:Float ){ }
+	
+	public function onGamepadButtonDown( gamepad, button ){ }
+	
+	public function onGamepadButtonUp( gamepad, button ){ }
+	
+	public function onGamepadConnect( gamepad ){}
+
+	public function onGamepadDisconnect( gamepad ){ }
+	
+	public function onJoystickAxisMove( joystick, axis:Int, value:Float ){ }
+
+	public function onJoystickButtonDown( joystick, button:Int ){ }
+
+	public function onJoystickButtonUp( joystick, button:Int ){ }
+
+	public function onJoystickConnect( joystick ){ }
+
+	public function onJoystickDisconnect( joystick ){ }
+
+	public function onJoystickHatMove( joystick, hat:Int, position ){ }
+	
+	public function onJoystickTrackballMove( joystick, trackball:Int, value:Float ){ }
+
+	public function onModuleExit( code:Int ){ }
+	
+	public function onMouseMoveRelative( window, x:Float, y:Float ){ }
+	
+	public function onPreloadComplete(  ){ }
+	
+	public function onPreloadProgress( loaded:Int, total:Int ){ }
+
+	public function onRenderContextLost( renderer ){ }
+
+	public function onRenderContextRestored( renderer, context ){ }
+
+	public function onTextEdit( window, text:String, start:Int, length:Int ){ }
+
+	public function onTextInput( window, text:String ){ }
+
+	public function onWindowActivate( window ){ }
+
+	public function onWindowClose( window ){ }
+	
+	public function onWindowCreate( window ){ }
+	
+	public function onWindowDeactivate( window ){ }
+
+	public function onWindowEnter( window ){ }
+	
+	public function onWindowFocusIn( window ){ }
+
+	public function onWindowFocusOut( window ){ }
+
+	public function onWindowFullscreen( window ){ }
+	
+	public function onWindowLeave( window ){ }
+	
+	public function onWindowMove( window, x:Float, y:Float ){ }
+	
+	public function onWindowMinimize( window ){ }
+	
+	public function onWindowRestore( window ){ }
+
+	public function onWindowDropFile( window, file:String ){ }
+
+	public function update( dt ){ }
+		
+}
+
+@:allow(hxd.impl.LimeStage)
+class Keyboard {
+		
+	static inline var NUMBER_0 = 48;
+	static inline var NUMBER_1 = 49;
+	static inline var NUMBER_2 = 50;
+	static inline var NUMBER_3 = 51;
+	static inline var NUMBER_4 = 52;
+	static inline var NUMBER_5 = 53;
+	static inline var NUMBER_6 = 54;
+	static inline var NUMBER_7 = 55;
+	static inline var NUMBER_8 = 56; 
+	static inline var NUMBER_9 = 57;
+	static inline var A = 65;
+	static inline var B = 66;
+	static inline var C = 67;
+	static inline var D = 68;
+	static inline var E = 69;
+	static inline var F = 70;
+	static inline var G = 71;
+	static inline var H = 72;
+	static inline var I = 73;
+	static inline var J = 74;
+	static inline var K = 75;
+	static inline var L = 76;
+	static inline var M = 77;
+	static inline var N = 78;
+	static inline var O = 79;
+	static inline var P = 80;
+	static inline var Q = 81;
+	static inline var R = 82;
+	static inline var S = 83;
+	static inline var T = 84;
+	static inline var U = 85;
+	static inline var V = 86;
+	static inline var W = 87;
+	static inline var X = 88;
+	static inline var Y = 89;
+	static inline var Z = 90;
+	static inline var NUMPAD_0 = 96;
+	static inline var NUMPAD_1 = 97;
+	static inline var NUMPAD_2 = 98;
+	static inline var NUMPAD_3 = 99;
+	static inline var NUMPAD_4 = 100;
+	static inline var NUMPAD_5 = 101;
+	static inline var NUMPAD_6 = 102;
+	static inline var NUMPAD_7 = 103;
+	static inline var NUMPAD_8 = 104;
+	static inline var NUMPAD_9 = 105;
+	static inline var NUMPAD_MULTIPLY = 106;
+	static inline var NUMPAD_ADD = 107;
+	static inline var NUMPAD_ENTER = 108;
+	static inline var NUMPAD_SUBTRACT = 109;
+	static inline var NUMPAD_DECIMAL = 110;
+	static inline var NUMPAD_DIVIDE = 111;
+	static inline var F1 = 112;
+	static inline var F2 = 113;
+	static inline var F3 = 114;
+	static inline var F4 = 115;
+	static inline var F5 = 116;
+	static inline var F6 = 117;
+	static inline var F7 = 118;
+	static inline var F8 = 119;
+	static inline var F9 = 120;
+	static inline var F10 = 121; //  F10 is used by browser.
+	static inline var F11 = 122;
+	static inline var F12 = 123;
+	static inline var F13 = 124;
+	static inline var F14 = 125;
+	static inline var F15 = 126;
+	static inline var BACKSPACE = 8;
+	static inline var TAB = 9;
+	static inline var ALTERNATE = 18;
+	static inline var ENTER = 13;
+	static inline var COMMAND = 15;
+	static inline var SHIFT = 16;
+	static inline var CONTROL = 17;
+	static inline var BREAK = 19;
+	static inline var CAPS_LOCK = 20;
+	static inline var NUMPAD = 21;
+	static inline var ESCAPE = 27;
+	static inline var SPACE = 32;
+	static inline var PAGE_UP = 33;
+	static inline var PAGE_DOWN = 34;
+	static inline var END = 35;
+	static inline var HOME = 36;
+	static inline var LEFT = 37;
+	static inline var RIGHT = 39;
+	static inline var UP = 38;
+	static inline var DOWN = 40;
+	static inline var INSERT = 45;
+	static inline var DELETE = 46;
+	static inline var NUMLOCK = 144;
+	static inline var SEMICOLON = 186;
+	static inline var EQUAL = 187;
+	static inline var COMMA = 188;
+	static inline var MINUS = 189;
+	static inline var PERIOD = 190;
+	static inline var SLASH = 191;
+	static inline var BACKQUOTE = 192;
+	static inline var LEFTBRACKET = 219;
+	static inline var BACKSLASH = 220;
+	static inline var RIGHTBRACKET = 221;
+	static inline var QUOTE = 222;
+	
+	static function convertKeyCode (key:lime.ui.KeyCode):Int {
+		return switch (key) {
+			case BACKSPACE: Keyboard.BACKSPACE;
+			case TAB: Keyboard.TAB;
+			case RETURN: Keyboard.ENTER;
+			case ESCAPE: Keyboard.ESCAPE;
+			case SPACE: Keyboard.SPACE;
+			case EXCLAMATION: Keyboard.NUMBER_1;
+			case QUOTE: Keyboard.QUOTE;
+			case HASH: Keyboard.NUMBER_3;
+			case DOLLAR: Keyboard.NUMBER_4;
+			case PERCENT: Keyboard.NUMBER_5;
+			case AMPERSAND: Keyboard.NUMBER_7;
+			case SINGLE_QUOTE: Keyboard.QUOTE;
+			case LEFT_PARENTHESIS: Keyboard.NUMBER_9;
+			case RIGHT_PARENTHESIS: Keyboard.NUMBER_0;
+			case ASTERISK: Keyboard.NUMBER_8;
+			case COMMA: Keyboard.COMMA;
+			case MINUS: Keyboard.MINUS;
+			case PERIOD: Keyboard.PERIOD;
+			case SLASH: Keyboard.SLASH;
+			case NUMBER_0: Keyboard.NUMBER_0;
+			case NUMBER_1: Keyboard.NUMBER_1;
+			case NUMBER_2: Keyboard.NUMBER_2;
+			case NUMBER_3: Keyboard.NUMBER_3;
+			case NUMBER_4: Keyboard.NUMBER_4;
+			case NUMBER_5: Keyboard.NUMBER_5;
+			case NUMBER_6: Keyboard.NUMBER_6;
+			case NUMBER_7: Keyboard.NUMBER_7;
+			case NUMBER_8: Keyboard.NUMBER_8;
+			case NUMBER_9: Keyboard.NUMBER_9;
+			case COLON: Keyboard.SEMICOLON;
+			case SEMICOLON: Keyboard.SEMICOLON;
+			case LESS_THAN: 60;
+			case EQUALS: Keyboard.EQUAL;
+			case GREATER_THAN: Keyboard.PERIOD;
+			case QUESTION: Keyboard.SLASH;
+			case AT: Keyboard.NUMBER_2;
+			case LEFT_BRACKET: Keyboard.LEFTBRACKET;
+			case BACKSLASH: Keyboard.BACKSLASH;
+			case RIGHT_BRACKET: Keyboard.RIGHTBRACKET;
+			case CARET: Keyboard.NUMBER_6;
+			case UNDERSCORE: Keyboard.MINUS;
+			case GRAVE: Keyboard.BACKQUOTE;
+			case A: Keyboard.A;
+			case B: Keyboard.B;
+			case C: Keyboard.C;
+			case D: Keyboard.D;
+			case E: Keyboard.E;
+			case F: Keyboard.F;
+			case G: Keyboard.G;
+			case H: Keyboard.H;
+			case I: Keyboard.I;
+			case J: Keyboard.J;
+			case K: Keyboard.K;
+			case L: Keyboard.L;
+			case M: Keyboard.M;
+			case N: Keyboard.N;
+			case O: Keyboard.O;
+			case P: Keyboard.P;
+			case Q: Keyboard.Q;
+			case R: Keyboard.R;
+			case S: Keyboard.S;
+			case T: Keyboard.T;
+			case U: Keyboard.U;
+			case V: Keyboard.V;
+			case W: Keyboard.W;
+			case X: Keyboard.X;
+			case Y: Keyboard.Y;
+			case Z: Keyboard.Z;
+			case DELETE: Keyboard.DELETE;
+			case CAPS_LOCK: Keyboard.CAPS_LOCK;
+			case F1: Keyboard.F1;
+			case F2: Keyboard.F2;
+			case F3: Keyboard.F3;
+			case F4: Keyboard.F4;
+			case F5: Keyboard.F5;
+			case F6: Keyboard.F6;
+			case F7: Keyboard.F7;
+			case F8: Keyboard.F8;
+			case F9: Keyboard.F9;
+			case F10: Keyboard.F10;
+			case F11: Keyboard.F11;
+			case F12: Keyboard.F12;
+			case PRINT_SCREEN: 301;
+			case SCROLL_LOCK: 145;
+			case PAUSE: Keyboard.BREAK;
+			case INSERT: Keyboard.INSERT;
+			case HOME: Keyboard.HOME;
+			case PAGE_UP: Keyboard.PAGE_UP;
+			case END: Keyboard.END;
+			case PAGE_DOWN: Keyboard.PAGE_DOWN;
+			case RIGHT: Keyboard.RIGHT;
+			case LEFT: Keyboard.LEFT;
+			case DOWN: Keyboard.DOWN;
+			case UP: Keyboard.UP;
+			case NUM_LOCK: Keyboard.NUMLOCK;
+			case NUMPAD_DIVIDE: Keyboard.NUMPAD_DIVIDE;
+			case NUMPAD_MULTIPLY: Keyboard.NUMPAD_MULTIPLY;
+			case NUMPAD_MINUS: Keyboard.NUMPAD_SUBTRACT;
+			case NUMPAD_PLUS: Keyboard.NUMPAD_ADD;
+			case NUMPAD_ENTER: Keyboard.NUMPAD_ENTER;
+			case NUMPAD_1: Keyboard.NUMPAD_1;
+			case NUMPAD_2: Keyboard.NUMPAD_2;
+			case NUMPAD_3: Keyboard.NUMPAD_3;
+			case NUMPAD_4: Keyboard.NUMPAD_4;
+			case NUMPAD_5: Keyboard.NUMPAD_5;
+			case NUMPAD_6: Keyboard.NUMPAD_6;
+			case NUMPAD_7: Keyboard.NUMPAD_7;
+			case NUMPAD_8: Keyboard.NUMPAD_8;
+			case NUMPAD_9: Keyboard.NUMPAD_9;
+			case NUMPAD_0: Keyboard.NUMPAD_0;
+			case NUMPAD_PERIOD: Keyboard.NUMPAD_DECIMAL;
+			case APPLICATION: 302;
+			case F13: Keyboard.F13;
+			case F14: Keyboard.F14;
+			case F15: Keyboard.F15;
+			case NUMPAD_DECIMAL: Keyboard.NUMPAD_DECIMAL;
+			case LEFT_CTRL: Keyboard.CONTROL;
+			case LEFT_SHIFT: Keyboard.SHIFT;
+			case LEFT_ALT: Keyboard.ALTERNATE;
+			case LEFT_META: Keyboard.COMMAND;
+			case RIGHT_CTRL: Keyboard.CONTROL;
+			case RIGHT_SHIFT: Keyboard.SHIFT;
+			case RIGHT_ALT: Keyboard.ALTERNATE;
+			case RIGHT_META: Keyboard.COMMAND;
+			default: key;
+		}
+	}
+
+	static function getCharCode (key:Int, shift:Bool = false):Int {
+		if (!shift) {
+			switch (key) {
+				case Keyboard.BACKSPACE: return 8;
+				case Keyboard.TAB: return 9;
+				case Keyboard.ENTER: return 13;
+				case Keyboard.ESCAPE: return 27;
+				case Keyboard.SPACE: return 32;
+				case Keyboard.SEMICOLON: return 59;
+				case Keyboard.EQUAL: return 61;
+				case Keyboard.COMMA: return 44;
+				case Keyboard.MINUS: return 45;
+				case Keyboard.PERIOD: return 46;
+				case Keyboard.SLASH: return 47;
+				case Keyboard.BACKQUOTE: return 96;
+				case Keyboard.LEFTBRACKET: return 91;
+				case Keyboard.BACKSLASH: return 92;
+				case Keyboard.RIGHTBRACKET: return 93;
+				case Keyboard.QUOTE: return 39;
+			}
+			
+			if (key >= Keyboard.NUMBER_0 && key <= Keyboard.NUMBER_9) 
+				return key - Keyboard.NUMBER_0 + 48;
+			
+			if (key >= Keyboard.A && key <= Keyboard.Z)
+				return key - Keyboard.A + 97;
+		}else{
+			switch (key) {
+				case Keyboard.NUMBER_0: return 41;
+				case Keyboard.NUMBER_1: return 33;
+				case Keyboard.NUMBER_2: return 64;
+				case Keyboard.NUMBER_3: return 35;
+				case Keyboard.NUMBER_4: return 36;
+				case Keyboard.NUMBER_5: return 37;
+				case Keyboard.NUMBER_6: return 94;
+				case Keyboard.NUMBER_7: return 38;
+				case Keyboard.NUMBER_8: return 42;
+				case Keyboard.NUMBER_9: return 40;
+				case Keyboard.SEMICOLON: return 58;
+				case Keyboard.EQUAL: return 43;
+				case Keyboard.COMMA: return 60;
+				case Keyboard.MINUS: return 95;
+				case Keyboard.PERIOD: return 62;
+				case Keyboard.SLASH: return 63;
+				case Keyboard.BACKQUOTE: return 126;
+				case Keyboard.LEFTBRACKET: return 123;
+				case Keyboard.BACKSLASH: return 124;
+				case Keyboard.RIGHTBRACKET: return 125;
+				case Keyboard.QUOTE: return 34;
+			}
+			
+			if (key >= Keyboard.A && key <= Keyboard.Z) 
+				return key - Keyboard.A + 65;
+		}
+		
+		if (key >= Keyboard.NUMPAD_0 && key <= Keyboard.NUMPAD_9)
+			return key - Keyboard.NUMPAD_0 + 48;
+		
+		switch (key) {
+			case Keyboard.NUMPAD_MULTIPLY: return 42;
+			case Keyboard.NUMPAD_ADD: return 43;
+			case Keyboard.NUMPAD_ENTER: return 44;
+			case Keyboard.NUMPAD_DECIMAL: return 45;
+			case Keyboard.NUMPAD_DIVIDE: return 46;
+			case Keyboard.DELETE: return 127;
+			case Keyboard.ENTER: return 13;
+			case Keyboard.BACKSPACE: return 8;
+		}
+		
+		return 0;
+		
+	}
+}

+ 3 - 1
hxd/res/Font.hx

@@ -11,10 +11,12 @@ class Font extends Resource {
 		#elseif js
 		var name = "R_" + ~/[^A-Za-z0-9_]/g.replace(entry.path, "_");
 		return FontBuilder.getFont(name, size, options);
+		#elseif lime
+		return FontBuilder.getFont(name, size, options);
 		#else
 		throw "Not implemented for this platform";
 		return null;
 		#end
 	}
 
-}
+}

+ 106 - 1
hxd/res/FontBuilder.hx

@@ -2,7 +2,8 @@ package hxd.res;
 
 typedef FontBuildOptions = {
 	?antiAliasing : Bool,
-	?chars : String,
+	?chars : hxd.UString,
+	?kerning : Bool,
 };
 
 /**
@@ -217,6 +218,110 @@ class FontBuilder {
 		return font;
 	}
 
+	#elseif lime
+
+	function build() : h2d.Font {
+		var f = lime.text.Font.fromBytes( hxd.Res.load(font.name).entry.getBytes() );
+		var surf = 0;
+		var bh = 0;
+		var tmp = [];
+		var gcode = new Map<lime.text.Glyph,Int>();
+		for( i in 0...options.chars.length ) {
+			var c = options.chars.charAt(i);
+			var code = options.chars.charCodeAt(i);
+			var g = f.getGlyph(c);
+			gcode.set(g, code);
+			var img = f.renderGlyph(g,font.size);
+			var m = f.getGlyphMetrics(g);
+			if( img == null ){
+				tmp[i] = { i: null, w:0, h:0, x:0, y:0, adv:Std.int(m.advance.x)>>6 };
+				continue;
+			}
+			var w = img.width;
+			var h = img.height;
+			var x = Std.int(img.x);
+			if( x < 0 )
+				x = 0;
+			var y = Std.int(img.y); // baseline y pos
+			surf += (w + 2) * (h + 2);
+			if( h+1 > font.lineHeight )
+				font.lineHeight = h + 1;
+			if( h - y > bh )
+				bh = h - y;
+			tmp[i] = { i: img, w:w, h:h, x: x, y:y, adv:Std.int(m.advance.x)>>6 };
+		}
+		var baseline = font.lineHeight - bh;
+
+		var side = Math.ceil( Math.sqrt(surf) );
+		var width = 1;
+		while( side > width )
+			width <<= 1;
+		var height = width;
+		while( width * height >> 1 > surf )
+			height >>= 1;
+
+		var all, px;
+		do {
+			font.glyphs = new Map();
+			all = [];
+			var x = 0, y = 0, lineH = font.lineHeight;
+			px = haxe.io.Bytes.alloc( width * height  );
+			for( i in 0...options.chars.length ) {
+				var size = tmp[i];
+				var w = size.x + size.w + 1;
+				if( x + w > width ) {
+					x = 0;
+					y += lineH + 1;
+				}
+				// no space, resize
+				if( y + lineH + 1 > height ) {
+					px = null;
+					height <<= 1;
+					break;
+				}
+				var gy = baseline-size.y;
+				var gx = size.x;
+				if( size.w > 0 && size.h > 0 ){
+					var ib = size.i.buffer.data.toBytes();
+					for( ty in 0...size.h )
+						px.blit( (gx+x+(y+gy+ty)*width), ib, ty*size.w, size.w );
+				}
+				var t = new h2d.Tile(innerTex, x, y, gx+size.w, gy+size.h);
+				all.push(t);
+				font.glyphs.set(options.chars.charCodeAt(i), new h2d.Font.FontChar(t,size.adv-1));
+				// next element
+				x += w + 1;
+			}
+		} while( px == null );
+
+		// Kerning
+		// lime font.decompose() currently force size to 320*64 (= 20*1024)
+		if( options.kerning ){
+			var kernratio = font.size / (320 * 64);
+			var kerning = f.decompose().kerning;
+			for( k in kerning ){
+				var v = Math.round(k.x * kernratio);
+				if( v == 0 || !gcode.exists(k.right_glyph) || !gcode.exists(k.left_glyph) )
+					continue;
+				var c = font.glyphs.get( gcode.get(k.right_glyph) );
+				c.addKerning( gcode.get(k.left_glyph), v );
+			}
+		}
+
+		var pixels = new hxd.Pixels( width, height, px, ALPHA );
+		if( innerTex == null ) {
+			innerTex = new h3d.mat.Texture(pixels.width, pixels.height, h3d.mat.Data.TextureFormat.ALPHA);
+			innerTex.uploadPixels(pixels);
+			font.tile = h2d.Tile.fromTexture(innerTex);
+			for( t in all )
+				t.setTexture(innerTex);
+			innerTex.realloc = build;
+		} else
+			innerTex.uploadPixels(pixels);
+		pixels.dispose();
+
+		return font;
+	}
 
 	#else
 

+ 10 - 1
hxd/res/Image.hx

@@ -85,6 +85,12 @@ class Image extends Resource {
 		switch( inf.format ) {
 		case Png:
 			var bytes = entry.getBytes(); // using getTmpBytes cause bug in E2
+
+			#if (lime && (cpp || neko || nodejs))
+			// native PNG loader is faster
+			var i = lime.graphics.format.PNG.decodeBytes( bytes, true );
+			pixels = new Pixels(inf.width, inf.height, i.data.toBytes(), RGBA );
+			#else
 			var png = new format.png.Reader(new haxe.io.BytesInput(bytes));
 			png.checkCRC = false;
 			pixels = Pixels.alloc(inf.width, inf.height, BGRA);
@@ -101,6 +107,7 @@ class Image extends Resource {
 			#else
 			format.png.Tools.extract32(png.read(), pixels.bytes);
 			#end
+			#end
 		case Gif:
 			var bytes = entry.getBytes();
 			var gif = new format.gif.Reader(new haxe.io.BytesInput(bytes)).read();
@@ -157,6 +164,7 @@ class Image extends Resource {
 			entry.loadBitmap(function(bmp) {
 				var bmp = bmp.toBitmap();
 				tex.alloc();
+				
 				if( bmp.width != tex.width || bmp.height != tex.height ) {
 					var pixels = bmp.getPixels();
 					pixels.makeSquare();
@@ -164,6 +172,7 @@ class Image extends Resource {
 					pixels.dispose();
 				} else
 					tex.uploadBitmap(bmp);
+				
 				bmp.dispose();
 				tex.realloc = loadTexture;
 				watch(watchCallb);
@@ -195,4 +204,4 @@ class Image extends Resource {
 		return h2d.Tile.fromTexture(toTexture()).sub(0, 0, size.width, size.height);
 	}
 
-}
+}

+ 8 - 4
hxsl/GlslOut.hx

@@ -372,12 +372,16 @@ class GlslOut {
 		decls = [];
 		buf = new StringBuf();
 		exprValues = [];
-		//Version is required on desktop for precision qualifier, but version 1.0.0 only is supported on webgl
-		#if !js
+		
+		//#if GL_ES_VERSION_2_0  would be the test to use at compilation time, but would require a GL context to call glGetString (GL_SHADING_LANGUAGE_VERSION)
+		//#ifdef GL_ES is to test in the shader itself but #version  muse be declared first
+		#if((cpp && mobile)||js)
+		decls.push("#version 100");
+		#else
 		decls.push("#version 130");
-		#end
+		#end 
 		decls.push("precision mediump float;");
-
+		
 		if( s.funs.length != 1 ) throw "assert";
 		var f = s.funs[0];
 		isVertex = f.kind == Vertex;