diff --git a/.travis.yml b/.travis.yml index bdcaa0db6f..405a750da2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,8 +35,12 @@ before_script: - haxelib run munit gen script: - - haxelib run lime test neko + #- haxelib run lime test neko + #- haxelib run lime test neko -Ddisable-cffi + - haxelib run lime build neko + - haxelib run lime build neko -Ddisable-cffi - haxelib run lime test neko -Dlegacy + - haxelib run lime test neko -Dhybrid - haxelib run munit test -as3 -norun - haxelib run munit test -browser phantomjs - haxelib run lime test linux diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ef6f3cb56..01ffc34241 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,70 @@ +3.0.6 (05/14/2015) +------------------ + +* Fix regression in event dispatch behavior + + +3.0.5 (05/13/2015) +------------------ + +* Improved formatting for thrown errors on HTML5 +* Separated the behavior of event preventDefault from stopPropagation +* Fixed the event dispatch order for DisplayObjectContainer +* Fixed support for -Dhybrid using latest Lime release + + +3.0.4 (05/12/2015) +------------------ + +* Improved accuracy of HTML5 canvas Graphics renderer +* Added support for window hardware=false +* Added initial Cairo renderer support +* Made big improvements to HTML5 canvas TextField input +* Added MouseEvent.MOUSE_LEAVE event support +* Improved HTML5 canvas linear gradient support +* Improved Stage3D texture uploads +* Implemented BitmapData.getColorBoundsRect +* Improved checks for invalid BitmapData in Assets +* Improved beginBitmapFill for GL Graphics +* Improved pixel snapping support for GL rendering +* Improved cleanup of native sound channels +* Improved compatibility between Stage3D and internal GL rendering +* Fixed HTML5 canvas scrollRect +* Fixed handling of embedded fonts in some cases +* Fixed some issues with bounds calculations +* Fixed support for initial SoundTransform volume on native +* Improved non-blocking HTTPS support (legacy) + + +3.0.3 (04/21/2015) +------------------ + +* Improved hit test when there are interactive and non-interactive matches +* Improved accuracy of text metrics +* Improved accuracy of GL TextField glyph positioning +* Added wordWrap support to canvas TextField +* Added handling of stage.focus on mouse down +* Fixed the start time and loop count for native sounds +* Fixed the behavior of sprite.contains to loop recursively +* Fixed upside-down BitmapData in some cases when using GL bitmapData.draw +* Fixed layering of GL bitmapData.draw over existing BitmapData contents +* Improved performance of getRGBAPixels (legacy) + + +3.0.2 (04/15/2015) +------------------ + +* Improved handling of keyCode/charCode in keyboard events +* Improved the frame timing when using hybrid mode +* Improved the font lookup behavior of GL TextField +* Added better auto-size left support to GL TextField +* Added basic text line metrics in TextField +* Added support for compilation with -Ddisable-cffi +* Added dynamic DisplayObject field support for MovieClip +* Fixed UVs when using drawTiles with bitmapData.draw (GL) +* Fixed blendMode setting when using bitmapData.draw (GL) + + 3.0.1 (04/09/2015) ------------------ diff --git a/haxe/Timer.hx b/haxe/Timer.hx index bc05c111e7..788bf2fefc 100644 --- a/haxe/Timer.hx +++ b/haxe/Timer.hx @@ -1,5 +1,5 @@ package haxe; -#if (macro || (!neko && !cpp)) +#if (macro || (!neko && !cpp && !nodejs)) // Original haxe.Timer class @@ -41,7 +41,7 @@ package haxe; the child class. **/ class Timer { - #if (flash || js || java) + #if (flash || js || java || python) #if (flash || js) private var id : Null; @@ -62,12 +62,9 @@ class Timer { The accuracy of this may be platform-dependent. **/ public function new( time_ms : Int ){ - #if flash9 + #if flash var me = this; id = untyped __global__["flash.utils.setInterval"](function() { me.run(); },time_ms); - #elseif flash - var me = this; - id = untyped _global["setInterval"](function() { me.run(); },time_ms); #elseif js var me = this; id = untyped setInterval(function() me.run(),time_ms); @@ -89,10 +86,8 @@ class Timer { #if (flash || js) if( id == null ) return; - #if flash9 + #if flash untyped __global__["flash.utils.clearInterval"](id); - #elseif flash - untyped _global["clearInterval"](id); #elseif js untyped clearInterval(id); #end @@ -190,7 +185,7 @@ private class TimerTask extends java.util.TimerTask { this.timer = timer; } - @:overload public function run():Void { + @:overload override public function run():Void { timer.run(); } } diff --git a/haxelib.json b/haxelib.json index d5437ab771..ac6c54a0fa 100644 --- a/haxelib.json +++ b/haxelib.json @@ -4,7 +4,7 @@ "license": "MIT", "tags": [], "description": "The \"Open Flash Library\" for fast 2D development", - "version": "3.0.1", - "releasenote": "Improved -Dhybrid support", + "version": "3.0.6", + "releasenote": "Minor fix", "contributors": [ "singmajesty" ] } diff --git a/openfl/Assets.hx b/openfl/Assets.hx index 009811e0b9..078e48e4ab 100644 --- a/openfl/Assets.hx +++ b/openfl/Assets.hx @@ -429,7 +429,7 @@ class Assets { #else - return (bitmapData != null); + return (bitmapData != null && #if !lime_hybrid bitmapData.__image != null #else bitmapData.__handle != null #end); #end #end @@ -442,7 +442,7 @@ class Assets { private static function isValidSound (sound:Sound):Bool { #if (tools && !display) - #if (cpp || neko) + #if (cpp || neko || nodejs) return true; //return (sound.__handle != null && sound.__handle != 0); @@ -1325,7 +1325,10 @@ class Assets { case EConst(CString(filePath)): - path = Context.resolvePath (filePath); + path = filePath; + if (!sys.FileSystem.exists(filePath)) { + path = Context.resolvePath (filePath); + } default: diff --git a/openfl/Lib.hx b/openfl/Lib.hx index 56c15f147f..1481e35d15 100644 --- a/openfl/Lib.hx +++ b/openfl/Lib.hx @@ -8,7 +8,7 @@ import openfl.display.MovieClip; import openfl.display.Stage; import openfl.net.URLRequest; -#if js +#if (js && html5) import js.Browser; #end @@ -96,7 +96,7 @@ import js.Browser; } - #if js + #if (js && html5) Browser.window.open (request.url, target); #elseif flash return flash.Lib.getURL (request, target); @@ -120,7 +120,7 @@ import js.Browser; public static function preventDefaultTouchMove ():Void { - #if js + #if (js && html5) Browser.document.addEventListener ("touchmove", function (evt:js.html.Event):Void { evt.preventDefault (); diff --git a/openfl/_internal/aglsl/AGLSLParser.hx b/openfl/_internal/aglsl/AGLSLParser.hx index ba1d3df4f5..606ac62736 100755 --- a/openfl/_internal/aglsl/AGLSLParser.hx +++ b/openfl/_internal/aglsl/AGLSLParser.hx @@ -22,6 +22,12 @@ class AGLSLParser { #if html5 header += "precision highp float;\n"; + #elseif (android || ios) + if (desc.header.type == "vertex") { + + header += "precision highp float;\n"; + + } #end var tag = desc.header.type.charAt (0); //TODO @@ -395,4 +401,4 @@ class AGLSLParser { } -} \ No newline at end of file +} diff --git a/openfl/_internal/renderer/AbstractMaskManager.hx b/openfl/_internal/renderer/AbstractMaskManager.hx new file mode 100644 index 0000000000..a7087a7380 --- /dev/null +++ b/openfl/_internal/renderer/AbstractMaskManager.hx @@ -0,0 +1,45 @@ +package openfl._internal.renderer; + + +import openfl.display.*; +import openfl.geom.*; + +@:access(openfl.display.DisplayObject) +@:keep + + +class AbstractMaskManager { + + + private var renderSession:RenderSession; + + + public function new (renderSession:RenderSession) { + + this.renderSession = renderSession; + + } + + + public function pushMask (mask:DisplayObject):Void { + + + + } + + + public function pushRect (rect:Rectangle, transform:Matrix):Void { + + + + } + + + public function popMask ():Void { + + + + } + + +} \ No newline at end of file diff --git a/openfl/_internal/renderer/AbstractRenderer.hx b/openfl/_internal/renderer/AbstractRenderer.hx index 5a3d769eec..00a4563e14 100644 --- a/openfl/_internal/renderer/AbstractRenderer.hx +++ b/openfl/_internal/renderer/AbstractRenderer.hx @@ -34,6 +34,13 @@ class AbstractRenderer { + } + + + public function setViewport (x:Int, y:Int, width:Int, height:Int):Void { + + + } diff --git a/openfl/_internal/renderer/RenderSession.hx b/openfl/_internal/renderer/RenderSession.hx index cd52b47e1b..81f5ce70ad 100644 --- a/openfl/_internal/renderer/RenderSession.hx +++ b/openfl/_internal/renderer/RenderSession.hx @@ -1,50 +1,46 @@ package openfl._internal.renderer; #if !flash +import lime.graphics.CairoRenderContext; import lime.graphics.CanvasRenderContext; import lime.graphics.DOMRenderContext; import lime.graphics.GLRenderContext; -import lime.math.Matrix4; +import lime.graphics.opengl.GLFramebuffer; import openfl._internal.renderer.opengl.utils.BlendModeManager; import openfl._internal.renderer.opengl.utils.FilterManager; -import openfl._internal.renderer.opengl.utils.MaskManager; import openfl._internal.renderer.opengl.utils.ShaderManager; import openfl._internal.renderer.opengl.utils.SpriteBatch; import openfl._internal.renderer.opengl.utils.StencilManager; import openfl.display.BlendMode; +import openfl.geom.Matrix; import openfl.geom.Point; class RenderSession { + public var cairo:CairoRenderContext; public var context:CanvasRenderContext; public var element:DOMRenderContext; public var gl:GLRenderContext; - //public var glProgram:ShaderProgram; - //public var mask:Bool; - //public var maskManager:MaskManager; - public var projectionMatrix:Matrix4; public var renderer:AbstractRenderer; - //public var scaleMode:ScaleMode; public var roundPixels:Bool; public var transformProperty:String; public var transformOriginProperty:String; public var vendorPrefix:String; public var z:Int; - //public var smoothProperty:Null = null; + public var projectionMatrix:Matrix; public var drawCount:Int; public var currentBlendMode:BlendMode; - public var projection:Point; - public var offset:Point; public var shaderManager:ShaderManager; - public var maskManager:#if neko MaskManager #else Dynamic #end; + public var maskManager:AbstractMaskManager; public var filterManager:FilterManager; public var blendModeManager:BlendModeManager; public var spriteBatch:SpriteBatch; public var stencilManager:StencilManager; + public var defaultFramebuffer:GLFramebuffer; public function new () { diff --git a/openfl/_internal/renderer/TextFieldGraphics.hx b/openfl/_internal/renderer/TextFieldGraphics.hx new file mode 100644 index 0000000000..f410c58584 --- /dev/null +++ b/openfl/_internal/renderer/TextFieldGraphics.hx @@ -0,0 +1,383 @@ +package openfl._internal.renderer; + + +import lime.graphics.Image; +import lime.text.Glyph; +import lime.text.TextLayout; +import openfl._internal.renderer.RenderSession; +import openfl.display.BitmapData; +import openfl.display.Graphics; +import openfl.display.Tilesheet; +import openfl.geom.Point; +import openfl.geom.Rectangle; +import openfl.text.Font; +import openfl.text.TextField; +import openfl.text.TextFieldAutoSize; +import openfl.text.TextFormat; +import openfl.text.TextFormatAlign; + +@:access(openfl.text.TextField) + + +class TextFieldGraphics { + + + private static var bitmapData = new Map> (); + private static var glyphs = new Map>> (); + private static var tilesheets = new Map (); + private static var tileIDs = new Map> (); + + + public static function render (textField:TextField) { + + update (textField); + + if (textField.__graphics == null) { + + textField.__graphics = new Graphics (); + + } + + var graphics = textField.__graphics; + graphics.clear (); + + if (textField.border || textField.background) { + + if (textField.border) { + + graphics.lineStyle (1, textField.borderColor); + + } + + if (textField.background) { + + graphics.beginFill (textField.backgroundColor); + + } + + graphics.drawRect (0.5, 0.5, textField.__width - 1, textField.__height - 1); + + } + + if (textField.__tileData != null) { + + for (tilesheet in textField.__tilesheets.keys ()) { + + graphics.drawTiles (tilesheet, textField.__tileData.get (tilesheet), true, Tilesheet.TILE_RGB, textField.__tileDataLength.get (tilesheet)); + + } + + } + + } + + + private static inline function renderText (textField:TextField, text:String, format:TextFormat, offsetX:Float, textWidth:Float):Void { + + var font = textField.__getFontInstance (format); + + if (font != null && format.size != null) { + + if (!glyphs.exists (font)) { + + glyphs.set (font, new Map ()); + + } + + var size = Std.int (format.size); + var fontGlyphs = glyphs.get (font); + + if (!fontGlyphs.exists (size)) { + + fontGlyphs.set (size, font.renderGlyphs (font.getGlyphs (), size)); + + } + + var images = fontGlyphs.get (size); + + if (!bitmapData.exists (font)) { + + bitmapData.set (font, new Map ()); + + } + + var fontBitmapData = bitmapData.get (font); + + if (!fontBitmapData.exists (size)) { + + var width, height, data; + + for (image in images) { + + width = image.buffer.width; + height = image.buffer.height; + data = image.data; + break; + + } + + var bitmapData = new BitmapData (width, height); + + for (x in 0...width) { + + for (y in 0...height) { + + var alpha = data[(y * width) + x]; + var color = alpha << 24 | 0xFF << 16 | 0xFF << 8 | 0xFF; + bitmapData.setPixel32 (x, y, color); + + } + + } + + fontBitmapData.set (size, bitmapData); + + } + + var bitmapData = fontBitmapData.get (size); + + if (!tilesheets.exists (bitmapData)) { + + var tilesheet = new Tilesheet (bitmapData); + var tileID = new Map (); + + var image, index; + + for (key in images.keys ()) { + + image = images.get (key); + index = tilesheet.addTileRect (new Rectangle (image.offsetX, image.offsetY, image.width, image.height)); + + tileID.set (key, index); + } + + tileIDs.set (bitmapData, tileID); + tilesheets.set (bitmapData, tilesheet); + + } + + var tilesheet = tilesheets.get (bitmapData); + var tileID = tileIDs.get (bitmapData); + + var r = ((format.color >> 16) & 0xFF) / 0xFF; + var g = ((format.color >> 8) & 0xFF) / 0xFF; + var b = ((format.color) & 0xFF) / 0xFF; + + var tlm = textField.getLineMetrics(0); + + var image; + var x:Float = offsetX; + var y:Float = 2 + tlm.ascent; + + //If you render with y == 0, the bottom pixel of the "T" in "The Quick Brown Fox" will rest on TOP of your text field. + //Flash API text fields have a 2px margin on all sides, so (2 + ASCENT) puts your text right where it needs to be. + + var tileData; + + textField.__tilesheets.set (tilesheet, true); + + if (!textField.__tileData.exists (tilesheet)) { + + tileData = new Array (); + textField.__tileData.set (tilesheet, tileData); + textField.__tileDataLength.set (tilesheet, 0); + + } + + tileData = textField.__tileData.get (tilesheet); + + var offsetY = 0; + var lines = text.split ("\n"); + + if (textField.__textLayout == null) { + + textField.__textLayout = new TextLayout (); + + } + + var textLayout:TextLayout = textField.__textLayout; + var length = 0; + + var line_i:Int = 0; + var oldX = x; + + for (line in lines) { + + tlm = textField.getLineMetrics (line_i); + + //x position must be reset every line and recalculated + x = oldX; + + x += switch (format.align) { + + case LEFT, JUSTIFY: 0; //the renderer has already positioned the text at the right spot past the 2px left margin + case CENTER: ((textField.__width - 4) - tlm.width) / 2; //subtract 4 from textfield.__width because __width includes the 2px margin on both sides, which doesn't count + case RIGHT: ((textField.__width - 4) - tlm.width); //same thing here + + } + + textLayout.text = null; + textLayout.font = font; + textLayout.size = size; + textLayout.text = line; + + for (position in textLayout.positions) { + + image = images.get (position.glyph); + + if (image != null) { + + if (length >= tileData.length) { + + tileData.push (x + position.offset.x + image.x); + tileData.push (y + position.offset.y - image.y); + tileData.push (tileID.get (position.glyph)); + tileData.push (r); + tileData.push (g); + tileData.push (b); + + } else { + + tileData[length] = x + position.offset.x + image.x; + tileData[length + 1] = y + position.offset.y - image.y; + tileData[length + 2] = tileID.get (position.glyph); + tileData[length + 3] = r; + tileData[length + 4] = g; + tileData[length + 5] = b; + + } + + length += 6; + + } + + x += position.advance.x; + y -= position.advance.y; + + } + + y += tlm.height; //always add the line height at the end + line_i++; + + } + + textField.__tileDataLength.set (tilesheet, length); + + } + + } + + + public static function update (textField:TextField):Bool { + + if (textField.__dirty) { + + if (((textField.__text == null || textField.__text == "") && !textField.background && !textField.border) || ((textField.width <= 0 || textField.height <= 0) && textField.autoSize != TextFieldAutoSize.LEFT)) { + + textField.__tilesheets = null; + textField.__tileData = null; + textField.__tileDataLength = null; + textField.__dirty = false; + + } else { + + textField.__tilesheets = new Map (); + + if (textField.__tileData == null) { + + textField.__tileData = new Map (); + textField.__tileDataLength = new Map (); + + } + + if (textField.__text != null && textField.__text != "") { + + var text = textField.text; + + if (textField.displayAsPassword) { + + var length = text.length; + var mask = ""; + + for (i in 0...length) { + + mask += "*"; + + } + + text = mask; + + } + + var measurements = textField.__measureText (); + var textWidth = 0.0; + + for (measurement in measurements) { + + textWidth += measurement; + + } + + if (textField.autoSize == TextFieldAutoSize.LEFT) { + + textField.__width = textWidth + 4; + textField.__height = textField.textHeight + 4; + + } + + if (textField.__ranges == null) { + + renderText (textField, text, textField.__textFormat, 2, textWidth); + + } else { + + var currentIndex = 0; + var range; + var offsetX = 2.0; + + for (i in 0...textField.__ranges.length) { + + range = textField.__ranges[i]; + + renderText (textField, text.substring (range.start, range.end), range.format, offsetX, textWidth); + offsetX += measurements[i]; + + } + + } + + } else { + + if (textField.autoSize == TextFieldAutoSize.LEFT) { + + textField.__width = 4; + textField.__height = 4; + + } + + } + + for (key in textField.__tileData.keys ()) { + + if (!textField.__tilesheets.exists (key)) { + + textField.__tileData.remove (key); + textField.__tileDataLength.remove (key); + + } + + } + + textField.__dirty = false; + return true; + + } + + } + + return false; + + } + + +} diff --git a/openfl/_internal/renderer/cairo/CairoBitmap.hx b/openfl/_internal/renderer/cairo/CairoBitmap.hx new file mode 100644 index 0000000000..98c82a3ad0 --- /dev/null +++ b/openfl/_internal/renderer/cairo/CairoBitmap.hx @@ -0,0 +1,114 @@ +package openfl._internal.renderer.cairo; + + +#if cpp +import cpp.Pointer; +#end +import lime.graphics.cairo.CairoFormat; +import lime.graphics.cairo.CairoPattern; +import lime.graphics.cairo.CairoSurface; +import openfl._internal.renderer.RenderSession; +import openfl.display.Bitmap; + +@:access(lime.graphics.ImageBuffer) +@:access(openfl.display.Bitmap) +@:access(openfl.display.BitmapData) +@:access(openfl.geom.Matrix) + + +class CairoBitmap { + + + public static inline function render (bitmap:Bitmap, renderSession:RenderSession):Void { + + if (!bitmap.__renderable || bitmap.__worldAlpha <= 0) return; + + //var context = renderSession.context; + var cairo = renderSession.cairo; + + if (bitmap.bitmapData != null && bitmap.bitmapData.__isValid) { + + if (bitmap.__mask != null) { + + renderSession.maskManager.pushMask (bitmap.__mask); + + } + + //bitmap.bitmapData.__sync (); + + //context.globalAlpha = bitmap.__worldAlpha; + var transform = bitmap.__worldTransform; + var scrollRect = bitmap.scrollRect; + + if (renderSession.roundPixels) { + + var matrix = transform.__toMatrix3 (); + matrix.tx = Math.round (matrix.tx); + matrix.ty = Math.round (matrix.ty); + cairo.matrix = matrix; + //context.setTransform (transform.a, transform.b, transform.c, transform.d, Std.int (transform.tx), Std.int (transform.ty)); + + } else { + + cairo.matrix = transform.__toMatrix3 (); + //context.setTransform (transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + + } + + //if (!bitmap.smoothing) { + // + //untyped (context).mozImageSmoothingEnabled = false; + //untyped (context).webkitImageSmoothingEnabled = false; + //untyped (context).imageSmoothingEnabled = false; + // + //} + + var surface = bitmap.bitmapData.getSurface (); + + if (surface != null) { + + cairo.setSourceSurface (surface, 0, 0); + + if (scrollRect != null) { + + cairo.pushGroup (); + cairo.setSourceSurface (surface, 0, 0); + cairo.newPath (); + cairo.rectangle (scrollRect.x, scrollRect.y, scrollRect.width, scrollRect.height); + cairo.fill (); + cairo.popGroupToSource (); + + } + + if (bitmap.__worldAlpha == 1) { + + cairo.paint (); + + } else { + + cairo.paintWithAlpha (bitmap.__worldAlpha); + + } + + } + + //if (!bitmap.smoothing) { + // + //untyped (context).mozImageSmoothingEnabled = true; + //untyped (context).webkitImageSmoothingEnabled = true; + //untyped (context).imageSmoothingEnabled = true; + // + //} + + if (bitmap.__mask != null) { + + renderSession.maskManager.popMask (); + + } + + } + + } + + +} \ No newline at end of file diff --git a/openfl/_internal/renderer/cairo/CairoGraphics.hx b/openfl/_internal/renderer/cairo/CairoGraphics.hx new file mode 100644 index 0000000000..5f6e5ab951 --- /dev/null +++ b/openfl/_internal/renderer/cairo/CairoGraphics.hx @@ -0,0 +1,1086 @@ +package openfl._internal.renderer.cairo; + + +import lime.graphics.cairo.Cairo; +import lime.graphics.cairo.CairoExtend; +import lime.graphics.cairo.CairoPattern; +import lime.graphics.cairo.CairoSurface; +import lime.math.Matrix3; +import lime.math.Vector2; +import openfl._internal.renderer.RenderSession; +import openfl.display.BitmapData; +import openfl.display.CapsStyle; +import openfl.display.DisplayObject; +import openfl.display.GradientType; +import openfl.display.Graphics; +import openfl.display.InterpolationMethod; +import openfl.display.SpreadMethod; +import openfl.geom.Matrix; +import openfl.geom.Point; +import openfl.geom.Rectangle; +import openfl.Lib; +import openfl.Vector; + +@:access(openfl.display.DisplayObject) +@:access(openfl.display.BitmapData) +@:access(openfl.display.Graphics) +@:access(openfl.display.Tilesheet) +@:access(openfl.geom.Matrix) + + +class CairoGraphics { + + + private static var SIN45 = 0.70710678118654752440084436210485; + private static var TAN22 = 0.4142135623730950488016887242097; + + private static var bounds:Rectangle; + private static var cairo:Cairo; + private static var fillCommands:Array; + private static var fillPattern:CairoPattern; + private static var fillPatternMatrix:Matrix; + private static var graphics:Graphics; + private static var hasFill:Bool; + private static var hasStroke:Bool; + private static var inversePendingMatrix:Matrix; + private static var pendingMatrix:Matrix; + private static var strokeCommands:Array; + private static var strokePattern:CairoPattern; + + private static var bitmapFill:BitmapData; + private static var bitmapRepeat:Bool; + + private static function createTempPatternCanvas (bitmap:BitmapData, repeat:Bool, width:Int, height:Int) : CairoPattern{ + + var surface = new CairoSurface (ARGB32, width, height); + var pattern = CairoPattern.createForSurface (bitmap.getSurface()); + + if (repeat) { + pattern.extend = CairoExtend.REPEAT; + } + + cairo.source = pattern; + cairo.paint (); + surface.destroy (); + return pattern; + } + + + private static function endFill ():Void { + + cairo.newPath (); + playCommands (fillCommands, false); + fillCommands = []; + } + + + private static function endStroke ():Void { + + cairo.newPath (); + playCommands (strokeCommands, true); + cairo.closePath (); + strokeCommands = []; + } + + + private static function drawRoundRect (x:Float, y:Float, width:Float, height:Float, rx:Float, ry:Float):Void { + + if (ry == -1) ry = rx; + + rx *= 0.5; + ry *= 0.5; + + if (rx > width / 2) rx = width / 2; + if (ry > height / 2) ry = height / 2; + + var xe = x + width, + ye = y + height, + cx1 = -rx + (rx * SIN45), + cx2 = -rx + (rx * TAN22), + cy1 = -ry + (ry * SIN45), + cy2 = -ry + (ry * TAN22); + + cairo.moveTo (xe, ye - ry); + quadraticCurveTo (xe, ye + cy2, xe + cx1, ye + cy1); + quadraticCurveTo (xe + cx2, ye, xe - rx, ye); + cairo.lineTo (x + rx, ye); + quadraticCurveTo (x - cx2, ye, x - cx1, ye + cy1); + quadraticCurveTo (x, ye + cy2, x, ye - ry); + cairo.lineTo (x, y + ry); + quadraticCurveTo (x, y - cy2, x - cx1, y - cy1); + quadraticCurveTo (x - cx2, y, x + rx, y); + cairo.lineTo (xe - rx, y); + quadraticCurveTo (xe + cx2, y, xe + cx1, y - cy1); + quadraticCurveTo (xe, y - cy2, xe, y + ry); + cairo.lineTo (xe, ye - ry); + + } + + + private static inline function isCCW (x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float) { + + return ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)) < 0; + + } + + + private static function normalizeUVT (uvt:Vector, skipT:Bool = false): { max:Float, uvt:Vector } { + + var max:Float = Math.NEGATIVE_INFINITY; + var tmp = Math.NEGATIVE_INFINITY; + var len = uvt.length; + + for (t in 1...len + 1) { + + if (skipT && t % 3 == 0) { + + continue; + + } + + tmp = uvt[t - 1]; + + if (max < tmp) { + + max = tmp; + + } + + } + + var result = new Vector (); + + for (t in 1...len + 1) { + + if (skipT && t % 3 == 0) { + + continue; + + } + + result.push ((uvt[t - 1] / max)); + + } + + return { max: max, uvt: result }; + + } + + private static function createGradientPattern( type:GradientType, colors:Array, alphas:Array, ratios:Array, matrix:Matrix, spreadMethod:Null, interpolationMethod:Null, focalPointRatio:Null ): CairoPattern { + + var pattern : CairoPattern = null; + + switch (type) { + + case RADIAL: + + if (matrix == null) matrix = new Matrix (); + + var point = matrix.transformPoint (new Point (1638.4, 0)); + + pattern = CairoPattern.createRadial (matrix.tx, matrix.ty, 0, matrix.tx, matrix.ty, (point.x - matrix.tx) / 2); + + case LINEAR: + + if (matrix == null) matrix = new Matrix (); + + var point1 = matrix.transformPoint (new Point (-819.2, 0)); + var point2 = matrix.transformPoint (new Point (819.2, 0)); + + pattern = CairoPattern.createLinear (point1.x, point1.y, point2.x, point2.y); + + } + + for (i in 0...colors.length) { + + var rgb = colors[i]; + var alpha = alphas[i]; + var r = ((rgb & 0xFF0000) >>> 16) / 0xFF; + var g = ((rgb & 0x00FF00) >>> 8) / 0xFF; + var b = (rgb & 0x0000FF) / 0xFF; + + var ratio = ratios[i] / 0xFF; + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; + + pattern.addColorStopRGBA (ratio, r, g, b, alpha); + + } + + var mat = pattern.matrix; + + mat.tx = bounds.x; + mat.ty = bounds.y; + + pattern.matrix = mat; + + return pattern; + } + + private static function createImagePattern (bitmapFill:BitmapData, matrix:Matrix, bitmapRepeat:Bool):CairoPattern { + + var pattern = CairoPattern.createForSurface (bitmapFill.getSurface ()); + + if (bitmapRepeat) { + + pattern.extend = CairoExtend.REPEAT; + + } + + fillPatternMatrix = matrix; + + //var mat = pattern.matrix; + // + //if ( matrix != null ) + //{ + //var matrix = matrix.invert (); + //mat.a = matrix.a; + //mat.b = matrix.b; + //mat.c = matrix.c; + //mat.d = matrix.d; + // + //mat.tx = matrix.tx - bounds.x; + //mat.ty = matrix.ty - bounds.y; + //} + //else + //{ + //mat.tx = bounds.x; + //mat.ty = bounds.y; + //} + // + //pattern.matrix = mat; + + return pattern; + } + + private static function closePath() : Void { + + if ( strokePattern == null ) + return; + + cairo.closePath(); + cairo.source = strokePattern; + cairo.strokePreserve (); + cairo.newPath (); + } + + private static function playCommands (commands:Array, stroke:Bool = false):Void { + + bounds = graphics.__bounds; + + var offsetX = bounds.x; + var offsetY = bounds.y; + + var positionX = 0.0; + var positionY = 0.0; + + var closeGap = false; + var startX = 0.0; + var startY = 0.0; + + cairo.fillRule = EVEN_ODD; + + for (command in commands) { + + switch (command) { + + case CubicCurveTo (cx1, cy1, cx2, cy2, x, y): + + cairo.curveTo (cx1 - offsetX, cy1 - offsetY, cx2 - offsetX, cy2 - offsetY, x - offsetX, y - offsetY); + + case CurveTo (cx, cy, x, y): + + quadraticCurveTo (cx - offsetX, cy - offsetY, x - offsetX, y - offsetY); + + case DrawCircle (x, y, radius): + + cairo.moveTo (x - offsetX + radius, y - offsetY); + cairo.arc (x - offsetX, y - offsetY, radius, 0, Math.PI * 2); + + case DrawRect (x, y, width, height): + + cairo.rectangle (x - offsetX, y - offsetY, width, height); + + case DrawEllipse (x, y, width, height): + + x -= offsetX; + y -= offsetY; + + var kappa = .5522848, + ox = (width / 2) * kappa, // control point offset horizontal + oy = (height / 2) * kappa, // control point offset vertical + xe = x + width, // x-end + ye = y + height, // y-end + xm = x + width / 2, // x-middle + ym = y + height / 2; // y-middle + + cairo.moveTo (x, ym); + cairo.curveTo (x, ym - oy, xm - ox, y, xm, y); + cairo.curveTo (xm + ox, y, xe, ym - oy, xe, ym); + cairo.curveTo (xe, ym + oy, xm + ox, ye, xm, ye); + cairo.curveTo (xm - ox, ye, x, ym + oy, x, ym); + + case DrawRoundRect (x, y, width, height, rx, ry): + + drawRoundRect (x - offsetX, y - offsetY, width, height, rx, ry); + + case LineTo (x, y): + + cairo.lineTo (x - offsetX, y - offsetY); + + positionX = x; + positionY = y; + + case MoveTo (x, y): + + cairo.moveTo (x - offsetX, y - offsetY); + + positionX = x; + positionY = y; + + closeGap = true; + startX = x; + startY = y; + + case LineStyle (thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit): + + if (stroke && hasStroke) { + closePath(); + } + cairo.moveTo (positionX - offsetX, positionY - offsetY); + + if (thickness == null) { + + hasStroke = false; + + } else { + + hasStroke = true; + + cairo.lineWidth = thickness; + + if (joints == null) { + + cairo.lineJoin = ROUND; + + } else { + + cairo.lineJoin = switch (joints) { + + case MITER: MITER; + case BEVEL: BEVEL; + default: ROUND; + + } + + } + + if (caps == null) { + + cairo.lineCap = ROUND; + + } else { + + cairo.lineCap = switch (caps) { + + case NONE: BUTT; + case SQUARE: SQUARE; + default: ROUND; + + } + + } + + cairo.miterLimit = (miterLimit == null ? 3 : miterLimit); + + if ( color != null ) + { + var r = ((color & 0xFF0000) >>> 16) / 0xFF; + var g = ((color & 0x00FF00) >>> 8) / 0xFF; + var b = (color & 0x0000FF) / 0xFF; + + + if (strokePattern != null) { + strokePattern.destroy(); + } + + if (alpha == 1 || alpha == null) { + + strokePattern = CairoPattern.createRGB (r, g, b); + + } else { + + strokePattern = CairoPattern.createRGBA (r, g, b, alpha); + } + } + } + + case LineGradientStyle (type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio): + + if (stroke && hasStroke) { + closePath(); + } + + if (strokePattern != null) { + strokePattern.destroy(); + } + + cairo.moveTo (positionX - offsetX, positionY - offsetY); + + strokePattern = createGradientPattern( type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio ); + + hasStroke = true; + + case LineBitmapStyle (bitmap, matrix, repeat, smooth): + + if (stroke && hasStroke) { + closePath(); + } + + if (strokePattern != null) { + strokePattern.destroy(); + } + + cairo.moveTo (positionX - offsetX, positionY - offsetY); + + strokePattern = createImagePattern( bitmap, matrix, repeat ); + + hasStroke = true; + + case BeginBitmapFill (bitmap, matrix, repeat, smooth): + + if (fillPattern != null) { + + fillPattern.destroy (); + + } + + fillPattern = createImagePattern (bitmap, matrix, repeat); + + bitmapFill = bitmap; + bitmapRepeat = repeat; + + hasFill = true; + + case BeginFill (rgb, alpha): + + if (alpha < 0.005) { + + hasFill = false; + + } else { + + if (fillPattern != null) { + + fillPattern.destroy (); + fillPatternMatrix = null; + + } + + fillPattern = CairoPattern.createRGBA (((rgb & 0xFF0000) >>> 16) / 0xFF, ((rgb & 0x00FF00) >>> 8) / 0xFF, (rgb & 0x0000FF) / 0xFF, alpha); + hasFill = true; + + } + + bitmapFill = null; + + + case BeginGradientFill (type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio): + + if (fillPattern != null) { + + fillPattern.destroy (); + fillPatternMatrix = null; + + } + + fillPattern = createGradientPattern( type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio ); + + hasFill = true; + bitmapFill = null; + + default: + + } + + } + + if (stroke && hasStroke) { + + if (hasFill && closeGap) { + + cairo.lineTo (startX - offsetX, startY - offsetY); + + } + + cairo.source = strokePattern; + cairo.strokePreserve (); + + } + + if (!stroke && hasFill) { + + cairo.translate ( -bounds.x, -bounds.y); + + if (fillPatternMatrix != null) { + + var matrix = fillPatternMatrix.clone (); + matrix.invert (); + + if (pendingMatrix != null) { + + matrix.concat (pendingMatrix); + + } + + fillPattern.matrix = matrix.__toMatrix3 (); + + } + + cairo.source = fillPattern; + + if (pendingMatrix != null) { + + cairo.transform (pendingMatrix.__toMatrix3 ()); + cairo.fillPreserve (); + cairo.transform (inversePendingMatrix.__toMatrix3 ()); + + } else { + + cairo.fillPreserve (); + + } + + cairo.translate (bounds.x, bounds.y); + cairo.closePath (); + } + + } + + + private static function quadraticCurveTo (cx:Float, cy:Float, x:Float, y:Float):Void { + + var current = null; + + if (!cairo.hasCurrentPoint) { + + cairo.moveTo (cx, cy); + current = new Vector2 (cx, cy); + + } else { + + current = cairo.currentPoint; + + } + + var cx1 = current.x + ((2 / 3) * (cx - current.x)); + var cy1 = current.y + ((2 / 3) * (cy - current.y)); + var cx2 = x + ((2 / 3) * (cx - x)); + var cy2 = y + ((2 / 3) * (cy - y)); + + cairo.curveTo (cx1, cy1, cx2, cy2, x, y); + + } + + + public static function render (graphics:Graphics, renderSession:RenderSession):Void { + + if (graphics.__dirty) { + + CairoGraphics.graphics = graphics; + bounds = graphics.__bounds; + + if (!graphics.__visible || graphics.__commands.length == 0 || bounds == null || bounds.width == 0 || bounds.height == 0) { + + if (graphics.__cairo != null) { + + graphics.__cairo.destroy (); + graphics.__cairo = null; + + } + + } else { + + if (graphics.__cairo != null) { + + var surface = graphics.__cairo.target; + + if (bounds.width != surface.width || bounds.height != surface.height) { + + graphics.__cairo.destroy (); + graphics.__cairo = null; + + } + + } + + if (graphics.__cairo == null) { + + var surface = new CairoSurface (ARGB32, Math.ceil (bounds.width+2), Math.ceil (bounds.height+2)); + graphics.__cairo = new Cairo (surface); + surface.destroy (); + + } + + cairo = graphics.__cairo; + + var offsetX = bounds.x; + var offsetY = bounds.y; + + fillCommands = new Array (); + strokeCommands = new Array (); + + hasFill = false; + hasStroke = false; + + fillPattern = null; + strokePattern = null; + + for (command in graphics.__commands) { + + switch (command) { + + case CubicCurveTo (_, _, _, _, _, _), CurveTo (_, _, _, _), LineTo (_, _), MoveTo (_, _): + + fillCommands.push (command); + strokeCommands.push (command); + + case EndFill: + + endFill (); + endStroke (); + hasFill = false; + bitmapFill = null; + + case LineStyle (_, _, _, _, _, _, _, _), LineGradientStyle (_, _, _, _, _, _, _, _), LineBitmapStyle (_, _, _, _): + + strokeCommands.push (command); + + case BeginBitmapFill (_, _, _, _), BeginFill (_, _), BeginGradientFill (_, _, _, _, _, _, _, _): + + endFill (); + endStroke (); + + fillCommands.push (command); + strokeCommands.push (command); + + case DrawCircle (_, _, _), DrawEllipse (_, _, _, _), DrawRect (_, _, _, _), DrawRoundRect (_, _, _, _, _, _): + + //endFill (); + //endStroke (); + + fillCommands.push (command); + strokeCommands.push (command); + + case DrawTriangles (vertices, indices, uvtData, culling, _, _): + + endFill (); + endStroke (); + + var v = vertices; + var ind = indices; + var uvt = uvtData; + var pattern:CairoPattern = null; + var colorFill = bitmapFill == null; + + if (colorFill && uvt != null) { + // Flash doesn't draw anything if the fill isn't a bitmap and there are uvt values + break; + } + + var width = 0; + var height = 0; + + if (!colorFill) { + + //TODO move this to Graphics? + + if (uvtData == null) { + + uvtData = new Vector (); + + for (i in 0...(Std.int (v.length / 2))) { + + uvtData.push (v[i * 2] / bitmapFill.width); + uvtData.push (v[i * 2 + 1] / bitmapFill.height); + + } + + } + + var skipT = uvtData.length != v.length; + var normalizedUVT = normalizeUVT (uvtData, skipT); + var maxUVT = normalizedUVT.max; + uvt = normalizedUVT.uvt; + + if (maxUVT > 1) { + width = Std.int (bounds.width); + height = Std.int (bounds.height); + + + } else { + + width = bitmapFill.width; + height = bitmapFill.height; + + } + + pattern = createTempPatternCanvas (bitmapFill, bitmapRepeat, width, height); + + } + + var i = 0; + var l = ind.length; + + var a:Int, b:Int, c:Int; + var iax:Int, iay:Int, ibx:Int, iby:Int, icx:Int, icy:Int; + var x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float; + var uvx1:Float, uvy1:Float, uvx2:Float, uvy2:Float, uvx3:Float, uvy3:Float; + var denom:Float; + var t1:Float, t2:Float, t3:Float, t4:Float; + var dx:Float, dy:Float; + + while (i < l) { + + a = i; + b = i + 1; + c = i + 2; + + iax = ind[a] * 2; + iay = ind[a] * 2 + 1; + ibx = ind[b] * 2; + iby = ind[b] * 2 + 1; + icx = ind[c] * 2; + icy = ind[c] * 2 + 1; + + x1 = v[iax]; + y1 = v[iay]; + x2 = v[ibx]; + y2 = v[iby]; + x3 = v[icx]; + y3 = v[icy]; + + switch (culling) { + + case POSITIVE: + + if (!isCCW (x1, y1, x2, y2, x3, y3)) { + + i += 3; + continue; + + } + + case NEGATIVE: + + if (isCCW (x1, y1, x2, y2, x3, y3)) { + + i += 3; + continue; + + } + + default: + + } + + if (colorFill) { + + cairo.newPath (); + cairo.moveTo (x1, y1); + cairo.lineTo (x2, y2); + cairo.lineTo (x3, y3); + cairo.closePath (); + cairo.fillPreserve (); + i += 3; + continue; + + } + + cairo.save (); + cairo.newPath (); + cairo.moveTo (x1, y1); + cairo.lineTo (x2, y2); + cairo.lineTo (x3, y3); + cairo.closePath (); + cairo.clip (); + + uvx1 = uvt[iax] * width; + uvx2 = uvt[ibx] * width; + uvx3 = uvt[icx] * width; + uvy1 = uvt[iay] * height; + uvy2 = uvt[iby] * height; + uvy3 = uvt[icy] * height; + + denom = uvx1 * (uvy3 - uvy2) - uvx2 * uvy3 + uvx3 * uvy2 + (uvx2 - uvx3) * uvy1; + + if (denom == 0) { + + i += 3; + continue; + + } + + t1 = - (uvy1 * (x3 - x2) - uvy2 * x3 + uvy3 * x2 + (uvy2 - uvy3) * x1) / denom; + t2 = (uvy2 * y3 + uvy1 * (y2 - y3) - uvy3 * y2 + (uvy3 - uvy2) * y1) / denom; + t3 = (uvx1 * (x3 - x2) - uvx2 * x3 + uvx3 * x2 + (uvx2 - uvx3) * x1) / denom; + t4 = - (uvx2 * y3 + uvx1 * (y2 - y3) - uvx3 * y2 + (uvx3 - uvx2) * y1) / denom; + dx = (uvx1 * (uvy3 * x2 - uvy2 * x3) + uvy1 * (uvx2 * x3 - uvx3 * x2) + (uvx3 * uvy2 - uvx2 * uvy3) * x1) / denom; + dy = (uvx1 * (uvy3 * y2 - uvy2 * y3) + uvy1 * (uvx2 * y3 - uvx3 * y2) + (uvx3 * uvy2 - uvx2 * uvy3) * y1) / denom; + + var matrix = new Matrix3 (t1, t2, t3, t4, dx, dy); + + cairo.transform( matrix ); + cairo.source = pattern; + + cairo.paint (); + cairo.restore (); + + i += 3; + + } + + case DrawTiles (sheet, tileData, smooth, flags, count): + + endFill (); + endStroke (); + + var useScale = (flags & Graphics.TILE_SCALE) > 0; + var useRotation = (flags & Graphics.TILE_ROTATION) > 0; + var useTransform = (flags & Graphics.TILE_TRANS_2x2) > 0; + var useRGB = (flags & Graphics.TILE_RGB) > 0; + var useAlpha = (flags & Graphics.TILE_ALPHA) > 0; + var useRect = (flags & Graphics.TILE_RECT) > 0; + var useOrigin = (flags & Graphics.TILE_ORIGIN) > 0; + var useBlendAdd = (flags & Graphics.TILE_BLEND_ADD) > 0; + + if (useTransform) { useScale = false; useRotation = false; } + + var scaleIndex = 0; + var rotationIndex = 0; + var rgbIndex = 0; + var alphaIndex = 0; + var transformIndex = 0; + + var numValues = 3; + + if (useRect) { numValues = useOrigin ? 8 : 6; } + if (useScale) { scaleIndex = numValues; numValues ++; } + if (useRotation) { rotationIndex = numValues; numValues ++; } + if (useTransform) { transformIndex = numValues; numValues += 4; } + if (useRGB) { rgbIndex = numValues; numValues += 3; } + if (useAlpha) { alphaIndex = numValues; numValues ++; } + + var totalCount = tileData.length; + if (count >= 0 && totalCount > count) totalCount = count; + var itemCount = Std.int (totalCount / numValues); + var index = 0; + + var rect = null; + var center = null; + var previousTileID = -1; + + var surface:Dynamic; + sheet.__bitmap.__sync (); + surface = sheet.__bitmap.getSurface (); + + if (useBlendAdd) { + cairo.operator = ADD; + } + + while (index < totalCount) { + + // Std.int doesn't handle null on neko target + #if neko + var f : Float = tileData[index + 2]; + var i = 0; + + if( f != null ) + i = Std.int ( f ); + #else + var i = Std.int( tileData[index +2] ); + #end + + var tileID = (!useRect) ? i : -1; + + if (!useRect && tileID != previousTileID) { + + rect = sheet.__tileRects[tileID]; + center = sheet.__centerPoints[tileID]; + + previousTileID = tileID; + + } else if (useRect) { + + rect = sheet.__rectTile; + rect.setTo (tileData[index + 2], tileData[index + 3], tileData[index + 4], tileData[index + 5]); + center = sheet.__point; + + if (useOrigin) { + + center.setTo (tileData[index + 6], tileData[index + 7]); + + } else { + + center.setTo (0, 0); + + } + + } + + if (rect != null && rect.width > 0 && rect.height > 0 && center != null) { + + cairo.save (); + + if (useTransform) { + var matrix = new Matrix3 (tileData[index + transformIndex], tileData[index + transformIndex + 1], tileData[index + transformIndex + 2], tileData[index + transformIndex + 3], 0, 0); + cairo.matrix = matrix; + } + + cairo.translate( tileData[index], tileData[index + 1] ); + + + if (useRotation) { + cairo.rotate(tileData[index + rotationIndex]); + } + + if (useScale) { + var scale = tileData[index + scaleIndex]; + cairo.scale( scale, scale ); + } + + cairo.setSourceSurface( surface, 0, 0 ); + + if (useAlpha) { + cairo.paintWithAlpha (tileData[index + alphaIndex]); + } else { + cairo.paint (); + } + + cairo.restore (); + + } + + index += numValues; + + } + + if (useBlendAdd) { + cairo.operator = OVER; + } + + default: + + openfl.Lib.notImplemented ("CairoGraphics"); + + } + + } + + } + + graphics.__dirty = false; + + if (fillCommands.length > 0) { + + endFill (); + + } + + if (strokeCommands.length > 0) { + + endStroke (); + + } + + } + + } + + + public static function renderMask (graphics:Graphics, renderSession:RenderSession) { + + if (graphics.__commands.length != 0) { + + var cairo = renderSession.cairo; + + var positionX = 0.0; + var positionY = 0.0; + + var offsetX = 0; + var offsetY = 0; + + for (command in graphics.__commands) { + + switch (command) { + + case CubicCurveTo (cx1, cx2, cy1, cy2, x, y): + + cairo.curveTo (cx1 - offsetX, cy1 - offsetY, cx2 - offsetX, cy2 - offsetY, x - offsetX, y - offsetY); + positionX = x; + positionY = y; + + case CurveTo (cx, cy, x, y): + + quadraticCurveTo (cx - offsetX, cy - offsetY, x - offsetX, y - offsetY); + positionX = x; + positionY = y; + + case DrawCircle (x, y, radius): + + cairo.arc (x - offsetX, y - offsetY, radius, 0, Math.PI * 2); + + case DrawEllipse (x, y, width, height): + + x -= offsetX; + y -= offsetY; + + var kappa = .5522848, + ox = (width / 2) * kappa, // control point offset horizontal + oy = (height / 2) * kappa, // control point offset vertical + xe = x + width, // x-end + ye = y + height, // y-end + xm = x + width / 2, // x-middle + ym = y + height / 2; // y-middle + + //closePath (false); + //beginPath (); + cairo.moveTo (x, ym); + cairo.curveTo (x, ym - oy, xm - ox, y, xm, y); + cairo.curveTo (xm + ox, y, xe, ym - oy, xe, ym); + cairo.curveTo (xe, ym + oy, xm + ox, ye, xm, ye); + cairo.curveTo (xm - ox, ye, x, ym + oy, x, ym); + //closePath (false); + + case DrawRect (x, y, width, height): + + cairo.rectangle (x - offsetX, y - offsetY, width, height); + + case DrawRoundRect (x, y, width, height, rx, ry): + + drawRoundRect (x - offsetX, y - offsetY, width, height, rx, ry); + + case LineTo (x, y): + + cairo.lineTo (x - offsetX, y - offsetY); + positionX = x; + positionY = y; + + case MoveTo (x, y): + + cairo.moveTo (x - offsetX, y - offsetY); + positionX = x; + positionY = y; + + default: + + } + + } + + } + + } + + +} diff --git a/openfl/_internal/renderer/cairo/CairoMaskManager.hx b/openfl/_internal/renderer/cairo/CairoMaskManager.hx new file mode 100644 index 0000000000..c87c5e38a2 --- /dev/null +++ b/openfl/_internal/renderer/cairo/CairoMaskManager.hx @@ -0,0 +1,65 @@ +package openfl._internal.renderer.cairo; + + +import lime.math.Matrix3; +import openfl._internal.renderer.AbstractMaskManager; +import openfl.display.*; +import openfl.geom.*; + +@:access(openfl.display.DisplayObject) +@:access(openfl.geom.Matrix) +@:keep + + +class CairoMaskManager extends AbstractMaskManager { + + + public function new (renderSession:RenderSession) { + + super (renderSession); + + } + + + public override function pushMask (mask:DisplayObject):Void { + + var cairo = renderSession.cairo; + + cairo.save (); + + //var cacheAlpha = mask.__worldAlpha; + var transform = mask.__getTransform (); + + cairo.matrix = transform.__toMatrix3 (); + + cairo.newPath (); + mask.__renderCairoMask (renderSession); + cairo.clip (); + + //mask.worldAlpha = cacheAlpha; + + } + + + public override function pushRect (rect:Rectangle, transform:Matrix):Void { + + var cairo = renderSession.cairo; + cairo.save (); + + cairo.matrix = new Matrix3 (transform.a, transform.c, transform.b, transform.d, transform.tx, transform.ty); + + cairo.newPath (); + cairo.rectangle (rect.x, rect.y, rect.width, rect.height); + cairo.clip (); + + } + + + public override function popMask ():Void { + + renderSession.cairo.restore (); + + } + + +} \ No newline at end of file diff --git a/openfl/_internal/renderer/cairo/CairoRenderer.hx b/openfl/_internal/renderer/cairo/CairoRenderer.hx new file mode 100644 index 0000000000..1160df3011 --- /dev/null +++ b/openfl/_internal/renderer/cairo/CairoRenderer.hx @@ -0,0 +1,51 @@ +package openfl._internal.renderer.cairo; + + +import lime.graphics.cairo.Cairo; +import lime.math.Matrix3; +import openfl._internal.renderer.AbstractRenderer; +import openfl._internal.renderer.RenderSession; +import openfl.display.Stage; + +@:access(openfl.display.Stage) +@:allow(openfl.display.Stage) + + +class CairoRenderer extends AbstractRenderer { + + + private var cairo:Cairo; + + + public function new (width:Int, height:Int, cairo:Cairo) { + + super (width, height); + + this.cairo = cairo; + + renderSession = new RenderSession (); + renderSession.cairo = cairo; + renderSession.roundPixels = true; + renderSession.renderer = this; + renderSession.maskManager = new CairoMaskManager (renderSession); + + } + + + public override function render (stage:Stage):Void { + + cairo.identityMatrix (); + + if (stage.__clearBeforeRender) { + + cairo.setSourceRGB (stage.__colorSplit[0], stage.__colorSplit[1], stage.__colorSplit[2]); + cairo.paint (); + + } + + stage.__renderCairo (renderSession); + + } + + +} \ No newline at end of file diff --git a/openfl/_internal/renderer/cairo/CairoShape.hx b/openfl/_internal/renderer/cairo/CairoShape.hx new file mode 100644 index 0000000000..a20b2adefe --- /dev/null +++ b/openfl/_internal/renderer/cairo/CairoShape.hx @@ -0,0 +1,78 @@ +package openfl._internal.renderer.cairo; + + +import openfl.display.DisplayObject; + +@:access(openfl.display.DisplayObject) +@:access(openfl.display.Graphics) +@:access(openfl.geom.Matrix) + + +class CairoShape { + + + public static inline function render (shape:DisplayObject, renderSession:RenderSession):Void { + + if (!shape.__renderable || shape.__worldAlpha <= 0) return; + + var graphics = shape.__graphics; + + if (graphics != null) { + + CairoGraphics.render (graphics, renderSession); + + if (graphics.__cairo != null) { + + if (shape.__mask != null) { + + renderSession.maskManager.pushMask (shape.__mask); + + } + + var cairo = renderSession.cairo; + var scrollRect = shape.scrollRect; + + //context.globalAlpha = shape.__worldAlpha; + var transform = shape.__worldTransform; + + if (renderSession.roundPixels) { + + var matrix = transform.__toMatrix3 (); + matrix.tx = Math.round (matrix.tx); + matrix.ty = Math.round (matrix.ty); + cairo.matrix = matrix; + + } else { + + cairo.matrix = transform.__toMatrix3 (); + + } + + cairo.setSourceSurface (graphics.__cairo.target, graphics.__bounds.x, graphics.__bounds.y); + + if (scrollRect != null) { + + cairo.pushGroup (); + cairo.newPath (); + cairo.rectangle (graphics.__bounds.x + scrollRect.x, graphics.__bounds.y + scrollRect.y, scrollRect.width, scrollRect.height); + cairo.fill (); + cairo.popGroupToSource (); + + } + + cairo.paintWithAlpha (shape.__worldAlpha); + + if (shape.__mask != null) { + + renderSession.maskManager.popMask (); + + } + + } + + } + + } + + +} \ No newline at end of file diff --git a/openfl/_internal/renderer/cairo/CairoTextField.hx b/openfl/_internal/renderer/cairo/CairoTextField.hx new file mode 100644 index 0000000000..d7829d55c0 --- /dev/null +++ b/openfl/_internal/renderer/cairo/CairoTextField.hx @@ -0,0 +1,23 @@ +package openfl._internal.renderer.cairo; + +import openfl._internal.renderer.cairo.CairoGraphics; +import openfl._internal.renderer.cairo.CairoTextRenderer; +import openfl._internal.renderer.opengl.utils.GraphicsRenderer; +import openfl._internal.renderer.RenderSession; +import openfl.display.Graphics; +import openfl.text.TextField; + +@:access(openfl.text.TextField) + + +class CairoTextField { + + + public static function render (textField:TextField, renderSession:RenderSession) { + + if (!textField.__renderable || textField.__worldAlpha <= 0) return; + + CairoTextRenderer.render (textField); + CairoShape.render (textField, renderSession); + } +} diff --git a/openfl/_internal/renderer/cairo/CairoTextRenderer.hx b/openfl/_internal/renderer/cairo/CairoTextRenderer.hx new file mode 100644 index 0000000000..93c9ebd1fc --- /dev/null +++ b/openfl/_internal/renderer/cairo/CairoTextRenderer.hx @@ -0,0 +1,295 @@ +package openfl._internal.renderer.cairo; + + +import lime.graphics.Image; +import lime.text.Font.NativeGlyphData; +import lime.text.Glyph; +import lime.text.TextLayout; +import openfl._internal.renderer.RenderSession; +import openfl.display.BitmapData; +import openfl.display.Graphics; +import openfl.display.Tilesheet; +import openfl.geom.Point; +import openfl.geom.Rectangle; +import openfl.text.Font; +import openfl.text.TextField; +import openfl.text.TextFieldAutoSize; +import openfl.text.TextFormat; +import openfl.text.TextFormatAlign; + +@:access(openfl.text.TextField) + + +class CairoTextRenderer { + + private static var glyphMap = new Map> (); + + + public static function render (textField:TextField) { + + if (textField.__graphics == null) { + + textField.__graphics = new Graphics (); + + } + + if (textField.__dirty) { + + var graphics = textField.__graphics; + graphics.clear (); + + if (textField.border || textField.background) { + + if (textField.border) { + + graphics.lineStyle (1, textField.borderColor); + + } + + if (textField.background) { + + graphics.beginFill (textField.backgroundColor); + + } + + graphics.drawRect (0.5, 0.5, textField.__width - 1, textField.__height - 1); + + } + + update( textField ); + + textField.__dirty = false; + } + } + + public static function update (textField:TextField):Bool { + + if (textField.__dirty) { + + if (textField.__text != null && textField.__text != "") { + + var text = textField.text; + + if (textField.displayAsPassword) { + + var length = text.length; + var mask = ""; + + for (i in 0...length) { + + mask += "*"; + + } + + text = mask; + + } + + var measurements = textField.__measureText (); + var textWidth = 0.0; + + for (measurement in measurements) { + + textWidth += measurement; + + } + + if (textField.autoSize == TextFieldAutoSize.LEFT) { + + textField.__width = textWidth + 4; + textField.__height = textField.textHeight + 4; + + } + + if (textField.__ranges == null) { + + renderText (textField, text, textField.__textFormat, 2, textWidth); + + } else { + + var currentIndex = 0; + var range; + var offsetX = 2.0; + + for (i in 0...textField.__ranges.length) { + + range = textField.__ranges[i]; + + renderText (textField, text.substring (range.start, range.end), range.format, offsetX, textWidth); + offsetX += measurements[i]; + + } + + } + + } else { + + if (textField.autoSize == TextFieldAutoSize.LEFT) { + + textField.__width = 4; + textField.__height = 4; + + } + + } + + textField.__dirty = false; + return true; + } + + return false; + + } + + private static inline function renderText (textField:TextField, text:String, format:TextFormat, offsetX:Float, textWidth:Float):Void { + + var font : Font = textField.__getFontInstance ( format ); + + if (font != null && format.size != null) { + + var graphics : Graphics = textField.__graphics; + + var glyphs : Array; + + textField.__textLayout.font = font; + + if ( glyphMap.exists( font ) ) + { + glyphs = glyphMap.get( font ); + } + else + { + var glyphData = font.decompose(); + glyphs = glyphData.glyphs; + + glyphMap.set( font, glyphs ); + } + + var tlm = textField.getLineMetrics (0); + + var x : Float = offsetX; + var y : Float = 2 + tlm.ascent; + + var size = Std.int (format.size); + + var denom : Float = 20 / size * 1024; + + var lines = text.split ("\n"); + + var line_i:Int = 0; + var oldX = x; + + if (textField.__textLayout == null) { + textField.__textLayout = new TextLayout (); + } + + var textLayout:TextLayout = textField.__textLayout; + + var glyphCodes = new Map(); + for ( g in glyphs ) + { + glyphCodes.set( g.char_code, g ); + } + + for (line in lines) + { + tlm = textField.getLineMetrics (line_i); + + //x position must be reset every line and recalculated + x = oldX; + + x += switch (format.align) { + + case LEFT, JUSTIFY: 0; //the renderer has already positioned the text at the right spot past the 2px left margin + case CENTER: ((textField.__width - 4) - tlm.width) / 2; //subtract 4 from textfield.__width because __width includes the 2px margin on both sides, which doesn't count + case RIGHT: ((textField.__width - 4) - tlm.width); //same thing here + + } + + textLayout.text = null; + textLayout.font = font; + textLayout.size = size; + textLayout.text = line; + + var a = 0; + + var tx : Float = 0; + var ty : Float = 0; + + for (position in textLayout.positions) + { + var glyph = glyphCodes[ line.charCodeAt( a ) ]; + + var points = glyph.points; + + var i = 0; + + graphics.beginFill( format.color ); + + while( i < points.length ) + { + switch( points[i] ) + { + case 1: + + tx = x + position.offset.x + points[i + 1]/denom; + ty = y + position.offset.y - points[i + 2]/denom; + + graphics.moveTo( tx, ty ); + + i += 3; + + case 2: + + tx += points[i + 1]/denom; + ty -= points[i + 2]/denom; + + graphics.lineTo( tx, ty ); + + i += 3; + + case 3: + + var cx = tx + points[i + 1]/denom; + var cy = ty - points[i + 2]/denom; + + tx = cx + points[i + 3]/denom; + ty = cy - points[i + 4]/denom; + + graphics.curveTo( cx, cy, tx, ty ); + i += 5; + + case 4: + + var c1x = tx + points[i + 1]/denom; + var c1y = ty - points[i + 2]/denom; + + var c2x = c1x + points[i + 3]/denom; + var c2y = c1y - points[i + 4]/denom; + + tx = c2x + points[i + 5]/denom; + ty = c2y - points[i + 6]/denom; + + graphics.cubicCurveTo( c1x, c1y, c2x, c2y, tx, ty ); + i += 7; + } + + } + + graphics.endFill( ); + + x += position.advance.x; + y -= position.advance.y; + + a++; + } + + y += tlm.height; //always add the line height at the end + line_i++; + } + + } + } + + +} diff --git a/openfl/_internal/renderer/canvas/CanvasBitmap.hx b/openfl/_internal/renderer/canvas/CanvasBitmap.hx index 1f25291369..aab7d73315 100644 --- a/openfl/_internal/renderer/canvas/CanvasBitmap.hx +++ b/openfl/_internal/renderer/canvas/CanvasBitmap.hx @@ -13,7 +13,7 @@ class CanvasBitmap { public static inline function render (bitmap:Bitmap, renderSession:RenderSession):Void { - #if js + #if (js && html5) if (!bitmap.__renderable || bitmap.__worldAlpha <= 0) return; var context = renderSession.context; diff --git a/openfl/_internal/renderer/canvas/CanvasGraphics.hx b/openfl/_internal/renderer/canvas/CanvasGraphics.hx index 260c6aa894..87cddc4078 100644 --- a/openfl/_internal/renderer/canvas/CanvasGraphics.hx +++ b/openfl/_internal/renderer/canvas/CanvasGraphics.hx @@ -12,7 +12,7 @@ import openfl.geom.Rectangle; import openfl.Lib; import openfl.Vector; -#if js +#if (js && html5) import js.html.CanvasElement; import js.html.CanvasPattern; import js.html.CanvasRenderingContext2D; @@ -31,99 +31,85 @@ class CanvasGraphics { private static var SIN45 = 0.70710678118654752440084436210485; private static var TAN22 = 0.4142135623730950488016887242097; + private static var bitmapFill:BitmapData; + private static var bitmapRepeat:Bool; private static var bounds:Rectangle; + private static var fillCommands:Array; + private static var graphics:Graphics; private static var hasFill:Bool; private static var hasStroke:Bool; - private static var inPath:Bool; private static var inversePendingMatrix:Matrix; private static var pendingMatrix:Matrix; - private static var positionX:Float; - private static var positionY:Float; - private static var setFill:Bool; + private static var strokeCommands:Array; - #if js + #if (js && html5) private static var context:CanvasRenderingContext2D; private static var pattern:CanvasPattern; #end - private static function beginPath ():Void { + private static function beginPatternFill (bitmapFill:BitmapData, bitmapRepeat:Bool):Void { + + #if (js && html5) + if (hasFill || bitmapFill == null) return; - #if js - if (!inPath) { + if (pattern == null) { - context.beginPath (); - inPath = true; + pattern = context.createPattern (bitmapFill.__image.src, bitmapRepeat ? "repeat" : "no-repeat"); } + + context.fillStyle = pattern; + hasFill = true; #end } - private static function beginPatternFill (bitmapFill:BitmapData, bitmapRepeat:Bool):Void { + private static function createTempPatternCanvas (bitmap:BitmapData, repeat:Bool, width:Int, height:Int) { - #if js - if (setFill || bitmapFill == null) return; + // TODO: Don't create extra canvas elements like this - if (pattern == null) { - - pattern = context.createPattern (bitmapFill.__image.src, bitmapRepeat ? "repeat" : "no-repeat"); - - } + #if (js && html5) + var canvas:CanvasElement = cast Browser.document.createElement ("canvas"); + var context = canvas.getContext ("2d"); - context.fillStyle = pattern; - setFill = true; + canvas.width = width; + canvas.height = height; + + context.fillStyle = context.createPattern (bitmap.__image.src, repeat ? "repeat" : "no-repeat"); + context.beginPath (); + context.moveTo (0, 0); + context.lineTo (0, height); + context.lineTo (width, height); + context.lineTo (width, 0); + context.lineTo (0, 0); + context.closePath (); + context.fill (); + return canvas; #end } - private static function closePath (closeFill:Bool):Void { + private static function endFill ():Void { - #if js - if (inPath) { - - if (hasFill) { - - context.translate( -bounds.x, -bounds.y); - - if (pendingMatrix != null) { - - context.transform (pendingMatrix.a, pendingMatrix.b, pendingMatrix.c, pendingMatrix.d, pendingMatrix.tx, pendingMatrix.ty); - context.fill (); - context.transform (inversePendingMatrix.a, inversePendingMatrix.b, inversePendingMatrix.c, inversePendingMatrix.d, inversePendingMatrix.tx, inversePendingMatrix.ty); - - } else { - - context.fill (); - - } - - context.translate (bounds.x, bounds.y); - - } - - context.closePath (); - - if (hasStroke) { - - context.stroke (); - - } - - } + #if (js && html5) + context.beginPath (); + playCommands (fillCommands, false); + fillCommands = []; + #end - inPath = false; + } + + + private static function endStroke ():Void { - if (closeFill) { - - hasFill = false; - hasStroke = false; - pendingMatrix = null; - inversePendingMatrix = null; - - } + #if (js && html5) + context.beginPath (); + playCommands (strokeCommands, true); + context.closePath (); + strokeCommands = []; #end } @@ -131,7 +117,7 @@ class CanvasGraphics { private static function drawRoundRect (x:Float, y:Float, width:Float, height:Float, rx:Float, ry:Float):Void { - #if js + #if (js && html5) if (ry == -1) ry = rx; rx *= 0.5; @@ -165,211 +151,386 @@ class CanvasGraphics { } - /*#if js - private static inline function setFillStyle(data:DrawPath, context:CanvasRenderingContext2D, worldAlpha:Float) { - if (data.hasFill) { + private static inline function isCCW (x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float) { + + return ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)) < 0; + + } + + + private static function normalizeUVT (uvt:Vector, skipT:Bool = false): { max:Float, uvt:Vector } { + + var max:Float = Math.NEGATIVE_INFINITY; + var tmp = Math.NEGATIVE_INFINITY; + var len = uvt.length; + + for (t in 1...len + 1) { - context.globalAlpha = data.fill.alpha * worldAlpha; - if (data.fill.bitmap != null) { - var bitmap = data.fill.bitmap; - var repeat = data.fill.repeat; - var pattern = context.createPattern (bitmap.__image.src, repeat ? "repeat" : "no-repeat"); - context.fillStyle = pattern; - } else { - context.fillStyle = '#' + StringTools.hex(data.fill.color, 6); + if (skipT && t % 3 == 0) { + + continue; + + } + + tmp = uvt[t - 1]; + + if (max < tmp) { + + max = tmp; + } + } + + var result = new Vector (); + + for (t in 1...len + 1) { + + if (skipT && t % 3 == 0) { + + continue; + + } + + result.push ((uvt[t - 1] / max)); + + } + + return { max: max, uvt: result }; + } - #end - public static function renderObjectGraphics(object:DisplayObject, renderSession:RenderSession):Void { - - #if js - - var worldAlpha = object.__worldAlpha; - var graphics = object.__graphics; - + + private static function playCommands (commands:Array, stroke:Bool = false):Void { + + #if (js && html5) bounds = graphics.__bounds; - - if(!graphics.__dirty) return; - - graphics.__dirty = false; - - if(bounds == null || bounds.width == 0 || bounds.height == 0) { - - graphics.__canvas = null; - graphics.__context = null; - - } else { - - if (graphics.__canvas == null) { + + var offsetX = bounds.x; + var offsetY = bounds.y; + + var positionX = 0.0; + var positionY = 0.0; + + var closeGap = false; + var startX = 0.0; + var startY = 0.0; + + for (command in commands) { + + switch (command) { - graphics.__canvas = cast Browser.document.createElement ("canvas"); - graphics.__context = graphics.__canvas.getContext ("2d"); - //untyped (context).mozImageSmoothingEnabled = false; - //untyped (context).webkitImageSmoothingEnabled = false; - //context.imageSmoothingEnabled = false; + case CubicCurveTo (cx1, cy1, cx2, cy2, x, y): + + context.bezierCurveTo (cx1 - offsetX, cy1 - offsetY, cx2 - offsetX, cy2 - offsetY, x - offsetX, y - offsetY); - } - - var context = graphics.__context; - - graphics.__canvas.width = Math.ceil (bounds.width); - graphics.__canvas.height = Math.ceil (bounds.height); - - var offsetX = bounds.x; - var offsetY = bounds.y; - - for (i in 0...graphics.__graphicsData.length) { - - var data = graphics.__graphicsData[i]; - var points = data.points; - - context.strokeStyle = '#' + StringTools.hex (data.line.color, 6); - context.lineWidth = data.line.width; - context.lineCap = Std.string(data.line.caps); - context.lineJoin = Std.string(data.line.joints); - context.miterLimit = data.line.miterLimit; - - setFillStyle(data, context, worldAlpha); + case CurveTo (cx, cy, x, y): + + context.quadraticCurveTo (cx - offsetX, cy - offsetY, x - offsetX, y - offsetY); - switch(data.type) { - - case Polygon: + case DrawCircle (x, y, radius): + + context.moveTo (x - offsetX + radius, y - offsetY); + context.arc (x - offsetX, y - offsetY, radius, 0, Math.PI * 2, true); + + case DrawEllipse (x, y, width, height): + + x -= offsetX; + y -= offsetY; + + var kappa = .5522848, + ox = (width / 2) * kappa, // control point offset horizontal + oy = (height / 2) * kappa, // control point offset vertical + xe = x + width, // x-end + ye = y + height, // y-end + xm = x + width / 2, // x-middle + ym = y + height / 2; // y-middle + + context.moveTo (x, ym); + context.bezierCurveTo (x, ym - oy, xm - ox, y, xm, y); + context.bezierCurveTo (xm + ox, y, xe, ym - oy, xe, ym); + context.bezierCurveTo (xe, ym + oy, xm + ox, ye, xm, ye); + context.bezierCurveTo (xm - ox, ye, x, ym + oy, x, ym); + + case DrawRoundRect (x, y, width, height, rx, ry): + + drawRoundRect (x - offsetX, y - offsetY, width, height, rx, ry); + + case LineTo (x, y): + + context.lineTo (x - offsetX, y - offsetY); + + positionX = x; + positionY = y; + + case MoveTo (x, y): + + context.moveTo (x - offsetX, y - offsetY); + + positionX = x; + positionY = y; + + closeGap = true; + startX = x; + startY = y; + + case LineStyle (thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit): + + if (stroke && hasStroke) { - context.beginPath(); - context.moveTo(points[0] - offsetX, points[1] - offsetY); - for(i in 1...Std.int(points.length/2)) { - context.lineTo(points[i * 2] - offsetX, points[i * 2 + 1] - offsetY); - } - context.closePath(); + context.closePath (); + context.stroke (); + context.beginPath (); - if(data.hasFill) { - context.fill(); - } + } + + context.moveTo (positionX - offsetX, positionY - offsetY); + + if (thickness == null) { - if(data.line.width > 0) { - context.globalAlpha = data.line.alpha * worldAlpha; - context.stroke(); + hasStroke = false; + + } else { + + context.lineWidth = thickness; + + context.lineJoin = (joints == null ? "round" : Std.string (joints).toLowerCase ()); + context.lineCap = (caps == null ? "round" : switch (caps) { + case CapsStyle.NONE: "butt"; + default: Std.string (caps).toLowerCase (); + }); + + context.miterLimit = (miterLimit == null ? 3 : miterLimit); + + if (alpha == 1 || alpha == null) { + + context.strokeStyle = (color == null ? "#000000" : "#" + StringTools.hex (color & 0x00FFFFFF, 6)); + + } else { + + var r = (color & 0xFF0000) >>> 16; + var g = (color & 0x00FF00) >>> 8; + var b = (color & 0x0000FF); + + context.strokeStyle = (color == null ? "#000000" : "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"); + } - - case Rectangle(round): - var rx = points[0] - offsetX; - var ry = points[1] - offsetY; - var width = points[2]; - var height = points[3]; - - if(round) { - - var radius = points[4]; - var maxRadius = Math.min(width, height) / 2; - radius = (radius > maxRadius) ? maxRadius : radius; - - context.beginPath(); - context.moveTo(rx, ry + radius); - context.lineTo(rx, ry + height - radius); - context.quadraticCurveTo(rx, ry + height, rx + radius, ry + height); - context.lineTo(rx + width - radius, ry + height); - context.quadraticCurveTo(rx + width, ry + height, rx + width, ry + height - radius); - context.lineTo(rx + width, ry + radius); - context.quadraticCurveTo(rx + width, ry, rx + width - radius, ry); - context.lineTo(rx + radius, ry); - context.quadraticCurveTo(rx, ry, rx, ry + radius); - context.closePath(); - - } + hasStroke = true; - if (data.hasFill) { - if (round) { - context.fill(); - } else { - context.fillRect(rx, ry, width, height); - } + } + + case BeginBitmapFill (bitmap, matrix, repeat, smooth): + + if (bitmap != bitmapFill || repeat != bitmapRepeat) { + + bitmapFill = bitmap; + bitmapRepeat = repeat; + pattern = null; + hasFill = false; + + bitmap.__sync (); + + } + + if (matrix != null) { + + pendingMatrix = matrix; + inversePendingMatrix = matrix.clone (); + inversePendingMatrix.invert (); + + } else { + + pendingMatrix = null; + inversePendingMatrix = null; + + } + + case BeginFill (rgb, alpha): + + if (alpha < 0.005) { + + hasFill = false; + + } else { + + if (alpha == 1) { + + context.fillStyle = "#" + StringTools.hex (rgb, 6); + + } else { + + var r = (rgb & 0xFF0000) >>> 16; + var g = (rgb & 0x00FF00) >>> 8; + var b = (rgb & 0x0000FF); + + context.fillStyle = "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"; + } - if(data.line.width > 0) { - context.globalAlpha = data.line.alpha * worldAlpha; - if(round) { - context.stroke(); + bitmapFill = null; + hasFill = true; + + } + + case BeginGradientFill (type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio): + + var gradientFill = null; + + switch (type) { + + case RADIAL: + + if (matrix == null) matrix = new Matrix (); + var point = matrix.transformPoint (new Point (1638.4, 0)); + gradientFill = context.createRadialGradient (matrix.tx, matrix.ty, 0, matrix.tx, matrix.ty, (point.x - matrix.tx) / 2); + + case LINEAR: + + var matrix = matrix != null ? matrix.clone () : new Matrix (); + var point1 = matrix.transformPoint (new Point (-819.2, 0)); + var point2 = matrix.transformPoint (new Point (819.2, 0)); + + gradientFill = context.createLinearGradient (point1.x, point1.y, point2.x, point2.y); + + } + + for (i in 0...colors.length) { + + var rgb = colors[i]; + var alpha = alphas[i]; + var r = (rgb & 0xFF0000) >>> 16; + var g = (rgb & 0x00FF00) >>> 8; + var b = (rgb & 0x0000FF); + + var ratio = ratios[i] / 0xFF; + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; + + gradientFill.addColorStop (ratio, "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"); + + } + + context.fillStyle = gradientFill; + + bitmapFill = null; + hasFill = true; + + case DrawRect (x, y, width, height): + + var optimizationUsed = false; + + if (bitmapFill != null) { + + var st:Float = 0; + var sr:Float = 0; + var sb:Float = 0; + var sl:Float = 0; + + var canOptimizeMatrix = true; + + if (pendingMatrix != null) { + + if (pendingMatrix.b != 0 || pendingMatrix.c != 0) { + + canOptimizeMatrix = false; + } else { - context.strokeRect(rx, ry, width, height); + + var stl = inversePendingMatrix.transformPoint(new Point(x, y)); + var sbr = inversePendingMatrix.transformPoint(new Point(x + width, y + height)); + + st = stl.y; + sl = stl.x; + sb = sbr.y; + sr = sbr.x; + } + + } else { + + st = y; + sl = x; + sb = y + height; + sr = x + width; + } - case Circle: - - context.beginPath(); - context.arc(points[0] - offsetX, points[1] - offsetY, points[2], 0, 2 * Math.PI, true); - context.closePath(); - - if(data.hasFill) { - context.fill(); - } - - if(data.line.width > 0) { - context.globalAlpha = data.line.alpha * worldAlpha; - context.stroke(); - } - - case Ellipse: - - var w = points[2]; - var h = points[3]; - var x = (points[0] - offsetX); - var y = (points[1] - offsetY); - - context.beginPath(); - var kappa = 0.5522848, - ox = (w / 2) * kappa, // control point offset horizontal - oy = (h / 2) * kappa, // control point offset vertical - xe = x + w, // x-end - ye = y + h, // y-end - xm = x + w / 2, // x-middle - ym = y + h / 2; // y-middle - context.moveTo(x, ym); - context.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym); - context.closePath(); - - if(data.hasFill) { - context.fill(); + if (canOptimizeMatrix && st >= 0 && sl >= 0 && sr <= bitmapFill.width && sb <= bitmapFill.height) { + + optimizationUsed = true; + context.drawImage (bitmapFill.__image.src, sl, st, sr - sl, sb - st, x - offsetX, y - offsetY, width, height); } + } + + if (!optimizationUsed) { - if(data.line.width > 0) { - context.globalAlpha = data.line.alpha * worldAlpha; - context.stroke(); - } - - case _: - + context.rect (x - offsetX, y - offsetY, width, height); + } + + + default: + + } + + } + + if (stroke && hasStroke) { + + if (hasFill && closeGap) { + + context.lineTo (startX - offsetX, startY - offsetY); + + } + + context.stroke (); + + } + + if (!stroke) { + + if (hasFill || bitmapFill != null) { + + if (bitmapFill != null) { + + beginPatternFill (bitmapFill, bitmapRepeat); + } - + + context.translate (-bounds.x, -bounds.y); + + if (pendingMatrix != null) { + + context.transform (pendingMatrix.a, pendingMatrix.b, pendingMatrix.c, pendingMatrix.d, pendingMatrix.tx, pendingMatrix.ty); + context.fill (); + context.transform (inversePendingMatrix.a, inversePendingMatrix.b, inversePendingMatrix.c, inversePendingMatrix.d, inversePendingMatrix.tx, inversePendingMatrix.ty); + + } else { + + context.fill (); + + } + + context.translate (bounds.x, bounds.y); + context.closePath (); + } - + } - #end - - }*/ + + } public static function render (graphics:Graphics, renderSession:RenderSession):Void { - #if js + #if (js && html5) if (graphics.__dirty) { + CanvasGraphics.graphics = graphics; bounds = graphics.__bounds; - hasFill = false; - hasStroke = false; - inPath = false; - positionX = 0; - positionY = 0; - if (!graphics.__visible || graphics.__commands.length == 0 || bounds == null || bounds.width == 0 || bounds.height == 0) { graphics.__canvas = null; @@ -384,7 +545,6 @@ class CanvasGraphics { //untyped (context).mozImageSmoothingEnabled = false; //untyped (context).webkitImageSmoothingEnabled = false; //context.imageSmoothingEnabled = false; - } context = graphics.__context; @@ -394,226 +554,212 @@ class CanvasGraphics { var offsetX = bounds.x; var offsetY = bounds.y; - - var bitmapFill:BitmapData = null; - var bitmapRepeat = false; - - for (command in graphics.__commands) { - - switch (command) { - - case BeginBitmapFill (bitmap, matrix, repeat, smooth): - - closePath (false); - - if (bitmap != bitmapFill || repeat != bitmapRepeat) { - - bitmapFill = bitmap; - bitmapRepeat = repeat; - pattern = null; - setFill = false; - - bitmap.__sync (); - - } - - if (matrix != null) { - - pendingMatrix = matrix; - inversePendingMatrix = matrix.clone (); - inversePendingMatrix.invert (); - - } else { - - pendingMatrix = null; - inversePendingMatrix = null; - - } - - hasFill = true; - - case BeginFill (rgb, alpha): - - closePath (false); - - if (alpha == 1) { - - context.fillStyle = "#" + StringTools.hex (rgb, 6); - - } else { - - var r = (rgb & 0xFF0000) >>> 16; - var g = (rgb & 0x00FF00) >>> 8; - var b = (rgb & 0x0000FF); - - context.fillStyle = "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"; - - } - - bitmapFill = null; - setFill = true; - hasFill = true; - - case BeginGradientFill (type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio): - - closePath (false); - - var gradientFill = null; - - switch (type) { - - case RADIAL: - - if (matrix == null) matrix = new Matrix (); - var point = matrix.transformPoint (new Point (1638.4, 0)); - gradientFill = context.createRadialGradient (matrix.tx, matrix.ty, 0, matrix.tx, matrix.ty, (point.x - matrix.tx) / 2); - - case LINEAR: - - var matrix = matrix != null ? matrix.clone () : new Matrix (); - matrix.tx -= matrix.a * 1638.4 / 2; - matrix.ty -= matrix.d * 1638.4 / 2; - - var point1 = matrix.transformPoint (new Point (0, 0)); - var point2 = matrix.transformPoint (new Point (1638.4, 0)); - - gradientFill = context.createLinearGradient (point1.x, point1.y, point2.x, point2.y); - - } - - for (i in 0...colors.length) { - - var rgb = colors[i]; - var alpha = alphas[i]; - var r = (rgb & 0xFF0000) >>> 16; - var g = (rgb & 0x00FF00) >>> 8; - var b = (rgb & 0x0000FF); - - var ratio = ratios[i] / 0xFF; - if (ratio < 0) ratio = 0; - if (ratio > 1) ratio = 1; - - gradientFill.addColorStop (ratio, "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"); - - } - - context.fillStyle = gradientFill; - - bitmapFill = null; - setFill = true; - hasFill = true; + + fillCommands = new Array (); + strokeCommands = new Array (); + + hasFill = false; + hasStroke = false; + bitmapFill = null; + bitmapRepeat = false; + + for (command in graphics.__commands) { + + switch (command) { - case CubicCurveTo (cx1, cy1, cx2, cy2, x, y): + case CubicCurveTo (_, _, _, _, _, _), CurveTo (_, _, _, _), LineTo (_, _), MoveTo (_, _): - beginPatternFill (bitmapFill, bitmapRepeat); - beginPath (); - context.bezierCurveTo (cx1 - offsetX, cy1 - offsetY, cx2 - offsetX, cy2 - offsetY, x - offsetX, y - offsetY); - positionX = x; - positionY = y; + fillCommands.push (command); + strokeCommands.push (command); - case CurveTo (cx, cy, x, y): + case EndFill: - beginPatternFill (bitmapFill, bitmapRepeat); - beginPath (); - context.quadraticCurveTo (cx - offsetX, cy - offsetY, x - offsetX, y - offsetY); - positionX = x; - positionY = y; + endFill (); + endStroke (); + hasFill = false; - case DrawCircle (x, y, radius): + case LineStyle (_, _, _, _, _, _, _, _): - beginPatternFill (bitmapFill, bitmapRepeat); - beginPath (); - context.moveTo (x - offsetX + radius, y - offsetY); - context.arc (x - offsetX, y - offsetY, radius, 0, Math.PI * 2, true); + strokeCommands.push (command); - case DrawEllipse (x, y, width, height): + case BeginBitmapFill (_, _, _, _), BeginFill (_, _), BeginGradientFill (_, _, _, _, _, _, _, _): + + endFill (); + endStroke (); - x -= offsetX; - y -= offsetY; + fillCommands.push (command); + strokeCommands.push (command); + + case DrawCircle (_, _, _), DrawEllipse (_, _, _, _), DrawRect (_, _, _, _), DrawRoundRect (_, _, _, _, _, _): - var kappa = .5522848, - ox = (width / 2) * kappa, // control point offset horizontal - oy = (height / 2) * kappa, // control point offset vertical - xe = x + width, // x-end - ye = y + height, // y-end - xm = x + width / 2, // x-middle - ym = y + height / 2; // y-middle + endFill (); + endStroke (); - beginPatternFill (bitmapFill, bitmapRepeat); - beginPath (); - context.moveTo (x, ym); - context.bezierCurveTo (x, ym - oy, xm - ox, y, xm, y); - context.bezierCurveTo (xm + ox, y, xe, ym - oy, xe, ym); - context.bezierCurveTo (xe, ym + oy, xm + ox, ye, xm, ye); - context.bezierCurveTo (xm - ox, ye, x, ym + oy, x, ym); + fillCommands.push (command); + strokeCommands.push (command); + + case DrawTriangles (vertices, indices, uvtData, culling, _, _): - case DrawRect (x, y, width, height): + endFill (); + endStroke (); - var optimizationUsed = false; + var v = vertices; + var ind = indices; + var uvt = uvtData; + var pattern:CanvasElement = null; + var colorFill = bitmapFill == null; - if (bitmapFill != null) { + if (colorFill && uvt != null) { + + // Flash doesn't draw anything if the fill isn't a bitmap and there are uvt values + break; - var st:Float = 0; - var sr:Float = 0; - var sb:Float = 0; - var sl:Float = 0; + } + + if (!colorFill) { - var canOptimizeMatrix = true; + //TODO move this to Graphics? - if (pendingMatrix != null) { + if (uvtData == null) { - if (pendingMatrix.b != 0 || pendingMatrix.c != 0) { - - canOptimizeMatrix = false; - - } else { - - var stl = inversePendingMatrix.transformPoint(new Point(x, y)); - var sbr = inversePendingMatrix.transformPoint(new Point(x + width, y + height)); + uvtData = new Vector (); + + for (i in 0...(Std.int (v.length / 2))) { - st = stl.y; - sl = stl.x; - sb = sbr.y; - sr = sbr.x; + uvtData.push (v[i * 2] / bitmapFill.width); + uvtData.push (v[i * 2 + 1] / bitmapFill.height); } + } + + var skipT = uvtData.length != v.length; + var normalizedUVT = normalizeUVT (uvtData, skipT); + var maxUVT = normalizedUVT.max; + uvt = normalizedUVT.uvt; + + if (maxUVT > 1) { + + pattern = createTempPatternCanvas (bitmapFill, bitmapRepeat, Std.int (bounds.width), Std.int (bounds.height)); + } else { - st = y; - sl = x; - sb = y + height; - sr = x + width; + pattern = createTempPatternCanvas (bitmapFill, bitmapRepeat, bitmapFill.width, bitmapFill.height); + + } + + } + + var i = 0; + var l = ind.length; + + var a:Int, b:Int, c:Int; + var iax:Int, iay:Int, ibx:Int, iby:Int, icx:Int, icy:Int; + var x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float; + var uvx1:Float, uvy1:Float, uvx2:Float, uvy2:Float, uvx3:Float, uvy3:Float; + var denom:Float; + var t1:Float, t2:Float, t3:Float, t4:Float; + var dx:Float, dy:Float; + + while (i < l) { + + a = i; + b = i + 1; + c = i + 2; + + iax = ind[a] * 2; + iay = ind[a] * 2 + 1; + ibx = ind[b] * 2; + iby = ind[b] * 2 + 1; + icx = ind[c] * 2; + icy = ind[c] * 2 + 1; + + x1 = v[iax]; + y1 = v[iay]; + x2 = v[ibx]; + y2 = v[iby]; + x3 = v[icx]; + y3 = v[icy]; + + switch (culling) { + + case POSITIVE: + + if (!isCCW (x1, y1, x2, y2, x3, y3)) { + + i += 3; + continue; + + } + + case NEGATIVE: + + if (isCCW (x1, y1, x2, y2, x3, y3)) { + + i += 3; + continue; + + } + default: + } - if (canOptimizeMatrix && st >= 0 && sl >= 0 && sr <= bitmapFill.width && sb <= bitmapFill.height) { + if (colorFill) { + + context.beginPath (); + context.moveTo (x1, y1); + context.lineTo (x2, y2); + context.lineTo (x3, y3); + context.closePath (); + context.fill (); + i += 3; + continue; + + } + + context.save (); + context.beginPath (); + context.moveTo (x1, y1); + context.lineTo (x2, y2); + context.lineTo (x3, y3); + context.closePath (); + + context.clip (); + + uvx1 = uvt[iax] * pattern.width; + uvx2 = uvt[ibx] * pattern.width; + uvx3 = uvt[icx] * pattern.width; + uvy1 = uvt[iay] * pattern.height; + uvy2 = uvt[iby] * pattern.height; + uvy3 = uvt[icy] * pattern.height; + + denom = uvx1 * (uvy3 - uvy2) - uvx2 * uvy3 + uvx3 * uvy2 + (uvx2 - uvx3) * uvy1; + + if (denom == 0) { - optimizationUsed = true; - context.drawImage (bitmapFill.__image.src, sl, st, sr - sl, sb - st, x - offsetX, y - offsetY, width, height); + i += 3; + continue; } - } - - if (!optimizationUsed) { + t1 = - (uvy1 * (x3 - x2) - uvy2 * x3 + uvy3 * x2 + (uvy2 - uvy3) * x1) / denom; + t2 = (uvy2 * y3 + uvy1 * (y2 - y3) - uvy3 * y2 + (uvy3 - uvy2) * y1) / denom; + t3 = (uvx1 * (x3 - x2) - uvx2 * x3 + uvx3 * x2 + (uvx2 - uvx3) * x1) / denom; + t4 = - (uvx2 * y3 + uvx1 * (y2 - y3) - uvx3 * y2 + (uvx3 - uvx2) * y1) / denom; + dx = (uvx1 * (uvy3 * x2 - uvy2 * x3) + uvy1 * (uvx2 * x3 - uvx3 * x2) + (uvx3 * uvy2 - uvx2 * uvy3) * x1) / denom; + dy = (uvx1 * (uvy3 * y2 - uvy2 * y3) + uvy1 * (uvx2 * y3 - uvx3 * y2) + (uvx3 * uvy2 - uvx2 * uvy3) * y1) / denom; + + context.transform (t1, t2, t3, t4, dx, dy); + context.drawImage (pattern, 0, 0); + context.restore (); - beginPatternFill (bitmapFill, bitmapRepeat); - beginPath (); - context.rect (x - offsetX, y - offsetY, width, height); + i += 3; } - case DrawRoundRect (x, y, width, height, rx, ry): - - beginPatternFill (bitmapFill, bitmapRepeat); - beginPath (); - drawRoundRect (x - offsetX, y - offsetY, width, height, rx, ry); - case DrawTiles (sheet, tileData, smooth, flags, count): - closePath (false); - var useScale = (flags & Graphics.TILE_SCALE) > 0; var useRotation = (flags & Graphics.TILE_ROTATION) > 0; var useTransform = (flags & Graphics.TILE_TRANS_2x2) > 0; @@ -652,9 +798,12 @@ class CanvasGraphics { var surface:Dynamic; sheet.__bitmap.__sync (); surface = sheet.__bitmap.__image.src; - - if (useBlendAdd) + + if (useBlendAdd) { + context.globalCompositeOperation = "lighter"; + + } while (index < totalCount) { @@ -667,20 +816,22 @@ class CanvasGraphics { previousTileID = tileID; - } - else if (useRect) - { + } else if (useRect) { + rect = sheet.__rectTile; - rect.setTo(tileData[index + 2], tileData[index + 3], tileData[index + 4], tileData[index + 5]); + rect.setTo (tileData[index + 2], tileData[index + 3], tileData[index + 4], tileData[index + 5]); center = sheet.__point; - if (useOrigin) - { - center.setTo(tileData[index + 6], tileData[index + 7]); - } - else - { - center.setTo(0, 0); + + if (useOrigin) { + + center.setTo (tileData[index + 6], tileData[index + 7]); + + } else { + + center.setTo (0, 0); + } + } if (rect != null && rect.width > 0 && rect.height > 0 && center != null) { @@ -722,193 +873,16 @@ class CanvasGraphics { index += numValues; } - - if (useBlendAdd) - context.globalCompositeOperation = "source-over"; - - case EndFill: - - closePath (true); - - case LineStyle (thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit): - - closePath (false); - if (thickness == null) { - - hasStroke = false; - - } else { - - context.lineWidth = thickness; - - context.lineJoin = (joints == null ? "round" : Std.string (joints).toLowerCase ()); - context.lineCap = (caps == null ? "round" : switch (caps) { - case CapsStyle.NONE: "butt"; - default: Std.string (caps).toLowerCase (); - }); - - context.miterLimit = (miterLimit == null ? 3 : miterLimit); - - if (alpha == 1 || alpha == null) { - - context.strokeStyle = (color == null ? "#000000" : "#" + StringTools.hex (color & 0x00FFFFFF, 6)); - - } else { - - var r = (color & 0xFF0000) >>> 16; - var g = (color & 0x00FF00) >>> 8; - var b = (color & 0x0000FF); - - context.strokeStyle = (color == null ? "#000000" : "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"); - - } + if (useBlendAdd) { - hasStroke = true; + context.globalCompositeOperation = "source-over"; } - case LineTo (x, y): - - beginPatternFill(bitmapFill, bitmapRepeat); - beginPath (); - context.lineTo (x - offsetX, y - offsetY); - positionX = x; - positionY = y; - - case MoveTo (x, y): - - beginPath (); - context.moveTo (x - offsetX, y - offsetY); - positionX = x; - positionY = y; - - case DrawTriangles (vertices, indices, uvtData, culling, _, _): - - closePath(false); - - var v = vertices; - var ind = indices; - var uvt = uvtData; - var pattern:CanvasElement = null; - var colorFill = bitmapFill == null; - - if (colorFill && uvt != null) { - // Flash doesn't draw anything if the fill isn't a bitmap and there are uvt values - break; - } - - if(!colorFill) { - //TODO move this to Graphics? - if (uvtData == null) { - uvtData = new Vector(); - for (i in 0...(Std.int(v.length / 2))) { - uvtData.push(v[i * 2] / bitmapFill.width); - uvtData.push(v[i * 2 + 1] / bitmapFill.height); - } - } - - var skipT = uvtData.length != v.length; - var normalizedUvt = normalizeUvt(uvtData, skipT); - var maxUvt = normalizedUvt.max; - uvt = normalizedUvt.uvt; - - if (maxUvt > 1) { - pattern = createTempPatternCanvas(bitmapFill, bitmapRepeat, bounds.width, bounds.height); - } else { - pattern = createTempPatternCanvas(bitmapFill, bitmapRepeat, bitmapFill.width, bitmapFill.height); - } - } - - var i = 0; - var l = ind.length; - - var a:Int, b:Int, c:Int; - var iax:Int, iay:Int, ibx:Int, iby:Int, icx:Int, icy:Int; - var x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float; - var uvx1:Float, uvy1:Float, uvx2:Float, uvy2:Float, uvx3:Float, uvy3:Float; - var denom:Float; - var t1:Float, t2:Float, t3:Float, t4:Float; - var dx:Float, dy:Float; - // code from http://tulrich.com/geekstuff/canvas/perspective.html - while (i < l) { - a = i; - b = i + 1; - c = i + 2; - - iax = ind[a] * 2; iay = ind[a] * 2 + 1; - ibx = ind[b] * 2; iby = ind[b] * 2 + 1; - icx = ind[c] * 2; icy = ind[c] * 2 + 1; - - x1 = v[iax]; y1 = v[iay]; - x2 = v[ibx]; y2 = v[iby]; - x3 = v[icx]; y3 = v[icy]; - - switch(culling) { - case POSITIVE: - if (!isCCW(x1, y1, x2, y2, x3, y3)) { - i += 3; - continue; - } - case NEGATIVE: - if (isCCW(x1, y1, x2, y2, x3, y3)) { - i += 3; - continue; - } - case _: - } - - if (colorFill) { - context.beginPath(); - context.moveTo(x1, y1); - context.lineTo(x2, y2); - context.lineTo(x3, y3); - context.closePath(); - context.fill(); - i += 3; - continue; - } - - context.save(); - context.beginPath(); - context.moveTo(x1, y1); - context.lineTo(x2, y2); - context.lineTo(x3, y3); - context.closePath(); - - context.clip(); - - uvx1 = uvt[iax] * pattern.width; - uvx2 = uvt[ibx] * pattern.width; - uvx3 = uvt[icx] * pattern.width; - uvy1 = uvt[iay] * pattern.height; - uvy2 = uvt[iby] * pattern.height; - uvy3 = uvt[icy] * pattern.height; - - denom = uvx1 * (uvy3 - uvy2) - uvx2 * uvy3 + uvx3 * uvy2 + (uvx2 - uvx3) * uvy1; - if (denom == 0) { - i += 3; - continue; - } - - t1 = - (uvy1 * (x3 - x2) - uvy2 * x3 + uvy3 * x2 + (uvy2 - uvy3) * x1) / denom; - t2 = (uvy2 * y3 + uvy1 * (y2 - y3) - uvy3 * y2 + (uvy3 - uvy2) * y1) / denom; - t3 = (uvx1 * (x3 - x2) - uvx2 * x3 + uvx3 * x2 + (uvx2 - uvx3) * x1) / denom; - t4 = - (uvx2 * y3 + uvx1 * (y2 - y3) - uvx3 * y2 + (uvx3 - uvx2) * y1) / denom; - dx = (uvx1 * (uvy3 * x2 - uvy2 * x3) + uvy1 * (uvx2 * x3 - uvx3 * x2) + (uvx3 * uvy2 - uvx2 * uvy3) * x1) / denom; - dy = (uvx1 * (uvy3 * y2 - uvy2 * y3) + uvy1 * (uvx2 * y3 - uvx3 * y2) + (uvx3 * uvy2 - uvx2 * uvy3) * y1) / denom; - - context.transform(t1, t2, t3, t4, dx, dy); - context.drawImage(pattern, 0, 0); - - context.restore(); - - i += 3; - - } + default: - case _: - openfl.Lib.notImplemented("CanvasGraphics"); + openfl.Lib.notImplemented ("CanvasGraphics"); } @@ -917,7 +891,18 @@ class CanvasGraphics { } graphics.__dirty = false; - closePath (false); + + if (fillCommands.length > 0) { + + endFill (); + + } + + if (strokeCommands.length > 0) { + + endStroke (); + + } } @@ -1010,49 +995,5 @@ class CanvasGraphics { } - private static function createTempPatternCanvas(bitmap:BitmapData, repeat:Bool, width:Float, height:Float) { - - #if js - var canvas:CanvasElement = cast Browser.document.createElement ("canvas"); - var context:CanvasRenderingContext2D = canvas.getContext ("2d"); - - canvas.width = Math.ceil (width); - canvas.height = Math.ceil (height); - - context.fillStyle = context.createPattern(bitmap.__image.src, repeat ? "repeat" : "no-repeat"); - context.beginPath(); - context.moveTo(0, 0); - context.lineTo(0, height); - context.lineTo(width, height); - context.lineTo(width, 0); - context.lineTo(0, 0); - context.closePath(); - context.fill(); - return canvas; - #end - } - - private static inline function isCCW(x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float) { - return ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)) < 0; - } - - private static function normalizeUvt(uvt:Vector, skipT:Bool = false):{max:Float, uvt:Vector } { - var max:Float = Math.NEGATIVE_INFINITY; - var tmp = Math.NEGATIVE_INFINITY; - var len = uvt.length; - for (t in 1...len+1) { - if (skipT && t % 3 == 0) continue; - tmp = uvt[t - 1]; - if (max < tmp) max = tmp; - } - - var result:Vector = new Vector(); - for (t in 1...len+1) { - if (skipT && t % 3 == 0) continue; - result.push((uvt[t - 1] / max)); - } - - return {max:max, uvt:result}; - } -} +} \ No newline at end of file diff --git a/openfl/_internal/renderer/canvas/MaskManager.hx b/openfl/_internal/renderer/canvas/CanvasMaskManager.hx similarity index 71% rename from openfl/_internal/renderer/canvas/MaskManager.hx rename to openfl/_internal/renderer/canvas/CanvasMaskManager.hx index f6bb8c83c8..3a12e5aad1 100644 --- a/openfl/_internal/renderer/canvas/MaskManager.hx +++ b/openfl/_internal/renderer/canvas/CanvasMaskManager.hx @@ -1,6 +1,7 @@ package openfl._internal.renderer.canvas; +import openfl._internal.renderer.AbstractMaskManager; import openfl.display.*; import openfl.geom.*; @@ -8,20 +9,17 @@ import openfl.geom.*; @:keep -class MaskManager { - - - private var renderSession:RenderSession; +class CanvasMaskManager extends AbstractMaskManager { public function new (renderSession:RenderSession) { - this.renderSession = renderSession; + super (renderSession); } - public function pushMask (mask:DisplayObject):Void { + public override function pushMask (mask:DisplayObject):Void { var context = renderSession.context; @@ -33,7 +31,7 @@ class MaskManager { context.setTransform (transform.a, transform.c, transform.b, transform.d, transform.tx, transform.ty); context.beginPath (); - mask.__renderMask (renderSession); + mask.__renderCanvasMask (renderSession); context.clip (); @@ -42,7 +40,7 @@ class MaskManager { } - public function pushRect (rect:Rectangle, transform:Matrix):Void { + public override function pushRect (rect:Rectangle, transform:Matrix):Void { var context = renderSession.context; context.save (); @@ -56,7 +54,7 @@ class MaskManager { } - public function popMask ():Void { + public override function popMask ():Void { renderSession.context.restore (); diff --git a/openfl/_internal/renderer/canvas/CanvasRenderer.hx b/openfl/_internal/renderer/canvas/CanvasRenderer.hx index 38a59813a7..99cc0f2344 100644 --- a/openfl/_internal/renderer/canvas/CanvasRenderer.hx +++ b/openfl/_internal/renderer/canvas/CanvasRenderer.hx @@ -26,7 +26,7 @@ class CanvasRenderer extends AbstractRenderer { renderSession.roundPixels = true; renderSession.renderer = this; #if !neko - renderSession.maskManager = new MaskManager(renderSession); + renderSession.maskManager = new CanvasMaskManager(renderSession); #end } diff --git a/openfl/_internal/renderer/canvas/CanvasShape.hx b/openfl/_internal/renderer/canvas/CanvasShape.hx index 56bd3afcce..ccbeb39022 100644 --- a/openfl/_internal/renderer/canvas/CanvasShape.hx +++ b/openfl/_internal/renderer/canvas/CanvasShape.hx @@ -12,7 +12,7 @@ class CanvasShape { public static inline function render (shape:DisplayObject, renderSession:RenderSession):Void { - #if js + #if (js && html5) if (!shape.__renderable || shape.__worldAlpha <= 0) return; var graphics = shape.__graphics; diff --git a/openfl/_internal/renderer/canvas/CanvasTextField.hx b/openfl/_internal/renderer/canvas/CanvasTextField.hx index df02edae74..8d208c8100 100644 --- a/openfl/_internal/renderer/canvas/CanvasTextField.hx +++ b/openfl/_internal/renderer/canvas/CanvasTextField.hx @@ -7,7 +7,7 @@ import openfl.text.TextFieldAutoSize; import openfl.text.TextFormat; import openfl.text.TextFormatAlign; -#if js +#if (js && html5) import js.html.CanvasRenderingContext2D; import js.Browser; #end @@ -18,14 +18,14 @@ import js.Browser; class CanvasTextField { - #if js + #if (js && html5) private static var context:CanvasRenderingContext2D; #end public static inline function render (textField:TextField, renderSession:RenderSession):Void { - #if js + #if (js && html5) if (!textField.__renderable || textField.__worldAlpha <= 0) return; @@ -68,14 +68,95 @@ class CanvasTextField { private static inline function renderText (textField:TextField, text:String, format:TextFormat, offsetX:Float):Void { - #if js + #if (js && html5) context.font = textField.__getFont (format); - context.textBaseline = "top"; context.fillStyle = "#" + StringTools.hex (format.color, 6); + context.textBaseline = "top"; + + var yOffset = 0.0; + + // Hack, baseline "top" is not consistent across browsers + + if (~/(iPad|iPhone|iPod|Firefox)/g.match (Browser.window.navigator.userAgent)) { + + yOffset = format.size * 0.185; + + } - var lines = text.split("\n"); - var yOffset:Float = 0; + var lines = []; + + if (textField.wordWrap) { + + var words = text.split (" "); + var line = ""; + + var word, newLineIndex, test; + + for (i in 0...words.length) { + + word = words[i]; + newLineIndex = word.indexOf ("\n"); + + if (newLineIndex > -1) { + + while (newLineIndex > -1) { + + test = line + word.substring (0, newLineIndex) + " "; + + if (context.measureText (test).width > textField.__width - 4 && i > 0) { + + lines.push (line); + lines.push (word.substring (0, newLineIndex)); + + } else { + + lines.push (line + word.substring (0, newLineIndex)); + + } + + word = word.substr (newLineIndex + 1); + newLineIndex = word.indexOf ("\n"); + line = ""; + + } + + if (word != "") { + + line = word + " "; + + } + + } else { + + test = line + words[i] + " "; + + if (context.measureText (test).width > textField.__width - 4 && i > 0) { + + lines.push (line); + line = words[i] + " "; + + } else { + + line = test; + + } + + } + + } + + if (line != "") { + + lines.push (line); + + } + + } else { + + lines = text.split ("\n"); + + } for (line in lines) { @@ -98,7 +179,8 @@ class CanvasTextField { } - yOffset += textField.textHeight; + yOffset += textField.textHeight; // TODO: Handle format.leading + } #end @@ -108,11 +190,11 @@ class CanvasTextField { public static function update (textField:TextField):Bool { - #if js + #if (js && html5) if (textField.__dirty) { - if (((textField.__text == null || textField.__text == "") && !textField.background && !textField.border) || ((textField.width <= 0 || textField.height <= 0) && textField.autoSize != TextFieldAutoSize.LEFT)) { + if (((textField.__text == null || textField.__text == "") && !textField.background && !textField.border && !textField.__hasFocus) || ((textField.width <= 0 || textField.height <= 0) && textField.autoSize != TextFieldAutoSize.LEFT)) { textField.__canvas = null; textField.__context = null; @@ -129,7 +211,7 @@ class CanvasTextField { context = textField.__context; - if (textField.__text != null && textField.__text != "") { + if ((textField.__text != null && textField.__text != "") || textField.__hasFocus) { var text = textField.text; @@ -189,19 +271,21 @@ class CanvasTextField { if (textField.__hasFocus && (textField.__selectionStart == textField.__cursorPosition) && textField.__showCursor) { - var cursorOffset = textField.__getTextWidth (text.substring (0, textField.__cursorPosition)); + var cursorOffset = textField.__getTextWidth (text.substring (0, textField.__cursorPosition)) + 3; context.fillStyle = "#" + StringTools.hex (textField.__textFormat.color, 6); - context.fillRect (cursorOffset, 5, 1, textField.__textFormat.size - 5); + context.fillRect (cursorOffset, 5, 1, (textField.__textFormat.size * 1.185) - 4); - } else if (textField.__hasFocus && (Math.abs (textField.__selectionStart - textField.__cursorPosition)) > 0 && !textField.__isKeyDown) { + } else if (textField.__hasFocus && (Math.abs (textField.__selectionStart - textField.__cursorPosition)) > 0) { var lowPos = Std.int (Math.min (textField.__selectionStart, textField.__cursorPosition)); var highPos = Std.int (Math.max (textField.__selectionStart, textField.__cursorPosition)); - var xPos = textField.__getTextWidth (text.substring (0, lowPos)); + var xPos = textField.__getTextWidth (text.substring (0, lowPos)) + 2; var widthPos = textField.__getTextWidth (text.substring (lowPos, highPos)); - context.fillStyle = "#" + StringTools.hex (textField.__textFormat.color, 6); - context.fillRect (xPos, 5, widthPos, textField.__textFormat.size - 5); + // TODO: White text + + context.fillStyle = "#000000"; + context.fillRect (xPos, 5, widthPos, (textField.__textFormat.size * 1.185) - 4); } diff --git a/openfl/_internal/renderer/dom/DOMBitmap.hx b/openfl/_internal/renderer/dom/DOMBitmap.hx index b54dc2cce6..0db98f664b 100644 --- a/openfl/_internal/renderer/dom/DOMBitmap.hx +++ b/openfl/_internal/renderer/dom/DOMBitmap.hx @@ -5,7 +5,7 @@ import lime.graphics.ImageBuffer; import openfl._internal.renderer.RenderSession; import openfl.display.Bitmap; -#if js +#if (js && html5) import js.Browser; #end @@ -19,7 +19,7 @@ class DOMBitmap { public static inline function render (bitmap:Bitmap, renderSession:RenderSession):Void { - #if js + #if (js && html5) if (bitmap.stage != null && bitmap.__worldVisible && bitmap.__renderable && bitmap.bitmapData != null && bitmap.bitmapData.__isValid) { if (bitmap.bitmapData.__image.buffer.__srcImage != null) { @@ -58,7 +58,7 @@ class DOMBitmap { private static function renderCanvas (bitmap:Bitmap, renderSession:RenderSession):Void { - #if js + #if (js && html5) if (bitmap.__image != null) { renderSession.element.removeChild (bitmap.__image); @@ -99,7 +99,7 @@ class DOMBitmap { private static function renderImage (bitmap:Bitmap, renderSession:RenderSession):Void { - #if js + #if (js && html5) if (bitmap.__canvas != null) { renderSession.element.removeChild (bitmap.__canvas); diff --git a/openfl/_internal/renderer/dom/DOMRenderer.hx b/openfl/_internal/renderer/dom/DOMRenderer.hx index 1f03fd92e4..59bcff6717 100644 --- a/openfl/_internal/renderer/dom/DOMRenderer.hx +++ b/openfl/_internal/renderer/dom/DOMRenderer.hx @@ -7,7 +7,7 @@ import openfl._internal.renderer.RenderSession; import openfl.display.DisplayObject; import openfl.display.Stage; -#if js +#if (js && html5) import js.html.Element; #end @@ -31,7 +31,7 @@ class DOMRenderer extends AbstractRenderer { renderSession.element = element; renderSession.roundPixels = true; - #if js + #if (js && html5) var prefix = untyped __js__ ("(function () { var styles = window.getComputedStyle(document.documentElement, ''), pre = (Array.prototype.slice @@ -60,7 +60,7 @@ class DOMRenderer extends AbstractRenderer { public static function applyStyle (displayObject:DisplayObject, renderSession:RenderSession, setTransform:Bool, setAlpha:Bool, setClip:Bool):Void { - #if js + #if (js && html5) var style = displayObject.__style; if (setTransform && displayObject.__worldTransformChanged) { @@ -109,7 +109,7 @@ class DOMRenderer extends AbstractRenderer { } - #if js + #if (js && html5) public static function initializeElement (displayObject:DisplayObject, element:Element, renderSession:RenderSession):Void { var style = displayObject.__style = element.style; diff --git a/openfl/_internal/renderer/dom/DOMShape.hx b/openfl/_internal/renderer/dom/DOMShape.hx index a20362a818..ce9bb5656e 100644 --- a/openfl/_internal/renderer/dom/DOMShape.hx +++ b/openfl/_internal/renderer/dom/DOMShape.hx @@ -5,7 +5,7 @@ import openfl._internal.renderer.canvas.CanvasGraphics; import openfl.display.DisplayObject; import openfl.geom.Matrix; -#if js +#if (js && html5) import js.Browser; #end @@ -18,7 +18,7 @@ class DOMShape { public static inline function render (shape:DisplayObject, renderSession:RenderSession):Void { - #if js + #if (js && html5) var graphics = shape.__graphics; if (shape.stage != null && shape.__worldVisible && shape.__renderable && graphics != null) { diff --git a/openfl/_internal/renderer/dom/DOMTextField.hx b/openfl/_internal/renderer/dom/DOMTextField.hx index 97c24f51fb..25a12fb080 100644 --- a/openfl/_internal/renderer/dom/DOMTextField.hx +++ b/openfl/_internal/renderer/dom/DOMTextField.hx @@ -6,7 +6,7 @@ import openfl.text.TextField; import openfl.text.TextFieldAutoSize; import openfl.text.TextFormatAlign; -#if js +#if (js && html5) import js.Browser; #end @@ -18,7 +18,7 @@ class DOMTextField { public static inline function render (textField:TextField, renderSession:RenderSession):Void { - #if js + #if (js && html5) if (textField.stage != null && textField.__worldVisible && textField.__renderable) { diff --git a/openfl/_internal/renderer/opengl/GLBitmap.hx b/openfl/_internal/renderer/opengl/GLBitmap.hx index a24726d95a..cebb81b10a 100644 --- a/openfl/_internal/renderer/opengl/GLBitmap.hx +++ b/openfl/_internal/renderer/opengl/GLBitmap.hx @@ -15,7 +15,7 @@ class GLBitmap { if (!bitmap.__renderable || bitmap.__worldAlpha <= 0 || bitmap.bitmapData == null || !bitmap.bitmapData.__isValid) return; - renderSession.spriteBatch.renderBitmapData(bitmap.bitmapData, bitmap.smoothing, bitmap.__worldTransform, bitmap.__worldColorTransform, bitmap.__worldAlpha, bitmap.blendMode); + renderSession.spriteBatch.renderBitmapData(bitmap.bitmapData, bitmap.smoothing, bitmap.__worldTransform, bitmap.__worldColorTransform, bitmap.__worldAlpha, bitmap.blendMode, bitmap.pixelSnapping); } diff --git a/openfl/_internal/renderer/opengl/GLRenderer.hx b/openfl/_internal/renderer/opengl/GLRenderer.hx index dfb1392488..b3d3a4a3be 100644 --- a/openfl/_internal/renderer/opengl/GLRenderer.hx +++ b/openfl/_internal/renderer/opengl/GLRenderer.hx @@ -11,6 +11,7 @@ import openfl.display.BlendMode; import openfl.display.DisplayObject; import openfl.display.Stage; import openfl.errors.Error; +import openfl.geom.Matrix; import openfl.geom.Point; @:access(lime.graphics.opengl.GL) @@ -30,7 +31,7 @@ class GLRenderer extends AbstractRenderer { public var filterManager:FilterManager; public var gl:GLRenderContext; public var _glContextId:Int; - public var maskManager:MaskManager; + public var maskManager:GLMaskManager; public var offset:Point; public var options:Dynamic; public var preserveDrawingBuffer:Bool; @@ -39,9 +40,15 @@ class GLRenderer extends AbstractRenderer { public var spriteBatch:SpriteBatch; public var stencilManager:StencilManager; public var view:Dynamic; + public var projectionMatrix:Matrix; private var __stage:Dynamic; + private var vpX:Int = 0; + private var vpY:Int = 0; + private var vpWidth:Int = 0; + private var vpHeight:Int = 0; + public function new (width:Int = 800, height:Int = 600, gl:GLRenderContext /*view:Dynamic = null*/, transparent:Bool = false, antialias:Bool = false, preserveDrawingBuffer:Bool = false) { @@ -93,6 +100,8 @@ class GLRenderer extends AbstractRenderer { } + projectionMatrix = new Matrix(); + projection = new Point (); projection.x = this.width / 2; projection.y = -this.height / 2; @@ -104,7 +113,6 @@ class GLRenderer extends AbstractRenderer { shaderManager = new ShaderManager (gl); spriteBatch = new SpriteBatch (gl); - maskManager = new MaskManager (gl); filterManager = new FilterManager (gl, this.transparent); stencilManager = new StencilManager (gl); blendModeManager = new BlendModeManager (gl); @@ -119,9 +127,11 @@ class GLRenderer extends AbstractRenderer { renderSession.spriteBatch = this.spriteBatch; renderSession.stencilManager = this.stencilManager; renderSession.renderer = this; + renderSession.defaultFramebuffer = this.defaultFramebuffer; + renderSession.projectionMatrix = this.projectionMatrix; - renderSession.projection = projection; - renderSession.offset = offset; + maskManager = new GLMaskManager (renderSession); + renderSession.maskManager = maskManager; shaderManager.setShader(shaderManager.defaultShader); @@ -160,6 +170,25 @@ class GLRenderer extends AbstractRenderer { } + public override function setViewport(x:Int, y:Int, width:Int, height:Int) { + if (!(vpX == x && vpY == y && vpWidth == width && vpHeight == height)) { + vpX = x; + vpY = y; + vpWidth = width; + vpHeight = height; + gl.viewport(x, y, width, height); + setOrtho(x, y, width, height); + } + } + + public function setOrtho(x:Float, y:Float, width:Float, height:Float) { + var o = projectionMatrix; + o.identity(); + o.a = 1 / width * 2; + o.d = -1 / height * 2; + o.tx = -1 - x * o.a; + o.ty = 1 - y * o.d; + } /*private static function destroyTexture (texture:BaseTexture):Void { @@ -235,7 +264,7 @@ class GLRenderer extends AbstractRenderer { gl.enable (gl.BLEND); gl.colorMask (true, true, true, transparent); - gl.viewport (0, 0, width, height); + setViewport (0, 0, width, height); /*for (key in Texture.TextureCache.keys ()) { @@ -256,7 +285,7 @@ class GLRenderer extends AbstractRenderer { //updateTextures (); var gl = this.gl; - gl.viewport (0, 0, width, height); + setViewport (0, 0, width, height); gl.bindFramebuffer (gl.FRAMEBUFFER, defaultFramebuffer); @@ -283,9 +312,6 @@ class GLRenderer extends AbstractRenderer { renderSession.drawCount = 0; renderSession.currentBlendMode = null; - renderSession.projection = projection; - renderSession.offset = offset; - spriteBatch.begin (renderSession); filterManager.begin (renderSession, buffer); displayObject.__renderGL (renderSession); @@ -302,7 +328,7 @@ class GLRenderer extends AbstractRenderer { super.resize (width, height); - gl.viewport (0, 0, width, height); + setViewport (0, 0, width, height); projection.x = width / 2; projection.y = -height / 2; diff --git a/openfl/_internal/renderer/opengl/GLTextField.hx b/openfl/_internal/renderer/opengl/GLTextField.hx index b61983d75b..c42b8d869e 100644 --- a/openfl/_internal/renderer/opengl/GLTextField.hx +++ b/openfl/_internal/renderer/opengl/GLTextField.hx @@ -1,21 +1,9 @@ package openfl._internal.renderer.opengl; -import lime.graphics.Image; -import lime.text.Glyph; -import lime.text.TextLayout; -import openfl._internal.renderer.canvas.CanvasTextField; import openfl._internal.renderer.opengl.utils.GraphicsRenderer; import openfl._internal.renderer.RenderSession; -import openfl.display.BitmapData; -import openfl.display.Graphics; -import openfl.display.Tilesheet; -import openfl.geom.Rectangle; -import openfl.text.Font; import openfl.text.TextField; -import openfl.text.TextFieldAutoSize; -import openfl.text.TextFormat; -import openfl.text.TextFormatAlign; @:access(openfl.text.TextField) @@ -23,325 +11,15 @@ import openfl.text.TextFormatAlign; class GLTextField { - private static var bitmapData = new Map> (); - private static var glyphs = new Map>> (); - private static var tilesheets = new Map (); - private static var tileIDs = new Map> (); - - public static function render (textField:TextField, renderSession:RenderSession) { if (!textField.__renderable || textField.__worldAlpha <= 0) return; - - update (textField); - - if (textField.__graphics == null) { - - textField.__graphics = new Graphics (); - - } - - var graphics = textField.__graphics; - graphics.clear (); - - if (textField.border || textField.background) { - - if (textField.border) { - - graphics.lineStyle (1, textField.borderColor); - - } - - if (textField.background) { - - graphics.beginFill (textField.backgroundColor); - - } - - graphics.drawRect (0.5, 0.5, textField.__width - 1, textField.__height - 1); - - } - - if (textField.__tilesheets != null) { - - for (i in 0...textField.__tilesheets.length) { - - graphics.drawTiles (textField.__tilesheets[i], textField.__tileData[i], true, Tilesheet.TILE_RGB); - - } - - } + + TextFieldGraphics.render (textField); GraphicsRenderer.render (textField, renderSession); } - private static inline function renderText (textField:TextField, text:String, format:TextFormat, offsetX:Float, textWidth:Float):Void { - - var font = textField.__getFontInstance (format); - - if (font != null && format.size != null) { - - if (!glyphs.exists (font)) { - - glyphs.set (font, new Map ()); - - } - - var size = Std.int (format.size); - var fontGlyphs = glyphs.get (font); - - if (!fontGlyphs.exists (size)) { - - fontGlyphs.set (size, font.renderGlyphs (font.getGlyphs (), size)); - - } - - var images = fontGlyphs.get (size); - - if (!bitmapData.exists (font)) { - - bitmapData.set (font, new Map ()); - - } - - var fontBitmapData = bitmapData.get (font); - - if (!fontBitmapData.exists (size)) { - - var width, height, data; - - for (image in images) { - - width = image.buffer.width; - height = image.buffer.height; - data = image.data; - break; - - } - - var bitmapData = new BitmapData (width, height); - - for (x in 0...width) { - - for (y in 0...height) { - - var alpha = data[(y * width) + x]; - var color = alpha << 24 | 0xFF << 16 | 0xFF << 8 | 0xFF; - bitmapData.setPixel32 (x, y, color); - - } - - } - - fontBitmapData.set (size, bitmapData); - - } - - var bitmapData = fontBitmapData.get (size); - - if (!tilesheets.exists (bitmapData)) { - - var tilesheet = new Tilesheet (bitmapData); - var tileID = new Map (); - - var image, index; - - for (key in images.keys ()) { - - image = images.get (key); - index = tilesheet.addTileRect (new Rectangle (image.offsetX, image.offsetY, image.width, image.height)); - - tileID.set (key, index); - - } - - tileIDs.set (bitmapData, tileID); - tilesheets.set (bitmapData, tilesheet); - - } - - var tilesheet = tilesheets.get (bitmapData); - var tileID = tileIDs.get (bitmapData); - - var r = ((format.color >> 16) & 0xFF) / 0xFF; - var g = ((format.color >> 8) & 0xFF) / 0xFF; - var b = ((format.color) & 0xFF) / 0xFF; - - var image; - var x:Float = offsetX; - var y:Float = size; - - if (format.align == TextFormatAlign.RIGHT) { - - x += textField.__width - textWidth; - - } else if (format.align == TextFormatAlign.CENTER) { - - x += (textField.__width - textWidth) / 2; - - } - - var tileData; - - if (textField.__tilesheets.length == 0 || textField.__tilesheets[textField.__tilesheets.length - 1] != tilesheet) { - - tileData = new Array (); - - textField.__tilesheets.push (tilesheet); - textField.__tileData.push (tileData); - - } else { - - tileData = textField.__tileData[textField.__tileData.length - 1]; - - } - - var offsetY = 0; - var lines = text.split ("\n"); - - if (textField.__textLayout == null) { - - textField.__textLayout = new TextLayout (); - - } - - var textLayout = textField.__textLayout; - - for (line in lines) { - - textLayout.text = null; - textLayout.font = font; - textLayout.size = size; - textLayout.text = line; - - for (position in textLayout.positions) { - - image = images.get (position.glyph); - - if (image != null) { - - tileData.push (x + position.offset.x + image.x); - tileData.push (y + position.offset.y - image.y); - tileData.push (tileID.get (position.glyph)); - tileData.push (r); - tileData.push (g); - tileData.push (b); - - } - - x += position.advance.x; - y -= position.advance.y; - - } - - x = 0; - y += size * 1.185; - - } - - } - - } - - - public static function update (textField:TextField):Bool { - - if (textField.__dirty) { - - if (((textField.__text == null || textField.__text == "") && !textField.background && !textField.border) || ((textField.width <= 0 || textField.height <= 0) && textField.autoSize != TextFieldAutoSize.LEFT)) { - - textField.__tilesheets = null; - textField.__tileData = null; - textField.__dirty = false; - - } else { - - //if (textField.__tilesheets == null) { - - textField.__tilesheets = new Array (); - textField.__tileData = new Array (); - - //} - - if (textField.__text != null && textField.__text != "") { - - var text = textField.text; - - if (textField.displayAsPassword) { - - var length = text.length; - var mask = ""; - - for (i in 0...length) { - - mask += "*"; - - } - - text = mask; - - } - - var measurements = textField.__measureText (); - var textWidth = 0.0; - - for (measurement in measurements) { - - textWidth += measurement; - - } - - if (textField.autoSize == TextFieldAutoSize.LEFT) { - - textField.__width = textWidth + 4; - textField.__height = textField.textHeight + 4; - - } - - if (textField.__ranges == null) { - - renderText (textField, text, textField.__textFormat, 2, textWidth); - - } else { - - var currentIndex = 0; - var range; - var offsetX = 2.0; - - for (i in 0...textField.__ranges.length) { - - range = textField.__ranges[i]; - - renderText (textField, text.substring (range.start, range.end), range.format, offsetX, textWidth); - offsetX += measurements[i]; - - } - - } - - } else { - - if (textField.autoSize == TextFieldAutoSize.LEFT) { - - textField.__width = 4; - textField.__height = 4; - - } - - } - - textField.__dirty = false; - return true; - - } - - } - - return false; - - } - - -} \ No newline at end of file +} diff --git a/openfl/_internal/renderer/opengl/shaders2/DefaultShader.hx b/openfl/_internal/renderer/opengl/shaders2/DefaultShader.hx index 4d3ffa0bce..4f1f815b22 100644 --- a/openfl/_internal/renderer/opengl/shaders2/DefaultShader.hx +++ b/openfl/_internal/renderer/opengl/shaders2/DefaultShader.hx @@ -13,16 +13,13 @@ class DefaultShader extends Shader { 'attribute vec2 ${Attrib.TexCoord};', 'attribute vec4 ${Attrib.Color};', - 'uniform vec2 ${Uniform.ProjectionVector};', - 'uniform vec2 ${Uniform.OffsetVector};', + 'uniform mat3 ${Uniform.ProjectionMatrix};', 'varying vec2 vTexCoord;', 'varying vec4 vColor;', - 'const vec2 center = vec2(-1.0, 1.0);', - 'void main(void) {', - ' gl_Position = vec4( ((${Attrib.Position} + ${Uniform.OffsetVector}) / ${Uniform.ProjectionVector}) + center , 0.0, 1.0);', + ' gl_Position = vec4((${Uniform.ProjectionMatrix} * vec3(${Attrib.Position}, 1.0)).xy, 0.0, 1.0);', ' vTexCoord = ${Attrib.TexCoord};', ' vColor = ${Attrib.Color};', '}' @@ -66,8 +63,7 @@ class DefaultShader extends Shader { getAttribLocation(Attrib.Position); getAttribLocation(Attrib.TexCoord); getAttribLocation(Attrib.Color); - getUniformLocation(Uniform.ProjectionVector); - getUniformLocation(Uniform.OffsetVector); + getUniformLocation(Uniform.ProjectionMatrix); getUniformLocation(Uniform.Sampler); getUniformLocation(Uniform.ColorMultiplier); getUniformLocation(Uniform.ColorOffset); @@ -84,8 +80,7 @@ class DefaultShader extends Shader { @:enum private abstract Uniform(String) from String to String { var Sampler = "uSampler0"; - var ProjectionVector = "uProjectionVector"; - var OffsetVector = "uOffsetVector"; + var ProjectionMatrix = "uProjectionMatrix"; var Color = "uColor"; var Alpha = "uAlpha"; var ColorMultiplier = "uColorMultiplier"; diff --git a/openfl/_internal/renderer/opengl/shaders2/DrawTrianglesShader.hx b/openfl/_internal/renderer/opengl/shaders2/DrawTrianglesShader.hx index c7ab789211..a775db9ce7 100644 --- a/openfl/_internal/renderer/opengl/shaders2/DrawTrianglesShader.hx +++ b/openfl/_internal/renderer/opengl/shaders2/DrawTrianglesShader.hx @@ -14,16 +14,13 @@ class DrawTrianglesShader extends Shader { 'attribute vec2 ${Attrib.Position};', 'attribute vec2 ${Attrib.TexCoord};', 'attribute vec4 ${Attrib.Color};', - 'uniform vec2 ${Uniform.ProjectionVector};', - 'uniform vec2 ${Uniform.OffsetVector};', + 'uniform mat3 ${Uniform.ProjectionMatrix};', 'varying vec2 vTexCoord;', 'varying vec4 vColor;', - - 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void) {', - ' gl_Position = vec4( ((${Attrib.Position} + ${Uniform.OffsetVector}) / ${Uniform.ProjectionVector}) + center , 0.0, 1.0);', + ' gl_Position = vec4((${Uniform.ProjectionMatrix} * vec3(${Attrib.Position}, 1.0)).xy, 0.0, 1.0);', ' vTexCoord = ${Attrib.TexCoord};', // the passed color is ARGB format ' vColor = ${Attrib.Color}.bgra;', @@ -73,14 +70,12 @@ class DrawTrianglesShader extends Shader { override function init() { super.init(); - // TODO Modify graphicsrenderer to draw projection -y getAttribLocation(Attrib.Position); getAttribLocation(Attrib.TexCoord); getAttribLocation(Attrib.Color); getUniformLocation(Uniform.Sampler); - getUniformLocation(Uniform.ProjectionVector); - getUniformLocation(Uniform.OffsetVector); + getUniformLocation(Uniform.ProjectionMatrix); getUniformLocation(Uniform.Color); getUniformLocation(Uniform.Alpha); getUniformLocation(Uniform.UseTexture); @@ -100,8 +95,7 @@ class DrawTrianglesShader extends Shader { @:enum private abstract Uniform(String) from String to String { var UseTexture = "uUseTexture"; var Sampler = DefUniform.Sampler; - var ProjectionVector = DefUniform.ProjectionVector; - var OffsetVector = DefUniform.OffsetVector; + var ProjectionMatrix = DefUniform.ProjectionMatrix; var Color = DefUniform.Color; var Alpha = DefUniform.Alpha; var ColorMultiplier = DefUniform.ColorMultiplier; diff --git a/openfl/_internal/renderer/opengl/shaders2/FillShader.hx b/openfl/_internal/renderer/opengl/shaders2/FillShader.hx index ac16823b1b..7113939ceb 100644 --- a/openfl/_internal/renderer/opengl/shaders2/FillShader.hx +++ b/openfl/_internal/renderer/opengl/shaders2/FillShader.hx @@ -12,8 +12,7 @@ class FillShader extends Shader { vertexSrc = [ 'attribute vec2 ${Attrib.Position};', 'uniform mat3 ${Uniform.TranslationMatrix};', - 'uniform vec2 ${Uniform.ProjectionVector};', - 'uniform vec2 ${Uniform.OffsetVector};', + 'uniform mat3 ${Uniform.ProjectionMatrix};', 'uniform vec4 ${Uniform.Color};', 'uniform float ${Uniform.Alpha};', @@ -32,9 +31,7 @@ class FillShader extends Shader { '}', 'void main(void) {', - ' vec3 v = ${Uniform.TranslationMatrix} * vec3(${Attrib.Position}, 1.0);', - ' v -= ${Uniform.OffsetVector}.xyx;', - ' gl_Position = vec4( v.x / ${Uniform.ProjectionVector}.x -1.0, v.y / - ${Uniform.ProjectionVector}.y + 1.0 , 0.0, 1.0);', + ' gl_Position = vec4((${Uniform.ProjectionMatrix} * ${Uniform.TranslationMatrix} * vec3(${Attrib.Position}, 1.0)).xy, 0.0, 1.0);', ' vColor = colorTransform(${Uniform.Color}, ${Uniform.Alpha}, ${Uniform.ColorMultiplier}, ${Uniform.ColorOffset});', '}' @@ -60,8 +57,7 @@ class FillShader extends Shader { getAttribLocation(Attrib.Position); getUniformLocation(Uniform.TranslationMatrix); - getUniformLocation(Uniform.ProjectionVector); - getUniformLocation(Uniform.OffsetVector); + getUniformLocation(Uniform.ProjectionMatrix); getUniformLocation(Uniform.Color); getUniformLocation(Uniform.ColorMultiplier); getUniformLocation(Uniform.ColorOffset); @@ -75,8 +71,7 @@ class FillShader extends Shader { @:enum private abstract Uniform(String) from String to String { var TranslationMatrix = "uTranslationMatrix"; - var ProjectionVector = DefUniform.ProjectionVector; - var OffsetVector = DefUniform.OffsetVector; + var ProjectionMatrix = DefUniform.ProjectionMatrix; var Color = DefUniform.Color; var Alpha = DefUniform.Alpha; var ColorMultiplier = DefUniform.ColorMultiplier; diff --git a/openfl/_internal/renderer/opengl/shaders2/PatternFillShader.hx b/openfl/_internal/renderer/opengl/shaders2/PatternFillShader.hx index bfd069d076..2a3b8b7b43 100644 --- a/openfl/_internal/renderer/opengl/shaders2/PatternFillShader.hx +++ b/openfl/_internal/renderer/opengl/shaders2/PatternFillShader.hx @@ -12,16 +12,13 @@ class PatternFillShader extends Shader { vertexSrc = [ 'attribute vec2 ${Attrib.Position};', 'uniform mat3 ${Uniform.TranslationMatrix};', - 'uniform vec2 ${Uniform.ProjectionVector};', - 'uniform vec2 ${Uniform.OffsetVector};', + 'uniform mat3 ${Uniform.ProjectionMatrix};', 'uniform mat3 ${Uniform.PatternMatrix};', 'varying vec2 vPosition;', 'void main(void) {', - ' vec3 v = ${Uniform.TranslationMatrix} * vec3(${Attrib.Position} , 1.0);', - ' v -= ${Uniform.OffsetVector}.xyx;', - ' gl_Position = vec4( v.x / ${Uniform.ProjectionVector}.x -1.0, v.y / - ${Uniform.ProjectionVector}.y + 1.0 , 0.0, 1.0);', + ' gl_Position = vec4((${Uniform.ProjectionMatrix} * ${Uniform.TranslationMatrix} * vec3(${Attrib.Position}, 1.0)).xy, 0.0, 1.0);', ' vPosition = (${Uniform.PatternMatrix} * vec3(${Attrib.Position}, 1)).xy;', '}' @@ -70,8 +67,7 @@ class PatternFillShader extends Shader { getUniformLocation(Uniform.TranslationMatrix); getUniformLocation(Uniform.PatternMatrix); - getUniformLocation(Uniform.ProjectionVector); - getUniformLocation(Uniform.OffsetVector); + getUniformLocation(Uniform.ProjectionMatrix); getUniformLocation(Uniform.Sampler); getUniformLocation(Uniform.PatternTL); getUniformLocation(Uniform.PatternBR); @@ -92,8 +88,7 @@ class PatternFillShader extends Shader { var PatternTL = "uPatternTL"; var PatternBR = "uPatternBR"; var Sampler = DefUniform.Sampler; - var ProjectionVector = DefUniform.ProjectionVector; - var OffsetVector = DefUniform.OffsetVector; + var ProjectionMatrix = DefUniform.ProjectionMatrix; var Color = DefUniform.Color; var Alpha = DefUniform.Alpha; var ColorMultiplier = DefUniform.ColorMultiplier; diff --git a/openfl/_internal/renderer/opengl/shaders2/PrimitiveShader.hx b/openfl/_internal/renderer/opengl/shaders2/PrimitiveShader.hx index d87d9f9dce..0c46df20c5 100644 --- a/openfl/_internal/renderer/opengl/shaders2/PrimitiveShader.hx +++ b/openfl/_internal/renderer/opengl/shaders2/PrimitiveShader.hx @@ -14,8 +14,7 @@ class PrimitiveShader extends Shader { 'attribute vec4 ${Attrib.Color};', 'uniform mat3 ${Uniform.TranslationMatrix};', - 'uniform vec2 ${Uniform.ProjectionVector};', - 'uniform vec2 ${Uniform.OffsetVector};', + 'uniform mat3 ${Uniform.ProjectionMatrix};', 'uniform vec4 ${Uniform.ColorMultiplier};', 'uniform vec4 ${Uniform.ColorOffset};', 'uniform float ${Uniform.Alpha};', @@ -32,9 +31,7 @@ class PrimitiveShader extends Shader { '}', 'void main(void) {', - ' vec3 v = ${Uniform.TranslationMatrix} * vec3(${Attrib.Position} , 1.0);', - ' v -= ${Uniform.OffsetVector}.xyx;', - ' gl_Position = vec4( v.x / ${Uniform.ProjectionVector}.x -1.0, v.y / -${Uniform.ProjectionVector}.y + 1.0 , 0.0, 1.0);', + ' gl_Position = vec4((${Uniform.ProjectionMatrix} * ${Uniform.TranslationMatrix} * vec3(${Attrib.Position}, 1.0)).xy, 0.0, 1.0);', ' vColor = colorTransform(${Attrib.Color}, ${Uniform.Alpha}, ${Uniform.ColorMultiplier}, ${Uniform.ColorOffset});', '}' ]; @@ -61,8 +58,7 @@ class PrimitiveShader extends Shader { getAttribLocation(Attrib.Position); getAttribLocation(Attrib.Color); getUniformLocation(Uniform.TranslationMatrix); - getUniformLocation(Uniform.ProjectionVector); - getUniformLocation(Uniform.OffsetVector); + getUniformLocation(Uniform.ProjectionMatrix); getUniformLocation(Uniform.Alpha); getUniformLocation(Uniform.ColorMultiplier); getUniformLocation(Uniform.ColorOffset); @@ -77,8 +73,7 @@ class PrimitiveShader extends Shader { @:enum private abstract Uniform(String) from String to String { var TranslationMatrix = "uTranslationMatrix"; - var ProjectionVector = DefUniform.ProjectionVector; - var OffsetVector = DefUniform.OffsetVector; + var ProjectionMatrix = DefUniform.ProjectionMatrix; var Alpha = DefUniform.Alpha; var ColorMultiplier = DefUniform.ColorMultiplier; var ColorOffset = DefUniform.ColorOffset; diff --git a/openfl/_internal/renderer/opengl/utils/BlendModeManager.hx b/openfl/_internal/renderer/opengl/utils/BlendModeManager.hx index d7bee6d554..f5f85515aa 100644 --- a/openfl/_internal/renderer/opengl/utils/BlendModeManager.hx +++ b/openfl/_internal/renderer/opengl/utils/BlendModeManager.hx @@ -27,10 +27,16 @@ class BlendModeManager { } - public function setBlendMode (blendMode:BlendMode):Bool { + public function setBlendMode (blendMode:BlendMode, ?force:Bool = false):Bool { - if (blendMode == null) blendMode = BlendMode.NORMAL; - if (currentBlendMode == blendMode) { + if (blendMode == null) { + + blendMode = BlendMode.NORMAL; + force = true; + + } + + if (!force && currentBlendMode == blendMode) { return false; @@ -46,4 +52,4 @@ class BlendModeManager { } -} \ No newline at end of file +} diff --git a/openfl/_internal/renderer/opengl/utils/FilterManager.hx b/openfl/_internal/renderer/opengl/utils/FilterManager.hx index 29aba486db..5292ad5f77 100644 --- a/openfl/_internal/renderer/opengl/utils/FilterManager.hx +++ b/openfl/_internal/renderer/opengl/utils/FilterManager.hx @@ -104,10 +104,9 @@ class FilterManager { this.renderSession = renderSession; defaultShader = renderSession.shaderManager.defaultShader; - var projection = renderSession.projection; - - width = Std.int (projection.x * 2); - height = Std.int (-projection.y * 2); + // TODO + width = 0; + height = 0; this.buffer = buffer; } diff --git a/openfl/_internal/renderer/opengl/utils/MaskManager.hx b/openfl/_internal/renderer/opengl/utils/GLMaskManager.hx similarity index 79% rename from openfl/_internal/renderer/opengl/utils/MaskManager.hx rename to openfl/_internal/renderer/opengl/utils/GLMaskManager.hx index a17d244b09..7d671f4dd8 100644 --- a/openfl/_internal/renderer/opengl/utils/MaskManager.hx +++ b/openfl/_internal/renderer/opengl/utils/GLMaskManager.hx @@ -2,32 +2,60 @@ package openfl._internal.renderer.opengl.utils; import lime.graphics.GLRenderContext; +import openfl._internal.renderer.AbstractMaskManager; import openfl._internal.renderer.RenderSession; import openfl.display.DisplayObject; -class MaskManager { + +class GLMaskManager extends AbstractMaskManager { + public var gl:GLRenderContext; - public function new(gl:GLRenderContext) { - setContext(gl); + + public function new (renderSession:RenderSession) { + + super (renderSession); + + setContext (renderSession.gl); + } - public function destroy() { + + public function destroy () { + gl = null; + } - public function setContext(gl:GLRenderContext) { - this.gl = gl; + + public override function pushMask (mask:DisplayObject) { + + renderSession.stencilManager.pushMask (mask, renderSession); + } - public function pushMask(object:DisplayObject, renderSession:RenderSession) { - renderSession.stencilManager.pushMask(object, renderSession); + + public override function popMask () { + + renderSession.stencilManager.popMask (null, renderSession); + } - public function popMask(object:DisplayObject, renderSession:RenderSession) { - renderSession.stencilManager.popMask(object, renderSession); + + public function setContext (gl:GLRenderContext) { + + if (renderSession != null) { + + renderSession.gl = gl; + + } + + this.gl = gl; + } + + } /* diff --git a/openfl/_internal/renderer/opengl/utils/GraphicsRenderer.hx b/openfl/_internal/renderer/opengl/utils/GraphicsRenderer.hx index 691e279726..e2256ac546 100644 --- a/openfl/_internal/renderer/opengl/utils/GraphicsRenderer.hx +++ b/openfl/_internal/renderer/opengl/utils/GraphicsRenderer.hx @@ -859,14 +859,13 @@ class GraphicsRenderer { } */ - renderGraphics(object, renderSession, renderSession.projection, false); + renderGraphics(object, renderSession, false); } - public static function renderGraphics (object:DisplayObject, renderSession:RenderSession, projection:Point, ?localCoords:Bool = false):Void { + public static function renderGraphics (object:DisplayObject, renderSession:RenderSession, ?localCoords:Bool = false):Void { var graphics = object.__graphics; var gl = renderSession.gl; - var offset = renderSession.offset; var glStack = graphics.__glStack[GLRenderer.glContextId]; var bucket:GLBucket; @@ -891,15 +890,15 @@ class GraphicsRenderer { if (batchDrawing && !localCoords) { renderSession.spriteBatch.finish(); } - renderSession.stencilManager.pushBucket(bucket, renderSession, projection, translationMatrix.toArray(true)); - var shader = prepareShader(bucket, renderSession, object, projection, translationMatrix.toArray(true)); + renderSession.stencilManager.pushBucket(bucket, renderSession, translationMatrix.toArray(true)); + var shader = prepareShader(bucket, renderSession, object, translationMatrix.toArray(true)); renderFill(bucket, shader, renderSession); renderSession.stencilManager.popBucket(object, bucket, renderSession); case DrawTriangles: if (batchDrawing && !localCoords) { renderSession.spriteBatch.finish(); } - var shader = prepareShader(bucket, renderSession, object, projection, null); + var shader = prepareShader(bucket, renderSession, object, null); renderDrawTriangles(bucket, shader, renderSession); case DrawTiles: if (!batchDrawing) { @@ -922,8 +921,7 @@ class GraphicsRenderer { renderSession.shaderManager.setShader (shader); gl.uniformMatrix3fv (shader.getUniformLocation(PrimitiveUniform.TranslationMatrix), false, translationMatrix.toArray(true)); - gl.uniform2f (shader.getUniformLocation(PrimitiveUniform.ProjectionVector), projection.x, -projection.y); - gl.uniform2f (shader.getUniformLocation(PrimitiveUniform.OffsetVector), -offset.x, -offset.y); + gl.uniformMatrix3fv (shader.getUniformLocation(PrimitiveUniform.ProjectionMatrix), false, renderSession.projectionMatrix.toArray(true)); gl.uniform1f (shader.getUniformLocation(PrimitiveUniform.Alpha), 1); gl.uniform4f (shader.getUniformLocation(FillUniform.ColorMultiplier), ct.redMultiplier, ct.greenMultiplier, ct.blueMultiplier, ct.alphaMultiplier); @@ -1035,8 +1033,6 @@ class GraphicsRenderer { bucket.uploadTileBuffer = true; //prepare the matrix - var tMatrix = bucket.textureMatrix; - tMatrix.identity(); var pMatrix:Matrix; if (m == null) { pMatrix = new Matrix(); @@ -1044,19 +1040,19 @@ class GraphicsRenderer { pMatrix = m.clone(); } - tMatrix.concat(pMatrix); - pMatrix = pMatrix.invert(); - var tx = (pMatrix.tx) / (b.width); - var ty = (pMatrix.ty) / (b.height); + pMatrix.invert(); + pMatrix.scale(1 / b.width, 1 / b.height); + var tx = pMatrix.tx; + var ty = pMatrix.ty; + pMatrix.tx = 0; + pMatrix.ty = 0; bucket.textureTL.x = tx; bucket.textureTL.y = ty; bucket.textureBR.x = tx + 1; bucket.textureBR.y = ty + 1; - - tMatrix.scale(1 / b.width, 1 / b.height); - - bucket.textureMatrix = tMatrix; + + bucket.textureMatrix = pMatrix; case _: bucket = switchBucket(path.fillIndex, glStack, Line); bucket.uploadTileBuffer = false; @@ -1109,9 +1105,8 @@ class GraphicsRenderer { return bucket; } - private static function prepareShader(bucket:GLBucket, renderSession:RenderSession, object:DisplayObject, projection:Point, translationMatrix:Float32Array) { + private static function prepareShader(bucket:GLBucket, renderSession:RenderSession, object:DisplayObject, translationMatrix:Float32Array) { var gl = renderSession.gl; - var offset = renderSession.offset; var shader:Shader = null; shader = switch(bucket.mode) { @@ -1130,8 +1125,8 @@ class GraphicsRenderer { var newShader = renderSession.shaderManager.setShader(shader); // common uniforms - gl.uniform2f (shader.getUniformLocation(DefUniform.OffsetVector), -offset.x, -offset.y); gl.uniform1f (shader.getUniformLocation(DefUniform.Alpha), object.__worldAlpha); + gl.uniformMatrix3fv(shader.getUniformLocation(DefUniform.ProjectionMatrix), false, @:privateAccess renderSession.projectionMatrix.toArray(true)); var ct:ColorTransform = object.__worldColorTransform; gl.uniform4f (shader.getUniformLocation(FillUniform.ColorMultiplier), ct.redMultiplier, ct.greenMultiplier, ct.blueMultiplier, ct.alphaMultiplier); @@ -1140,17 +1135,14 @@ class GraphicsRenderer { // specific uniforms switch(bucket.mode) { case Fill: - gl.uniform2f (shader.getUniformLocation(DefUniform.ProjectionVector), projection.x, -projection.y); gl.uniformMatrix3fv (shader.getUniformLocation(FillUniform.TranslationMatrix), false, translationMatrix); gl.uniform4fv (shader.getUniformLocation(FillUniform.Color), new Float32Array (bucket.color)); case PatternFill: - gl.uniform2f (shader.getUniformLocation(PatternFillUniform.ProjectionVector), projection.x, -projection.y); gl.uniformMatrix3fv (shader.getUniformLocation(PatternFillUniform.TranslationMatrix), false, translationMatrix); gl.uniform2f(shader.getUniformLocation(PatternFillUniform.PatternTL), bucket.textureTL.x, bucket.textureTL.y); gl.uniform2f(shader.getUniformLocation(PatternFillUniform.PatternBR), bucket.textureBR.x, bucket.textureBR.y); - gl.uniformMatrix3fv(shader.getUniformLocation(PatternFillUniform.PatternMatrix), false, bucket.textureMatrix.toArray(false)); + gl.uniformMatrix3fv(shader.getUniformLocation(PatternFillUniform.PatternMatrix), false, bucket.textureMatrix.toArray(true)); case DrawTriangles: - gl.uniform2f (shader.getUniformLocation(DrawTrianglesUniform.ProjectionVector), projection.x, projection.y); if (bucket.texture != null) { gl.uniform1i(shader.getUniformLocation(DrawTrianglesUniform.UseTexture), 1); } else { @@ -1194,7 +1186,7 @@ class GraphicsRenderer { gl.bindTexture(gl.TEXTURE_2D, bucket.texture); // TODO Fix this: webgl can only repeat textures that are power of two - if (bucket.textureRepeat #if js && bucket.bitmap.__image.powerOfTwo #end) { + if (bucket.textureRepeat #if (js && html5) && bucket.bitmap.__image.powerOfTwo #end) { gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } else { @@ -1525,6 +1517,7 @@ class GLBucketData { stride = 0; rawVerts = false; rawIndices = false; + drawMode = gl.TRIANGLE_STRIP; } public function upload():Void { diff --git a/openfl/_internal/renderer/opengl/utils/ShaderManager.hx b/openfl/_internal/renderer/opengl/utils/ShaderManager.hx index 65fa350167..1910e1da5b 100644 --- a/openfl/_internal/renderer/opengl/utils/ShaderManager.hx +++ b/openfl/_internal/renderer/opengl/utils/ShaderManager.hx @@ -44,11 +44,20 @@ class ShaderManager { } public function setShader(shader:Shader, ?force:Bool = false) { - if (!force && currentShader.ID == shader.ID) return false; + if (shader == null) { + // Assume we want to force, if we get called with null. + currentShader = null; + gl.useProgram(null); + return true; + } + + if (currentShader != null && !force && currentShader.ID == shader.ID) { + return false; + } currentShader = shader; gl.useProgram(shader.program); return true; } -} \ No newline at end of file +} diff --git a/openfl/_internal/renderer/opengl/utils/SpriteBatch.hx b/openfl/_internal/renderer/opengl/utils/SpriteBatch.hx index 13730ded4c..d788c35ac8 100644 --- a/openfl/_internal/renderer/opengl/utils/SpriteBatch.hx +++ b/openfl/_internal/renderer/opengl/utils/SpriteBatch.hx @@ -8,6 +8,7 @@ import openfl._internal.renderer.opengl.utils.VertexAttribute; import openfl._internal.renderer.RenderSession; import openfl.display.BitmapData; import openfl.display.DisplayObject; +import openfl.display.PixelSnapping; import openfl.display.Tilesheet; import openfl.geom.ColorTransform; import openfl.geom.Matrix; @@ -22,6 +23,7 @@ import lime.utils.*; @:access(openfl.display.Graphics) @:access(openfl.display.DisplayObject) @:access(openfl.display.Tilesheet) +@:access(openfl.geom.Matrix) class SpriteBatch { static inline var VERTS_PER_SPRITE:Int = 4; @@ -148,7 +150,7 @@ class SpriteBatch { flush(); } - public function renderBitmapData(bitmapData:BitmapData, smoothing:Bool, matrix:Matrix, ct:ColorTransform, ?alpha:Float = 1, ?blendMode:BlendMode) { + public function renderBitmapData(bitmapData:BitmapData, smoothing:Bool, matrix:Matrix, ct:ColorTransform, ?alpha:Float = 1, ?blendMode:BlendMode, ?pixelSnapping:PixelSnapping) { if (bitmapData == null) return; var texture = bitmapData.getTexture(gl); @@ -165,7 +167,7 @@ class SpriteBatch { enableAttributes(0); var index = batchedSprites * 4 * elementsPerVertex; - fillVertices(index, bitmapData.width, bitmapData.height, matrix, uvs, null, color); + fillVertices(index, bitmapData.width, bitmapData.height, matrix, uvs, null, color, pixelSnapping); setState(batchedSprites, texture, smoothing, blendMode, ct, true); @@ -321,23 +323,16 @@ class SpriteBatch { matrix.tx = tx * oMatrix.a + ty * oMatrix.c + oMatrix.tx; matrix.ty = tx * oMatrix.b + ty * oMatrix.d + oMatrix.ty; - if (sheet.__bitmap.__uvFlipped) { - uvs.x0 = tileUV.x; uvs.y0 = tileUV.height; - uvs.x1 = tileUV.width; uvs.y1 = tileUV.height; - uvs.x2 = tileUV.width; uvs.y2 = tileUV.y; - uvs.x3 = tileUV.x; uvs.y3 = tileUV.y; - } else { - uvs.x0 = tileUV.x; uvs.y0 = tileUV.y; - uvs.x1 = tileUV.width; uvs.y1 = tileUV.y; - uvs.x2 = tileUV.width; uvs.y2 = tileUV.height; - uvs.x3 = tileUV.x; uvs.y3 = tileUV.height; - } + uvs.x0 = tileUV.x; uvs.y0 = tileUV.y; + uvs.x1 = tileUV.width; uvs.y1 = tileUV.y; + uvs.x2 = tileUV.width; uvs.y2 = tileUV.height; + uvs.x3 = tileUV.x; uvs.y3 = tileUV.height; bIndex = batchedSprites * 4 * elementsPerVertex; color = ((Std.int(alpha * 255)) & 0xFF) << 24 | (tint & 0xFF) << 16 | ((tint >> 8) & 0xFF) << 8 | ((tint >> 16) & 0xFF); - fillVertices(bIndex, rect.width, rect.height, matrix, uvs, null, color); + fillVertices(bIndex, rect.width, rect.height, matrix, uvs, null, color, NEVER); setState(batchedSprites, texture, smooth, blendMode, object.__worldColorTransform, false); @@ -382,10 +377,11 @@ class SpriteBatch { } inline function fillVertices(index:Int, width:Float, height:Float, matrix:Matrix, uvs:TextureUvs, ?pivot:Point, - ?color:Int = 0xFFFFFFFF) { + ?color:Int = 0xFFFFFFFF, ?pixelSnapping:PixelSnapping) { var w0:Float, w1:Float, h0:Float, h1:Float; + if (pivot == null) { w0 = width; w1 = 0; h0 = height; h1 = 0; @@ -396,6 +392,11 @@ class SpriteBatch { h1 = height * -pivot.y; } + if (pixelSnapping == null) { + pixelSnapping = PixelSnapping.NEVER; + } + + var snap = pixelSnapping != NEVER; var a = matrix.a; var b = matrix.b; var c = matrix.c; @@ -404,33 +405,52 @@ class SpriteBatch { var ty = matrix.ty; var cOffsetIndex = 0; - positions[index++] = (a * w1 + c * h1 + tx); - positions[index++] = (d * h1 + b * w1 + ty); + if(!snap) { + positions[index++] = (a * w1 + c * h1 + tx); + positions[index++] = (d * h1 + b * w1 + ty); + } else { + positions[index++] = Math.fround(a * w1 + c * h1 + tx); + positions[index++] = Math.fround(d * h1 + b * w1 + ty); + } positions[index++] = uvs.x0; positions[index++] = uvs.y0; if(enableColor) { colors[index++] = color; } - positions[index++] = (a * w0 + c * h1 + tx); - positions[index++] = (d * h1 + b * w0 + ty); + if(!snap) { + positions[index++] = (a * w0 + c * h1 + tx); + positions[index++] = (d * h1 + b * w0 + ty); + } else { + positions[index++] = Math.fround(a * w0 + c * h1 + tx); + positions[index++] = Math.fround(d * h1 + b * w0 + ty); + } positions[index++] = uvs.x1; positions[index++] = uvs.y1; if(enableColor) { colors[index++] = color; } - positions[index++] = (a * w0 + c * h0 + tx); - positions[index++] = (d * h0 + b * w0 + ty); + if(!snap) { + positions[index++] = (a * w0 + c * h0 + tx); + positions[index++] = (d * h0 + b * w0 + ty); + } else { + positions[index++] = Math.fround(a * w0 + c * h0 + tx); + positions[index++] = Math.fround(d * h0 + b * w0 + ty); + } positions[index++] = uvs.x2; positions[index++] = uvs.y2; if(enableColor) { colors[index++] = color; } - - positions[index++] = (a * w1 + c * h0 + tx); - positions[index++] = (d * h0 + b * w1 + ty); + if(!snap) { + positions[index++] = (a * w1 + c * h0 + tx); + positions[index++] = (d * h0 + b * w1 + ty); + } else { + positions[index++] = Math.fround(a * w1 + c * h0 + tx); + positions[index++] = Math.fround(d * h0 + b * w1 + ty); + } positions[index++] = uvs.x3; positions[index++] = uvs.y3; if(enableColor) { @@ -533,8 +553,7 @@ class SpriteBatch { // TODO cache this somehow?, don't do each state change? shader.bindVertexArray(vertexArray); - var projection = renderSession.projection; - gl.uniform2f(shader.getUniformLocation(DefUniform.ProjectionVector), projection.x, projection.y); + gl.uniformMatrix3fv(shader.getUniformLocation(DefUniform.ProjectionMatrix), false, renderSession.projectionMatrix.toArray(true)); if (state.colorTransform != null) { var ct = state.colorTransform; @@ -551,6 +570,7 @@ class SpriteBatch { gl.bindTexture(gl.TEXTURE_2D, state.texture); if (state.textureSmooth) { + //if (false) { gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); } else { @@ -597,6 +617,7 @@ class SpriteBatch { return r; } + } @:access(openfl.geom.ColorTransform) diff --git a/openfl/_internal/renderer/opengl/utils/StencilManager.hx b/openfl/_internal/renderer/opengl/utils/StencilManager.hx index 527f347d04..1fe9c69c99 100644 --- a/openfl/_internal/renderer/opengl/utils/StencilManager.hx +++ b/openfl/_internal/renderer/opengl/utils/StencilManager.hx @@ -38,21 +38,19 @@ class StencilManager { } - public inline function prepareGraphics(fill:GLBucketData, renderSession:RenderSession, projection:Point, translationMatrix:Float32Array):Void { - var offset = renderSession.offset; + public inline function prepareGraphics(fill:GLBucketData, renderSession:RenderSession, translationMatrix:Float32Array):Void { var shader = renderSession.shaderManager.fillShader; renderSession.shaderManager.setShader (shader); gl.uniformMatrix3fv (shader.getUniformLocation(FillUniform.TranslationMatrix), false, translationMatrix); - gl.uniform2f (shader.getUniformLocation(FillUniform.ProjectionVector), projection.x, -projection.y); - gl.uniform2f (shader.getUniformLocation(FillUniform.OffsetVector), -offset.x, -offset.y); + gl.uniformMatrix3fv (shader.getUniformLocation(FillUniform.ProjectionMatrix), false, renderSession.projectionMatrix.toArray(true)); fill.vertexArray.bind(); shader.bindVertexArray(fill.vertexArray); gl.bindBuffer (gl.ELEMENT_ARRAY_BUFFER, fill.indexBuffer); } - public function pushBucket (bucket:GLBucket, renderSession:RenderSession, projection:Point, translationMatrix:Float32Array, ?isMask:Bool = false):Void { + public function pushBucket (bucket:GLBucket, renderSession:RenderSession, translationMatrix:Float32Array, ?isMask:Bool = false):Void { if(!isMask) { gl.enable(gl.STENCIL_TEST); @@ -68,7 +66,7 @@ class StencilManager { for (fill in bucket.fills) { if (fill.available) continue; - prepareGraphics(fill, renderSession, projection, translationMatrix); + prepareGraphics(fill, renderSession, translationMatrix); gl.drawElements (fill.drawMode, fill.glIndices.length, gl.UNSIGNED_SHORT, 0); } @@ -126,7 +124,7 @@ class StencilManager { switch(bucket.mode) { case Fill, PatternFill: - pushBucket(bucket, renderSession, renderSession.projection, translationMatrix.toArray(true), true); + pushBucket(bucket, renderSession, translationMatrix.toArray(true), true); case _: } } @@ -139,10 +137,10 @@ class StencilManager { public function popMask(object:DisplayObject, renderSession:RenderSession) { - var maskGraphics:Graphics = object.__maskGraphics; - if (maskGraphics == null || maskGraphics.__commands.length <= 0) { - return; - } + //var maskGraphics:Graphics = object.__maskGraphics; + //if (maskGraphics == null || maskGraphics.__commands.length <= 0) { + //return; + //} stencilMask--; diff --git a/openfl/_legacy/Assets.hx b/openfl/_legacy/Assets.hx index 7ccc571d6c..eabd5214d7 100644 --- a/openfl/_legacy/Assets.hx +++ b/openfl/_legacy/Assets.hx @@ -1870,7 +1870,11 @@ class Assets { case EConst(CString(filePath)): - path = Context.resolvePath (filePath); + path = filePath; + if (!sys.FileSystem.exists(filePath)) { + path = Context.resolvePath (filePath); + } + default: diff --git a/openfl/_legacy/display/BitmapData.hx b/openfl/_legacy/display/BitmapData.hx index 234dc8bbaf..b23496acfb 100644 --- a/openfl/_legacy/display/BitmapData.hx +++ b/openfl/_legacy/display/BitmapData.hx @@ -3,6 +3,7 @@ package openfl._legacy.display; #if openfl_legacy import haxe.io.Bytes; import openfl._legacy.Assets; +import openfl.display.BitmapDataChannel; import openfl.display.JPEGEncoderOptions; import openfl.display.PNGEncoderOptions; import openfl.filters.BitmapFilter; @@ -324,21 +325,17 @@ class BitmapData implements IBitmapDrawable { public inline static function getRGBAPixels (bitmapData:BitmapData):ByteArray { - var data = bitmapData.getPixels (new Rectangle (0, 0, bitmapData.width, bitmapData.height)); - var size = bitmapData.width * bitmapData.height; - var v; + var rgbaData = new BitmapData (bitmapData.width, bitmapData.height, bitmapData.transparent); - data.position = 0; + var rect = bitmapData.rect; + var point = new Point (0, 0); - for (i in 0...size) { - - v = data.readInt (); - data.position = i << 2; - data.writeInt ((((v >>> 0) & 0xFF) << 8) | (((v >>> 8) & 0xFF) << 16) | (((v >>> 16) & 0xFF) << 24) | (((v >>> 24) & 0xFF) << 0)); - - } + rgbaData.copyChannel (bitmapData, rect, point, BitmapDataChannel.GREEN, BitmapDataChannel.RED); + rgbaData.copyChannel (bitmapData, rect, point, BitmapDataChannel.BLUE, BitmapDataChannel.GREEN); + rgbaData.copyChannel (bitmapData, rect, point, BitmapDataChannel.ALPHA, BitmapDataChannel.BLUE); + rgbaData.copyChannel (bitmapData, rect, point, BitmapDataChannel.RED, BitmapDataChannel.ALPHA); - return data; + return rgbaData.getPixels (rect); } diff --git a/openfl/_legacy/display/HybridStage.hx b/openfl/_legacy/display/HybridStage.hx index d30318b194..1c250cf909 100644 --- a/openfl/_legacy/display/HybridStage.hx +++ b/openfl/_legacy/display/HybridStage.hx @@ -9,6 +9,7 @@ import lime.ui.GamepadAxis; import lime.ui.GamepadButton; import lime.ui.KeyCode; import lime.ui.KeyModifier; +import openfl._legacy.events.Event; import openfl._legacy.Lib; import openfl.ui.Keyboard; @@ -189,6 +190,20 @@ class HybridStage extends ManagedStage implements IModule { } + public function onTextEdit (text:String, start:Int, length:Int):Void { + + + + } + + + public function onTextInput (text:String):Void { + + + + } + + public function onTouchMove (x:Float, y:Float, id:Int):Void { var flags = ManagedStage.efPrimaryTouch; @@ -237,6 +252,13 @@ class HybridStage extends ManagedStage implements IModule { } + public function onWindowEnter ():Void { + + + + } + + public function onWindowFocusIn ():Void { pumpEvent ( { type: ManagedStage.etGotInputFocus } ); @@ -258,6 +280,13 @@ class HybridStage extends ManagedStage implements IModule { } + public function onWindowLeave ():Void { + + dispatchEvent (new Event (Event.MOUSE_LEAVE)); + + } + + public function onWindowMinimize ():Void { diff --git a/openfl/_legacy/display/SimpleButton.hx b/openfl/_legacy/display/SimpleButton.hx index 74077bb64f..a13e685577 100644 --- a/openfl/_legacy/display/SimpleButton.hx +++ b/openfl/_legacy/display/SimpleButton.hx @@ -76,7 +76,7 @@ class SimpleButton extends InteractiveObject { } - public function set_overState (value:DisplayObject):DisplayObject { + private function set_overState (value:DisplayObject):DisplayObject { overState = value; lime_simple_button_set_state (__handle, 2, value == null ? null : value.__handle); @@ -106,7 +106,7 @@ class SimpleButton extends InteractiveObject { } - public function set_upState (value:DisplayObject):DisplayObject { + private function set_upState (value:DisplayObject):DisplayObject { upState = value; lime_simple_button_set_state (__handle, 0, value == null ? null : value.__handle); diff --git a/openfl/_legacy/net/URLLoader.hx b/openfl/_legacy/net/URLLoader.hx index da5a5e5311..0f5e4f911b 100644 --- a/openfl/_legacy/net/URLLoader.hx +++ b/openfl/_legacy/net/URLLoader.hx @@ -13,298 +13,414 @@ import openfl.net.URLRequestHeader; import openfl.net.URLVariables; import openfl.utils.ByteArray; import openfl.Lib; +#if cpp +import cpp.vm.Thread; +import cpp.vm.Mutex; +#elseif neko +import neko.vm.Thread; +import neko.vm.Mutex; +#end +private enum ManagersThreadMessage { + GetCookiesCall (callerThread : Thread, handle : Dynamic); + GetCookiesResponse (ret : Array); + InitializeCall (caCertFilePath : String); +} + +private class URLLoadersManager { + + static var instance : URLLoadersManager; + + var managersThread : Thread; + var activeLoaders : List; + var loadsQueue : Array<{loader : URLLoader, request : URLRequest}>; + var loadsQueueMutex : Mutex; + + public static function getInstance () : URLLoadersManager { + if (instance==null) { + instance = new URLLoadersManager (); + } + return instance; + } + + function new () { + activeLoaders = new List (); + loadsQueue = []; + loadsQueueMutex = new Mutex (); + managersThread = Thread.create (mainLoop); + } + + function mainLoop () { + + while (true) { + + loadsQueueMutex.acquire (); + var loadCall = loadsQueue.shift (); + loadsQueueMutex.release (); + if (loadCall!=null) { + loadCall.loader.loadInCURLThread (loadCall.request); + } + + if (!activeLoaders.isEmpty ()) { + lime_curl_process_loaders (); + var oldLoaders = activeLoaders; + activeLoaders = new List (); + for (loader in oldLoaders) { + loader.update (); + if (loader.state == URLLoader.urlLoading) { + activeLoaders.push (loader); + } + } + } + + var msg = Thread.readMessage(false); + if (msg!=null) { + msg = cast (msg, ManagersThreadMessage); + switch (msg) { + case GetCookiesCall (callerThread, handle): { + var cookies : Array = lime_curl_get_cookies (handle); + callerThread.sendMessage (GetCookiesResponse (cookies)); + } + case InitializeCall (caCertFilePath): { + lime_curl_initialize (caCertFilePath); + } + default: {} + } + } + + Sys.sleep(0.1); + + } + + } // mainLoop + + public function enqueueLoad (loader : URLLoader, request : URLRequest) { + loadsQueueMutex.acquire (); + loadsQueue.push ({loader : loader, request : request}); + loadsQueueMutex.release (); + } + + public function activeLoadersIsEmpty() { + return activeLoaders.isEmpty (); + } + + public function getActiveLoaders () : List { + return activeLoaders; + } + + public function create (request : URLRequest) : Dynamic { + return lime_curl_create (request); + } + + public function updateLoader (handle : Dynamic, loader : URLLoader) : Void { + lime_curl_update_loader (handle, loader); + } + + public function getCode (handle : Dynamic) : Int { + return lime_curl_get_code (handle); + } + + public function getErrorMessage (handle : Dynamic) : String { + return lime_curl_get_error_message (handle); + } + + public function getData (handle : Dynamic) : ByteArray { + return lime_curl_get_data (handle); + } + + public function getHeaders (handle : Dynamic) : Array { + return lime_curl_get_headers (handle); + } + + public function initialize (caCertFilePath : String) : Void { + managersThread.sendMessage (InitializeCall (caCertFilePath)); + return; + } + + public function getCookies (handle : Dynamic) : Array { + managersThread.sendMessage (GetCookiesCall (Thread.current(), handle)); + var msg : ManagersThreadMessage = Thread.readMessage(true); + switch (msg) { + case (GetCookiesResponse (result)): { + return result; + } + default: { + return []; + } + } + } + + // Native Methods + private static var lime_curl_create = Lib.load ("lime-legacy", "lime_legacy_curl_create", 1); + private static var lime_curl_process_loaders = Lib.load ("lime-legacy", "lime_legacy_curl_process_loaders", 0); + private static var lime_curl_update_loader = Lib.load ("lime-legacy", "lime_legacy_curl_update_loader", 2); + private static var lime_curl_get_code = Lib.load ("lime-legacy", "lime_legacy_curl_get_code", 1); + private static var lime_curl_get_error_message = Lib.load ("lime-legacy", "lime_legacy_curl_get_error_message", 1); + private static var lime_curl_get_data = Lib.load ("lime-legacy", "lime_legacy_curl_get_data", 1); + private static var lime_curl_get_cookies = Lib.load ("lime-legacy", "lime_legacy_curl_get_cookies", 1); + private static var lime_curl_get_headers = Lib.load ("lime-legacy", "lime_legacy_curl_get_headers", 1); + private static var lime_curl_initialize = Lib.load ("lime-legacy", "lime_legacy_curl_initialize", 1); + +} class URLLoader extends EventDispatcher { - - + + public var bytesLoaded (default, null):Int; public var bytesTotal (default, null):Int; public var data:Dynamic; public var dataFormat:URLLoaderDataFormat; - - @:noCompletion private static var activeLoaders = new List (); + @:noCompletion private static inline var urlInvalid = 0; @:noCompletion private static inline var urlInit = 1; + + @:allow(openfl._legacy.net.URLLoadersManager) @:noCompletion private static inline var urlLoading = 2; + @:noCompletion private static inline var urlComplete = 3; @:noCompletion private static inline var urlError = 4; - + + @:allow(openfl._legacy.net.URLLoadersManager) @:noCompletion private var state:Int; + + @:noCompletion static var eventsQueue : Array<{loader : URLLoader, event : Event}> = []; + @:noCompletion private var __handle:Dynamic; @:noCompletion public var __onComplete:Dynamic -> Bool; - - + + public function new (request:URLRequest = null) { - + super (); - + __handle = 0; bytesLoaded = 0; bytesTotal = -1; state = urlInvalid; dataFormat = URLLoaderDataFormat.TEXT; - + if (request != null) { - + load (request); - + } - + } - - + + public function close ():Void { - - - + + + } - - + + private function dispatchHTTPStatus (code:Int):Void { - + var event = new HTTPStatusEvent (HTTPStatusEvent.HTTP_STATUS, false, false, code); - var headers:Array = lime_curl_get_headers (__handle); - + var headers = URLLoadersManager.getInstance().getHeaders(__handle); + for (header in headers) { - + var index = header.indexOf(": "); if (index > 0) { - + event.responseHeaders.push (new URLRequestHeader (header.substr (0, index), header.substr (index + 2, header.length - index - 4))); - + } - + } - - dispatchEvent (event); - + + enqueueEvent(this, event); + } - - + + public function getCookies ():Array { - - return lime_curl_get_cookies (__handle); - + return URLLoadersManager.getInstance().getCookies (__handle); } - - + + public static function hasActive ():Bool { - - return !activeLoaders.isEmpty (); - + + return !URLLoadersManager.getInstance().activeLoadersIsEmpty (); + } - - + + public static function initialize (caCertFilePath:String):Void { - lime_curl_initialize (caCertFilePath); - + URLLoadersManager.getInstance().initialize (caCertFilePath); + } - - + + public function load (request:URLRequest):Void { - + + URLLoadersManager.getInstance().enqueueLoad(this, request); + + } + + @:allow(openfl._legacy.net.URLLoadersManager) + function loadInCURLThread (request:URLRequest):Void { + state = urlInit; - + var pref = request.url.substr (0, 7); if (pref != "http://" && pref != "https:/") { - + try { - + var bytes = ByteArray.readFile (request.url); - + if (bytes == null) { - + throw ("Could not open file \"" + request.url + "\""); - + } - + switch (dataFormat) { - + case TEXT: data = bytes.asString (); case VARIABLES: data = new URLVariables(bytes.asString()); default: data = bytes; - + } - + } catch (e:Dynamic) { - + onError (e); return; - + } - + __dataComplete (); - + } else { - + request.__prepare (); - __handle = lime_curl_create (request); - + + __handle = URLLoadersManager.getInstance().create(request); + if (__handle == null) { - + onError ("Could not open URL"); - + } else { - - activeLoaders.push (this); - + + URLLoadersManager.getInstance().getActiveLoaders().push (this); + } - + } - + } - - + + private function onError (msg:String):Void { - - activeLoaders.remove (this); - dispatchEvent (new IOErrorEvent (IOErrorEvent.IO_ERROR, true, false, msg)); - + + URLLoadersManager.getInstance().getActiveLoaders().remove (this); + enqueueEvent(this, new IOErrorEvent (IOErrorEvent.IO_ERROR, true, false, msg)); + } - - + + @:allow(openfl._legacy.net.URLLoadersManager) private function update ():Void { - + if (__handle != null) { - + var old_loaded = bytesLoaded; var old_total = bytesTotal; - lime_curl_update_loader (__handle, this); - + URLLoadersManager.getInstance().updateLoader(__handle, this); + if (old_total < 0 && bytesTotal > 0) { - - dispatchEvent (new Event (Event.OPEN)); - + enqueueEvent(this, new Event (Event.OPEN)); } - + if (bytesTotal > 0 && bytesLoaded != old_loaded) { - - dispatchEvent (new ProgressEvent (ProgressEvent.PROGRESS, false, false, bytesLoaded, bytesTotal)); - + var evt = new ProgressEvent (ProgressEvent.PROGRESS, false, false, bytesLoaded, bytesTotal); + enqueueEvent(this, evt); } - - var code:Int = lime_curl_get_code (__handle); - + + var code = URLLoadersManager.getInstance().getCode(__handle); + if (state == urlComplete) { - + dispatchHTTPStatus (code); - - var bytes:ByteArray = lime_curl_get_data (__handle); - + var bytes = URLLoadersManager.getInstance().getData(__handle); switch (dataFormat) { - case TEXT, VARIABLES: data = bytes == null ? "" : bytes.asString (); default: data = bytes; - } - + if (code < 400) { - __dataComplete (); - } else { - var event = new IOErrorEvent (IOErrorEvent.IO_ERROR, true, false, data, code); __handle = null; - dispatchEvent (event); - + enqueueEvent(this, event); } - + } else if (state == urlError) { - dispatchHTTPStatus (code); - - var event = new IOErrorEvent (IOErrorEvent.IO_ERROR, true, false, lime_curl_get_error_message (__handle), code); + var errorMessage = URLLoadersManager.getInstance().getErrorMessage(__handle); + var event = new IOErrorEvent (IOErrorEvent.IO_ERROR, true, false, errorMessage, code); __handle = null; - dispatchEvent (event); - + enqueueEvent(this, event); } - + } - + } - - + + @:noCompletion private function __dataComplete ():Void { - - activeLoaders.remove (this); - + + URLLoadersManager.getInstance().getActiveLoaders().remove (this); + if (__onComplete != null) { - + if (__onComplete (data)) { - - dispatchEvent (new Event (Event.COMPLETE)); - + + enqueueEvent(this, new Event (Event.COMPLETE)); + } else { - + __dispatchIOErrorEvent (); - + } - + } else { - - dispatchEvent (new Event (Event.COMPLETE)); - + + enqueueEvent(this, new Event (Event.COMPLETE)); + } - + } - - + + @:noCompletion public static function __loadPending ():Bool { - - return !activeLoaders.isEmpty (); - + + return !URLLoadersManager.getInstance().activeLoadersIsEmpty(); + + } + + static function enqueueEvent(loader : URLLoader, event : Event) { + eventsQueue.push({loader : loader, event : event}); } - - + @:noCompletion public static function __pollData ():Void { - - if (!activeLoaders.isEmpty ()) { - - lime_curl_process_loaders (); - var oldLoaders = activeLoaders; - activeLoaders = new List (); - - for (loader in oldLoaders) { - - loader.update (); - if (loader.state == urlLoading) { - - activeLoaders.push (loader); - - } - - } - + var evt = eventsQueue.shift(); + if (evt!=null) { + evt.loader.dispatchEvent(evt.event); } - } - - - - - // Native Methods - - - - - private static var lime_curl_create = Lib.load ("lime-legacy", "lime_legacy_curl_create", 1); - private static var lime_curl_process_loaders = Lib.load ("lime-legacy", "lime_legacy_curl_process_loaders", 0); - private static var lime_curl_update_loader = Lib.load ("lime-legacy", "lime_legacy_curl_update_loader", 2); - private static var lime_curl_get_code = Lib.load ("lime-legacy", "lime_legacy_curl_get_code", 1); - private static var lime_curl_get_error_message = Lib.load ("lime-legacy", "lime_legacy_curl_get_error_message", 1); - private static var lime_curl_get_data = Lib.load ("lime-legacy", "lime_legacy_curl_get_data", 1); - private static var lime_curl_get_cookies = Lib.load ("lime-legacy", "lime_legacy_curl_get_cookies", 1); - private static var lime_curl_get_headers = Lib.load ("lime-legacy", "lime_legacy_curl_get_headers", 1); - private static var lime_curl_initialize = Lib.load ("lime-legacy", "lime_legacy_curl_initialize", 1); - + } #else typedef URLLoader = openfl.net.URLLoader; -#end \ No newline at end of file +#end diff --git a/openfl/_legacy/text/TextFormat.hx b/openfl/_legacy/text/TextFormat.hx index bfe7679726..4f514580c7 100644 --- a/openfl/_legacy/text/TextFormat.hx +++ b/openfl/_legacy/text/TextFormat.hx @@ -13,7 +13,7 @@ package openfl._legacy.text; #if openfl_legacy public var font:String; public var indent:Null; public var italic:Null; - public var kerning:Null; + public var kerning:Null; public var leading:Null; public var leftMargin:Null; public var letterSpacing:Null; @@ -47,4 +47,4 @@ package openfl._legacy.text; #if openfl_legacy } -#end \ No newline at end of file +#end diff --git a/openfl/_legacy/utils/SystemPath.hx b/openfl/_legacy/utils/SystemPath.hx index fa425da300..641a91a28d 100644 --- a/openfl/_legacy/utils/SystemPath.hx +++ b/openfl/_legacy/utils/SystemPath.hx @@ -58,7 +58,7 @@ class SystemPath { #if lime_hybrid - switch (which) { + switch (inWhich) { case APP: return System.applicationDirectory; case STORAGE: return System.applicationStorageDirectory; @@ -89,4 +89,4 @@ class SystemPath { } -#end \ No newline at end of file +#end diff --git a/openfl/display/Bitmap.hx b/openfl/display/Bitmap.hx index 74b64ea6c3..66689f2d0a 100644 --- a/openfl/display/Bitmap.hx +++ b/openfl/display/Bitmap.hx @@ -1,6 +1,7 @@ package openfl.display; #if !flash #if !openfl_legacy +import openfl._internal.renderer.cairo.CairoBitmap; import openfl._internal.renderer.canvas.CanvasBitmap; import openfl._internal.renderer.dom.DOMBitmap; import openfl._internal.renderer.opengl.GLBitmap; @@ -9,7 +10,7 @@ import openfl.geom.Matrix; import openfl.geom.Point; import openfl.geom.Rectangle; -#if js +#if (js && html5) import js.html.ImageElement; #end @@ -86,7 +87,7 @@ class Bitmap extends DisplayObjectContainer { */ public var smoothing:Bool; - #if js + #if (js && html5) @:noCompletion private var __image:ImageElement; #end @@ -113,7 +114,7 @@ class Bitmap extends DisplayObjectContainer { if (bitmapData != null) { var bounds = new Rectangle (0, 0, bitmapData.width, bitmapData.height); - bounds = bounds.transform (__worldTransform); + bounds = bounds.transform (matrix); rect.__expand (bounds.x, bounds.y, bounds.width, bounds.height); @@ -145,6 +146,20 @@ class Bitmap extends DisplayObjectContainer { } + @:noCompletion @:dox(hide) public override function __renderCairo (renderSession:RenderSession):Void { + + CairoBitmap.render (this, renderSession); + + } + + + @:noCompletion @:dox(hide) public override function __renderCairoMask (renderSession:RenderSession):Void { + + renderSession.cairo.rectangle (0, 0, width, height); + + } + + @:noCompletion @:dox(hide) public override function __renderCanvas (renderSession:RenderSession):Void { CanvasBitmap.render (this, renderSession); @@ -152,40 +167,42 @@ class Bitmap extends DisplayObjectContainer { } - @:noCompletion @:dox(hide) public override function __renderDOM (renderSession:RenderSession):Void { + @:noCompletion @:dox(hide) public override function __renderCanvasMask (renderSession:RenderSession):Void { - DOMBitmap.render (this, renderSession); + renderSession.context.rect (0, 0, width, height); } - @:noCompletion @:dox(hide) public override function __renderGL (renderSession:RenderSession):Void { + @:noCompletion @:dox(hide) public override function __renderDOM (renderSession:RenderSession):Void { - GLBitmap.render (this, renderSession); + DOMBitmap.render (this, renderSession); } - @:noCompletion @:dox(hide) public override function __renderMask (renderSession:RenderSession):Void { + @:noCompletion @:dox(hide) public override function __renderGL (renderSession:RenderSession):Void { - renderSession.context.rect (0, 0, width, height); + GLBitmap.render (this, renderSession); } @:noCompletion @:dox(hide) public override function __updateMask (maskGraphics:Graphics):Void { - - maskGraphics.__commands.push(OverrideMatrix(this.__worldTransform)); - maskGraphics.beginFill(0); - maskGraphics.drawRect(0, 0, bitmapData.width, bitmapData.height); - + + maskGraphics.__commands.push (OverrideMatrix (this.__worldTransform)); + maskGraphics.beginFill (0); + maskGraphics.drawRect (0, 0, bitmapData.width, bitmapData.height); + if (maskGraphics.__bounds == null) { - maskGraphics.__bounds = new Rectangle(); + + maskGraphics.__bounds = new Rectangle (); + } - __getBounds(maskGraphics.__bounds, @:privateAccess Matrix.__identity); + __getBounds (maskGraphics.__bounds, @:privateAccess Matrix.__identity); - super.__updateMask(maskGraphics); + super.__updateMask (maskGraphics); } diff --git a/openfl/display/BitmapData.hx b/openfl/display/BitmapData.hx index f8d1c8ff35..d78269099d 100644 --- a/openfl/display/BitmapData.hx +++ b/openfl/display/BitmapData.hx @@ -1,6 +1,8 @@ package openfl.display; #if !flash #if !openfl_legacy +import lime.graphics.cairo.CairoSurface; +import lime.graphics.ImageChannel; import lime.graphics.opengl.GLBuffer; import lime.graphics.opengl.GLTexture; import lime.graphics.GLRenderContext; @@ -22,7 +24,7 @@ import openfl.geom.Rectangle; import openfl.utils.ByteArray; import openfl.Vector; -#if js +#if (js && html5) import js.html.CanvasElement; import js.html.CanvasRenderingContext2D; import js.html.ImageData; @@ -94,6 +96,7 @@ import js.Browser; @:access(lime.graphics.ImageBuffer) @:access(lime.math.Rectangle) @:access(openfl.geom.ColorTransform) +@:access(openfl.geom.Matrix) @:access(openfl.geom.Point) @:access(openfl.geom.Rectangle) @@ -133,17 +136,18 @@ class BitmapData implements IBitmapDrawable { @:noCompletion @:dox(hide) public var blendMode:BlendMode; @:noCompletion @:dox(hide) public var __worldTransform:Matrix; @:noCompletion @:dox(hide) public var __worldColorTransform:ColorTransform; + @:noCompletion @:dox(hide) public var __cacheAsBitmap:Bool; @:noCompletion private var __buffer:GLBuffer; @:noCompletion private var __image:Image; @:noCompletion private var __isValid:Bool; + @:noCompletion private var __surface:CairoSurface; + @:noCompletion private var __surfaceImage:Image; @:noCompletion private var __texture:GLTexture; @:noCompletion private var __textureImage:Image; @:noCompletion private var __framebuffer:FilterTexture; @:noCompletion private var __uvData:TextureUvs; - @:noCompletion private var __uvFlipped:Bool = false; - - private var __spritebatch:SpriteBatch; + @:noCompletion private var __usingFramebuffer:Bool = false; /** * Creates a BitmapData object with a specified width and height. If you specify a value for @@ -179,25 +183,31 @@ class BitmapData implements IBitmapDrawable { if (width > 0 && height > 0) { if (transparent) { - - if ((fillColor & 0xFF000000) == 0) { + + if ((fillColor & 0xFF000000) == 0) { + fillColor = 0; + } - - } - else { + + } else { fillColor = (0xFF << 24) | (fillColor & 0xFFFFFF); } + fillColor = (fillColor << 8) | ((fillColor >> 24) & 0xFF); + __image = new Image (null, 0, 0, width, height, fillColor); __image.transparent = transparent; __isValid = true; } - __createUVs (); + __createUVs (); + + __worldTransform = new Matrix(); + __worldColorTransform = new ColorTransform(); } @@ -231,14 +241,14 @@ class BitmapData implements IBitmapDrawable { if (!__isValid || sourceBitmapData == null || !sourceBitmapData.__isValid) return; - #if js + #if (js && html5) ImageCanvasUtil.convertToCanvas (__image); ImageCanvasUtil.createImageData (__image); ImageCanvasUtil.convertToCanvas (sourceBitmapData.__image); ImageCanvasUtil.createImageData (sourceBitmapData.__image); #end - #if js + #if (js && html5) filter.__applyFilter (__image.buffer.__srcImageData, sourceBitmapData.__image.buffer.__srcImageData, sourceRect, destPoint); #end @@ -278,6 +288,7 @@ class BitmapData implements IBitmapDrawable { if (!__isValid) return; __image.colorTransform (rect.__toLimeRectangle (), colorTransform.__toLimeColorMatrix ()); + __usingFramebuffer = false; } @@ -350,6 +361,7 @@ class BitmapData implements IBitmapDrawable { } __image.copyChannel (sourceBitmapData.__image, sourceRect.__toLimeRectangle (), destPoint.__toLimeVector2 (), sourceChannel, destChannel); + __usingFramebuffer = false; } @@ -398,7 +410,7 @@ class BitmapData implements IBitmapDrawable { if (!__isValid || sourceBitmapData == null) return; __image.copyPixels (sourceBitmapData.__image, sourceRect.__toLimeRectangle (), destPoint.__toLimeVector2 (), alphaBitmapData != null ? alphaBitmapData.__image : null, alphaPoint != null ? alphaPoint.__toLimeVector2 () : null, mergeAlpha); - + __usingFramebuffer = false; } @@ -430,6 +442,24 @@ class BitmapData implements IBitmapDrawable { rect = null; __isValid = false; + if (__texture != null) { + var renderer = @:privateAccess Lib.current.stage.__renderer; + if(renderer != null) { + var renderSession = @:privateAccess renderer.renderSession; + var gl = renderSession.gl; + if (gl != null) { + gl.deleteTexture(__texture); + } + } + + } + + if (__framebuffer != null) { + + __framebuffer.destroy(); + + } + } @@ -516,7 +546,7 @@ class BitmapData implements IBitmapDrawable { ImageCanvasUtil.convertToCanvas (__image); ImageCanvasUtil.sync (__image); - #if js + #if (js && html5) var buffer = __image.buffer; var renderSession = new RenderSession (); @@ -551,89 +581,9 @@ class BitmapData implements IBitmapDrawable { case DATA: - var renderSession = @:privateAccess Lib.current.stage.__renderer.renderSession; - var gl:GLRenderContext = renderSession.gl; - if (gl == null) return; - - - var mainSpritebatch = renderSession.spriteBatch; - var mainProjection = renderSession.projection; - var renderTransparent = renderSession.renderer.transparent; - - if (clipRect == null) { - clipRect = new Rectangle(0, 0, width, height); - } - var tmpRect = clipRect.clone(); - // Flip Y - tmpRect.y = height - tmpRect.bottom; - - var drawSelf = false; - if (__spritebatch == null) { - __spritebatch = new SpriteBatch(gl); - drawSelf = true; - } - - renderSession.spriteBatch = __spritebatch; - renderSession.projection = new Point((width / 2), -(height / 2)); - renderSession.renderer.transparent = transparent; - - if (__framebuffer == null) { - __framebuffer = new FilterTexture(gl, width, height, smoothing); - } - - __framebuffer.resize(width, height); - gl.bindFramebuffer(gl.FRAMEBUFFER, __framebuffer.frameBuffer); - - gl.viewport (0, 0, width, height); - - __spritebatch.begin(renderSession, drawSelf ? null : tmpRect); - - // enable writing to all the colors and alpha - gl.colorMask(true, true, true, true); - renderSession.blendModeManager.setBlendMode(BlendMode.NORMAL); - - if (drawSelf) { - __framebuffer.clear(); - this.__renderGL(renderSession); - __spritebatch.stop(); - // TODO remove the bitmap texture from vram when done? - __spritebatch.start(tmpRect); - } - - var ctCache = source.__worldColorTransform; - var matrixCache = source.__worldTransform; - var blendModeCache = source.blendMode; - - source.__worldTransform = matrix != null ? matrix : new Matrix (); - source.__worldColorTransform = colorTransform != null ? colorTransform : new ColorTransform(); - source.blendMode = blendMode; - source.__updateChildren (false); - - source.__renderGL (renderSession); - - source.__worldColorTransform = ctCache; - source.__worldTransform = matrixCache; - source.blendMode = blendModeCache; - source.__updateChildren (true); - - __spritebatch.finish(); - - gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, __image.buffer.data); - - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - gl.viewport(0, 0, renderSession.renderer.width, renderSession.renderer.height); - - renderSession.spriteBatch = mainSpritebatch; - renderSession.projection = mainProjection; - renderSession.renderer.transparent = renderTransparent; - - gl.colorMask(true, true, true, renderSession.renderer.transparent); - - __texture = __framebuffer.texture; - __image.dirty = false; - __createUVs(true); + var renderSession = @:privateAccess Lib.current.stage.__renderer.renderSession; + __drawGL(renderSession, width, height, source, matrix, colorTransform, blendMode, clipRect, smoothing, !__usingFramebuffer, false, true); default: @@ -678,7 +628,8 @@ class BitmapData implements IBitmapDrawable { public function fillRect (rect:Rectangle, color:Int):Void { if (!__isValid || rect == null) return; - __image.fillRect (rect.__toLimeRectangle (), color); + __image.fillRect (rect.__toLimeRectangle (), color, ARGB); + __usingFramebuffer = false; } @@ -697,7 +648,8 @@ class BitmapData implements IBitmapDrawable { public function floodFill (x:Int, y:Int, color:Int):Void { if (!__isValid) return; - __image.floodFill (x, y, color); + __image.floodFill (x, y, color, ARGB); + __usingFramebuffer = false; } @@ -720,7 +672,7 @@ class BitmapData implements IBitmapDrawable { } - #if js + #if (js && html5) public static function fromCanvas (canvas:CanvasElement, transparent:Bool = true):BitmapData { var bitmapData = new BitmapData (0, 0, transparent); @@ -847,7 +799,8 @@ class BitmapData implements IBitmapDrawable { public function getColorBoundsRect (mask:Int, color:Int, findColor:Bool = true):Rectangle { if (!__isValid) return new Rectangle (0, 0, width, height); - return __image.rect.__toFlashRectangle (); + var rect = __image.getColorBoundsRect (mask, color, findColor); + return new Rectangle(rect.x, rect.y, rect.width, rect.height); } @@ -878,7 +831,7 @@ class BitmapData implements IBitmapDrawable { public function getPixel (x:Int, y:Int):Int { if (!__isValid) return 0; - return __image.getPixel (x, y); + return __image.getPixel (x, y, ARGB); } @@ -908,7 +861,7 @@ class BitmapData implements IBitmapDrawable { public function getPixel32 (x:Int, y:Int):Int { if (!__isValid) return 0; - return __image.getPixel32 (x, y); + return __image.getPixel32 (x, y, ARGB); } @@ -926,7 +879,38 @@ class BitmapData implements IBitmapDrawable { if (!__isValid) return null; if (rect == null) rect = this.rect; - return __image.getPixels (rect.__toLimeRectangle ()); + return __image.getPixels (rect.__toLimeRectangle (), ARGB); + + } + + + public function getSurface ():CairoSurface { + + if (!__isValid) return null; + + if (__surface == null) { + + __image.dirty = true; + + } + + if (__image != null && __image.dirty) { + + if (__surface != null) { + + __surface.destroy (); + + } + + __surfaceImage = __image.clone (); + __surfaceImage.format = BGRA; + __surfaceImage.premultiplied = true; + __surface = CairoSurface.fromImage (__surfaceImage); + __image.dirty = false; + + } + + return __surface; } @@ -935,19 +919,23 @@ class BitmapData implements IBitmapDrawable { if (!__isValid) return null; + if (__usingFramebuffer && __framebuffer != null) { + return __framebuffer.texture; + } + if (__texture == null) { __texture = gl.createTexture (); gl.bindTexture (gl.TEXTURE_2D, __texture); gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); __image.dirty = true; } - if (__image.dirty) { + if (__image != null && __image.dirty) { var format = (__image.buffer.bitsPerPixel == 1 ? gl.ALPHA : gl.RGBA); gl.bindTexture (gl.TEXTURE_2D, __texture); @@ -1037,6 +1025,7 @@ class BitmapData implements IBitmapDrawable { if (!__isValid || sourceBitmapData == null || !sourceBitmapData.__isValid || sourceRect == null || destPoint == null) return; __image.merge (sourceBitmapData.__image, sourceRect.__toLimeRectangle (), destPoint.__toLimeVector2 (), redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier); + __usingFramebuffer = false; } @@ -1230,7 +1219,8 @@ class BitmapData implements IBitmapDrawable { public function setPixel (x:Int, y:Int, color:Int):Void { if (!__isValid) return; - __image.setPixel (x, y, color); + __image.setPixel (x, y, color, ARGB); + __usingFramebuffer = false; } @@ -1269,7 +1259,8 @@ class BitmapData implements IBitmapDrawable { public function setPixel32 (x:Int, y:Int, color:Int):Void { if (!__isValid) return; - __image.setPixel32 (x, y, color); + __image.setPixel32 (x, y, color, ARGB); + __usingFramebuffer = false; } @@ -1296,7 +1287,8 @@ class BitmapData implements IBitmapDrawable { public function setPixels (rect:Rectangle, byteArray:ByteArray):Void { if (!__isValid || rect == null) return; - __image.setPixels (rect.__toLimeRectangle (), byteArray); + __image.setPixels (rect.__toLimeRectangle (), byteArray, ARGB); + __usingFramebuffer = false; } @@ -1553,32 +1545,148 @@ class BitmapData implements IBitmapDrawable { } - @:noCompletion private function __createUVs (?verticalFlip:Bool = false):Void { + @:noCompletion private function __createUVs ():Void { if (__uvData == null) __uvData = new TextureUvs(); - __uvFlipped = verticalFlip; - - if (verticalFlip) { - __uvData.x0 = 0; - __uvData.y0 = 1; - __uvData.x1 = 1; - __uvData.y1 = 1; - __uvData.x2 = 1; - __uvData.y2 = 0; - __uvData.x3 = 0; - __uvData.y3 = 0; - } else { - __uvData.x0 = 0; - __uvData.y0 = 0; - __uvData.x1 = 1; - __uvData.y1 = 0; - __uvData.x2 = 1; - __uvData.y2 = 1; - __uvData.x3 = 0; - __uvData.y3 = 1; + __uvData.x0 = 0; + __uvData.y0 = 0; + __uvData.x1 = 1; + __uvData.y1 = 0; + __uvData.x2 = 1; + __uvData.y2 = 1; + __uvData.x3 = 0; + __uvData.y3 = 1; + + } + + + @:noCompletion @:dox(hide) public function __drawGL (renderSession:RenderSession, width:Int, height:Int, source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:BlendMode = null, clipRect:Rectangle = null, smoothing:Bool = false, drawSelf:Bool = false, clearBuffer:Bool = false, readPixels:Bool = false):Void { + + var renderer = @:privateAccess Lib.current.stage.__renderer; + if (renderer == null) return; + + var renderSession = @:privateAccess renderer.renderSession; + var gl:GLRenderContext = renderSession.gl; + if (gl == null) return; + + var spritebatch = renderSession.spriteBatch; + var renderTransparent = renderSession.renderer.transparent; + + var tmpRect = clipRect == null ? new Rectangle (0, 0, width, height) : clipRect.clone (); + + renderSession.renderer.transparent = transparent; + + if (__framebuffer == null) { + + __framebuffer = new FilterTexture (gl, width, height, smoothing); + + } + + __framebuffer.resize (width, height); + gl.bindFramebuffer (gl.FRAMEBUFFER, __framebuffer.frameBuffer); + + renderer.setViewport (0, 0, width, height); + + spritebatch.begin (renderSession, drawSelf ? null : tmpRect); + + // enable writing to all the colors and alpha + gl.colorMask (true, true, true, true); + renderSession.blendModeManager.setBlendMode (BlendMode.NORMAL); + + renderSession.shaderManager.setShader (renderSession.shaderManager.defaultShader, true); + + if (clearBuffer || drawSelf) { + + __framebuffer.clear (); + + } + + if (drawSelf) { + + __worldTransform.identity (); + __flipMatrix (__worldTransform); + this.__renderGL (renderSession); + spritebatch.stop (); + gl.deleteTexture (__texture); + spritebatch.start (tmpRect); + } + var ctCache = source.__worldColorTransform; + var matrixCache = source.__worldTransform; + var blendModeCache = source.blendMode; + var cached = source.__cacheAsBitmap; + + var m = matrix != null ? matrix.clone () : new Matrix (); + + __flipMatrix (m); + + source.__worldTransform = m; + source.__worldColorTransform = colorTransform != null ? colorTransform : new ColorTransform (); + source.blendMode = blendMode; + source.__cacheAsBitmap = false; + + source.__updateChildren (false); + + source.__renderGL (renderSession); + + source.__worldColorTransform = ctCache; + source.__worldTransform = matrixCache; + source.blendMode = blendModeCache; + source.__cacheAsBitmap = cached; + + source.__updateChildren (true); + + spritebatch.finish (); + + if (readPixels) { + + // TODO is this possible? + if (__image.width != width || __image.height != height) { + + __image.resize (width, height); + + } + + gl.readPixels (0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, __image.buffer.data); + + } + + gl.bindFramebuffer (gl.FRAMEBUFFER, renderSession.defaultFramebuffer); + + renderer.setViewport (0, 0, renderSession.renderer.width, renderSession.renderer.height); + + renderSession.renderer.transparent = renderTransparent; + + gl.colorMask (true, true, true, renderSession.renderer.transparent); + + __usingFramebuffer = true; + + if (__image != null) { + + __image.dirty = false; + __image.premultiplied = true; + + } + + __createUVs (); + __isValid = true; + + } + + + @:noCompletion @:dox(hide) private inline function __flipMatrix (m:Matrix):Void { + + var tx = m.tx; + var ty = m.ty; + m.tx = 0; + m.ty = 0; + m.scale (1, -1); + m.translate (0, height); + m.tx += tx; + m.ty -= ty; + } @@ -1614,7 +1722,7 @@ class BitmapData implements IBitmapDrawable { if (rawAlpha != null) { - #if js + #if (js && html5) ImageCanvasUtil.convertToCanvas (__image); ImageCanvasUtil.createImageData (__image); #end @@ -1671,9 +1779,54 @@ class BitmapData implements IBitmapDrawable { } + @:noCompletion @:dox(hide) public function __renderCairo (renderSession:RenderSession):Void { + + if (!__isValid) return; + + var cairo = renderSession.cairo; + + if (__worldTransform == null) __worldTransform = new Matrix (); + + //context.globalAlpha = 1; + var transform = __worldTransform; + + if (renderSession.roundPixels) { + + var matrix = transform.__toMatrix3 (); + matrix.tx = Math.round (matrix.tx); + matrix.ty = Math.round (matrix.ty); + cairo.matrix = matrix; + //context.setTransform (transform.a, transform.b, transform.c, transform.d, Std.int (transform.tx), Std.int (transform.ty)); + + } else { + + cairo.matrix = transform.__toMatrix3 (); + //context.setTransform (transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + + } + + var surface = getSurface (); + + if (surface != null) { + + cairo.setSourceSurface (surface, 0, 0); + cairo.paint (); + + } + + } + + + @:noCompletion @:dox(hide) public function __renderCairoMask (renderSession:RenderSession):Void { + + + + } + + @:noCompletion @:dox(hide) public function __renderCanvas (renderSession:RenderSession):Void { - #if js + #if (js && html5) if (!__isValid) return; ImageCanvasUtil.sync (__image); @@ -1701,32 +1854,23 @@ class BitmapData implements IBitmapDrawable { } - @:noCompletion @:dox(hide) public function __renderGL (renderSession:RenderSession):Void { - - if (__worldTransform == null) __worldTransform = new Matrix(); - if (__worldColorTransform == null) __worldColorTransform = new ColorTransform(); - renderSession.spriteBatch.renderBitmapData(this, true, __worldTransform, __worldColorTransform, __worldColorTransform.alphaMultiplier, blendMode); - - } - - - @:noCompletion @:dox(hide) public function __renderMask (renderSession:RenderSession):Void { + @:noCompletion @:dox(hide) public function __renderCanvasMask (renderSession:RenderSession):Void { } - @:noCompletion @:dox(hide) public function __updateMask (maskGraphics:Graphics):Void { - + @:noCompletion @:dox(hide) public function __renderGL (renderSession:RenderSession):Void { + renderSession.spriteBatch.renderBitmapData (this, false, __worldTransform, __worldColorTransform, __worldColorTransform.alphaMultiplier, blendMode); } @:noCompletion private function __sync ():Void { - #if js + #if (js && html5) ImageCanvasUtil.sync (__image); #end @@ -1791,6 +1935,13 @@ class BitmapData implements IBitmapDrawable { + } + + + @:noCompletion @:dox(hide) public function __updateMask (maskGraphics:Graphics):Void { + + + } diff --git a/openfl/display/DisplayObject.hx b/openfl/display/DisplayObject.hx index 3dbb2b50b9..b9bf64cd78 100644 --- a/openfl/display/DisplayObject.hx +++ b/openfl/display/DisplayObject.hx @@ -1,6 +1,9 @@ package openfl.display; #if !flash #if !openfl_legacy +import lime.ui.MouseCursor; +import openfl._internal.renderer.cairo.CairoGraphics; +import openfl._internal.renderer.cairo.CairoShape; import openfl._internal.renderer.canvas.CanvasGraphics; import openfl._internal.renderer.canvas.CanvasShape; import openfl._internal.renderer.dom.DOMShape; @@ -19,7 +22,7 @@ import openfl.geom.Rectangle; import openfl.geom.Transform; import openfl.Lib; -#if js +#if (js && html5) import js.html.CanvasElement; import js.html.CanvasRenderingContext2D; import js.html.CSSStyleDeclaration; @@ -740,8 +743,9 @@ class DisplayObject extends EventDispatcher implements IBitmapDrawable { @:noCompletion private var __worldZ:Int; @:noCompletion private var __x:Float; @:noCompletion private var __y:Float; + @:noCompletion private var __cacheAsBitmap:Bool = false; - #if js + #if (js && html5) @:noCompletion private var __canvas:CanvasElement; @:noCompletion private var __context:CanvasRenderingContext2D; @:noCompletion private var __style:CSSStyleDeclaration; @@ -937,7 +941,7 @@ class DisplayObject extends EventDispatcher implements IBitmapDrawable { if (parent != null) { var bounds = new Rectangle (); - __getBounds (bounds, null); + __getBounds (bounds, __getTransform ()); return bounds.containsPoint (new Point (x, y)); @@ -1003,16 +1007,23 @@ class DisplayObject extends EventDispatcher implements IBitmapDrawable { if (__graphics != null) { - __graphics.__getBounds (rect, matrix != null ? matrix : __worldTransform); + __graphics.__getBounds (rect, matrix); } } - @:noCompletion private function __getInteractive (stack:Array):Void { + @:noCompletion private function __getCursor ():MouseCursor { + return null; + + } + + + @:noCompletion private function __getInteractive (stack:Array):Bool { + return false; } @@ -1095,6 +1106,28 @@ class DisplayObject extends EventDispatcher implements IBitmapDrawable { } + @:noCompletion @:dox(hide) public function __renderCairo (renderSession:RenderSession):Void { + + if (__graphics != null) { + + CairoShape.render (this, renderSession); + + } + + } + + + @:noCompletion @:dox(hide) public function __renderCairoMask (renderSession:RenderSession):Void { + + if (__graphics != null) { + + CairoGraphics.renderMask (__graphics, renderSession); + + } + + } + + @:noCompletion @:dox(hide) public function __renderCanvas (renderSession:RenderSession):Void { if (__graphics != null) { @@ -1106,35 +1139,35 @@ class DisplayObject extends EventDispatcher implements IBitmapDrawable { } - @:noCompletion @:dox(hide) public function __renderDOM (renderSession:RenderSession):Void { + @:noCompletion @:dox(hide) public function __renderCanvasMask (renderSession:RenderSession):Void { if (__graphics != null) { - DOMShape.render (this, renderSession); + CanvasGraphics.renderMask (__graphics, renderSession); } } - @:noCompletion @:dox(hide) public function __renderGL (renderSession:RenderSession):Void { - - if (!__renderable || __worldAlpha <= 0) return; + @:noCompletion @:dox(hide) public function __renderDOM (renderSession:RenderSession):Void { if (__graphics != null) { - GraphicsRenderer.render (this, renderSession); + DOMShape.render (this, renderSession); } } - @:noCompletion @:dox(hide) public function __renderMask (renderSession:RenderSession):Void { + @:noCompletion @:dox(hide) public function __renderGL (renderSession:RenderSession):Void { + + if (!__renderable || __worldAlpha <= 0) return; if (__graphics != null) { - CanvasGraphics.renderMask (__graphics, renderSession); + GraphicsRenderer.render (this, renderSession); } @@ -1394,15 +1427,18 @@ class DisplayObject extends EventDispatcher implements IBitmapDrawable { if (__graphics != null) { - maskGraphics.__commands.push(OverrideMatrix(this.__worldTransform)); - maskGraphics.__commands = maskGraphics.__commands.concat(__graphics.__commands); + maskGraphics.__commands.push (OverrideMatrix (this.__worldTransform)); + maskGraphics.__commands = maskGraphics.__commands.concat (__graphics.__commands); maskGraphics.__dirty = true; maskGraphics.__visible = true; + if (maskGraphics.__bounds == null) { + maskGraphics.__bounds = new Rectangle(); + } - __graphics.__getBounds(maskGraphics.__bounds, @:privateAccess Matrix.__identity); + __graphics.__getBounds (maskGraphics.__bounds, @:privateAccess Matrix.__identity); } diff --git a/openfl/display/DisplayObjectContainer.hx b/openfl/display/DisplayObjectContainer.hx index ce9eb13519..f1f357a061 100644 --- a/openfl/display/DisplayObjectContainer.hx +++ b/openfl/display/DisplayObjectContainer.hx @@ -1,6 +1,7 @@ package openfl.display; #if !flash #if !openfl_legacy +import openfl._internal.renderer.cairo.CairoGraphics; import openfl._internal.renderer.canvas.CanvasGraphics; import openfl._internal.renderer.RenderSession; import openfl.display.Stage; @@ -287,21 +288,13 @@ class DisplayObjectContainer extends InteractiveObject { */ public function contains (child:DisplayObject):Bool { - #if (haxe_ver > 3.100) - - return __children.indexOf (child) > -1; - - #else - - for (i in __children) { + while (child != this && child != null) { - if (i == child) return true; + child = child.parent; } - return false; - - #end + return child == this; } @@ -642,7 +635,9 @@ class DisplayObjectContainer extends InteractiveObject { } - if (notifyChilden) { + var result = super.__broadcast (event, notifyChilden); + + if (!event.__isCancelled && notifyChilden) { for (child in __children) { @@ -658,14 +653,14 @@ class DisplayObjectContainer extends InteractiveObject { } - return super.__broadcast (event, notifyChilden); + return result; } @:noCompletion private override function __getBounds (rect:Rectangle, matrix:Matrix):Void { - super.__getBounds(rect, matrix); + super.__getBounds (rect, matrix); if (__children.length == 0) return; @@ -682,7 +677,7 @@ class DisplayObjectContainer extends InteractiveObject { for (child in __children) { if (!child.__renderable) continue; - child.__getBounds (rect, null); + child.__getBounds (rect, child.__worldTransform); } @@ -726,18 +721,38 @@ class DisplayObjectContainer extends InteractiveObject { var length = stack.length; + var interactive = false; + var hitTest = false; + while (--i >= 0) { - if (__children[i].__hitTest (x, y, shapeFlag, stack, interactiveOnly)) { - - stack.insert (length, this); + interactive = __children[i].__getInteractive (null); + + if (interactive || !hitTest) { - return true; + if (__children[i].__hitTest (x, y, shapeFlag, stack, true)) { + + hitTest = true; + + if (interactive) { + + break; + + } + + } } } + if (hitTest) { + + stack.insert (length, this); + return true; + + } + } } else { @@ -756,6 +771,69 @@ class DisplayObjectContainer extends InteractiveObject { } + @:noCompletion @:dox(hide) public override function __renderCairo (renderSession:RenderSession):Void { + + if (!__renderable || __worldAlpha <= 0) return; + + super.__renderCairo (renderSession); + + if (scrollRect != null) { + + renderSession.maskManager.pushRect (scrollRect, __worldTransform); + + } + + if (__mask != null) { + + renderSession.maskManager.pushMask (__mask); + + } + + for (child in __children) { + + child.__renderCairo (renderSession); + + } + + __removedChildren = []; + + if (__mask != null) { + + renderSession.maskManager.popMask (); + + } + + if (scrollRect != null) { + + renderSession.maskManager.popMask (); + + } + + } + + + @:noCompletion @:dox(hide) public override function __renderCairoMask (renderSession:RenderSession):Void { + + if (__graphics != null) { + + CairoGraphics.renderMask (__graphics, renderSession); + + } + + //var bounds = new Rectangle (); + //__getLocalBounds (bounds); + // + //renderSession.cairo.rectangle (0, 0, bounds.width, bounds.height); + + for (child in __children) { + + child.__renderCairoMask (renderSession); + + } + + } + + @:noCompletion @:dox(hide) public override function __renderCanvas (renderSession:RenderSession):Void { if (!__renderable || __worldAlpha <= 0) return; @@ -766,7 +844,7 @@ class DisplayObjectContainer extends InteractiveObject { if (scrollRect != null) { - //renderSession.maskManager.pushRect (scrollRect, __worldTransform); + renderSession.maskManager.pushRect (scrollRect, __worldTransform); } @@ -792,7 +870,7 @@ class DisplayObjectContainer extends InteractiveObject { if (scrollRect != null) { - //renderSession.maskManager.popMask (); + renderSession.maskManager.popMask (); } @@ -801,6 +879,28 @@ class DisplayObjectContainer extends InteractiveObject { } + @:noCompletion @:dox(hide) public override function __renderCanvasMask (renderSession:RenderSession):Void { + + if (__graphics != null) { + + CanvasGraphics.renderMask (__graphics, renderSession); + + } + + var bounds = new Rectangle (); + __getLocalBounds (bounds); + + renderSession.context.rect (0, 0, bounds.width, bounds.height); + + /*for (child in __children) { + + child.__renderMask (renderSession); + + }*/ + + } + + @:noCompletion @:dox(hide) public override function __renderDOM (renderSession:RenderSession):Void { #if !neko @@ -855,7 +955,7 @@ class DisplayObjectContainer extends InteractiveObject { if (masked) { renderSession.spriteBatch.stop (); - renderSession.maskManager.pushMask (this, renderSession); + renderSession.maskManager.pushMask (this); renderSession.spriteBatch.start (); } @@ -868,35 +968,16 @@ class DisplayObjectContainer extends InteractiveObject { } - if(masked) { - renderSession.spriteBatch.stop(); - renderSession.maskManager.popMask(this, renderSession); - renderSession.spriteBatch.start(); - } - - __removedChildren = []; - - } - - - @:noCompletion @:dox(hide) public override function __renderMask (renderSession:RenderSession):Void { - - if (__graphics != null) { + if (masked) { - CanvasGraphics.renderMask (__graphics, renderSession); + renderSession.spriteBatch.stop (); + //renderSession.maskManager.popMask (this); + renderSession.maskManager.popMask (); + renderSession.spriteBatch.start (); } - var bounds = new Rectangle (); - __getLocalBounds (bounds); - - renderSession.context.rect (0, 0, bounds.width, bounds.height); - - /*for (child in __children) { - - child.__renderMask (renderSession); - - }*/ + __removedChildren = []; } @@ -995,4 +1076,4 @@ typedef DisplayObjectContainer = openfl._legacy.display.DisplayObjectContainer; #end #else typedef DisplayObjectContainer = flash.display.DisplayObjectContainer; -#end \ No newline at end of file +#end diff --git a/openfl/display/Graphics.hx b/openfl/display/Graphics.hx index f74fb86398..f6321fac9a 100644 --- a/openfl/display/Graphics.hx +++ b/openfl/display/Graphics.hx @@ -1,6 +1,7 @@ package openfl.display; #if !flash #if !openfl_legacy +import lime.graphics.cairo.Cairo; import openfl._internal.renderer.opengl.utils.FilterTexture; import openfl.errors.ArgumentError; import openfl._internal.renderer.opengl.utils.GraphicsRenderer; @@ -12,7 +13,7 @@ import openfl.geom.Point; import openfl.geom.Rectangle; import openfl.Vector; -#if js +#if (js && html5) import js.html.CanvasElement; import js.html.CanvasRenderingContext2D; #end @@ -46,6 +47,7 @@ class Graphics { public static inline var TILE_BLEND_ADD = 0x00010000; @:noCompletion private var __bounds:Rectangle; + @:noCompletion private var __cairo:Cairo; @:noCompletion private var __commands:Array = []; @:noCompletion private var __dirty(default, set):Bool = true; @:noCompletion private var __glStack:Array = []; @@ -58,12 +60,13 @@ class Graphics { @:noCompletion private var __cachedTexture:FilterTexture; @:noCompletion private var __owner:DisplayObject; - #if js + #if (js && html5) @:noCompletion private var __canvas:CanvasElement; @:noCompletion private var __context:CanvasRenderingContext2D; #end + public function new () { __commands = new Array (); @@ -71,7 +74,7 @@ class Graphics { __positionX = 0; __positionY = 0; - #if js + #if (js && html5) moveTo( 0, 0); #end } @@ -249,7 +252,7 @@ class Graphics { __visible = false; - #if js + #if (js && html5) moveTo( 0, 0); #end } @@ -706,7 +709,7 @@ class Graphics { */ public function lineBitmapStyle (bitmap:BitmapData, matrix:Matrix = null, repeat:Bool = true, smooth:Bool = false):Void { - openfl.Lib.notImplemented ("Graphics.lineBitmapStyle"); + __commands.push (LineBitmapStyle (bitmap, matrix != null ? matrix.clone () : null, repeat, smooth)); } @@ -765,7 +768,7 @@ class Graphics { */ public function lineGradientStyle (type:GradientType, colors:Array, alphas:Array, ratios:Array, matrix:Matrix = null, spreadMethod:SpreadMethod = null, interpolationMethod:InterpolationMethod = null, focalPointRatio:Null = null):Void { - openfl.Lib.notImplemented ("Graphics.lineGradientStyle"); + __commands.push (LineGradientStyle (type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio)); } @@ -911,7 +914,7 @@ class Graphics { */ public function lineStyle (thickness:Null = null, color:Null = null, alpha:Null = null, pixelHinting:Null = null, scaleMode:LineScaleMode = null, caps:CapsStyle = null, joints:JointStyle = null, miterLimit:Null = null):Void { - __halfStrokeWidth = (thickness != null) ? thickness / 2 : 0; + __halfStrokeWidth = thickness > __halfStrokeWidth ? thickness/2 : __halfStrokeWidth; __commands.push (LineStyle (thickness, color, alpha, pixelHinting, scaleMode, caps, joints, miterLimit)); if (thickness != null) __visible = true; @@ -979,7 +982,7 @@ class Graphics { if (__bounds == null) return; - var bounds = __bounds.clone ().transform (matrix); + var bounds = __bounds.transform (matrix); rect.__expand (bounds.x, bounds.y, bounds.width, bounds.height); } @@ -991,7 +994,7 @@ class Graphics { if (__bounds == null) return false; - var bounds = __bounds.clone ().transform (matrix); + var bounds = __bounds.transform (matrix); return (x > bounds.x && y > bounds.y && x <= bounds.right && y <= bounds.bottom); } @@ -1064,6 +1067,8 @@ class Graphics { DrawTriangles (vertices:Vector, indices:Vector, uvtData:Vector, culling:TriangleCulling, colors:Vector, blendMode:Int); EndFill; LineStyle (thickness:Null, color:Null, alpha:Null, pixelHinting:Null, scaleMode:LineScaleMode, caps:CapsStyle, joints:JointStyle, miterLimit:Null); + LineBitmapStyle (bitmap:BitmapData, matrix:Matrix, repeat:Bool, smooth:Bool); + LineGradientStyle (type:GradientType, colors:Array, alphas:Array, ratios:Array, matrix:Matrix, spreadMethod:Null, interpolationMethod:Null, focalPointRatio:Null); LineTo (x:Float, y:Float); MoveTo (x:Float, y:Float); DrawPathC(commands:Vector, data:Vector, winding:GraphicsPathWinding); diff --git a/openfl/display/IBitmapDrawable.hx b/openfl/display/IBitmapDrawable.hx index a999882f5a..81bcfbc0e2 100644 --- a/openfl/display/IBitmapDrawable.hx +++ b/openfl/display/IBitmapDrawable.hx @@ -12,9 +12,13 @@ interface IBitmapDrawable { var __worldColorTransform:ColorTransform; var blendMode:BlendMode; + private var __cacheAsBitmap:Bool; + + function __renderCairo (renderSession:RenderSession):Void; + function __renderCairoMask (renderSession:RenderSession):Void; function __renderCanvas (renderSession:RenderSession):Void; + function __renderCanvasMask (renderSession:RenderSession):Void; function __renderGL (renderSession:RenderSession):Void; - function __renderMask (renderSession:RenderSession):Void; function __updateChildren (transformOnly:Bool):Void; function __updateMask (maskGraphics:Graphics):Void; diff --git a/openfl/display/InteractiveObject.hx b/openfl/display/InteractiveObject.hx index 855142b3cd..30089d4bbf 100644 --- a/openfl/display/InteractiveObject.hx +++ b/openfl/display/InteractiveObject.hx @@ -1161,16 +1161,22 @@ class InteractiveObject extends DisplayObject { } - @:noCompletion private override function __getInteractive (stack:Array):Void { + @:noCompletion private override function __getInteractive (stack:Array):Bool { - stack.push (this); - - if (parent != null) { + if (stack != null) { + + stack.push (this); - parent.__getInteractive (stack); + if (parent != null) { + + parent.__getInteractive (stack); + + } } + return true; + } diff --git a/openfl/display/OpenGLView.hx b/openfl/display/OpenGLView.hx index 31185ca587..9ef2c09e67 100644 --- a/openfl/display/OpenGLView.hx +++ b/openfl/display/OpenGLView.hx @@ -10,7 +10,7 @@ import openfl.geom.Rectangle; import openfl.gl.GL; import openfl.Lib; -#if js +#if (js && html5) import js.html.CanvasElement; import js.Browser; #end @@ -92,7 +92,7 @@ class OpenGLView extends DirectRenderer { #if !flash @:noCompletion public override function __renderDOM (renderSession:RenderSession):Void { - #if js + #if (js && html5) if (stage != null && __worldVisible && __renderable) { if (!__added) { @@ -184,6 +184,8 @@ class OpenGLView extends DirectRenderer { if (__render != null) __render (rect); + renderSession.shaderManager.setShader(null); + renderSession.blendModeManager.setBlendMode(null); } } @@ -240,4 +242,4 @@ class OpenGLView extends DirectRenderer { #else typedef OpenGLView = openfl._legacy.display.OpenGLView; -#end \ No newline at end of file +#end diff --git a/openfl/display/SimpleButton.hx b/openfl/display/SimpleButton.hx index ea4d491cfe..ecd96a5f6b 100644 --- a/openfl/display/SimpleButton.hx +++ b/openfl/display/SimpleButton.hx @@ -1,6 +1,7 @@ package openfl.display; #if !flash #if !openfl_legacy +import lime.ui.MouseCursor; import openfl.display.DisplayObject; import openfl.display.InteractiveObject; import openfl.events.MouseEvent; @@ -163,6 +164,13 @@ class SimpleButton extends DisplayObjectContainer { } + @:noCompletion private override function __getCursor ():MouseCursor { + + return useHandCursor ? POINTER : null; + + } + + // Getters & Setters diff --git a/openfl/display/Sprite.hx b/openfl/display/Sprite.hx index d1e39f818b..da51ab9c74 100644 --- a/openfl/display/Sprite.hx +++ b/openfl/display/Sprite.hx @@ -1,6 +1,7 @@ package openfl.display; #if !flash #if !openfl_legacy +import lime.ui.MouseCursor; import openfl._internal.renderer.canvas.CanvasGraphics; import openfl._internal.renderer.canvas.CanvasShape; import openfl._internal.renderer.dom.DOMShape; @@ -159,6 +160,13 @@ class Sprite extends DisplayObjectContainer { } + @:noCompletion private override function __getCursor ():MouseCursor { + + return (buttonMode && useHandCursor) ? POINTER : null; + + } + + @:noCompletion private override function __hitTest (x:Float, y:Float, shapeFlag:Bool, stack:Array, interactiveOnly:Bool):Bool { if (!visible || (interactiveOnly && !mouseEnabled)) return false; diff --git a/openfl/display/Stage.hx b/openfl/display/Stage.hx index 6506d75b71..18cdd21efc 100644 --- a/openfl/display/Stage.hx +++ b/openfl/display/Stage.hx @@ -19,6 +19,7 @@ import lime.ui.KeyCode; import lime.ui.KeyModifier; import lime.ui.Mouse; import openfl._internal.renderer.AbstractRenderer; +import openfl._internal.renderer.cairo.CairoRenderer; import openfl._internal.renderer.canvas.CanvasRenderer; import openfl._internal.renderer.dom.DOMRenderer; import openfl._internal.renderer.opengl.GLRenderer; @@ -33,10 +34,11 @@ import openfl.geom.Matrix; import openfl.geom.Point; import openfl.geom.Rectangle; import openfl.text.TextField; +import openfl.ui.GameInput; import openfl.ui.Keyboard; import openfl.ui.KeyLocation; -#if js +#if (js && html5) import js.html.CanvasElement; import js.html.DivElement; import js.html.Element; @@ -162,6 +164,7 @@ import js.Browser; */ @:access(openfl.events.Event) +@:access(openfl.ui.GameInput) @:access(openfl.ui.Keyboard) @@ -318,7 +321,7 @@ class Stage extends DisplayObjectContainer implements IModule { * For more information, see the "Security" chapter in * the ActionScript 3.0 Developer's Guide. */ - public var frameRate:Float; + public var frameRate (get, set):Float; /** * A value from the StageQuality class that specifies which rendering quality @@ -529,9 +532,9 @@ class Stage extends DisplayObjectContainer implements IModule { @:noCompletion private var __fullscreen:Bool; @:noCompletion private var __invalidated:Bool; @:noCompletion private var __lastClickTime:Int; - @:noCompletion private var __mouseOutStack = []; - @:noCompletion private var __mouseX:Float = 0; - @:noCompletion private var __mouseY:Float = 0; + @:noCompletion private var __mouseOutStack:Array; + @:noCompletion private var __mouseX:Float; + @:noCompletion private var __mouseY:Float; @:noCompletion private var __originalWidth:Int; @:noCompletion private var __originalHeight:Int; @:noCompletion private var __renderer:AbstractRenderer; @@ -540,7 +543,7 @@ class Stage extends DisplayObjectContainer implements IModule { @:noCompletion private var __transparent:Bool; @:noCompletion private var __wasDirty:Bool; - #if js + #if (js && html5) //@:noCompletion private var __div:DivElement; //@:noCompletion private var __element:HtmlElement; #if stats @@ -569,7 +572,8 @@ class Stage extends DisplayObjectContainer implements IModule { __displayState = NORMAL; __mouseX = 0; __mouseY = 0; - + __lastClickTime = 0; + stageWidth = width; stageHeight = height; @@ -577,13 +581,13 @@ class Stage extends DisplayObjectContainer implements IModule { align = StageAlign.TOP_LEFT; allowsFullScreen = false; - frameRate = 60; quality = StageQuality.HIGH; scaleMode = StageScaleMode.NO_SCALE; stageFocusRect = true; __clearBeforeRender = true; __stack = []; + __mouseOutStack = []; stage3Ds = new Vector (); stage3Ds.push (new Stage3D ()); @@ -604,7 +608,9 @@ class Stage extends DisplayObjectContainer implements IModule { case OPENGL (gl): + #if !disable_cffi __renderer = new GLRenderer (stageWidth, stageHeight, gl); + #end case CANVAS (context): @@ -614,6 +620,10 @@ class Stage extends DisplayObjectContainer implements IModule { __renderer = new DOMRenderer (stageWidth, stageHeight, element); + case CAIRO (cairo): + + __renderer = new CairoRenderer (stageWidth, stageHeight, cairo); + default: } @@ -659,35 +669,35 @@ class Stage extends DisplayObjectContainer implements IModule { public function onGamepadAxisMove (gamepad:Gamepad, axis:GamepadAxis, value:Float):Void { - + GameInput.__onGamepadAxisMove (gamepad, axis, value); } public function onGamepadButtonDown (gamepad:Gamepad, button:GamepadButton):Void { - + GameInput.__onGamepadButtonDown (gamepad, button); } public function onGamepadButtonUp (gamepad:Gamepad, button:GamepadButton):Void { - + GameInput.__onGamepadButtonUp (gamepad, button); } public function onGamepadConnect (gamepad:Gamepad):Void { - + GameInput.__onGamepadConnect (gamepad); } public function onGamepadDisconnect (gamepad:Gamepad):Void { - + GameInput.__onGamepadDisconnect (gamepad); } @@ -768,6 +778,20 @@ class Stage extends DisplayObjectContainer implements IModule { + } + + + public function onTextEdit (text:String, start:Int, length:Int):Void { + + + + } + + + public function onTextInput (text:String):Void { + + + } @@ -815,6 +839,13 @@ class Stage extends DisplayObjectContainer implements IModule { } + public function onWindowEnter ():Void { + + + + } + + public function onWindowFocusIn ():Void { @@ -836,6 +867,13 @@ class Stage extends DisplayObjectContainer implements IModule { } + public function onWindowLeave ():Void { + + dispatchEvent (new Event (Event.MOUSE_LEAVE)); + + } + + public function onWindowMinimize ():Void { @@ -893,6 +931,17 @@ class Stage extends DisplayObjectContainer implements IModule { if (__renderer != null) { + switch (context) { + + case CAIRO (cairo): + + cast (__renderer, CairoRenderer).cairo = cairo; + @:privateAccess (__renderer.renderSession).cairo = cairo; + + default: + + } + __renderer.render (this); } @@ -1012,9 +1061,15 @@ class Stage extends DisplayObjectContainer implements IModule { } - @:noCompletion private override function __getInteractive (stack:Array):Void { + @:noCompletion private override function __getInteractive (stack:Array):Bool { - stack.push (this); + if (stack != null) { + + stack.push (this); + + } + + return true; } @@ -1103,6 +1158,12 @@ class Stage extends DisplayObjectContainer implements IModule { } + if (type == MouseEvent.MOUSE_DOWN) { + + focus = target; + + } + __fireEvent (MouseEvent.__create (type, button, __mouseX, __mouseY, (target == this ? targetPoint : target.globalToLocal (targetPoint)), target), stack); var clickType = switch (type) { @@ -1136,49 +1197,22 @@ class Stage extends DisplayObjectContainer implements IModule { } - if (Std.is (target, Sprite)) { - - var targetSprite:Sprite = cast target; - - if (targetSprite.buttonMode && targetSprite.useHandCursor) { - - Mouse.cursor = POINTER; - - } else { - - Mouse.cursor = ARROW; - - } - - } else if (Std.is (target, SimpleButton)) { - - var targetButton:SimpleButton = cast target; - - if (targetButton.useHandCursor) { - - Mouse.cursor = POINTER; - - } else { - - Mouse.cursor = ARROW; - - } - - } else if (Std.is (target, TextField)) { + var cursor = null; + + for (target in stack) { - var targetTextField:TextField = cast target; + cursor = target.__getCursor (); - if (targetTextField.type == INPUT) { + if (cursor != null) { - Mouse.cursor = TEXT; - - } else { - - Mouse.cursor = ARROW; + Mouse.cursor = cursor; + break; } - } else { + } + + if (cursor == null) { Mouse.cursor = ARROW; @@ -1509,7 +1543,7 @@ class Stage extends DisplayObjectContainer implements IModule { - #if js + #if (js && html5) @:noCompletion private function canvas_onContextLost (event:js.html.webgl.ContextEvent):Void { //__glContextLost = true; @@ -1553,6 +1587,34 @@ class Stage extends DisplayObjectContainer implements IModule { } + @:noCompletion private inline function get_displayState ():StageDisplayState { + + return __displayState; + + } + + + @:noCompletion private function set_displayState (value:StageDisplayState):StageDisplayState { + + switch (value) { + + case NORMAL: + + //Lib.application.window.minimized = false; + Lib.application.window.fullscreen = false; + + default: + + //Lib.application.window.minimized = false; + Lib.application.window.fullscreen = true; + + } + + return __displayState = value; + + } + + @:noCompletion private function get_focus ():InteractiveObject { return __focus; @@ -1593,30 +1655,16 @@ class Stage extends DisplayObjectContainer implements IModule { } - @:noCompletion private inline function get_displayState ():StageDisplayState { + @:noCompletion private function get_frameRate ():Float { - return __displayState; + return Lib.application.frameRate; } - @:noCompletion private function set_displayState (value:StageDisplayState):StageDisplayState { + @:noCompletion private function set_frameRate (value:Float):Float { - switch (value) { - - case NORMAL: - - //Lib.application.window.minimized = false; - Lib.application.window.fullscreen = false; - - default: - - //Lib.application.window.minimized = false; - Lib.application.window.fullscreen = true; - - } - - return __displayState = value; + return Lib.application.frameRate = value; } diff --git a/openfl/display3D/Context3D.hx b/openfl/display3D/Context3D.hx index fea746fbb8..dd7648bcda 100755 --- a/openfl/display3D/Context3D.hx +++ b/openfl/display3D/Context3D.hx @@ -99,7 +99,7 @@ class Context3D { } - #if (cpp || neko) + #if (cpp || neko || nodejs) GL.depthMask (true); #end GL.clearColor (red, green, blue, alpha); @@ -489,7 +489,7 @@ class Context3D { GL.disableVertexAttribArray (location); - #if (cpp || neko) + #if (cpp || neko || nodejs) GL.bindBuffer (GL.ARRAY_BUFFER, null); #end @@ -640,6 +640,8 @@ class Context3D { } + GL.viewport (Std.int (scrollRect.x), Std.int (scrollRect.y), Std.int (scrollRect.width), Std.int (scrollRect.height)); + } @@ -777,7 +779,7 @@ class Context3D { if (Std.is (texture, Texture)) { - #if (cpp || neko) + #if (cpp || neko || nodejs) GL.bindTexture (GL.TEXTURE_2D, cast (texture, Texture).glTexture); #end @@ -854,7 +856,7 @@ class Context3D { } else if (Std.is (texture, RectangleTexture)) { - #if (cpp || neko) + #if (cpp || neko || nodejs) GL.bindTexture (GL.TEXTURE_2D, cast(texture, RectangleTexture).glTexture); #end @@ -901,7 +903,7 @@ class Context3D { } else if (Std.is (texture, CubeTexture)) { - #if (cpp || neko) + #if (cpp || neko || nodejs) GL.bindTexture (GL.TEXTURE_CUBE_MAP, cast (texture, CubeTexture).glTexture); #end @@ -1046,4 +1048,4 @@ private class SamplerState { #else typedef Context3D = flash.display3D.Context3D; -#end \ No newline at end of file +#end diff --git a/openfl/display3D/textures/RectangleTexture.hx b/openfl/display3D/textures/RectangleTexture.hx index 13547f9097..60da190043 100755 --- a/openfl/display3D/textures/RectangleTexture.hx +++ b/openfl/display3D/textures/RectangleTexture.hx @@ -25,7 +25,7 @@ class RectangleTexture extends TextureBase { super (glTexture, width, height); - #if (cpp || neko) + #if (cpp || neko || nodejs) if (optimizeForRenderToTexture) GL.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, 1); @@ -67,7 +67,7 @@ class RectangleTexture extends TextureBase { GL.bindTexture (GL.TEXTURE_2D, glTexture); - #if js + #if (js && html5) if (optimizeForRenderToTexture) GL.pixelStorei(GL.UNPACK_FLIP_Y_WEBGL, 1); diff --git a/openfl/display3D/textures/Texture.hx b/openfl/display3D/textures/Texture.hx index db9e66ed03..f25af8de0a 100755 --- a/openfl/display3D/textures/Texture.hx +++ b/openfl/display3D/textures/Texture.hx @@ -28,7 +28,7 @@ class Texture extends TextureBase { super (glTexture, width, height); - #if (cpp || neko) + #if (cpp || neko || nodejs) if (optimizeForRenderToTexture) { GL.pixelStorei (GL.UNPACK_FLIP_Y_WEBGL, 1); @@ -52,37 +52,38 @@ class Texture extends TextureBase { public function uploadFromBitmapData (bitmapData:BitmapData, miplevel:Int = 0):Void { - // TODO: Support upload from UInt8Array directly - #if openfl_legacy - var p = BitmapData.getRGBAPixels (bitmapData); - #elseif js - var p = ByteArray.__ofBuffer (@:privateAccess (bitmapData.__image).data.buffer); - #else - var p = @:privateAccess (bitmapData.__image).data.buffer; - #end + + var pixels = BitmapData.getRGBAPixels (bitmapData); width = bitmapData.width; height = bitmapData.height; - uploadFromByteArray (p, 0, miplevel); - } - - - public function uploadFromByteArray (data:ByteArray, byteArrayOffset:Int, miplevel:Int = 0):Void { + uploadFromByteArray (pixels, 0, miplevel); - GL.bindTexture (GL.TEXTURE_2D, glTexture); - - if (optimizeForRenderToTexture) { + #else + + var image = @:privateAccess (bitmapData.__image); + + if (!image.premultiplied) { - GL.pixelStorei (GL.UNPACK_FLIP_Y_WEBGL, 1); - GL.texParameteri (GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST); - GL.texParameteri (GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST); - GL.texParameteri (GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE); - GL.texParameteri (GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE); + image = image.clone (); + image.premultiplied = true; } + width = image.width; + height = image.height; + + uploadFromUInt8Array (image.data, miplevel); + + #end + + } + + + public function uploadFromByteArray (data:ByteArray, byteArrayOffset:Int, miplevel:Int = 0):Void { + #if js var source = new UInt8Array (data.length); data.position = byteArrayOffset; @@ -99,7 +100,26 @@ class Texture extends TextureBase { var source = new UInt8Array (data); #end - GL.texImage2D (GL.TEXTURE_2D, miplevel, GL.RGBA, width, height, 0, GL.RGBA, GL.UNSIGNED_BYTE, source); + uploadFromUInt8Array (source, miplevel); + + } + + + public function uploadFromUInt8Array (data:UInt8Array, miplevel:Int = 0):Void { + + GL.bindTexture (GL.TEXTURE_2D, glTexture); + + if (optimizeForRenderToTexture) { + + GL.pixelStorei (GL.UNPACK_FLIP_Y_WEBGL, 1); + GL.texParameteri (GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST); + GL.texParameteri (GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST); + GL.texParameteri (GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE); + GL.texParameteri (GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE); + + } + + GL.texImage2D (GL.TEXTURE_2D, miplevel, GL.RGBA, width, height, 0, GL.RGBA, GL.UNSIGNED_BYTE, data); GL.bindTexture (GL.TEXTURE_2D, null); } @@ -110,4 +130,4 @@ class Texture extends TextureBase { #else typedef Texture = flash.display3D.textures.Texture; -#end \ No newline at end of file +#end diff --git a/openfl/errors/ArgumentError.hx b/openfl/errors/ArgumentError.hx index 10f3a807c3..560ba336e7 100644 --- a/openfl/errors/ArgumentError.hx +++ b/openfl/errors/ArgumentError.hx @@ -4,9 +4,11 @@ package openfl.errors; #if !flash class ArgumentError extends Error { - public function new (inMessage:String = "") { + public function new (message:String = "") { - super (inMessage); + super (message); + + name = "ArgumentError"; } diff --git a/openfl/errors/EOFError.hx b/openfl/errors/EOFError.hx index b6b81a679d..d7b30e21cd 100644 --- a/openfl/errors/EOFError.hx +++ b/openfl/errors/EOFError.hx @@ -19,6 +19,8 @@ class EOFError extends Error { super ("End of file was encountered", 2030); + name = "EOFError"; + } diff --git a/openfl/errors/IOError.hx b/openfl/errors/IOError.hx index b3337cd1c5..4d6622af49 100644 --- a/openfl/errors/IOError.hx +++ b/openfl/errors/IOError.hx @@ -8,6 +8,8 @@ class IOError extends Error { super (message); + name = "IOError"; + } diff --git a/openfl/errors/IllegalOperationError.hx b/openfl/errors/IllegalOperationError.hx index 557e07fefa..a420f0c70c 100644 --- a/openfl/errors/IllegalOperationError.hx +++ b/openfl/errors/IllegalOperationError.hx @@ -31,9 +31,11 @@ class IllegalOperationError extends Error { * * @param message A string associated with the error object. */ - public function new (inMessage:String = "") { + public function new (message:String = "") { - super (inMessage, 0); + super (message, 0); + + name = "IllegalOperationError"; } diff --git a/openfl/errors/RangeError.hx b/openfl/errors/RangeError.hx index b4cd239867..cecbee995f 100644 --- a/openfl/errors/RangeError.hx +++ b/openfl/errors/RangeError.hx @@ -4,9 +4,11 @@ package openfl.errors; #if !flash class RangeError extends Error { - public function new (inMessage:String = "") { + public function new (message:String = "") { - super (inMessage, 0); + super (message, 0); + + name = "RangeError"; } diff --git a/openfl/errors/SecurityError.hx b/openfl/errors/SecurityError.hx index e86788b289..499f11f2c8 100644 --- a/openfl/errors/SecurityError.hx +++ b/openfl/errors/SecurityError.hx @@ -4,9 +4,11 @@ package openfl.errors; #if !flash class SecurityError extends Error { - public function new (inMessage:String = "") { + public function new (message:String = "") { - super (inMessage, 0); + super (message, 0); + + name = "SecurityError"; } diff --git a/openfl/errors/TypeError.hx b/openfl/errors/TypeError.hx index 45bdb122fd..4870e6f397 100644 --- a/openfl/errors/TypeError.hx +++ b/openfl/errors/TypeError.hx @@ -4,9 +4,11 @@ package openfl.errors; #if !flash class TypeError extends Error { - public function new (inMessage:String = "") { + public function new (message:String = "") { - super (inMessage, 0); + super (message, 0); + + name = "TypeError"; } diff --git a/openfl/events/Event.hx b/openfl/events/Event.hx index c87c5201f5..068cb3d46c 100644 --- a/openfl/events/Event.hx +++ b/openfl/events/Event.hx @@ -341,6 +341,7 @@ class Event { @:noCompletion private var __isCancelled:Bool; @:noCompletion private var __isCancelledNow:Bool; + @:noCompletion private var __preventDefault:Bool; /** @@ -408,7 +409,24 @@ class Event { */ public function isDefaultPrevented ():Bool { - return (__isCancelled || __isCancelledNow); + return __preventDefault; + + } + + + /** + * Cancels an event's default behavior if that behavior can be canceled. + * Many events have associated behaviors that are carried out by default. For example, if a user types a character into a text field, the default behavior is that the character is displayed in the text field. Because the TextEvent.TEXT_INPUT event's default behavior can be canceled, you can use the preventDefault() method to prevent the character from appearing. + * An example of a behavior that is not cancelable is the default behavior associated with the Event.REMOVED event, which is generated whenever Flash Player is about to remove a display object from the display list. The default behavior (removing the element) cannot be canceled, so the preventDefault() method has no effect on this default behavior. + * You can use the Event.cancelable property to check whether you can prevent the default behavior associated with a particular event. If the value of Event.cancelable is true, then preventDefault() can be used to cancel the event; otherwise, preventDefault() has no effect. + */ + public function preventDefault ():Void { + + if (cancelable) { + + __preventDefault = true; + + } } diff --git a/openfl/events/GameInputEvent.hx b/openfl/events/GameInputEvent.hx new file mode 100644 index 0000000000..ebcc171c5c --- /dev/null +++ b/openfl/events/GameInputEvent.hx @@ -0,0 +1,63 @@ +package openfl.events; #if !flash + + +import openfl.ui.GameInputDevice; + + +/** + * The GameInputEvent class represents an event that is dispatched when a game input device has either been added or removed from the application platform. A game input device also dispatches events when it is turned on or off. + */ +class GameInputEvent extends Event { + + + /** + * Indicates that a compatible device has been connected or turned on. + */ + public static var DEVICE_ADDED = "deviceAdded"; + + /** + * Indicates that one of the enumerated devices has been disconnected or turned off. + */ + public static var DEVICE_REMOVED = "deviceRemoved"; + + /** + * Dispatched when a game input device is connected but is not usable. + */ + public static var DEVICE_UNUSABLE = "deviceUnusable"; + + + /** + * Returns a reference to the device that was added or removed. When a device is added, use this property to get a reference to the new device, instead of enumerating all of the devices to find the new one. + */ + public var device (default, null):GameInputDevice; + + + public function new (type:String, bubbles:Bool = true, cancelable:Bool = false, device:GameInputDevice = null) { + + super (type, bubbles, cancelable); + + this.device = device; + + } + + + public override function clone ():Event { + + return new GameInputEvent (type, bubbles, cancelable, device); + + } + + + public override function toString ():String { + + return "[GameInputEvent type=" + type + " bubbles=" + bubbles + " cancelable=" + cancelable + " device=" + device + "]"; + + } + + +} + + +#else +typedef GameInputEvent = flash.events.GameInputEvent; +#end \ No newline at end of file diff --git a/openfl/events/TouchEvent.hx b/openfl/events/TouchEvent.hx index a4059e58ca..fe5ff7f2d5 100644 --- a/openfl/events/TouchEvent.hx +++ b/openfl/events/TouchEvent.hx @@ -269,7 +269,7 @@ class TouchEvent extends Event { @:noCompletion public static function __create (type:String, /*event:lime.ui.TouchEvent,*/ touch:Dynamic /*js.html.Touch*/, stageX:Float, stageY:Float, local:Point, target:InteractiveObject):TouchEvent { - #if js + #if (js && html5) var evt = new TouchEvent (type, true, false, local.x, local.y, null, false, false, false/*event.ctrlKey, event.altKey, event.shiftKey*/, false /* note: buttonDown not supported on w3c spec */, 0, 0); evt.stageX = stageX; diff --git a/openfl/external/ExternalInterface.hx b/openfl/external/ExternalInterface.hx index fdbdfc0fa1..c0dcaf8f1c 100644 --- a/openfl/external/ExternalInterface.hx +++ b/openfl/external/ExternalInterface.hx @@ -167,7 +167,7 @@ class ExternalInterface { */ public static function addCallback (functionName:String, closure:Dynamic):Void { - #if js + #if (js && html5) if (Lib.application.window.backend.element != null) { untyped Lib.application.window.backend.element[functionName] = closure; @@ -251,7 +251,7 @@ class ExternalInterface { */ public static function call (functionName:String, ?p1:Dynamic, ?p2:Dynamic, ?p3:Dynamic, ?p4:Dynamic, ?p5:Dynamic):Dynamic { - #if js + #if (js && html5) var callResponse:Dynamic = null; var thisArg = functionName.split('.').slice(0, -1).join('.'); diff --git a/openfl/filters/BitmapFilter.hx b/openfl/filters/BitmapFilter.hx index 0485948993..53b8c1f69b 100644 --- a/openfl/filters/BitmapFilter.hx +++ b/openfl/filters/BitmapFilter.hx @@ -4,7 +4,7 @@ package openfl.filters; #if !flash #if !openfl_legacy import openfl.geom.Point; import openfl.geom.Rectangle; -#if js +#if (js && html5) import js.html.ImageData; #end @@ -42,7 +42,7 @@ class BitmapFilter { } - #if js + #if (js && html5) @:noCompletion public function __applyFilter (sourceData:ImageData, targetData:ImageData, sourceRect:Rectangle, destPoint:Point):Void { diff --git a/openfl/filters/ColorMatrixFilter.hx b/openfl/filters/ColorMatrixFilter.hx index b8397a8380..b96c382dab 100644 --- a/openfl/filters/ColorMatrixFilter.hx +++ b/openfl/filters/ColorMatrixFilter.hx @@ -4,7 +4,7 @@ package openfl.filters; #if !flash #if !openfl_legacy import openfl.geom.Point; import openfl.geom.Rectangle; -#if js +#if (js && html5) import js.html.ImageData; #end @@ -38,7 +38,7 @@ class ColorMatrixFilter extends BitmapFilter { } - #if js + #if (js && html5) @:noCompletion public override function __applyFilter (sourceData:ImageData, targetData:ImageData, sourceRect:Rectangle, destPoint:Point):Void { var source = sourceData.data; diff --git a/openfl/geom/Matrix.hx b/openfl/geom/Matrix.hx index d4dbe5d573..96ab46889f 100644 --- a/openfl/geom/Matrix.hx +++ b/openfl/geom/Matrix.hx @@ -1,6 +1,7 @@ package openfl.geom; #if !flash #if !openfl_legacy +import lime.math.Matrix3; import openfl.geom.Point; import lime.utils.Float32Array; @@ -755,6 +756,13 @@ class Matrix { } + @:noCompletion private function __toMatrix3 ():Matrix3 { + + return new Matrix3 (a, b, c, d, tx, ty); + + } + + @:noCompletion public inline function __transformX (pos:Point):Float { return pos.x * a + pos.y * c + tx; diff --git a/openfl/media/Sound.hx b/openfl/media/Sound.hx index bb8182ca0a..4abb66a3cb 100644 --- a/openfl/media/Sound.hx +++ b/openfl/media/Sound.hx @@ -421,21 +421,24 @@ class Sound extends EventDispatcher { */ public function play (startTime:Float = 0.0, loops:Int = 0, sndTransform:SoundTransform = null):SoundChannel { + // TODO: handle pan + + #if !html5 + var source = new AudioSource (__buffer); + source.offset = Std.int (startTime * 1000); + if (loops > 1) source.loops = loops - 1; + if (sndTransform != null) source.gain = sndTransform.volume; + return new SoundChannel (source); + #else if (sndTransform == null) { sndTransform = new SoundTransform (1, 0); } - // TODO: handle start time, loops, sound transform - - #if !html5 - var source = new AudioSource (__buffer); - return new SoundChannel (source); - #else var instance = if (loops > 1) - SoundJS.play (__soundID, SoundJS.INTERRUPT_ANY, 0, Std.int (startTime), loops-1, sndTransform.volume, sndTransform.pan); + SoundJS.play (__soundID, SoundJS.INTERRUPT_ANY, 0, Std.int (startTime), loops - 1, sndTransform.volume, sndTransform.pan); else SoundJS.play (__soundID, SoundJS.INTERRUPT_ANY, 0, Std.int (startTime), 0, sndTransform.volume, sndTransform.pan); diff --git a/openfl/media/SoundChannel.hx b/openfl/media/SoundChannel.hx index 98953fb624..afc8894421 100644 --- a/openfl/media/SoundChannel.hx +++ b/openfl/media/SoundChannel.hx @@ -106,6 +106,7 @@ class SoundChannel extends EventDispatcher { #if !html5 __source.stop (); + __dispose (); #else __soundInstance.stop (); #end @@ -113,16 +114,20 @@ class SoundChannel extends EventDispatcher { } - #if html5 @:noCompletion private function __dispose ():Void { if (!__isValid) return; + #if !html5 + __source.dispose (); + #else __soundInstance.stop (); __soundInstance = null; + #end + + __isValid = false; } - #end @@ -137,7 +142,7 @@ class SoundChannel extends EventDispatcher { if (!__isValid) return 0; #if !html5 - return __source.timeOffset / 1000; + return (__source.currentTime + __source.offset) / 1000; #else return __soundInstance.getPosition (); #end @@ -150,7 +155,7 @@ class SoundChannel extends EventDispatcher { if (!__isValid) return 0; #if !html5 - __source.timeOffset = Std.int (value * 1000); + __source.currentTime = Std.int (value * 1000) - __source.offset; return value; #else __soundInstance.setPosition (Std.int (value)); @@ -213,6 +218,7 @@ class SoundChannel extends EventDispatcher { @:noCompletion private function source_onComplete ():Void { + __dispose (); dispatchEvent (new Event (Event.SOUND_COMPLETE)); } diff --git a/openfl/media/Video.hx b/openfl/media/Video.hx index ad2fe051e6..d568deef77 100644 --- a/openfl/media/Video.hx +++ b/openfl/media/Video.hx @@ -9,7 +9,7 @@ import openfl.geom.Point; import openfl.geom.Rectangle; import openfl.net.NetStream; -#if js +#if (js && html5) import openfl._internal.renderer.dom.DOMRenderer; import js.html.MediaElement; import js.Browser; @@ -65,7 +65,7 @@ class Video extends DisplayObject { @:noCompletion private override function __getBounds (rect:Rectangle, matrix:Matrix):Void { var bounds = new Rectangle (0, 0, __width, __height); - bounds.transform (__worldTransform); + bounds = bounds.transform (matrix); rect.__expand (bounds.x, bounds.y, bounds.width, bounds.height); diff --git a/openfl/net/SharedObject.hx b/openfl/net/SharedObject.hx index 90537b93db..274c3a5e16 100644 --- a/openfl/net/SharedObject.hx +++ b/openfl/net/SharedObject.hx @@ -9,7 +9,7 @@ import openfl.events.EventDispatcher; import openfl.net.SharedObjectFlushStatus; import openfl.Lib; -#if js +#if (js && html5) import js.html.Storage; import js.Browser; #end @@ -208,7 +208,7 @@ class SharedObject extends EventDispatcher { data = { }; - #if js + #if (js && html5) try { __getLocalStorage ().removeItem (__key); @@ -279,7 +279,7 @@ class SharedObject extends EventDispatcher { */ public function flush (minDiskSpace:Int = 0):SharedObjectFlushStatus { - #if js + #if (js && html5) var data = Serializer.run (data); try { @@ -452,7 +452,7 @@ class SharedObject extends EventDispatcher { */ public static function getLocal (name:String, localPath:String = null, secure:Bool = false /* note: unsupported */) { - #if js + #if (js && html5) if (localPath == null) { localPath = Browser.window.location.href; @@ -464,7 +464,7 @@ class SharedObject extends EventDispatcher { so.__key = localPath + ":" + name; var rawData = null; - #if js + #if (js && html5) try { // user may have privacy settings which prevent reading @@ -494,7 +494,7 @@ class SharedObject extends EventDispatcher { } - #if js + #if (js && html5) @:noCompletion private static function __getLocalStorage ():Storage { var res = Browser.getLocalStorage (); diff --git a/openfl/net/Socket.hx b/openfl/net/Socket.hx index cf085bab47..9c6c0ddd8e 100644 --- a/openfl/net/Socket.hx +++ b/openfl/net/Socket.hx @@ -86,7 +86,7 @@ class Socket extends EventDispatcher /*implements IDataInput implements IDataOut if( port < 0 || port > 65535 ) throw new SecurityError("Invalid socket port number specified."); - #if js + #if (js && html5) _stamp = haxe.Timer.stamp(); #else var h : Host = null; @@ -107,12 +107,12 @@ class Socket extends EventDispatcher /*implements IDataInput implements IDataOut _output.endian = endian; _input = new ByteArray(); _input.endian = endian; - #if js + #if (js && html5) _inputBuffer = new ByteArray(); _inputBuffer.endian = endian; #end - #if js + #if (js && html5) _socket = untyped __js__("new WebSocket(\"ws://\" + host + \":\" + port)"); _socket.onopen = onOpenHandler; @@ -134,7 +134,7 @@ class Socket extends EventDispatcher /*implements IDataInput implements IDataOut } @:noCompletion private function onFrame( _ ){ - #if js + #if (js && html5) if (_inputBuffer.bytesAvailable > 0) { var newInput = new ByteArray(); @@ -360,7 +360,7 @@ class Socket extends EventDispatcher /*implements IDataInput implements IDataOut } //public function writeObject( object:Dynamic ):Void { _output.writeObject(object); } - #if js + #if (js && html5) @:noCompletion private function onOpenHandler (_):Void { _connected = true; dispatchEvent (new Event (Event.CONNECT)); @@ -390,7 +390,7 @@ class Socket extends EventDispatcher /*implements IDataInput implements IDataOut throw new IOError("Operation attempted on invalid socket."); if( _output.length > 0 ){ try { - #if js + #if (js && html5) _socket.send( _output.__getBuffer() ); #else _socket.output.write( _output ); diff --git a/openfl/net/URLLoader.hx b/openfl/net/URLLoader.hx index b4a989bd0b..b545a69614 100644 --- a/openfl/net/URLLoader.hx +++ b/openfl/net/URLLoader.hx @@ -1,6 +1,8 @@ package openfl.net; #if !flash #if (!openfl_legacy || disable_legacy_networking) +import lime.app.Event; +import lime.utils.ByteArray; import openfl.events.Event; import openfl.events.EventDispatcher; import openfl.events.HTTPStatusEvent; @@ -10,13 +12,21 @@ import openfl.errors.IOError; import openfl.events.SecurityErrorEvent; import openfl.utils.ByteArray; -#if js +#if (js && html5) import js.html.EventTarget; import js.html.XMLHttpRequest; import js.Browser; import js.Lib; #end +#if lime_curl +import lime.net.curl.CURL; +import lime.net.curl.CURLEasy; +import lime.net.curl.CURLCode; +import lime.net.curl.CURLInfo; +import lime.net.curl.CURLOption; +#end + /** * The URLLoader class downloads data from a URL as text, binary data, or @@ -149,6 +159,12 @@ class URLLoader extends EventDispatcher { public var dataFormat (default, set):URLLoaderDataFormat; + #if lime_curl + private var __curl:CURL; + private var __data:String; + #end + + /** * Creates a URLLoader object. * @@ -165,6 +181,11 @@ class URLLoader extends EventDispatcher { bytesTotal = 0; dataFormat = URLLoaderDataFormat.TEXT; + #if lime_curl + __data = ""; + __curl = CURLEasy.init (); + #end + if (request != null) { load (request); @@ -182,7 +203,9 @@ class URLLoader extends EventDispatcher { */ public function close ():Void { - + #if lime_curl + CURLEasy.cleanup (__curl); + #end } @@ -297,14 +320,16 @@ class URLLoader extends EventDispatcher { */ public function load (request:URLRequest):Void { - #if js + #if (js && html5) + requestUrl (request.url, request.method, request.data, request.formatRequestHeaders ()); + #elseif lime_curl requestUrl (request.url, request.method, request.data, request.formatRequestHeaders ()); #end } - #if js + #if (js && html5) @:noCompletion private function registerEvents (subject:EventTarget):Void { var self = this; @@ -466,6 +491,186 @@ class URLLoader extends EventDispatcher { }; } + + + #elseif lime_curl + + + private function prepareData (data:Dynamic):ByteArray { + + var uri:ByteArray = new ByteArray (); + + if (Std.is (data, ByteArray)) { + + var data:ByteArray = cast data; + uri = data; + + } else if (Std.is (data, URLVariables)) { + + var data:URLVariables = cast data; + var tmp:String = ""; + + for (p in Reflect.fields (data)) { + + if (tmp.length != 0) tmp += "&"; + tmp += StringTools.urlEncode (p) + "=" + StringTools.urlEncode (Reflect.field (data, p)); + + } + + uri.writeUTFBytes (tmp); + + } else { + + if (data != null) { + + uri.writeUTFBytes (Std.string (data)); + + } + + } + + return uri; + + } + + + private function requestUrl (url:String, method:URLRequestMethod, data:Dynamic, requestHeaders:Array):Void { + + var uri = prepareData(data); + uri.position = 0; + + __data = ""; + bytesLoaded = 0; + bytesTotal = 0; + + CURLEasy.reset (__curl); + CURLEasy.setopt (__curl, URL, url); + + switch (method) { + + case HEAD: + + CURLEasy.setopt(__curl, NOBODY, true); + + case GET: + + CURLEasy.setopt(__curl, HTTPGET, true); + + case POST: + + CURLEasy.setopt(__curl, POST, true); + CURLEasy.setopt(__curl, READFUNCTION, readFunction.bind(_, uri)); + CURLEasy.setopt(__curl, POSTFIELDSIZE, uri.length); + CURLEasy.setopt(__curl, INFILESIZE, uri.length); + + case PUT: + + CURLEasy.setopt(__curl, UPLOAD, true); + CURLEasy.setopt(__curl, READFUNCTION, readFunction.bind(_, uri)); + CURLEasy.setopt(__curl, INFILESIZE, uri.length); + + case _: + + CURLEasy.setopt(__curl, CUSTOMREQUEST, cast method); + CURLEasy.setopt(__curl, READFUNCTION, readFunction.bind(_, uri)); + CURLEasy.setopt(__curl, INFILESIZE, uri.length); + + } + + var headers:Array = []; + headers.push ("Expect: "); // removes the default cURL value + + for (requestHeader in requestHeaders) { + + headers.push ('${requestHeader.name}: ${requestHeader.value}'); + + } + + CURLEasy.setopt (__curl, HTTPHEADER, headers); + + CURLEasy.setopt (__curl, PROGRESSFUNCTION, progressFunction); + + CURLEasy.setopt (__curl, WRITEFUNCTION, writeFunction); + CURLEasy.setopt (__curl, HEADERFUNCTION, headerFunction); + + CURLEasy.setopt (__curl, SSL_VERIFYPEER, false); + CURLEasy.setopt (__curl, SSL_VERIFYHOST, false); + CURLEasy.setopt (__curl, USERAGENT, "libcurl-agent/1.0"); + CURLEasy.setopt (__curl, CONNECTTIMEOUT, 30); + + var result = CURLEasy.perform (__curl); + var responseCode = CURLEasy.getinfo (__curl, RESPONSE_CODE); + + if (result == CURLCode.OK) { + + /* + switch(dataFormat) { + case BINARY: this.data = __data; + default: this.data = __data.asString(); + } + */ + this.data = __data; + + onStatus (Std.parseInt (responseCode)); + + var evt = new Event (Event.COMPLETE); + evt.currentTarget = this; + dispatchEvent (evt); + + } else { + + onError ("Problem with curl: " + result); + + } + + } + + + private function writeFunction (output:String, size:Int, nmemb:Int):Int { + + __data += output; + return size * nmemb; + + } + + + private function headerFunction (output:String, size:Int, nmemb:Int):Int { + + // TODO + return size * nmemb; + + } + + + private function progressFunction (dltotal:Float, dlnow:Float, uptotal:Float, upnow:Float):Int { + + if (upnow > bytesLoaded || dlnow > bytesLoaded || uptotal > bytesTotal || dltotal > bytesTotal) { + + if (upnow > bytesLoaded) bytesLoaded = Std.int (upnow); + if (dlnow > bytesLoaded) bytesLoaded = Std.int (dlnow); + if (uptotal > bytesTotal) bytesTotal = Std.int (uptotal); + if (dltotal > bytesTotal) bytesTotal = Std.int (dltotal); + + var evt = new ProgressEvent (ProgressEvent.PROGRESS); + evt.currentTarget = this; + evt.bytesLoaded = bytesLoaded; + evt.bytesTotal = bytesTotal; + dispatchEvent (evt); + + } + + return 0; + + } + + + private function readFunction (max:Int, input:ByteArray):String { + + return input == null ? "" : input.readUTFBytes (Std.int (Math.min (max, input.length - input.position))); + + } + + #end @@ -478,7 +683,7 @@ class URLLoader extends EventDispatcher { @:noCompletion private function onData (_):Void { - #if js + #if (js && html5) var content:Dynamic = getData (); switch (dataFormat) { @@ -554,7 +759,7 @@ class URLLoader extends EventDispatcher { @:noCompletion private function set_dataFormat (inputVal:URLLoaderDataFormat):URLLoaderDataFormat { - #if js + #if (js && html5) // prevent inadvertently using typed arrays when they are unsupported // @todo move these sorts of tests somewhere common in the vein of Modernizr diff --git a/openfl/net/URLRequestMethod.hx b/openfl/net/URLRequestMethod.hx index 5da36ee804..be572d1f3c 100644 --- a/openfl/net/URLRequestMethod.hx +++ b/openfl/net/URLRequestMethod.hx @@ -6,27 +6,27 @@ package openfl.net; #if !flash #if (!openfl_legacy || disable_legacy_networking) * URLRequest object should use the POST method or the * GET method when sending data to a server. */ -class URLRequestMethod { +@:enum abstract URLRequestMethod(String) from String to String { /** * Specifies that the URLRequest object is a DELETE. */ - public static var DELETE:String = "DELETE"; + var DELETE = "DELETE"; /** * Specifies that the URLRequest object is a GET. */ - public static var GET:String = "GET"; + var GET = "GET"; /** * Specifies that the URLRequest object is a HEAD. */ - public static var HEAD:String = "HEAD"; + var HEAD = "HEAD"; /** * Specifies that the URLRequest object is OPTIONS. */ - public static var OPTIONS:String = "OPTIONS"; + var OPTIONS = "OPTIONS"; /** * Specifies that the URLRequest object is a POST. @@ -36,12 +36,12 @@ class URLRequestMethod { * that uses the POST method(one that has its method property * set to URLRequestMethod.POST) as using the GET method.

*/ - public static var POST:String = "POST"; + var POST = "POST"; /** * Specifies that the URLRequest object is a PUT. */ - public static var PUT:String = "PUT"; + var PUT = "PUT"; } diff --git a/openfl/net/XMLSocket.hx b/openfl/net/XMLSocket.hx index bc08d986a4..68642470c6 100644 --- a/openfl/net/XMLSocket.hx +++ b/openfl/net/XMLSocket.hx @@ -45,7 +45,7 @@ class XMLSocket extends EventDispatcher { public function connectWithProto (host: String, port:Int, protocol:String):Void { - #if js + #if (js && html5) if (protocol == null) { _socket = untyped __js__("new WebSocket(\"ws://\" + host + \":\" + port)"); } diff --git a/openfl/sensors/Accelerometer.hx b/openfl/sensors/Accelerometer.hx index 0f09a4b55c..371e8fa9d1 100644 --- a/openfl/sensors/Accelerometer.hx +++ b/openfl/sensors/Accelerometer.hx @@ -7,7 +7,7 @@ import openfl.errors.ArgumentError; import openfl.events.AccelerometerEvent; import openfl.events.EventDispatcher; -#if js +#if (js && html5) import js.Browser; #end diff --git a/openfl/system/Capabilities.hx b/openfl/system/Capabilities.hx index bb83d8e5bb..c6c85631e3 100644 --- a/openfl/system/Capabilities.hx +++ b/openfl/system/Capabilities.hx @@ -3,7 +3,7 @@ package openfl.system; #if !flash #if !openfl_legacy import haxe.macro.Compiler; -#if js +#if (js && html5) import js.html.DivElement; import js.html.Element; import js.Browser; @@ -439,7 +439,7 @@ class Capabilities { @:noCompletion private static function get_screenResolutionX ():Float { - #if js + #if (js && html5) return Browser.window.screen.width; @@ -454,7 +454,7 @@ class Capabilities { @:noCompletion private static function get_screenResolutionY ():Float { - #if js + #if (js && html5) return Browser.window.screen.height; @@ -469,7 +469,7 @@ class Capabilities { @:noCompletion private static function get_language ():String { - #if js + #if (js && html5) return untyped navigator.language; diff --git a/openfl/text/TextField.hx b/openfl/text/TextField.hx index 650162aee4..5cc7d9b56c 100644 --- a/openfl/text/TextField.hx +++ b/openfl/text/TextField.hx @@ -2,6 +2,7 @@ package openfl.text; #if !flash #if !openfl_legacy import haxe.io.Path; +import haxe.Utf8; import haxe.xml.Fast; import haxe.Timer; import lime.graphics.opengl.GLTexture; @@ -9,8 +10,10 @@ import lime.system.System; import lime.text.TextLayout; import lime.ui.Mouse; import lime.ui.MouseCursor; +import openfl._internal.renderer.cairo.CairoShape; import openfl._internal.renderer.canvas.CanvasTextField; import openfl._internal.renderer.dom.DOMTextField; +import openfl._internal.renderer.cairo.CairoTextField; import openfl._internal.renderer.opengl.GLTextField; import openfl._internal.renderer.RenderSession; import openfl.display.DisplayObject; @@ -25,8 +28,9 @@ import openfl.geom.Point; import openfl.geom.Rectangle; import openfl.text.Font; import openfl.text.TextFormatAlign; +import openfl.Lib; -#if js +#if (js && html5) import js.html.CanvasElement; import js.html.CanvasRenderingContext2D; import js.html.CSSStyleDeclaration; @@ -476,7 +480,7 @@ class TextField extends InteractiveObject { * * @default true */ - public var selectable:Bool; + public var selectable (default, set):Bool; /** * The zero-based character index value of the first character in the current @@ -567,18 +571,21 @@ class TextField extends InteractiveObject { @:noCompletion private var __measuredHeight:Int; @:noCompletion private var __measuredWidth:Int; @:noCompletion private var __ranges:Array; + @:noCompletion private var __selectable:Bool; @:noCompletion private var __selectionStart:Int; @:noCompletion private var __showCursor:Bool; @:noCompletion private var __text:String; @:noCompletion private var __textFormat:TextFormat; @:noCompletion private var __textLayout:TextLayout; @:noCompletion private var __texture:GLTexture; - @:noCompletion private var __tileData:Array>; - @:noCompletion private var __tilesheets:Array; + @:noCompletion private var __tileData:Map>; + @:noCompletion private var __tileDataLength:Map; + @:noCompletion private var __tilesheets:Map; @:noCompletion private var __width:Float; + @:noCompletion private static var __utf8_endline_code:Int = 10; - #if js + #if (js && html5) private var __div:DivElement; private var __hiddenInput:InputElement; #end @@ -713,13 +720,23 @@ class TextField extends InteractiveObject { */ public function getLineMetrics (lineIndex:Int):TextLineMetrics { - //openfl.Lib.notImplemented ("TextField.getLineMetrics"); - var height = textHeight; - return new TextLineMetrics (0, textWidth, height, height, 0, 0); + var lineWidth = __getLineWidth(lineIndex); + var lineHeight = __getLineMetric(lineIndex, LINE_HEIGHT); + + var ascender = __getLineMetric(lineIndex, ASCENDER); + var descender = __getLineMetric(lineIndex, DESCENDER); - //return new TextLineMetrics (0, 0, 0, 0, 0, 0); + var leading = __getLineMetric(lineIndex, LEADING); + + var margin = switch(__textFormat.align) { + case LEFT, JUSTIFY: 2; + case RIGHT: (width - lineWidth) - 2; + case CENTER: (width - lineWidth) / 2; + } + + return new TextLineMetrics (margin, lineWidth, lineHeight, ascender, descender, leading); } @@ -907,16 +924,32 @@ class TextField extends InteractiveObject { __hiddenInput.type = 'text'; __hiddenInput.style.position = 'absolute'; __hiddenInput.style.opacity = "0"; + __hiddenInput.style.color = "transparent"; + + // TODO: Position for mobile browsers better + + __hiddenInput.style.left = "0px"; + __hiddenInput.style.top = "50%"; + + if (~/(iPad|iPhone|iPod).*OS 8_/gi.match (Browser.window.navigator.userAgent)) { + + __hiddenInput.style.fontSize = "0px"; + __hiddenInput.style.width = '0px'; + __hiddenInput.style.height = '0px'; + + } else { + + __hiddenInput.style.width = '1px'; + __hiddenInput.style.height = '1px'; + + } + untyped (__hiddenInput.style).pointerEvents = 'none'; - __hiddenInput.style.left = (x + ((__canvas != null) ? __canvas.offsetLeft : 0)) + 'px'; - __hiddenInput.style.top = (y + ((__canvas != null) ? __canvas.offsetTop : 0)) + 'px'; - __hiddenInput.style.width = __width + 'px'; - __hiddenInput.style.height = __height + 'px'; - __hiddenInput.style.zIndex = "0"; + __hiddenInput.style.zIndex = "-10000000"; - if (this.maxChars > 0) { + if (maxChars > 0) { - __hiddenInput.maxLength = this.maxChars; + __hiddenInput.maxLength = maxChars; } @@ -976,20 +1009,27 @@ class TextField extends InteractiveObject { @:noCompletion private override function __getBounds (rect:Rectangle, matrix:Matrix):Void { var bounds = new Rectangle (0, 0, __width, __height); - bounds.transform (__worldTransform); + bounds = bounds.transform (matrix); rect.__expand (bounds.x, bounds.y, bounds.width, bounds.height); } + @:noCompletion private override function __getCursor ():MouseCursor { + + return (type == INPUT && selectable) ? TEXT : null; + + } + + @:noCompletion private function __getFont (format:TextFormat):String { var font = format.italic ? "italic " : "normal "; font += "normal "; font += format.bold ? "bold " : "normal "; font += format.size + "px"; - font += "/" + (format.size + format.leading + 4) + "px "; + font += "/" + (format.size + format.leading) + "px "; font += "'" + switch (format.font) { @@ -1110,15 +1150,215 @@ class TextField extends InteractiveObject { } + @:noCompletion private function __getLineBreaks():Int { + + //returns the number of line breaks in the text + + var lines:Int = 0; + for (i in 0...Utf8.length(text)) { + var char = Utf8.charCodeAt(text, i); + if (char == __utf8_endline_code) { + lines++; + } + } + return lines; + } + + @:noCompletion private function __getLineBreakIndeces():Array { + + //returns the exact character indeces where the line breaks occur + + var breaks = []; + + for (i in 0...Utf8.length(text)) { + try{ + var char = Utf8.charCodeAt(text, i); + if (char == __utf8_endline_code) { + breaks.push(i); + } + } + } + return breaks; + } + + @:noCompletion private function __getLineBreaksInRange(i:Int):Int { + + //returns the number of line breaks that occur within a given format range + + var lines:Int = 0; + if (__ranges.length > i && i >= 0) { + var range = __ranges[i]; + + //TODO: this could quite possibly cause crash errors if range indeces are not based on Utf8 character indeces + if (range.start > 0 && range.end < text.length) { + for (j in range.start...range.end + 1) { + var char = Utf8.charCodeAt(text, i); + if (char == __utf8_endline_code) { + lines++; + } + } + } + } + return lines; + } + + + + @:noCompletion private function __getLineIndeces(line:Int):Array { + + //tells you what the first and last (non-linebreak) character indeces are in a given line + + var breaks = __getLineBreakIndeces(); + var i:Int = 0; + var first_char = 0; + var last_char = text.length - 1; + + for (br in breaks) { + + //if this is the line we care about + if (i == line) { + //the first actual character in our line is the index after this line break + first_char = br + 1; + + //if there's another line break left in the list + if (i != breaks.length-1) { + //the last character is the index before the next line break + //(otherwise it's the last character in the text field) + last_char = breaks[i + 1] - 1; + } + } + i++; + } + return [first_char, last_char]; + } + + @:noCompletion private function __getLineWidth(line:Int):Float { + + //Returns the width of a given line, or if -1 is supplied, the largest width of any single line + + var measurements = __measureTextSub(false); + + var currWidth = 0.0; + var bestWidth = 0.0; + + var linebreaks = __getLineBreakIndeces(); + + var currLine:Int = 0; + for (i in 0...measurements.length) { + var measure = measurements[i]; + if (linebreaks.indexOf(i) != -1) { //if this character is a line break + if (currLine == line) { //if we're currently on the desired line + return currWidth; //return the built up width immediately + } + else if (line == -1 && currWidth > bestWidth) { //if we are looking at ALL lines, and this width is bigger than the last one + bestWidth = currWidth; //this is the new best width + } + + currWidth = 0; //reset current width + currLine++; + } + else { + currWidth += measurements[i]; //keep building up the width + } + } + + if (currLine == line) { //we reached end of the loop & this is the line we want + bestWidth = currWidth; + } + else if (line == -1 && currWidth > bestWidth) { //looking at ALL lines, and this one's bigger + bestWidth = currWidth; + } + + return bestWidth; + } + + private static inline var ASCENDER:Int = 0; + private static inline var DESCENDER:Int = 1; + private static inline var LINE_HEIGHT:Int = 2; + private static inline var LEADING:Int = 3; + + @:noCompletion private function __getLineMetric(line:Int,metric:Int):Float { + + //returns the specified line metric for the given line + + if (__ranges == null) { + return __getLineMetricSubRangesNull(true, metric); + } + else { + return __getLineMetricSubRangesNotNull(line, metric); + } + } + + @:noCompletion private function __getLineMetricSubRangesNull(singleLine:Bool=false, metric:Int):Float { + + //subroutine if ranges are null + + var font = __getFontInstance (__textFormat); + + if (font != null) { + + return switch(metric) { + case LINE_HEIGHT: __getLineMetricSubRangesNull(singleLine, ASCENDER) + + __getLineMetricSubRangesNull(singleLine, DESCENDER) + + __getLineMetricSubRangesNull(singleLine, LEADING); + case ASCENDER: font.ascender / font.unitsPerEM * __textFormat.size; + case DESCENDER: Math.abs(font.descender / font.unitsPerEM * __textFormat.size); + case LEADING: __textFormat.leading; + default: 0; + } + } + + return 0; + } + + @:noCompletion private function __getLineMetricSubRangesNotNull(specificLine:Int, metric:Int):Float { + + //subroutine if ranges are not null + //TODO: test this more thoroughly + + var lineChars = __getLineIndeces(specificLine); + + var m = 0.0; + var best_m = 0.0; + + for (range in __ranges) { + + if (range.start >= lineChars[0]) { + var font = __getFontInstance (range.format); + + if (font != null) { + + m = switch(metric) { + case LINE_HEIGHT: __getLineMetricSubRangesNotNull(specificLine, ASCENDER) + + __getLineMetricSubRangesNotNull(specificLine, DESCENDER) + + __getLineMetricSubRangesNotNull(specificLine, LEADING); + case ASCENDER: font.ascender / font.unitsPerEM * __textFormat.size; + case DESCENDER: Math.abs(font.descender / font.unitsPerEM * __textFormat.size); + case LEADING: __textFormat.leading; + default: 0; + } + } + } + + if (m > best_m) { + best_m = m; + } + m = 0; + } + + return best_m; + } @:noCompletion private function __getPosition (x:Float, y:Float):Int { + if (x <= 2) return 0; + var value:String = text; var text:String = value; - var totalW:Float = 0; + var totalW:Float = 2; var pos = text.length; - if (x < __getTextWidth (text)) { + if (x < __getTextWidth (text) + 2) { for (i in 0...text.length) { @@ -1139,7 +1379,6 @@ class TextField extends InteractiveObject { } - @:noCompletion private function __getTextWidth (text:String):Float { #if (js && html5) @@ -1187,10 +1426,9 @@ class TextField extends InteractiveObject { } - - @:noCompletion private function __measureText ():Array { + @:noCompletion private function __measureText (condense:Bool=true):Array { - #if js + #if (js && html5) if (__context == null) { @@ -1219,7 +1457,24 @@ class TextField extends InteractiveObject { } - #elseif (cpp || neko) + #elseif (cpp || neko || nodejs) + + //the "condense" flag, if true, will return the widths of individual text format ranges, if false will return the widths of each character + //TODO: look into whether this method and others can replace the JS stuff yet or not + + return __measureTextSub(condense); + + #else + + return null; + + #end + + } + + @:noCompletion private function __measureTextSub(condense:Bool):Array { + + //subroutine for measuring text (width) if (__textLayout == null) { @@ -1229,70 +1484,94 @@ class TextField extends InteractiveObject { if (__ranges == null) { - var font = __getFontInstance (__textFormat); - var width = 0.0; + return __measureTextSubRangesNull(condense); - if (font != null && __textFormat.size != null) { - - __textLayout.text = null; - __textLayout.font = font; - __textLayout.size = Std.int (__textFormat.size); - __textLayout.text = __text; + } else { + + return __measureTextSubRangesNotNull(condense); + } + + return null; + } + + @:noCompletion private function __measureTextSubRangesNull(condense:Bool):Array { + + //subroutine if format ranges are null + + var font = __getFontInstance (__textFormat); + var width = 0.0; + var widths = []; + + if (font != null && __textFormat.size != null) { + + __textLayout.text = null; + __textLayout.font = font; + __textLayout.size = Std.int (__textFormat.size); + __textLayout.text = __text; + + for (position in __textLayout.positions) { - for (position in __textLayout.positions) { - + if (condense) { width += position.advance.x; - + } + else { + widths.push(position.advance.x); } } - return [ width ]; + } + + if (condense) { + widths.push(width); + } + + return widths; + } + + @:noCompletion private function __measureTextSubRangesNotNull(condense:Bool):Array { + + //subroutine if format ranges are not null + + var measurements = []; + + for (range in __ranges) { - } else { + var font = __getFontInstance (range.format); + var width = 0.0; - var measurements = []; + if (font != null && range.format.size != null) { - for (range in __ranges) { - - var font = __getFontInstance (range.format); - var width = 0.0; + __textLayout.text = null; + __textLayout.font = font; + __textLayout.size = Std.int (range.format.size); + __textLayout.text = text.substring (range.start, range.end); - if (font != null && range.format.size != null) { - - __textLayout.text = null; - __textLayout.font = font; - __textLayout.size = Std.int (range.format.size); - __textLayout.text = text.substring (range.start, range.end); + for (position in __textLayout.positions) { - for (position in __textLayout.positions) { - + if (condense) { width += position.advance.x; - + } + else { + measurements.push(position.advance.x); } } - measurements.push (width); - } - return measurements; + if (condense) { + measurements.push (width); + } } - #else - - return null; - - #end - + return measurements; } - @:noCompletion private function __measureTextWithDOM ():Void { - #if js + #if (js && html5) var div:Element = __div; @@ -1301,6 +1580,7 @@ class TextField extends InteractiveObject { div = cast Browser.document.createElement ("div"); div.innerHTML = new EReg ("\n", "g").replace (__text, "
"); div.style.setProperty ("font", __getFont (__textFormat), null); + div.style.setProperty ("pointer-events", "none", null); div.style.position = "absolute"; div.style.top = "110%"; // position off-screen! Browser.document.body.appendChild (div); @@ -1313,7 +1593,7 @@ class TextField extends InteractiveObject { // function of the flow within the width bounds... if (__div == null) { - div.style.width = Std.string (__width) + "px"; + div.style.width = Std.string (__width - 4) + "px"; } @@ -1329,6 +1609,10 @@ class TextField extends InteractiveObject { } + @:noCompletion public override function __renderCairo (renderSession:RenderSession):Void { + + CairoTextField.render (this, renderSession); + } @:noCompletion public override function __renderCanvas (renderSession:RenderSession):Void { @@ -1388,8 +1672,18 @@ class TextField extends InteractiveObject { __ranges = null; __isHTML = false; - __cursorPosition = __hiddenInput.selectionStart; - __selectionStart = __cursorPosition; + if (__hiddenInput.selectionDirection == "backward") { + + __cursorPosition = __hiddenInput.selectionStart; + __selectionStart = __hiddenInput.selectionEnd; + + } else { + + __cursorPosition = __hiddenInput.selectionEnd; + __selectionStart = __hiddenInput.selectionStart; + + } + __dirty = true; dispatchEvent (new Event (Event.CHANGE, true)); @@ -1405,71 +1699,102 @@ class TextField extends InteractiveObject { var keyCode = event.which; var isShift = event.shiftKey; - if (keyCode == 65 && (event.ctrlKey || event.metaKey)) { // Command/Ctrl + A + //if (keyCode == 65 && (event.ctrlKey || event.metaKey)) { // Command/Ctrl + A + // + //__hiddenInput.selectionStart = 0; + //__hiddenInput.selectionEnd = text.length; + //event.preventDefault (); + //__dirty = true; + //return; + // + //} + // + //if (keyCode == 17 || event.metaKey || event.ctrlKey) { + // + //return; + // + //} + + __text = __hiddenInput.value; + __ranges = null; + __isHTML = false; + + if (__hiddenInput.selectionDirection == "backward") { - __hiddenInput.selectionStart = 0; - __hiddenInput.selectionEnd = text.length; - event.preventDefault (); - __dirty = true; - return; + __cursorPosition = __hiddenInput.selectionStart; + __selectionStart = __hiddenInput.selectionEnd; - } - - if (keyCode == 17 || event.metaKey || event.ctrlKey) { + } else { - return; + __cursorPosition = __hiddenInput.selectionEnd; + __selectionStart = __hiddenInput.selectionStart; } - __text = __hiddenInput.value; - __ranges = null; - __isHTML = false; - - __selectionStart = __hiddenInput.selectionStart; __dirty = true; } - @:noCompletion private function stage_onFocusOut (event:Event):Void { + @:noCompletion private function stage_onMouseMove (event:MouseEvent) { - __cursorPosition = -1; - __hasFocus = false; - __stopCursorTimer (); - __hiddenInput.blur (); - __dirty = true; + if (__hasFocus && __selectionStart >= 0) { + + var localPoint = globalToLocal (new Point (event.stageX, event.stageY)); + __cursorPosition = __getPosition (localPoint.x, localPoint.y); + __dirty = true; + + } } - @:noCompletion private function stage_onMouseMove (event:MouseEvent) { + @:noCompletion private function stage_onMouseUp (event:MouseEvent):Void { - if (__hasFocus && __selectionStart >= 0) { + stage.removeEventListener (MouseEvent.MOUSE_MOVE, stage_onMouseMove); + stage.removeEventListener (MouseEvent.MOUSE_UP, stage_onMouseUp); + + if (stage.focus == this) { - __cursorPosition = __getPosition (event.localX, event.localY); - __dirty = true; + var localPoint = globalToLocal (new Point (event.stageX, event.stageY)); + var upPos:Int = __getPosition (localPoint.x, localPoint.y); + var leftPos:Int; + var rightPos:Int; + + leftPos = Std.int (Math.min (__selectionStart, upPos)); + rightPos = Std.int (Math.max (__selectionStart, upPos)); + + __selectionStart = leftPos; + __cursorPosition = rightPos; + + this_onFocusIn (null); } } - @:noCompletion private function stage_onMouseUp (event:MouseEvent):Void { + @:noCompletion private function this_onAddedToStage (event:Event):Void { - var upPos:Int = __getPosition (event.localX, event.localY); - var leftPos:Int; - var rightPos:Int; + addEventListener (FocusEvent.FOCUS_IN, this_onFocusIn); + addEventListener (FocusEvent.FOCUS_OUT, this_onFocusOut); - leftPos = Std.int (Math.min (__selectionStart, upPos)); - rightPos = Std.int (Math.max (__selectionStart, upPos)); + __hiddenInput.addEventListener ('keydown', input_onKeyDown, true); + __hiddenInput.addEventListener ('keyup', input_onKeyUp, true); + __hiddenInput.addEventListener ('input', input_onKeyUp, true); - __selectionStart = leftPos; - __cursorPosition = rightPos; + addEventListener (MouseEvent.MOUSE_DOWN, this_onMouseDown); - stage.removeEventListener (MouseEvent.MOUSE_MOVE, stage_onMouseMove); - stage.addEventListener (MouseEvent.MOUSE_UP, stage_onMouseUp); + if (stage.focus == this) { + + this_onFocusIn (null); + + } - stage.focus = this; + } + + + @:noCompletion private function this_onFocusIn (event:Event):Void { if (__cursorPosition < 0) { @@ -1488,25 +1813,29 @@ class TextField extends InteractiveObject { __hasFocus = true; __dirty = true; + stage.addEventListener (MouseEvent.MOUSE_UP, stage_onMouseUp); + } - @:noCompletion private function this_onAddedToStage (event:Event):Void { - - stage.addEventListener (FocusEvent.FOCUS_OUT, stage_onFocusOut); + @:noCompletion private function this_onFocusOut (event:Event):Void { - __hiddenInput.addEventListener ('keydown', input_onKeyDown); - __hiddenInput.addEventListener ('keyup', input_onKeyUp); - __hiddenInput.addEventListener ('input', input_onKeyUp); - - addEventListener (MouseEvent.MOUSE_DOWN, this_onMouseDown); + __cursorPosition = -1; + __hasFocus = false; + __stopCursorTimer (); + if (__hiddenInput != null) __hiddenInput.blur (); + __dirty = true; } @:noCompletion private function this_onMouseDown (event:MouseEvent):Void { - __selectionStart = __getPosition (event.localX, event.localY); + if (!selectable) return; + + var localPoint = globalToLocal (new Point (event.stageX, event.stageY)); + __selectionStart = __getPosition (localPoint.x, localPoint.y); + __cursorPosition = __selectionStart; stage.addEventListener (MouseEvent.MOUSE_MOVE, stage_onMouseMove); stage.addEventListener (MouseEvent.MOUSE_UP, stage_onMouseUp); @@ -1516,11 +1845,14 @@ class TextField extends InteractiveObject { @:noCompletion private function this_onRemovedFromStage (event:Event):Void { - if (stage != null) stage.removeEventListener (FocusEvent.FOCUS_OUT, stage_onFocusOut); + removeEventListener (FocusEvent.FOCUS_IN, this_onFocusIn); + removeEventListener (FocusEvent.FOCUS_OUT, this_onFocusOut); + + this_onFocusOut (null); - if (__hiddenInput != null) __hiddenInput.removeEventListener ('keydown', input_onKeyDown); - if (__hiddenInput != null) __hiddenInput.removeEventListener ('keyup', input_onKeyUp); - if (__hiddenInput != null) __hiddenInput.removeEventListener ('input', input_onKeyUp); + if (__hiddenInput != null) __hiddenInput.removeEventListener ('keydown', input_onKeyDown, true); + if (__hiddenInput != null) __hiddenInput.removeEventListener ('keyup', input_onKeyUp, true); + if (__hiddenInput != null) __hiddenInput.removeEventListener ('input', input_onKeyUp, true); removeEventListener (MouseEvent.MOUSE_DOWN, this_onMouseDown); if (stage != null) stage.removeEventListener (MouseEvent.MOUSE_MOVE, stage_onMouseMove); @@ -1658,7 +1990,17 @@ class TextField extends InteractiveObject { if (segments.length == 1) { value = new EReg ("<.*?>", "g").replace (value, ""); - #if (js && html5) if (__hiddenInput != null) __hiddenInput.value = value; #end + #if (js && html5) + if (__text != value && __hiddenInput != null) { + + var selectionStart = __hiddenInput.selectionStart; + var selectionEnd = __hiddenInput.selectionEnd; + __hiddenInput.value = value; + __hiddenInput.selectionStart = selectionStart; + __hiddenInput.selectionEnd = selectionEnd; + + } + #end return __text = value; } else { @@ -1729,7 +2071,17 @@ class TextField extends InteractiveObject { } - #if (js && html5) if (__hiddenInput != null) __hiddenInput.value = value; #end + #if (js && html5) + if (__text != value && __hiddenInput != null) { + + var selectionStart = __hiddenInput.selectionStart; + var selectionEnd = __hiddenInput.selectionEnd; + __hiddenInput.value = value; + __hiddenInput.selectionStart = selectionStart; + __hiddenInput.selectionEnd = selectionEnd; + + } + #end return __text = value; } @@ -1760,7 +2112,22 @@ class TextField extends InteractiveObject { } - @:noCompletion public function get_text ():String { + @:noCompletion private function set_selectable (value:Bool):Bool { + + #if (js && html5) + if (!value && selectable && type == TextFieldType.INPUT) { + + this_onRemovedFromStage (null); + + } + #end + + return selectable = value; + + } + + + @:noCompletion private function get_text ():String { if (__isHTML) { @@ -1773,9 +2140,20 @@ class TextField extends InteractiveObject { } - @:noCompletion public function set_text (value:String):String { + @:noCompletion private function set_text (value:String):String { + + #if (js && html5) + if (__text != value && __hiddenInput != null) { + + var selectionStart = __hiddenInput.selectionStart; + var selectionEnd = __hiddenInput.selectionEnd; + __hiddenInput.value = value; + __hiddenInput.selectionStart = selectionStart; + __hiddenInput.selectionEnd = selectionEnd; + + } + #end - #if (js && html5) if (__text != value && __hiddenInput != null) __hiddenInput.value = value; #end if (__isHTML || __text != value) __dirty = true; __ranges = null; __isHTML = false; @@ -1784,14 +2162,14 @@ class TextField extends InteractiveObject { } - @:noCompletion public function get_textColor ():Int { + @:noCompletion private function get_textColor ():Int { return __textFormat.color; } - @:noCompletion public function set_textColor (value:Int):Int { + @:noCompletion private function set_textColor (value:Int):Int { if (value != __textFormat.color) __dirty = true; @@ -1809,10 +2187,9 @@ class TextField extends InteractiveObject { } - - @:noCompletion public function get_textWidth ():Float { + @:noCompletion private function get_textWidth ():Float { - #if js + #if (js && html5) if (__canvas != null) { @@ -1838,18 +2215,11 @@ class TextField extends InteractiveObject { } - #elseif (cpp || neko) + #elseif (cpp || neko || nodejs) - var sizes = __measureText (); - var total:Float = 0; - - for (size in sizes) { - - total += size; - - } - - return total; + //return the largest width of any given single line + //TODO: need to check actual left/right bounding volume in case of pathological cases (multiple format ranges for instance) + return __getLineWidth( -1); #else @@ -1860,9 +2230,9 @@ class TextField extends InteractiveObject { } - @:noCompletion public function get_textHeight ():Float { + @:noCompletion private function get_textHeight ():Float { - #if js + #if (js && html5) if (__canvas != null) { @@ -1884,14 +2254,23 @@ class TextField extends InteractiveObject { #else - return __textFormat.size * 1.185; + //sum the heights of all the lines, but don't count the leading of the last line + //TODO: might need robustness check for pathological cases (multiple format ranges) -- would need to change how line heights are calculated + var th = 0.0; + for (i in 0...numLines) { + th += __getLineMetric(i, LINE_HEIGHT); + if (i == numLines - 1) { + th -= __getLineMetric(i, LEADING); + } + } + return th; #end } - @:noCompletion public function set_type (value:TextFieldType):TextFieldType { + @:noCompletion private function set_type (value:TextFieldType):TextFieldType { if (value != type) { @@ -1916,7 +2295,7 @@ class TextField extends InteractiveObject { } - override public function get_width ():Float { + override private function get_width ():Float { if (autoSize == TextFieldAutoSize.LEFT) { @@ -1932,7 +2311,7 @@ class TextField extends InteractiveObject { } - override public function set_width (value:Float):Float { + override private function set_width (value:Float):Float { if (scaleX != 1 || __width != value) { @@ -1947,14 +2326,14 @@ class TextField extends InteractiveObject { } - @:noCompletion public function get_wordWrap ():Bool { + @:noCompletion private function get_wordWrap ():Bool { return wordWrap; } - @:noCompletion public function set_wordWrap (value:Bool):Bool { + @:noCompletion private function set_wordWrap (value:Bool):Bool { //if (value != wordWrap) __dirty = true; return wordWrap = value; diff --git a/openfl/ui/GameInput.hx b/openfl/ui/GameInput.hx new file mode 100644 index 0000000000..d458f080c2 --- /dev/null +++ b/openfl/ui/GameInput.hx @@ -0,0 +1,157 @@ +package openfl.ui; #if !flash + + +import lime.ui.Gamepad; +import lime.ui.GamepadAxis; +import lime.ui.GamepadButton; +import openfl.events.Event; +import openfl.events.EventDispatcher; +import openfl.events.GameInputEvent; + +@:access(openfl.ui.GameInputControl) +@:access(openfl.ui.GameInputDevice) + + +class GameInput extends EventDispatcher { + + + public static var isAvailable = true; + public static var numDevices (default, null) = 0; + + private static var __devices = new Map (); + private static var __instances = []; + + + public function new () { + + super (); + + __instances.push (this); + + } + + + public static function getDeviceAt (index:Int):GameInputDevice { + + if (Gamepad.devices.exists (index)) { + + return __devices.get (Gamepad.devices.get (index)); + + } + + return null; + + } + + + private static function __onGamepadAxisMove (gamepad:Gamepad, axis:GamepadAxis, value:Float):Void { + + var device = __devices.get (gamepad); + + if (device.enabled) { + + if (!device.__axis.exists (axis)) { + + var control = new GameInputControl (device, "AXIS_" + axis, -1, 1); + device.__axis.set (axis, control); + device.__controls.push (control); + + } + + var control = device.__axis.get (axis); + control.value = value; + control.dispatchEvent (new Event (Event.CHANGE)); + + } + + } + + + private static function __onGamepadButtonDown (gamepad:Gamepad, button:GamepadButton):Void { + + var device = __devices.get (gamepad); + + if (device.enabled) { + + if (!device.__button.exists (button)) { + + var control = new GameInputControl (device, "BUTTON_" + button, 0, 1); + device.__button.set (button, control); + device.__controls.push (control); + + } + + var control = device.__button.get (button); + control.value = 1; + control.dispatchEvent (new Event (Event.CHANGE)); + + } + + } + + + private static function __onGamepadButtonUp (gamepad:Gamepad, button:GamepadButton):Void { + + var device = __devices.get (gamepad); + + if (device.enabled) { + + if (!device.__button.exists (button)) { + + var control = new GameInputControl (device, "BUTTON_" + button, 0, 1); + device.__button.set (button, control); + device.__controls.push (control); + + } + + var control = device.__button.get (button); + control.value = 0; + control.dispatchEvent (new Event (Event.CHANGE)); + + } + + } + + + private static function __onGamepadConnect (gamepad:Gamepad):Void { + + var device = new GameInputDevice (gamepad.guid, gamepad.name); + __devices.set (gamepad, device); + numDevices = Lambda.count (__devices); + + for (instance in __instances) { + + instance.dispatchEvent (new GameInputEvent (GameInputEvent.DEVICE_ADDED, device)); + + } + + } + + + private static function __onGamepadDisconnect (gamepad:Gamepad):Void { + + var device = __devices.get (gamepad); + + if (device != null) { + + __devices.remove (gamepad); + numDevices = Lambda.count (__devices); + + for (instance in __instances) { + + instance.dispatchEvent (new GameInputEvent (GameInputEvent.DEVICE_REMOVED, device)); + + } + + } + + } + + + +} + + +#else +typedef GameInput = flash.ui.GameInput; +#end \ No newline at end of file diff --git a/openfl/ui/GameInputControl.hx b/openfl/ui/GameInputControl.hx new file mode 100644 index 0000000000..60421f0a5d --- /dev/null +++ b/openfl/ui/GameInputControl.hx @@ -0,0 +1,54 @@ +package openfl.ui; #if !flash + + +import openfl.events.EventDispatcher; + + +class GameInputControl extends EventDispatcher { + + + /** + * Returns the GameInputDevice object that contains this control. + */ + public var device (default, null):GameInputDevice; + + /** + * Returns the id of this control. + */ + public var id (default, null):String; + + /** + * Returns the maximum value for this control. + */ + public var maxValue (default, null):Float; + + /** + * Returns the minimum value for this control. + */ + public var minValue (default, null):Float; + + /** + * Returns the value for this control. + */ + public var value (default, null):Float; + + + private function new (device:GameInputDevice, id:String, minValue:Float, maxValue:Float, value:Float = 0) { + + super (); + + this.device = device; + this.id = id; + this.minValue = minValue; + this.maxValue = maxValue; + this.value = value; + + } + + +} + + +#else +typedef GameInputControl = flash.ui.GameInputControl; +#end \ No newline at end of file diff --git a/openfl/ui/GameInputDevice.hx b/openfl/ui/GameInputDevice.hx new file mode 100644 index 0000000000..7abaa2ae63 --- /dev/null +++ b/openfl/ui/GameInputDevice.hx @@ -0,0 +1,144 @@ +package openfl.ui; #if !flash + + +import lime.ui.Gamepad; +import openfl.utils.ByteArray; + +@:access(openfl.ui.GameInputControl) + + +class GameInputDevice { + + + public static var MAX_BUFFER_SIZE = 32000; + + /** + * Enables or disables this device. + */ + public var enabled:Bool; + + /** + * Returns the ID of this device. + */ + public var id (default, null):String; + + /** + * Returns the name of this device. + */ + public var name (default, null):String; + + /** + * Returns the number of controls on this device. + */ + public var numControls (get, never):Int; + + /** + * Specifies the rate (in milliseconds) at which to retrieve control values. + */ + public var sampleInterval:Int; + + @:noCompletion private var __axis = new Map (); + @:noCompletion private var __button = new Map (); + @:noCompletion private var __controls = new Array (); + @:noCompletion private var __gamepad:Gamepad; + + + @:noCompletion private function new (id:String, name:String) { + + this.id = id; + this.name = name; + + var control; + + for (i in 0...6) { + + control = new GameInputControl (this, "AXIS_" + i, -1, 1); + __axis.set (i, control); + __controls.push (control); + + } + + for (i in 0...15) { + + control = new GameInputControl (this, "BUTTON_" + i, 0, 1); + __button.set (i, control); + __controls.push (control); + + } + + } + + + /** + * Writes cached sample values to the ByteArray. + * @param data + * @param append + * @return + */ + public function getCachedSamples (data:ByteArray, append:Bool = false):Int { + + return 0; + + } + + + /** + * Retrieves a specific control from a device. + * @param i + * @return + */ + public function getControlAt (i:Int):GameInputControl { + + if (i >= 0 && i < __controls.length) { + + return __controls[i]; + + } + + return null; + + } + + + /** + * Requests this device to start keeping a cache of sampled values. + * @param numSamples + * @param controls + */ + public function startCachingSamples (numSamples:Int, controls:Vector):Void { + + + + } + + + /** + * Stops sample caching. + */ + public function stopCachingSamples ():Void { + + + + } + + + + + // Get & Set Methods + + + + + private function get_numControls ():Int { + + return __controls.length; + + } + + +} + + +#else +typedef GameInputDevice = flash.ui.GameInputDevice; +#end \ No newline at end of file diff --git a/openfl/ui/Multitouch.hx b/openfl/ui/Multitouch.hx index 6f0515b541..210390903b 100644 --- a/openfl/ui/Multitouch.hx +++ b/openfl/ui/Multitouch.hx @@ -4,7 +4,7 @@ package openfl.ui; #if !flash #if !openfl_legacy import openfl.ui.MultitouchInputMode; import openfl.Lib; -#if js +#if (js && html5) import js.Browser; #end @@ -149,7 +149,7 @@ class Multitouch { @:noCompletion private static function get_supportsTouchEvents ():Bool { - #if js + #if (js && html5) if (untyped __js__ ("('ontouchstart' in document.documentElement) || (window.DocumentTouch && document instanceof DocumentTouch)")) { return true; diff --git a/openfl/utils/Timer.hx b/openfl/utils/Timer.hx index 2e56a4c6a6..6e6693b0a1 100644 --- a/openfl/utils/Timer.hx +++ b/openfl/utils/Timer.hx @@ -6,7 +6,7 @@ import openfl.errors.Error; import openfl.events.EventDispatcher; import openfl.events.TimerEvent; -#if js +#if (js && html5) import js.Browser; #end @@ -142,7 +142,7 @@ class Timer extends EventDispatcher { running = true; - #if js + #if (js && html5) __timerID = Browser.window.setInterval (timer_onTimer, Std.int (__delay)); #else __timer = new HaxeTimer (__delay); @@ -164,7 +164,7 @@ class Timer extends EventDispatcher { running = false; - #if js + #if (js && html5) if (__timerID != null) { Browser.window.clearInterval (__timerID); diff --git a/templates/haxe/ApplicationMain.hx b/templates/haxe/ApplicationMain.hx index fdcf96fb64..68f4bd68b6 100644 --- a/templates/haxe/ApplicationMain.hx +++ b/templates/haxe/ApplicationMain.hx @@ -114,6 +114,7 @@ class ApplicationMain { file: "::APP_FILE::", fps: Std.int (::WIN_FPS::), fullscreen: ::WIN_FULLSCREEN::, + hardware: ::WIN_HARDWARE::, height: Std.int (::WIN_HEIGHT::), orientation: "::WIN_ORIENTATION::", packageName: "::META_PACKAGE_NAME::", diff --git a/templates/legacy/haxe/ApplicationMain.hx b/templates/legacy/haxe/ApplicationMain.hx index e74ed73d02..77e9b38515 100644 --- a/templates/legacy/haxe/ApplicationMain.hx +++ b/templates/legacy/haxe/ApplicationMain.hx @@ -74,7 +74,7 @@ class ApplicationMain { #elseif linux try { - if (!sys.FileSystem.exists (Sys.getCwd () + "/lime.ndll")) { + if (!sys.FileSystem.exists (Sys.getCwd () + "/lime-legacy.ndll")) { Sys.setCwd (haxe.io.Path.directory (Sys.executablePath ())); diff --git a/tests/test/openfl/display/BitmapDataTest.hx b/tests/test/openfl/display/BitmapDataTest.hx index 22a10da6ce..c8a0b9b1ea 100644 --- a/tests/test/openfl/display/BitmapDataTest.hx +++ b/tests/test/openfl/display/BitmapDataTest.hx @@ -250,6 +250,8 @@ class BitmapDataTest { var bitmapData = new BitmapData (100, 100, true, 0xFF000000); var bitmapData2 = new BitmapData (100, 100, true, 0xFFFF0000); + Assert.areEqual (hex (0xFF000000), hex (bitmapData.getPixel32 (0, 0))); + bitmapData.copyChannel (bitmapData2, bitmapData2.rect, new Point (), BitmapDataChannel.RED, BitmapDataChannel.RED); Assert.areEqual (hex (0xFFFF0000), hex (bitmapData.getPixel32 (0, 0))); @@ -474,6 +476,22 @@ class BitmapDataTest { } + @Test public function merge () { + + var color = 0xFF000000; + var color2 = 0xFFFF0000; + + var bitmapData = new BitmapData (100, 100, true, color); + var sourceBitmapData = new BitmapData (100, 100, true, color2); + + bitmapData.merge (sourceBitmapData, sourceBitmapData.rect, new Point (), 256, 256, 256, 256); + + var pixel = bitmapData.getPixel32 (1, 1); + Assert.areEqual (StringTools.hex (0xFFFF0000), StringTools.hex (pixel)); + + } + + @Test public function noise () { // TODO: Confirm functionality @@ -596,6 +614,7 @@ class BitmapDataTest { var expectedColor = color; if (sourceAlpha) { + // TODO: Native behavior is different than the flash target here. // The flash target premultiplies RGB by the alpha value. // If the native behavior is changed, this test needs to be @@ -603,8 +622,8 @@ class BitmapDataTest { if ((expectedColor & 0xFF000000) == 0) { expectedColor = 0; } - } - else { + + } else { // Surfaces that don't support alpha return FF for the alpha value, so // set our expected alpha to FF no matter what the initial value was expectedColor |= 0xFF000000; @@ -667,7 +686,9 @@ class BitmapDataTest { // TODO: Native targets do not match the flash behavior here. // If the native target is changed to match flash, // testGetSetPixels() must be changed to match. + #if !neko testGetSetPixels(0x80112233, true, true); + #end } @Test public function testGetAndSetPixelsOpqaueARGBToARGB() { @@ -679,7 +700,10 @@ class BitmapDataTest { } @Test public function testGetAndSetPixelsSemiARGBToRGB() { + // TODO + #if !neko testGetSetPixels(0x80112233, true, false); + #end } @Test public function testGetAndSetPixelsOpqaueARGBToRGB() { diff --git a/tests/test/openfl/display/DisplayObjectContainerTest.hx b/tests/test/openfl/display/DisplayObjectContainerTest.hx index 75dd40ecb2..fb79f4a612 100644 --- a/tests/test/openfl/display/DisplayObjectContainerTest.hx +++ b/tests/test/openfl/display/DisplayObjectContainerTest.hx @@ -145,6 +145,7 @@ class DisplayObjectContainerTest { var sprite = new Sprite (); var sprite2 = new Sprite (); + Assert.isTrue (sprite.contains (sprite)); Assert.isFalse (sprite.contains (sprite2)); sprite.addChild (sprite2); @@ -152,16 +153,22 @@ class DisplayObjectContainerTest { Assert.isTrue (sprite.contains (sprite2)); var sprite3 = new Sprite (); + var sprite4 = new Sprite (); + sprite3.addChild (sprite4); sprite.addChild (sprite3); Assert.isTrue (sprite.contains (sprite3)); + Assert.isTrue (sprite.contains (sprite4)); + Assert.isFalse (sprite3.contains (sprite)); + Assert.isFalse (sprite4.contains (sprite)); sprite.removeChild (sprite3); sprite.removeChild (sprite2); Assert.isFalse (sprite.contains (sprite2)); Assert.isFalse (sprite.contains (sprite3)); + Assert.isFalse (sprite.contains (sprite4)); }