diff --git a/source/openfl/display/BitmapData.hx b/source/openfl/display/BitmapData.hx new file mode 100644 index 00000000..fb3ba480 --- /dev/null +++ b/source/openfl/display/BitmapData.hx @@ -0,0 +1,3455 @@ +package openfl.display; + +#if !flash +import openfl.display._internal.IBitmapDrawableType; +import openfl.display._internal.PerlinNoise; +import openfl.display3D._internal.GLFramebuffer; +import openfl.display3D._internal.GLRenderbuffer; +import openfl.display3D.textures.TextureBase; +import openfl.display3D.Context3DClearMask; +import openfl.display3D.Context3D; +import openfl.display3D.IndexBuffer3D; +import openfl.display3D.VertexBuffer3D; +import openfl.errors.Error; +import openfl.filters.BitmapFilter; +import openfl.geom.ColorTransform; +import openfl.geom.Matrix; +import openfl.geom.Point; +import openfl.geom.Rectangle; +import openfl.utils._internal.Float32Array; +import openfl.utils._internal.UInt8Array; +import openfl.utils._internal.UInt16Array; +import openfl.utils.ByteArray; +import openfl.utils.Endian; +import openfl.utils.Future; +import openfl.utils.Object; +import openfl.Lib; +import openfl.Vector; +#if lime +import lime._internal.graphics.ImageCanvasUtil; // TODO +import lime.app.Application; +import lime.graphics.cairo.CairoImageSurface; +import lime.graphics.cairo.CairoSurface; +import lime.graphics.cairo.Cairo; +import lime.graphics.Image; +import lime.graphics.ImageChannel; +import lime.graphics.ImageBuffer; +import lime.graphics.RenderContext; +import lime.math.ARGB; +import lime.math.Vector2; +#end +#if (js && html5) +import js.html.CanvasElement; +#end +#if gl_stats +import openfl.display._internal.stats.Context3DStats; +import openfl.display._internal.stats.DrawCallContext; +#end + +/** + The BitmapData class lets you work with the data (pixels) of a Bitmap + object. You can use the methods of the BitmapData class to create + arbitrarily sized transparent or opaque bitmap images and manipulate them + in various ways at runtime. You can also access the BitmapData for a bitmap + image that you load with the `openfl.Assets` or + `openfl.display.Loader` classes. + + This class lets you separate bitmap rendering operations from the + internal display updating routines of OpenFL. By manipulating a + BitmapData object directly, you can create complex images without incurring + the per-frame overhead of constantly redrawing the content from vector + data. + + The methods of the BitmapData class support effects that are not + available through the filters available to non-bitmap display objects. + + A BitmapData object contains an array of pixel data. This data can + represent either a fully opaque bitmap or a transparent bitmap that + contains alpha channel data. Either type of BitmapData object is stored as + a buffer of 32-bit integers. Each 32-bit integer determines the properties + of a single pixel in the bitmap. + + Each 32-bit integer is a combination of four 8-bit channel values (from + 0 to 255) that describe the alpha transparency and the red, green, and blue + (ARGB) values of the pixel. (For ARGB values, the most significant byte + represents the alpha channel value, followed by red, green, and blue.) + + The four channels (alpha, red, green, and blue) are represented as + numbers when you use them with the `BitmapData.copyChannel()` + method or the `DisplacementMapFilter.componentX` and + `DisplacementMapFilter.componentY` properties, and these numbers + are represented by the following constants in the BitmapDataChannel + class: + + * `BitmapDataChannel.ALPHA` + * `BitmapDataChannel.RED` + * `BitmapDataChannel.GREEN` + * `BitmapDataChannel.BLUE` + + You can attach BitmapData objects to a Bitmap object by using the + `bitmapData` property of the Bitmap object. + + You can use a BitmapData object to fill a Graphics object by using the + `Graphics.beginBitmapFill()` method. + + You can also use a BitmapData object to perform batch tile rendering + using the `openfl.display.Tilemap` class. + + In Flash Player 10, the maximum size for a BitmapData object + is 8,191 pixels in width or height, and the total number of pixels cannot + exceed 16,777,215 pixels. (So, if a BitmapData object is 8,191 pixels wide, + it can only be 2,048 pixels high.) In Flash Player 9 and earlier, the limitation + is 2,880 pixels in height and 2,880 in width. + + @see [Working with bitmaps](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/) + @see [The Bitmap and BitmapData classes](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/the-bitmap-and-bitmapdata-classes.html) + @see [Manipulating pixels](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/manipulating-pixels.html) + @see [Copying bitmap data](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/copying-bitmap-data.html) + @see [Compressing bitmap data](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/compressing-bitmap-data.html) + @see [Making textures with noise functions](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/making-textures-with-noise-functions.html) + @see [Scrolling bitmaps](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/scrolling-bitmaps.html) + @see `openfl.display.Bitmap.bitmapData` + @see `openfl.display.Graphics.beginBitmapFill()` + @see `openfl.display.Graphics.lineBitmapStyle()` +**/ +@:access(lime.graphics.opengl.GL) +@:access(lime.graphics.Image) +@:access(lime.graphics.ImageBuffer) +@:access(lime.math.Rectangle) +@:access(openfl.display3D.textures.TextureBase) +@:access(openfl.display3D.Context3D) +@:access(openfl.display.DisplayObject) +@:access(openfl.display.DisplayObjectShader) +@:access(openfl.display.Graphics) +@:access(openfl.display.Shader) +@:access(openfl.filters.BitmapFilter) +@:access(openfl.geom.ColorTransform) +@:access(openfl.geom.Matrix) +@:access(openfl.geom.Point) +@:access(openfl.geom.Rectangle) +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:autoBuild(openfl.utils._internal.AssetsMacro.embedBitmap()) +class BitmapData implements IBitmapDrawable +{ + @:noCompletion private static inline var VERTEX_BUFFER_STRIDE:Int = 14; + @:noCompletion private static var __supportsBGRA:Null = null; + @:noCompletion private static var __textureFormat:Int; + @:noCompletion private static var __textureInternalFormat:Int; + #if lime + @:noCompletion private static var __tempVector:Vector2 = new Vector2(); + @:noCompletion private static var __fillRectRectangle:Rectangle = new Rectangle(); + #end + + /** + The height of the bitmap image in pixels. + **/ + public var height(default, null):Int; + + /** + The Lime image that holds the pixels for the current image. + + In Flash Player, this property is always `null`. + **/ + @SuppressWarnings("checkstyle:Dynamic") + public var image(default, null):#if lime Image #else Dynamic #end; + + // #if !flash_doc_gen + + /** + Defines whether the bitmap image is readable. Hardware-only bitmap images + do not support `getPixels`, `setPixels` and other + BitmapData methods, nor may they be used with + `Graphics.beginBitmapFill`. However, hardware-only bitmap images may + still be used inside a Bitmap object or other display objects that do + not need to modify the pixels. + + As an exception to the rule, `bitmapData.draw` is supported for + non-readable bitmap images. + + Since non-readable bitmap images do not have a software image buffer, they + will need to be recreated if the current hardware rendering context is lost. + **/ + @:beta public var readable(default, null):Bool; + + // #end + + /** + The rectangle that defines the size and location of the bitmap image. The + top and left of the rectangle are 0; the width and height are equal to the + width and height in pixels of the BitmapData object. + **/ + public var rect(default, null):Rectangle; + + /** + Defines whether the bitmap image supports per-pixel transparency. You can + set this value only when you construct a BitmapData object by passing in + `true` for the `transparent` parameter of the + constructor. Then, after you create a BitmapData object, you can check + whether it supports per-pixel transparency by determining if the value of + the `transparent` property is `true`. + **/ + public var transparent(default, null):Bool; + + /** + The width of the bitmap image in pixels. + **/ + public var width(default, null):Int; + + @:noCompletion private var __blendMode:BlendMode; + @:noCompletion private var __drawableType:IBitmapDrawableType; + // @:noCompletion private var __vertexBufferColorTransform:ColorTransform; + // @:noCompletion private var __vertexBufferAlpha:Float; + @:noCompletion private var __framebuffer:GLFramebuffer; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __framebufferContext:#if lime RenderContext #else Dynamic #end; + @:noCompletion private var __indexBuffer:IndexBuffer3D; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __indexBufferContext:#if lime RenderContext #else Dynamic #end; + @:noCompletion private var __indexBufferData:UInt16Array; + @:noCompletion private var __indexBufferGrid:Rectangle; + @:noCompletion private var __isMask:Bool; + @:noCompletion private var __isValid:Bool; + @:noCompletion private var __mask:DisplayObject; + @:noCompletion private var __renderable:Bool; + @:noCompletion private var __renderTransform:Matrix; + @:noCompletion private var __scrollRect:Rectangle; + @:noCompletion private var __stencilBuffer:GLRenderbuffer; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __surface:#if lime CairoSurface #else Dynamic #end; + @:noCompletion private var __texture:TextureBase; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __textureContext:#if lime RenderContext #else Dynamic #end; + @:noCompletion private var __textureHeight:Int; + @:noCompletion private var __textureVersion:Int; + @:noCompletion private var __textureWidth:Int; + @:noCompletion private var __transform:Matrix; + @:noCompletion private var __uvRect:Rectangle; + @:noCompletion private var __vertexBuffer:VertexBuffer3D; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __vertexBufferContext:#if lime RenderContext #else Dynamic #end; + @:noCompletion private var __vertexBufferData:Float32Array; + @:noCompletion private var __vertexBufferGrid:Rectangle; + @:noCompletion private var __vertexBufferHeight:Float; + @:noCompletion private var __vertexBufferScaleX:Float; + @:noCompletion private var __vertexBufferScaleY:Float; + @:noCompletion private var __vertexBufferWidth:Float; + @:noCompletion private var __worldAlpha:Float; + @:noCompletion private var __worldColorTransform:ColorTransform; + @:noCompletion private var __worldTransform:Matrix; + @:noCompletion private var __renderer:OpenGLRenderer; + + /** + Creates a BitmapData object with a specified width and height. If you specify a value for + the `fillColor` parameter, every pixel in the bitmap is set to that color. + + By default, the bitmap is created as transparent, unless you pass the value `false` + for the transparent parameter. After you create an opaque bitmap, you cannot change it + to a transparent bitmap. Every pixel in an opaque bitmap uses only 24 bits of color channel + information. If you define the bitmap as transparent, every pixel uses 32 bits of color + channel information, including an alpha transparency channel. + + @param width The width of the bitmap image in pixels. + @param height The height of the bitmap image in pixels. + @param transparent Specifies whether the bitmap image supports per-pixel transparency. The default value is `true`(transparent). To create a fully transparent bitmap, set the value of the `transparent` parameter to `true` and the value of the `fillColor` parameter to 0x00000000 (or to 0). Setting the `transparent` property to `false` can result in minor improvements in rendering performance. + @param fillColor A 32-bit ARGB color value that you use to fill the bitmap image area. The default value is 0xFFFFFFFF (solid white). + **/ + public function new(width:Int, height:Int, transparent:Bool = true, fillColor:UInt = 0xFFFFFFFF) + { + __drawableType = BITMAP_DATA; + + this.transparent = transparent; + + #if (neko || (js && html5)) + width = width == null ? 0 : width; + height = height == null ? 0 : height; + #end + + width = width < 0 ? 0 : width; + height = height < 0 ? 0 : height; + + this.width = width; + this.height = height; + rect = new Rectangle(0, 0, width, height); + + __textureWidth = width; + __textureHeight = height; + + if (width > 0 && height > 0) + { + if (transparent) + { + if ((fillColor & 0xFF000000) == 0) + { + fillColor = 0; + } + } + else + { + fillColor = (0xFF << 24) | (fillColor & 0xFFFFFF); + } + + fillColor = (fillColor << 8) | ((fillColor >> 24) & 0xFF); + + #if lime + #if sys + var buffer = new ImageBuffer(new UInt8Array(width * height * 4), width, height); + buffer.format = BGRA32; + buffer.premultiplied = true; + + image = new Image(buffer, 0, 0, width, height); + + if (fillColor != 0) + { + image.fillRect(image.rect, fillColor); + } + // #elseif (js && html5) + // var buffer = new ImageBuffer (null, width, height); + // var canvas:CanvasElement = cast Browser.document.createElement ("canvas"); + // buffer.__srcCanvas = canvas; + // buffer.__srcContext = canvas.getContext ("2d"); + // + // image = new Image (buffer, 0, 0, width, height); + // image.type = CANVAS; + // + // if (fillColor != 0) { + // + // image.fillRect (image.rect, fillColor); + // + // } + #else + image = new Image(null, 0, 0, width, height, fillColor); + #end + + image.transparent = transparent; + #end + + __isValid = true; + readable = true; + } + + __renderTransform = new Matrix(); + __worldAlpha = 1; + __worldTransform = new Matrix(); + __worldColorTransform = new ColorTransform(); + __renderable = true; + } + + /** + Takes a source image and a filter object and generates the filtered image. + + This method relies on the behavior of built-in filter objects, which determine the + destination rectangle that is affected by an input source rectangle. + + After a filter is applied, the resulting image can be larger than the input image. + For example, if you use a BlurFilter class to blur a source rectangle of(50,50,100,100) + and a destination point of(10,10), the area that changes in the destination image is + larger than(10,10,60,60) because of the blurring. This happens internally during the + applyFilter() call. + + If the `sourceRect` parameter of the sourceBitmapData parameter is an + interior region, such as(50,50,100,100) in a 200 x 200 image, the filter uses the source + pixels outside the `sourceRect` parameter to generate the destination rectangle. + + If the BitmapData object and the object specified as the `sourceBitmapData` + parameter are the same object, the application uses a temporary copy of the object to + perform the filter. For best performance, avoid this situation. + + @param sourceBitmapData The input bitmap image to use. The source image can be a different BitmapData object or it can refer to the current BitmapData instance. + @param sourceRect A rectangle that defines the area of the source image to use as input. + @param destPoint The point within the destination image (the current BitmapData instance) that corresponds to the upper-left corner of the source rectangle. + @param filter The filter object that you use to perform the filtering operation. + **/ + public function applyFilter(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, filter:BitmapFilter):Void + { + if (!readable || sourceBitmapData == null || !sourceBitmapData.readable) return; + + // TODO: Ways to optimize this? + + var needSecondBitmapData = filter.__needSecondBitmapData; + var needCopyOfOriginal = filter.__preserveObject; + + var bitmapData2:BitmapData = null; + var bitmapData3:BitmapData = null; + + if (needSecondBitmapData) + { + bitmapData2 = new BitmapData(width, height, true, 0); + } + else + { + bitmapData2 = this; + } + + if (needCopyOfOriginal) + { + bitmapData3 = new BitmapData(width, height, true, 0); + } + + if (filter.__preserveObject) + { + bitmapData3.copyPixels(this, rect, destPoint); + } + + var lastBitmap = filter.__applyFilter(bitmapData2, this, sourceRect, destPoint); + + if (filter.__preserveObject) + { + lastBitmap.draw(bitmapData3, null, null); + } + + if (needSecondBitmapData && lastBitmap == bitmapData2) + { + bitmapData2.image.version = image.version; + image = bitmapData2.image; + } + + image.dirty = true; + image.version++; + } + + /** + Returns a new BitmapData object that is a clone of the original instance with an exact copy of the contained bitmap. + @return A new BitmapData object that is identical to the original. + **/ + public function clone():BitmapData + { + #if lime + var bitmapData:BitmapData; + + if (!__isValid) + { + bitmapData = new BitmapData(width, height, transparent, 0); + } + else if (!readable && image == null) + { + bitmapData = new BitmapData(0, 0, transparent, 0); + + bitmapData.width = width; + bitmapData.height = height; + bitmapData.__textureWidth = __textureWidth; + bitmapData.__textureHeight = __textureHeight; + bitmapData.rect.copyFrom(rect); + + bitmapData.__framebuffer = __framebuffer; + bitmapData.__framebufferContext = __framebufferContext; + bitmapData.__texture = __texture; + bitmapData.__textureContext = __textureContext; + bitmapData.__isValid = true; + } + else + { + bitmapData = BitmapData.fromImage(image.clone(), transparent); + } + + bitmapData.__worldTransform.copyFrom(__worldTransform); + bitmapData.__renderTransform.copyFrom(__renderTransform); + + return bitmapData; + #else + return null; + #end + } + + /** + Adjusts the color values in a specified area of a bitmap image by using a `ColorTransform` + object. If the rectangle matches the boundaries of the bitmap image, this method transforms the color + values of the entire image. + @param rect A Rectangle object that defines the area of the image in which the ColorTransform object is applied. + @param colorTransform A ColorTransform object that describes the color transformation values to apply. + **/ + public function colorTransform(rect:Rectangle, colorTransform:ColorTransform):Void + { + if (!readable) return; + + #if lime + image.colorTransform(rect.__toLimeRectangle(), colorTransform.__toLimeColorMatrix()); + #end + } + + /** + Compares two BitmapData objects. If the two BitmapData objects have the same dimensions (width and height), the method returns a new BitmapData object, in which each pixel is the "difference" between the pixels in the two source objects: + + - If two pixels are equal, the difference pixel is 0x00000000. + - If two pixels have different RGB values (ignoring the alpha value), the difference pixel is 0xFFRRGGBB where RR/GG/BB are the individual difference values between red, green, and blue channels. Alpha channel differences are ignored in this case. + - If only the alpha channel value is different, the pixel value is 0xZZFFFFFF, where ZZ is the difference in the alpha value. + + @param otherBitmapData The BitmapData object to compare with the source BitmapData object. + @return If the two BitmapData objects have the same dimensions (width and height), the method returns a new BitmapData object that has the difference between the two objects (see the main discussion).If the BitmapData objects are equivalent, the method returns the number 0. If no argument is passed or if the argument is not a BitmapData object, the method returns -1. If either BitmapData object has been disposed of, the method returns -2. If the widths of the BitmapData objects are not equal, the method returns the number -3. If the heights of the BitmapData objects are not equal, the method returns the number -4. + **/ + @SuppressWarnings("checkstyle:Dynamic") + public function compare(otherBitmapData:BitmapData):Dynamic + { + #if lime + if (otherBitmapData == this) + { + return 0; + } + else if (otherBitmapData == null) + { + return -1; + } + else if (readable == false || otherBitmapData.readable == false) + { + return -2; + } + else if (width != otherBitmapData.width) + { + return -3; + } + else if (height != otherBitmapData.height) + { + return -4; + } + + if (image != null && otherBitmapData.image != null && image.format == otherBitmapData.image.format) + { + var bytes = image.data; + var otherBytes = otherBitmapData.image.data; + var equal = true; + + for (i in 0...bytes.length) + { + if (bytes[i] != otherBytes[i]) + { + equal = false; + break; + } + } + + if (equal) + { + return 0; + } + } + + var bitmapData:BitmapData = null; + var foundDifference, + pixel:ARGB, + otherPixel:ARGB, + comparePixel:ARGB, + r, + g, + b, + a; + + for (y in 0...height) + { + for (x in 0...width) + { + foundDifference = false; + + pixel = getPixel32(x, y); + otherPixel = otherBitmapData.getPixel32(x, y); + comparePixel = 0; + + if (pixel != otherPixel) + { + r = pixel.r - otherPixel.r; + g = pixel.g - otherPixel.g; + b = pixel.b - otherPixel.b; + + if (r < 0) r *= -1; + if (g < 0) g *= -1; + if (b < 0) b *= -1; + + if (r == 0 && g == 0 && b == 0) + { + a = pixel.a - otherPixel.a; + + if (a != 0) + { + comparePixel.r = 0xFF; + comparePixel.g = 0xFF; + comparePixel.b = 0xFF; + comparePixel.a = a; + + foundDifference = true; + } + } + else + { + comparePixel.r = r; + comparePixel.g = g; + comparePixel.b = b; + comparePixel.a = 0xFF; + + foundDifference = true; + } + } + + if (foundDifference) + { + if (bitmapData == null) + { + bitmapData = new BitmapData(width, height, transparent || otherBitmapData.transparent, 0x00000000); + } + + bitmapData.setPixel32(x, y, comparePixel); + } + } + } + + if (bitmapData == null) + { + return 0; + } + + return bitmapData; + #else + return 0; + #end + } + + /** + Transfers data from one channel of another BitmapData object or the + current BitmapData object into a channel of the current BitmapData object. + All of the data in the other channels in the destination BitmapData object + are preserved. + + The source channel value and destination channel value can be one of + following values: + + * `BitmapDataChannel.RED` + * `BitmapDataChannel.GREEN` + * `BitmapDataChannel.BLUE` + * `BitmapDataChannel.ALPHA` + + + @param sourceBitmapData The input bitmap image to use. The source image + can be a different BitmapData object or it can + refer to the current BitmapData object. + @param sourceRect The source Rectangle object. To copy only channel + data from a smaller area within the bitmap, + specify a source rectangle that is smaller than + the overall size of the BitmapData object. + @param destPoint The destination Point object that represents the + upper-left corner of the rectangular area where + the new channel data is placed. To copy only + channel data from one area to a different area in + the destination image, specify a point other than + (0,0). + @param sourceChannel The source channel. Use a value from the + BitmapDataChannel class + (`BitmapDataChannel.RED`, + `BitmapDataChannel.BLUE`, + `BitmapDataChannel.GREEN`, + `BitmapDataChannel.ALPHA`). + @param destChannel The destination channel. Use a value from the + BitmapDataChannel class + (`BitmapDataChannel.RED`, + `BitmapDataChannel.BLUE`, + `BitmapDataChannel.GREEN`, + `BitmapDataChannel.ALPHA`). + @throws TypeError The sourceBitmapData, sourceRect or destPoint are null. + + @see [Copying bitmap data](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/copying-bitmap-data.html) + **/ + public function copyChannel(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, sourceChannel:BitmapDataChannel, + destChannel:BitmapDataChannel):Void + { + if (!readable) return; + + #if lime + var sourceChannel = switch (sourceChannel) + { + case 1: ImageChannel.RED; + case 2: ImageChannel.GREEN; + case 4: ImageChannel.BLUE; + case 8: ImageChannel.ALPHA; + default: return; + } + + var destChannel = switch (destChannel) + { + case 1: ImageChannel.RED; + case 2: ImageChannel.GREEN; + case 4: ImageChannel.BLUE; + case 8: ImageChannel.ALPHA; + default: return; + } + + image.copyChannel(sourceBitmapData.image, sourceRect.__toLimeRectangle(), destPoint.__toLimeVector2(), sourceChannel, destChannel); + #end + } + + /** + Provides a fast routine to perform pixel manipulation between images with + no stretching, rotation, or color effects. This method copies a + rectangular area of a source image to a rectangular area of the same size + at the destination point of the destination BitmapData object. + + If you include the `alphaBitmap` and `alphaPoint` + parameters, you can use a secondary image as an alpha source for the + source image. If the source image has alpha data, both sets of alpha data + are used to composite pixels from the source image to the destination + image. The `alphaPoint` parameter is the point in the alpha + image that corresponds to the upper-left corner of the source rectangle. + Any pixels outside the intersection of the source image and alpha image + are not copied to the destination image. + + The `mergeAlpha` property controls whether or not the alpha + channel is used when a transparent image is copied onto another + transparent image. To copy pixels with the alpha channel data, set the + `mergeAlpha` property to `true`. By default, the + `mergeAlpha` property is `false`. + + @param sourceBitmapData The input bitmap image from which to copy pixels. + The source image can be a different BitmapData + instance, or it can refer to the current + BitmapData instance. + @param sourceRect A rectangle that defines the area of the source + image to use as input. + @param destPoint The destination point that represents the + upper-left corner of the rectangular area where + the new pixels are placed. + @param alphaBitmapData A secondary, alpha BitmapData object source. + @param alphaPoint The point in the alpha BitmapData object source + that corresponds to the upper-left corner of the + `sourceRect` parameter. + @param mergeAlpha To use the alpha channel, set the value to + `true`. To copy pixels with no alpha + channel, set the value to `false`. + @throws TypeError The sourceBitmapData, sourceRect, destPoint are null. + + @see [Copying bitmap data](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/copying-bitmap-data.html) + **/ + public function copyPixels(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, alphaBitmapData:BitmapData = null, alphaPoint:Point = null, + mergeAlpha:Bool = false):Void + { + if (!readable || sourceBitmapData == null) return; + + #if lime + if (alphaPoint != null) + { + __tempVector.x = alphaPoint.x; + __tempVector.y = alphaPoint.y; + } + + image.copyPixels(sourceBitmapData.image, sourceRect.__toLimeRectangle(), destPoint.__toLimeVector2(), + alphaBitmapData != null ? alphaBitmapData.image : null, alphaPoint != null ? __tempVector : null, mergeAlpha); + #end + } + + // @:noCompletion @:dox(hide) @:require(flash11_4) public function copyPixelsToByteArray (rect:Rectangle, data:ByteArray):Void; + + /** + Frees memory that is used to store the BitmapData object. + + When the `dispose()` method is called on an image, the width + and height of the image are set to 0. All subsequent calls to methods or + properties of this BitmapData instance fail, and an exception is thrown. + + + `BitmapData.dispose()` releases the memory occupied by the + actual bitmap data, immediately(a bitmap can consume up to 64 MB of + memory). After using `BitmapData.dispose()`, the BitmapData + object is no longer usable and an exception may be thrown if + you call functions on the BitmapData object. However, + `BitmapData.dispose()` does not garbage collect the BitmapData + object(approximately 128 bytes); the memory occupied by the actual + BitmapData object is released at the time the BitmapData object is + collected by the garbage collector. + + **/ + public function dispose():Void + { + image = null; + + width = 0; + height = 0; + rect = null; + + __isValid = false; + readable = false; + + __surface = null; + + __vertexBuffer = null; + __framebuffer = null; + __framebufferContext = null; + __texture = null; + __textureContext = null; + + // if (__texture != null) { + // + // var renderer = @:privateAccess Lib.current.stage.__renderer; + // + // if(renderer != null) { + // + // var renderer = @:privateAccess renderer.renderer; + // var gl = renderer.__gl; + // + // if (gl != null) { + // + // gl.deleteTexture (__texture); + // __texture = null; + // + // } + // + // } + // + // } + } + + /** + Frees the backing Lime image buffer, if possible. + + When using a software renderer, such as Flash Player or desktop targets + without OpenGL, the software buffer will be retained so that the BitmapData + will work properly. When using a hardware renderer, the Lime image + buffer will be available to garbage collection after a hardware texture + has been created internally. + + `BitmapData.disposeImage()` will immediately change the value of + the `readable` property to `false`. + **/ + @:beta public function disposeImage():Void + { + readable = false; + } + + /** + Draws the `source` display object onto the bitmap image, using + the OpenFL software renderer. You can specify `matrix`, + `colorTransform`, `blendMode`, and a destination + `clipRect` parameter to control how the rendering performs. + Optionally, you can specify whether the bitmap should be smoothed when + scaled(this works only if the source object is a BitmapData object). + + The source display object does not use any of its applied + transformations for this call. It is treated as it exists in the library + or file, with no matrix transform, no color transform, and no blend mode. + To draw a display object (such as a movie clip) by using its own transform + properties, you can copy its `transform` property object to the + `transform` property of the Bitmap object that uses the + BitmapData object. + + @param source The display object or BitmapData object to draw to + the BitmapData object.(The DisplayObject and + BitmapData classes implement the IBitmapDrawable + interface.) + @param matrix A Matrix object used to scale, rotate, or translate + the coordinates of the bitmap. If you do not want to + apply a matrix transformation to the image, set this + parameter to an identity matrix, created with the + default `new Matrix()` constructor, or + pass a `null` value. + @param colorTransform A ColorTransform object that you use to adjust the + color values of the bitmap. If no object is + supplied, the bitmap image's colors are not + transformed. If you must pass this parameter but you + do not want to transform the image, set this + parameter to a ColorTransform object created with + the default `new ColorTransform()` + constructor. + @param blendMode A string value, from the openfl.display.BlendMode + class, specifying the blend mode to be applied to + the resulting bitmap. + @param clipRect A Rectangle object that defines the area of the + source object to draw. If you do not supply this + value, no clipping occurs and the entire source + object is drawn. + @param smoothing A Boolean value that determines whether a BitmapData + object is smoothed when scaled or rotated, due to a + scaling or rotation in the `matrix` + parameter. The `smoothing` parameter only + applies if the `source` parameter is a + BitmapData object. With `smoothing` set + to `false`, the rotated or scaled + BitmapData image can appear pixelated or jagged. For + example, the following two images use the same + BitmapData object for the `source` + parameter, but the `smoothing` parameter + is set to `true` on the left and + `false` on the right: + + ![Two images: the left one with smoothing and the right one without smoothing.](/images/bitmapData_draw_smoothing.jpg) + + Drawing a bitmap with `smoothing` set + to `true` takes longer than doing so with + `smoothing` set to + `false`. + @throws ArgumentError The `source` parameter is not a + BitmapData or DisplayObject object. + @throws ArgumentError The source is null or not a valid IBitmapDrawable + object. + @throws SecurityError The `source` object and (in the case of a + Sprite or MovieClip object) all of its child objects + do not come from the same domain as the caller, or + are not in a content that is accessible to the + caller by having called the + `Security.allowDomain()` method. This + restriction does not apply to AIR content in the + application security sandbox. + + @see [Copying bitmap data](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/copying-bitmap-data.html) + **/ + public function draw(source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:BlendMode = null, + clipRect:Rectangle = null, smoothing:Bool = false):Void + { + if (source == null) return; + + var wasVisible = true; + var sourceAsDisplayObject:DisplayObject = null; + if (#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (source, DisplayObject)) + { + sourceAsDisplayObject = cast(source, DisplayObject); + if (!sourceAsDisplayObject.visible) + { + wasVisible = false; + sourceAsDisplayObject.visible = true; + } + } + + source.__update(false, true); + + var transform = Matrix.__pool.get(); + + transform.copyFrom(source.__renderTransform); + transform.invert(); + + if (matrix != null) + { + transform.concat(matrix); + } + + var clipMatrix:Matrix = null; + + if (clipRect != null) + { + clipMatrix = Matrix.__pool.get(); + clipMatrix.copyFrom(transform); + clipMatrix.invert(); + } + + var _colorTransform = new ColorTransform(); + _colorTransform.__copyFrom(source.__worldColorTransform); + _colorTransform.__invert(); + + if (!readable && Lib.current.stage.context3D != null) + { + if (__textureContext == null) + { + // TODO: Some way to select current GL context for renderer? + __textureContext = Application.current.window.context; + } + + if (colorTransform != null) + { + _colorTransform.__combine(colorTransform); + } + + if (__renderer == null) + { + __renderer = new OpenGLRenderer(Lib.current.stage.context3D, this); + } + else + { + @:privateAccess __renderer.__cleanup(); + __renderer.setShader(@:privateAccess __renderer.__defaultShader); + } + __renderer.__allowSmoothing = smoothing; + __renderer.__pixelRatio = #if openfl_disable_hdpi 1 #else Lib.current.stage.window.scale #end; + __renderer.__overrideBlendMode = blendMode; + + __renderer.__worldTransform = transform; + __renderer.__worldAlpha = 1 / source.__worldAlpha; + __renderer.__worldColorTransform = _colorTransform; + + __renderer.__resize(width, height); + + if (clipRect != null) + { + __renderer.__pushMaskRect(clipRect, clipMatrix); + } + + __drawGL(source, __renderer); + + if (clipRect != null) + { + __renderer.__popMaskRect(); + Matrix.__pool.release(clipMatrix); + } + } + else + { + #if ((js && html5) || lime_cairo) + if (colorTransform != null) + { + var bounds = Rectangle.__pool.get(); + var boundsMatrix = Matrix.__pool.get(); + + source.__getBounds(bounds, boundsMatrix); + + var width:Int = Math.ceil(bounds.width); + var height:Int = Math.ceil(bounds.height); + + boundsMatrix.tx = -bounds.x; + boundsMatrix.ty = -bounds.y; + + var copy = new BitmapData(width, height, true, 0); + copy.draw(source, boundsMatrix); + + copy.colorTransform(copy.rect, colorTransform); + copy.__renderTransform.identity(); + copy.__renderTransform.tx = bounds.x; + copy.__renderTransform.ty = bounds.y; + copy.__renderTransform.concat(source.__renderTransform); + copy.__worldAlpha = source.__worldAlpha; + copy.__worldColorTransform.__copyFrom(source.__worldColorTransform); + source = copy; + + Rectangle.__pool.release(bounds); + Matrix.__pool.release(boundsMatrix); + } + + #if (js && html5) + ImageCanvasUtil.convertToCanvas(image); + var renderer = new CanvasRenderer(image.buffer.__srcContext); + #else + var renderer = new CairoRenderer(new Cairo(getSurface())); + #end + + renderer.__allowSmoothing = smoothing; + renderer.__overrideBlendMode = blendMode; + + renderer.__worldTransform = transform; + renderer.__worldAlpha = 1 / source.__worldAlpha; + renderer.__worldColorTransform = _colorTransform; + + if (clipRect != null) + { + renderer.__pushMaskRect(clipRect, clipMatrix); + } + + #if (js && html5) + __drawCanvas(source, renderer); + #else + __drawCairo(source, renderer); + #end + + if (clipRect != null) + { + renderer.__popMaskRect(); + Matrix.__pool.release(clipMatrix); + } + #end + } + + Matrix.__pool.release(transform); + + if (sourceAsDisplayObject != null && !wasVisible) + { + sourceAsDisplayObject.visible = false; + } + } + + /** + Draws the `source` display object onto the bitmap image, using the Flash runtime + vector renderer. You can specify `matrix`, `colorTransform`, `blendMode`, and a + destination `clipRect` parameter to control how the rendering performs. + Optionally, you can specify whether the bitmap should be smoothed when scaled + (this works only if the source object is a BitmapData object). + + **Note:** The `drawWithQuality()` method works exactly like the `draw()` method, + but instead of using the `Stage.quality` property to determine the quality of + vector rendering, you specify the `quality` parameter to the `drawWithQuality()` + method. + + This method directly corresponds to how objects are drawn with the standard + vector renderer for objects in the authoring tool interface. + + The source display object does not use any of its applied transformations for + this call. It is treated as it exists in the library or file, with no matrix + transform, no color transform, and no blend mode. To draw a display object + (such as a movie clip) by using its own transform properties, you can copy its + `transform` property object to the `transform` property of the Bitmap object that + uses the BitmapData object. + + This method is supported over RTMP in Flash Player 9.0.115.0 and later and in + Adobe AIR. You can control access to streams on Flash Media Server in a + server-side script. For more information, see the `Client.audioSampleAccess` and + `Client.videoSampleAccess` properties in Server-Side ActionScript Language + Reference for Adobe Flash Media Server. + + If the source object and (in the case of a Sprite or MovieClip object) all of + its child objects do not come from the same domain as the caller, or are not in + a content that is accessible to the caller by having called the + `Security.allowDomain()` method, a call to the `drawWithQuality()` throws a + SecurityError exception. This restriction does not apply to AIR content in the + application security sandbox. + + There are also restrictions on using a loaded bitmap image as the source. A call + to the `drawWithQuality()` method is successful if the loaded image comes from the + same domain as the caller. Also, a cross-domain policy file on the image's server + can grant permission to the domain of the SWF content calling the + `drawWithQuality()` method. In this case, you must set the `checkPolicyFile` property + of a LoaderContext object, and use this object as the `context` parameter when + calling the `load()` method of the Loader object used to load the image. These + restrictions do not apply to AIR content in the application security sandbox. + + On Windows, the `drawWithQuality()` method cannot capture SWF content embedded in an + HTML page in an HTMLLoader object in Adobe AIR. + + The `drawWithQuality()` method cannot capture PDF content in Adobe AIR. Nor can it + capture or SWF content embedded in HTML in which the `wmode` attribute is set to + `"window"` in Adobe AIR. + + @param source The display object or BitmapData object to draw to the BitmapData + object. (The DisplayObject and BitmapData classes implement the IBitmapDrawable + interface.) + @param matrix A Matrix object used to scale, rotate, or translate the coordinates + of the bitmap. If you do not want to apply a matrix transformation to the image, + set this parameter to an identity matrix, created with the default `new Matrix()` + constructor, or pass a `null` value. + @param colorTransform A ColorTransform object that you use to adjust the color + values of the bitmap. If no object is supplied, the bitmap image's colors are not + transformed. If you must pass this parameter but you do not want to transform the + image, set this parameter to a ColorTransform object created with the default + `new ColorTransform()` constructor. + @param blendMode A string value, from the flash.display.BlendMode class, + specifying the blend mode to be applied to the resulting bitmap. + @param clipRect A Rectangle object that defines the area of the source object + to draw. If you do not supply this value, no clipping occurs and the entire source + object is drawn. + @param smoothing A Boolean value that determines whether a BitmapData object is + smoothed when scaled or rotated, due to a scaling or rotation in the `matrix` + parameter. The smoothing parameter only applies if the `source` parameter is a + BitmapData object. With `smoothing` set to `false`, the rotated or scaled BitmapData + image can appear pixelated or jagged. For example, the following two images use the + same BitmapData object for the `source` parameter, but the `smoothing` parameter is + set to `true` on the left and `false` on the right: + ![Two images: the left one with smoothing and the right one without smoothing.](/images/bitmapData_draw_smoothing.jpg) + Drawing a bitmap with `smoothing` set to `true` takes longer than doing so with + `smoothing` set to `false`. + @param quality Any of one of the StageQuality values. Selects the antialiasing + quality to be used when drawing vectors graphics. + @throws ArgumentError The source parameter is not a BitmapData or DisplayObject + object. + @throws SecurityError The source object and (in the case of a Sprite or MovieClip + object) all of its child objects do not come from the same domain as the caller, + or are not in a content that is accessible to the caller by having called the + `Security.allowDomain()` method. This restriction does not apply to AIR content + in the application security sandbox. + @throws ArgumentError The source is `null` or not a valid IBitmapDrawable object. + + @see [Copying bitmap data](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/copying-bitmap-data.html) + **/ + public function drawWithQuality(source:IBitmapDrawable, matrix:Matrix = null, colorTransform:ColorTransform = null, blendMode:BlendMode = null, + clipRect:Rectangle = null, smoothing:Bool = false, quality:StageQuality = null):Void + { + draw(source, matrix, colorTransform, blendMode, clipRect, quality != LOW ? smoothing : false); + } + + /** + Compresses this BitmapData object using the selected compressor algorithm and + returns a new ByteArray object. Optionally, writes the resulting data to the + specified ByteArray. The `compressor` argument specifies the encoding algorithm, + and can be PNGEncoderOptions, JPEGEncoderOptions, or JPEGXREncoderOptions. + + The following example compresses a BitmapData object using the JPEGEncoderOptions: + + ```haxe + // Compress a BitmapData object as a JPEG file. + var bitmapData:BitmapData = new BitmapData(640,480,false,0x00FF00); + var byteArray:ByteArray = new ByteArray(); + bitmapData.encode(new Rectangle(0,0,640,480), new openfl.display.JPEGEncoderOptions(), byteArray); + ``` + + @param rect The area of the BitmapData object to compress. + @param compressor The compressor type to use. Valid values are: + flash.display.PNGEncoderOptions, flash.display.JPEGEncoderOptions, and + flash.display.JPEGXREncoderOptions. + @param byteArray The output ByteArray to hold the encoded image. + @return A ByteArray containing the encoded image. + + @see [Compressing bitmap data](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/compressing-bitmap-data.html) + **/ + public function encode(rect:Rectangle, compressor:Object, byteArray:ByteArray = null):ByteArray + { + #if lime + if (!readable || rect == null) return byteArray = null; + if (byteArray == null) byteArray = new ByteArray(); + + var image = this.image; + + if (!rect.equals(this.rect)) + { + var matrix = Matrix.__pool.get(); + matrix.tx = Math.round(-rect.x); + matrix.ty = Math.round(-rect.y); + + var bitmapData = new BitmapData(Math.ceil(rect.width), Math.ceil(rect.height), true, 0); + bitmapData.draw(this, matrix); + + image = bitmapData.image; + + Matrix.__pool.release(matrix); + } + + if ((compressor is PNGEncoderOptions)) + { + byteArray.writeBytes(ByteArray.fromBytes(image.encode(PNG))); + return byteArray; + } + else if ((compressor is JPEGEncoderOptions)) + { + byteArray.writeBytes(ByteArray.fromBytes(image.encode(JPEG, cast(compressor, JPEGEncoderOptions).quality))); + return byteArray; + } + #end + + return byteArray = null; + } + + /** + Fills a rectangular area of pixels with a specified ARGB color. + + @param rect The rectangular area to fill. + @param color The ARGB color value that fills the area. ARGB colors are + often specified in hexadecimal format; for example, + 0xFF336699. + @throws TypeError The rect is null. + **/ + public function fillRect(rect:Rectangle, color:Int):Void + { + __fillRect(rect, color, true); + } + + /** + Performs a flood fill operation on an image starting at an(_x_, + _y_) coordinate and filling with a certain color. The + `floodFill()` method is similar to the paint bucket tool in + various paint programs. The color is an ARGB color that contains alpha + information and color information. + + @param x The _x_ coordinate of the image. + @param y The _y_ coordinate of the image. + @param color The ARGB color to use as a fill. + **/ + public function floodFill(x:Int, y:Int, color:Int):Void + { + #if lime + if (!readable) return; + image.floodFill(x, y, color, ARGB32); + #end + } + + #if (!openfl_doc_gen || (!js && !html5 && !flash_doc_gen)) + /** + Creates a new BitmapData instance from Base64-encoded data + synchronously. This means that the BitmapData will be returned + immediately (if supported). The bytes must be of a supported bitmap file + format, such as PNG or JPG. To use raw ARGB pixel data, call + `setPixels` or `setVector` instead. + + HTML5 and Flash do not support creating BitmapData synchronously, so these targets + always return `null`. Other targets will return `null` if decoding was unsuccessful. + + @param base64 Base64-encoded data + @param type The MIME-type for the encoded data ("image/jpeg", etc) + @returns A new BitmapData if successful, or `null` if unsuccessful + **/ + public static function fromBase64(base64:String, type:String):BitmapData + { + #if (js && html5) + return null; + #else + var bitmapData = new BitmapData(0, 0, true, 0); + bitmapData.__fromBase64(base64, type); + return bitmapData; + #end + } + #end + + #if (!openfl_doc_gen || (!js && !html5 && !flash_doc_gen)) + /** + Creates a new BitmapData from bytes (a `haxe.io.Bytes` or + `openfl.utils.ByteArray`) synchronously. This means that the BitmapData + will be returned immediately (if supported). The bytes must be of a + supported bitmap file format, such as PNG or JPG. To use raw ARGB pixel + data, call `setPixels` or `setVector` instead. + + HTML5 and Flash do not support creating BitmapData synchronously, so these targets + always return `null`. Other targets will return `null` if decoding was unsuccessful. + + The optional `rawAlpha` parameter makes it easier to process images that have alpha + data stored separately. + + @param bytes A haxe.io.Bytes or openfl.utils.ByteArray instance + @param rawAlpha An optional byte array with alpha data + @returns A new BitmapData if successful, or `null` if unsuccessful + **/ + public static function fromBytes(bytes:ByteArray, rawAlpha:ByteArray = null):BitmapData + { + #if (js && html5) + return null; + #else + var bitmapData = new BitmapData(0, 0, true, 0); + bitmapData.__fromBytes(bytes, rawAlpha); + return bitmapData; + #end + } + #end + + #if (js && html5) + /** + Creates a new BitmapData from an HTML5 canvas element immediately. + + All targets except from HTML5 targets will return `null`. + + @param canvas An HTML5 canvas element + @param transparent Whether the new BitmapData object should be considered + transparent + @returns A new BitmapData if successful, or `null` if unsuccessful + **/ + public static function fromCanvas(canvas:CanvasElement, transparent:Bool = true):BitmapData + { + if (canvas == null) return null; + + var bitmapData = new BitmapData(0, 0, transparent, 0); + bitmapData.__fromImage(Image.fromCanvas(canvas)); + bitmapData.image.transparent = transparent; + return bitmapData; + } + #end + + #if (!openfl_doc_gen || (!js && !html5 && !flash_doc_gen)) + /** + Creates a new BitmapData from a file path synchronously. This means that the + BitmapData will be returned immediately (if supported). + + HTML5 and Flash do not support creating BitmapData synchronously, so these targets + always return `null`. + + In order to load files from a remote web address, use the `loadFromFile` method, + which supports asynchronous loading. + + @param path A local file path containing an image + @returns A new BitmapData if successful, or `null` if unsuccessful + **/ + public static function fromFile(path:String):BitmapData + { + #if (js && html5) + return null; + #else + var bitmapData = new BitmapData(0, 0, true, 0); + bitmapData.__fromFile(path); + return bitmapData.image != null ? bitmapData : null; + #end + } + #end + + #if lime + /** + Creates a new BitmapData using an existing Lime Image instance. + + @param image A Lime Image object + @param transparent Whether the new BitmapData object should be considered + transparent + @returns A new BitmapData if the Image (and associated ImageBuffer) are not + `null`, otherwise `null` will be returned + **/ + public static function fromImage(image:Image, transparent:Bool = true):BitmapData + { + if (image == null || image.buffer == null) return null; + + var bitmapData = new BitmapData(0, 0, transparent, 0); + bitmapData.__fromImage(image); + bitmapData.image.transparent = transparent; + return bitmapData.image != null ? bitmapData : null; + } + #end + + /** + **BETA** + + Creates a new BitmapData instance from a Stage3D rectangle texture. The + BitmapData instance will hardware-only, and the `readable` property will + be false, meaning that some operations will not be permitted. + + This method is not supported by the Flash target. + + @param texture A Texture or RectangleTexture instance + @returns A new BitmapData if successful, or `null` if unsuccessful + + @see `BitmapData.readable` + **/ + public static function fromTexture(texture:TextureBase):BitmapData + { + if (texture == null) return null; + + var bitmapData = new BitmapData(texture.__width, texture.__height, true, 0); + bitmapData.readable = false; + bitmapData.__texture = texture; + bitmapData.__textureContext = texture.__textureContext; + bitmapData.image = null; + return bitmapData; + } + + /** + Determines the destination rectangle that the `applyFilter()` + method call affects, given a BitmapData object, a source rectangle, and a + filter object. + + For example, a blur filter normally affects an area larger than the + size of the original image. A 100 x 200 pixel image that is being filtered + by a default BlurFilter instance, where `blurX = blurY = 4` + generates a destination rectangle of `(-2,-2,104,204)`. The + `generateFilterRect()` method lets you find out the size of + this destination rectangle in advance so that you can size the destination + image appropriately before you perform a filter operation. + + Some filters clip their destination rectangle based on the source image + size. For example, an inner `DropShadow` does not generate a + larger result than its source image. In this API, the BitmapData object is + used as the source bounds and not the source `rect` + parameter. + + @param sourceRect A rectangle defining the area of the source image to use + as input. + @param filter A filter object that you use to calculate the + destination rectangle. + @return A destination rectangle computed by using an image, the + `sourceRect` parameter, and a filter. + @throws TypeError The sourceRect or filter are null. + **/ + public function generateFilterRect(sourceRect:Rectangle, filter:BitmapFilter):Rectangle + { + return sourceRect.clone(); + } + + /** + **BETA** + + Get the IndexBuffer3D object associated with this BitmapData object + + @param context A Stage3D context + @returns An IndexBuffer3D object for use with rendering + **/ + @:dox(hide) public function getIndexBuffer(context:Context3D, scale9Grid:Rectangle = null):IndexBuffer3D + { + var gl = context.gl; + + if (__indexBuffer == null + || __indexBufferContext != context.__context + || (scale9Grid != null && __indexBufferGrid == null) + || (__indexBufferGrid != null && !__indexBufferGrid.equals(scale9Grid))) + { + // TODO: Use shared buffer on context + // TODO: Support for UVs other than scale-9 grid? + + #if lime + __indexBufferContext = context.__context; + __indexBuffer = null; + + if (scale9Grid != null) + { + if (__indexBufferGrid == null) __indexBufferGrid = new Rectangle(); + __indexBufferGrid.copyFrom(scale9Grid); + + var centerX = scale9Grid.width; + var centerY = scale9Grid.height; + if (centerX != 0 && centerY != 0) + { + __indexBufferData = new UInt16Array(54); + + // 3 ——— 2 ——— 5 ——— 7 + // | / | / | / | + // 1 ——— 0 ——— 4 ——— 6 + // | / | / | / | + // 9 ——— 8 —— 10 —— 11 + // | / | / | / | + // 13 — 12 —— 14 —— 15 + + // top left + __indexBufferData[0] = 0; + __indexBufferData[1] = 1; + __indexBufferData[2] = 2; + __indexBufferData[3] = 2; + __indexBufferData[4] = 1; + __indexBufferData[5] = 3; + + // top center + __indexBufferData[6] = 4; + __indexBufferData[7] = 0; + __indexBufferData[8] = 5; + __indexBufferData[9] = 5; + __indexBufferData[10] = 0; + __indexBufferData[11] = 2; + + // top right + __indexBufferData[12] = 6; + __indexBufferData[13] = 4; + __indexBufferData[14] = 7; + __indexBufferData[15] = 7; + __indexBufferData[16] = 4; + __indexBufferData[17] = 5; + + // middle left + __indexBufferData[18] = 8; + __indexBufferData[19] = 9; + __indexBufferData[20] = 0; + __indexBufferData[21] = 0; + __indexBufferData[22] = 9; + __indexBufferData[23] = 1; + + // middle center + __indexBufferData[24] = 10; + __indexBufferData[25] = 8; + __indexBufferData[26] = 4; + __indexBufferData[27] = 4; + __indexBufferData[28] = 8; + __indexBufferData[29] = 0; + + // middle right + __indexBufferData[30] = 11; + __indexBufferData[31] = 10; + __indexBufferData[32] = 6; + __indexBufferData[33] = 6; + __indexBufferData[34] = 10; + __indexBufferData[35] = 4; + + // bottom left + __indexBufferData[36] = 12; + __indexBufferData[37] = 13; + __indexBufferData[38] = 8; + __indexBufferData[39] = 8; + __indexBufferData[40] = 13; + __indexBufferData[41] = 9; + + // bottom center + __indexBufferData[42] = 14; + __indexBufferData[43] = 12; + __indexBufferData[44] = 10; + __indexBufferData[45] = 10; + __indexBufferData[46] = 12; + __indexBufferData[47] = 8; + + // bottom center + __indexBufferData[48] = 15; + __indexBufferData[49] = 14; + __indexBufferData[50] = 11; + __indexBufferData[51] = 11; + __indexBufferData[52] = 14; + __indexBufferData[53] = 10; + + __indexBuffer = context.createIndexBuffer(54); + } + else if (centerX == 0 && centerY != 0) + { + __indexBufferData = new UInt16Array(18); + + // 3 ——— 2 + // | / | + // 1 ——— 0 + // | / | + // 5 ——— 4 + // | / | + // 7 ——— 6 + + // top + __indexBufferData[0] = 0; + __indexBufferData[1] = 1; + __indexBufferData[2] = 2; + __indexBufferData[3] = 2; + __indexBufferData[4] = 1; + __indexBufferData[5] = 3; + + // middle + __indexBufferData[6] = 4; + __indexBufferData[7] = 5; + __indexBufferData[8] = 0; + __indexBufferData[9] = 0; + __indexBufferData[10] = 5; + __indexBufferData[11] = 1; + + // bottom + __indexBufferData[12] = 6; + __indexBufferData[13] = 7; + __indexBufferData[14] = 4; + __indexBufferData[15] = 4; + __indexBufferData[16] = 7; + __indexBufferData[17] = 5; + + __indexBuffer = context.createIndexBuffer(18); + } + else if (centerX != 0 && centerY == 0) + { + __indexBufferData = new UInt16Array(18); + + // 3 ——— 2 ——— 5 ——— 7 + // | / | / | / | + // 1 ——— 0 ——— 4 ——— 6 + + // left + __indexBufferData[0] = 0; + __indexBufferData[1] = 1; + __indexBufferData[2] = 2; + __indexBufferData[3] = 2; + __indexBufferData[4] = 1; + __indexBufferData[5] = 3; + + // center + __indexBufferData[6] = 4; + __indexBufferData[7] = 0; + __indexBufferData[8] = 5; + __indexBufferData[9] = 5; + __indexBufferData[10] = 0; + __indexBufferData[11] = 2; + + // right + __indexBufferData[12] = 6; + __indexBufferData[13] = 4; + __indexBufferData[14] = 7; + __indexBufferData[15] = 7; + __indexBufferData[16] = 4; + __indexBufferData[17] = 5; + + __indexBuffer = context.createIndexBuffer(18); + } + } + else + { + __indexBufferGrid = null; + } + + if (__indexBuffer == null) + { + __indexBufferData = new UInt16Array(6); + __indexBufferData[0] = 0; + __indexBufferData[1] = 1; + __indexBufferData[2] = 2; + __indexBufferData[3] = 2; + __indexBufferData[4] = 1; + __indexBufferData[5] = 3; + __indexBuffer = context.createIndexBuffer(6); + } + + __indexBuffer.uploadFromTypedArray(__indexBufferData); + #end + } + + return __indexBuffer; + } + + /** + **BETA** + + Get the VertexBuffer3D object associated with this BitmapData object + + @param context A Stage3D context + @returns A VertexBuffer3D object for use with rendering + **/ + @:dox(hide) public function getVertexBuffer(context:Context3D, scale9Grid:Rectangle = null, targetObject:DisplayObject = null):VertexBuffer3D + { + var gl = context.gl; + + // TODO: Support for UVs other than scale-9 grid? + // TODO: Better way of handling object transform? + + if (__vertexBuffer == null + || __vertexBufferContext != context.__context + || (scale9Grid != null && __vertexBufferGrid == null) + || (__vertexBufferGrid != null && !__vertexBufferGrid.equals(scale9Grid)) + || (targetObject != null + && (__vertexBufferWidth != targetObject.width + || __vertexBufferHeight != targetObject.height + || __vertexBufferScaleX != targetObject.scaleX + || __vertexBufferScaleY != targetObject.scaleY))) + { + #if openfl_power_of_two + var newWidth = 1; + var newHeight = 1; + + while (newWidth < width) + { + newWidth <<= 1; + } + + while (newHeight < height) + { + newHeight <<= 1; + } + + __uvRect = new Rectangle(0, 0, newWidth, newHeight); + + var uvWidth = width / newWidth; + var uvHeight = height / newHeight; + + __textureWidth = newWidth; + __textureHeight = newHeight; + #else + __uvRect = new Rectangle(0, 0, width, height); + + var uvWidth = 1; + var uvHeight = 1; + #end + + // __vertexBufferData = new Float32Array ([ + // + // width, height, 0, uvWidth, uvHeight, alpha, (color transform, color offset...) + // 0, height, 0, 0, uvHeight, alpha, (color transform, color offset...) + // width, 0, 0, uvWidth, 0, alpha, (color transform, color offset...) + // 0, 0, 0, 0, 0, alpha, (color transform, color offset...) + // + // + // ]); + + // [ colorTransform.redMultiplier, 0, 0, 0, 0, colorTransform.greenMultiplier, 0, 0, 0, 0, colorTransform.blueMultiplier, 0, 0, 0, 0, colorTransform.alphaMultiplier ]; + // [ colorTransform.redOffset / 255, colorTransform.greenOffset / 255, colorTransform.blueOffset / 255, colorTransform.alphaOffset / 255 ] + + #if lime + __vertexBufferContext = context.__context; + __vertexBuffer = null; + + if (targetObject != null) + { + __vertexBufferWidth = targetObject.width; + __vertexBufferHeight = targetObject.height; + __vertexBufferScaleX = targetObject.scaleX; + __vertexBufferScaleY = targetObject.scaleY; + } + + if (scale9Grid != null && targetObject != null) + { + if (__vertexBufferGrid == null) __vertexBufferGrid = new Rectangle(); + __vertexBufferGrid.copyFrom(scale9Grid); + + __vertexBufferWidth = targetObject.width; + __vertexBufferHeight = targetObject.height; + __vertexBufferScaleX = targetObject.scaleX; + __vertexBufferScaleY = targetObject.scaleY; + + var centerX = scale9Grid.width; + var centerY = scale9Grid.height; + if (centerX != 0 && centerY != 0) + { + __vertexBufferData = new Float32Array(VERTEX_BUFFER_STRIDE * 16); + + var left = scale9Grid.x; + var top = scale9Grid.y; + var right = width - centerX - left; + var bottom = height - centerY - top; + + var uvLeft = left / width; + var uvTop = top / height; + var uvCenterX = centerX / width; + var uvCenterY = centerY / height; + var uvRight = right / width; + var uvBottom = bottom / height; + + var renderedLeft = left / targetObject.scaleX; + var renderedTop = top / targetObject.scaleY; + var renderedRight = right / targetObject.scaleX; + var renderedBottom = bottom / targetObject.scaleY; + var renderedCenterX = (targetObject.width / targetObject.scaleX) - renderedLeft - renderedRight; + var renderedCenterY = (targetObject.height / targetObject.scaleY) - renderedTop - renderedBottom; + + // 3 ——— 2 ——— 5 ——— 7 + // | / | / | / | + // 1 ——— 0 ——— 4 ——— 6 + // | / | / | / | + // 9 ——— 8 —— 10 —— 11 + // | / | / | / | + // 13 — 12 —— 14 —— 15 + + // top left <0-1-2> <2-1-3> + __vertexBufferData[0] = renderedLeft; + __vertexBufferData[1] = renderedTop; + __vertexBufferData[3] = uvWidth * uvLeft; + __vertexBufferData[4] = uvHeight * uvTop; + + __vertexBufferData[VERTEX_BUFFER_STRIDE + 1] = renderedTop; + __vertexBufferData[VERTEX_BUFFER_STRIDE + 4] = uvHeight * uvTop; + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2] = renderedLeft; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2 + 3] = uvWidth * uvLeft; + + // top center <4-0-5> <5-0-2> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4] = renderedLeft + renderedCenterX; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4 + 1] = renderedTop; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4 + 3] = uvWidth * (uvLeft + uvCenterX); + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4 + 4] = uvHeight * uvTop; + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 5] = renderedLeft + renderedCenterX; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 5 + 3] = uvWidth * (uvLeft + uvCenterX); + + // top right <6-4-7> <7-4-5> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6] = width; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6 + 1] = renderedTop; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6 + 3] = uvWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6 + 4] = uvHeight * uvTop; + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 7] = width; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 7 + 3] = uvWidth; + + // middle left <8-9-0> <0-9-1> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 8] = renderedLeft; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 8 + 1] = renderedTop + renderedCenterY; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 8 + 3] = uvWidth * uvLeft; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 8 + 4] = uvHeight * (uvTop + uvCenterY); + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 9 + 1] = renderedTop + renderedCenterY; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 9 + 4] = uvHeight * (uvTop + uvCenterY); + + // middle center <10-8-4> <4-8-0> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 10] = renderedLeft + renderedCenterX; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 10 + 1] = renderedTop + renderedCenterY; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 10 + 3] = uvWidth * (uvLeft + uvCenterX); + __vertexBufferData[VERTEX_BUFFER_STRIDE * 10 + 4] = uvHeight * (uvTop + uvCenterY); + + // middle right <11-10-6> <6-10-4> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 11] = width; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 11 + 1] = renderedTop + renderedCenterY; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 11 + 3] = uvWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 11 + 4] = uvHeight * (uvTop + uvCenterY); + + // bottom left <12-13-8> <8-13-9> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 12] = renderedLeft; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 12 + 1] = height; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 12 + 3] = uvWidth * uvLeft; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 12 + 4] = uvHeight; + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 13 + 1] = height; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 13 + 4] = uvHeight; + + // bottom center <14-12-10> <10-12-8> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 14] = renderedLeft + renderedCenterX; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 14 + 1] = height; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 14 + 3] = uvWidth * (uvLeft + uvCenterX); + __vertexBufferData[VERTEX_BUFFER_STRIDE * 14 + 4] = uvHeight; + + // bottom right <15-14-11> <11-14-10> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 15] = width; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 15 + 1] = height; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 15 + 3] = uvWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 15 + 4] = uvHeight; + + __vertexBuffer = context.createVertexBuffer(16, VERTEX_BUFFER_STRIDE); + } + else if (centerX == 0 && centerY != 0) + { + __vertexBufferData = new Float32Array(VERTEX_BUFFER_STRIDE * 8); + + var top = scale9Grid.y; + var bottom = height - centerY - top; + + var uvTop = top / height; + var uvCenterY = centerY / height; + var uvBottom = bottom / height; + + var renderedTop = top / targetObject.scaleY; + var renderedBottom = bottom / targetObject.scaleY; + var renderedCenterY = (targetObject.height / targetObject.scaleY) - renderedTop - renderedBottom; + + var renderedWidth = targetObject.width / targetObject.scaleX; + + // 3 ——— 2 + // | / | + // 1 ——— 0 + // | / | + // 5 ——— 4 + // | / | + // 7 ——— 6 + + // top <0-1-2> <2-1-3> + __vertexBufferData[0] = renderedWidth; + __vertexBufferData[1] = renderedTop; + __vertexBufferData[3] = uvWidth; + __vertexBufferData[4] = uvHeight * uvTop; + + __vertexBufferData[VERTEX_BUFFER_STRIDE + 1] = renderedTop; + __vertexBufferData[VERTEX_BUFFER_STRIDE + 4] = uvHeight * uvTop; + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2] = renderedWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2 + 3] = uvWidth; + + // middle <4-5-0> <0-5-1> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4] = renderedWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4 + 1] = renderedTop + renderedCenterY; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4 + 3] = uvWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4 + 4] = uvHeight * (uvTop + uvCenterY); + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 5 + 1] = renderedTop + renderedCenterY; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 5 + 4] = uvHeight * (uvTop + uvCenterY); + + // bottom <6-7-4> <4-7-5> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6] = renderedWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6 + 1] = height; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6 + 3] = uvWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6 + 4] = uvHeight; + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 7 + 1] = height; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 7 + 4] = uvHeight; + + __vertexBuffer = context.createVertexBuffer(8, VERTEX_BUFFER_STRIDE); + } + else if (centerY == 0 && centerX != 0) + { + __vertexBufferData = new Float32Array(VERTEX_BUFFER_STRIDE * 8); + + var left = scale9Grid.x; + var right = width - centerX - left; + + var uvLeft = left / width; + var uvCenterX = centerX / width; + var uvRight = right / width; + + var renderedLeft = left / targetObject.scaleX; + var renderedRight = right / targetObject.scaleX; + var renderedCenterX = (targetObject.width / targetObject.scaleX) - renderedLeft - renderedRight; + + var renderedHeight = targetObject.height / targetObject.scaleY; + + // 3 ——— 2 ——— 5 ——— 7 + // | / | / | / | + // 1 ——— 0 ——— 4 ——— 6 + + // top left <0-1-2> <2-1-3> + __vertexBufferData[0] = renderedLeft; + __vertexBufferData[1] = renderedHeight; + __vertexBufferData[3] = uvWidth * uvLeft; + __vertexBufferData[4] = uvHeight; + + __vertexBufferData[VERTEX_BUFFER_STRIDE + 1] = renderedHeight; + __vertexBufferData[VERTEX_BUFFER_STRIDE + 4] = uvHeight; + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2] = renderedLeft; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2 + 3] = uvWidth * uvLeft; + + // top center <4-0-5> <5-0-2> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4] = renderedLeft + renderedCenterX; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4 + 1] = renderedHeight; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4 + 3] = uvWidth * (uvLeft + uvCenterX); + __vertexBufferData[VERTEX_BUFFER_STRIDE * 4 + 4] = uvHeight; + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 5] = renderedLeft + renderedCenterX; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 5 + 3] = uvWidth * (uvLeft + uvCenterX); + + // top right <6-4-7> <7-4-5> + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6] = width; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6 + 1] = renderedHeight; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6 + 3] = uvWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 6 + 4] = uvHeight; + + __vertexBufferData[VERTEX_BUFFER_STRIDE * 7] = width; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 7 + 3] = uvWidth; + + __vertexBuffer = context.createVertexBuffer(8, VERTEX_BUFFER_STRIDE); + } + } + else + { + __vertexBufferGrid = null; + } + + if (__vertexBuffer == null) + { + __vertexBufferData = new Float32Array(VERTEX_BUFFER_STRIDE * 4); + + __vertexBufferData[0] = width; + __vertexBufferData[1] = height; + __vertexBufferData[3] = uvWidth; + __vertexBufferData[4] = uvHeight; + __vertexBufferData[VERTEX_BUFFER_STRIDE + 1] = height; + __vertexBufferData[VERTEX_BUFFER_STRIDE + 4] = uvHeight; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2] = width; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2 + 3] = uvWidth; + + __vertexBuffer = context.createVertexBuffer(3, VERTEX_BUFFER_STRIDE); + } + + // for (i in 0...4) { + + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 5] = alpha; + + // if (colorTransform != null) { + + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 6] = colorTransform.redMultiplier; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 7] = colorTransform.greenMultiplier; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 8] = colorTransform.blueMultiplier; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 9] = colorTransform.alphaMultiplier; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 10] = colorTransform.redOffset / 255; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 11] = colorTransform.greenOffset / 255; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 12] = colorTransform.blueOffset / 255; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 13] = colorTransform.alphaOffset / 255; + + // } + + // } + + // __vertexBufferAlpha = alpha; + // __vertexBufferColorTransform = colorTransform != null ? colorTransform.__clone () : null; + + __vertexBuffer.uploadFromTypedArray(__vertexBufferData); + #end + } + else + { + // var dirty = false; + + // if (__vertexBufferAlpha != alpha) { + + // dirty = true; + + // for (i in 0...4) { + + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 5] = alpha; + + // } + + // __vertexBufferAlpha = alpha; + + // } + + // if ((__vertexBufferColorTransform == null && colorTransform != null) || (__vertexBufferColorTransform != null && !__vertexBufferColorTransform.__equals (colorTransform))) { + + // dirty = true; + + // if (colorTransform != null) { + + // if (__vertexBufferColorTransform == null) { + // __vertexBufferColorTransform = colorTransform.__clone (); + // } else { + // __vertexBufferColorTransform.__copyFrom (colorTransform); + // } + + // for (i in 0...4) { + + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 6] = colorTransform.redMultiplier; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 11] = colorTransform.greenMultiplier; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 16] = colorTransform.blueMultiplier; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 21] = colorTransform.alphaMultiplier; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 22] = colorTransform.redOffset / 255; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 23] = colorTransform.greenOffset / 255; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 24] = colorTransform.blueOffset / 255; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 25] = colorTransform.alphaOffset / 255; + + // } + + // } else { + + // for (i in 0...4) { + + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 6] = 1; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 11] = 1; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 16] = 1; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 21] = 1; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 22] = 0; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 23] = 0; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 24] = 0; + // __vertexBufferData[VERTEX_BUFFER_STRIDE * i + 25] = 0; + + // } + + // } + + // } + + // context.__bindGLArrayBuffer (__vertexBuffer); + + // if (dirty) { + + // gl.bufferData (gl.ARRAY_BUFFER, __vertexBufferData.byteLength, __vertexBufferData, gl.STATIC_DRAW); + + // } + } + + return __vertexBuffer; + } + + /** + Determines a rectangular region that either fully encloses all pixels of a + specified color within the bitmap image (if the `findColor` + parameter is set to `true`) or fully encloses all pixels that + do not include the specified color (if the `findColor` + parameter is set to `false`). + + For example, if you have a source image and you want to determine the + rectangle of the image that contains a nonzero alpha channel, pass + `{mask: 0xFF000000, color: 0x00000000}` as parameters. If the + `findColor` parameter is set to `true`, the entire + image is searched for the bounds of pixels for which `(value & mask) + == color`(where `value` is the color value of the + pixel). If the `findColor` parameter is set to + `false`, the entire image is searched for the bounds of pixels + for which `(value & mask) != color`(where `value` + is the color value of the pixel). To determine white space around an + image, pass `{mask: 0xFFFFFFFF, color: 0xFFFFFFFF}` to find the + bounds of nonwhite pixels. + + @param mask A hexadecimal value, specifying the bits of the ARGB + color to consider. The color value is combined with this + hexadecimal value, by using the `&`(bitwise + AND) operator. + @param color A hexadecimal value, specifying the ARGB color to match + (if `findColor` is set to `true`) + or _not_ to match(if `findColor` is set + to `false`). + @param findColor If the value is set to `true`, returns the + bounds of a color value in an image. If the value is set + to `false`, returns the bounds of where this + color doesn't exist in an image. + @return The region of the image that is the specified color. + **/ + public function getColorBoundsRect(mask:Int, color:Int, findColor:Bool = true):Rectangle + { + #if lime + if (!readable) return new Rectangle(0, 0, width, height); + + if (!transparent || ((mask >> 24) & 0xFF) > 0) + { + var color = (color : ARGB); + if (color.a == 0) color = 0; + } + + var rect = image.getColorBoundsRect(mask, color, findColor, ARGB32); + return new Rectangle(rect.x, rect.y, rect.width, rect.height); + #else + return new Rectangle(0, 0, width, height); + #end + } + + /** + Returns an integer that represents an RGB pixel value from a BitmapData + object at a specific point(_x_, _y_). The + `getPixel()` method returns an unmultiplied pixel value. No + alpha information is returned. + + All pixels in a BitmapData object are stored as premultiplied color + values. A premultiplied image pixel has the red, green, and blue color + channel values already multiplied by the alpha data. For example, if the + alpha value is 0, the values for the RGB channels are also 0, independent + of their unmultiplied values. This loss of data can cause some problems + when you perform operations. All BitmapData methods take and return + unmultiplied values. The internal pixel representation is converted from + premultiplied to unmultiplied before it is returned as a value. During a + set operation, the pixel value is premultiplied before the raw image pixel + is set. + + @param x The _x_ position of the pixel. + @param y The _y_ position of the pixel. + @return A number that represents an RGB pixel value. If the(_x_, + _y_) coordinates are outside the bounds of the image, the + method returns 0. + + @see [Manipulating pixels](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/manipulating-pixels.html) + **/ + public function getPixel(x:Int, y:Int):Int + { + if (!readable) return 0; + #if lime + return image.getPixel(x, y, ARGB32); + #else + return 0; + #end + } + + /** + Returns an ARGB color value that contains alpha channel data and RGB data. + This method is similar to the `getPixel()` method, which + returns an RGB color without alpha channel data. + + All pixels in a BitmapData object are stored as premultiplied color + values. A premultiplied image pixel has the red, green, and blue color + channel values already multiplied by the alpha data. For example, if the + alpha value is 0, the values for the RGB channels are also 0, independent + of their unmultiplied values. This loss of data can cause some problems + when you perform operations. All BitmapData methods take and return + unmultiplied values. The internal pixel representation is converted from + premultiplied to unmultiplied before it is returned as a value. During a + set operation, the pixel value is premultiplied before the raw image pixel + is set. + + @param x The _x_ position of the pixel. + @param y The _y_ position of the pixel. + @return A number representing an ARGB pixel value. If the(_x_, + _y_) coordinates are outside the bounds of the image, 0 is + returned. + + @see [Manipulating pixels](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/manipulating-pixels.html) + **/ + public function getPixel32(x:Int, y:Int):Int + { + if (!readable) return 0; + #if lime + return image.getPixel32(x, y, ARGB32); + #else + return 0; + #end + } + + /** + Generates a byte array from a rectangular region of pixel data. Writes an + unsigned integer (a 32-bit unmultiplied pixel value) for each pixel into + the byte array. + + @param rect A rectangular area in the current BitmapData object. + @return A ByteArray representing the pixels in the given Rectangle. + @throws TypeError The rect is null. + **/ + public function getPixels(rect:Rectangle):ByteArray + { + #if lime + if (!readable) return null; + if (rect == null) rect = this.rect; + var byteArray = ByteArray.fromBytes(image.getPixels(rect.__toLimeRectangle(), ARGB32)); + // TODO: System endian order + byteArray.endian = Endian.BIG_ENDIAN; + return byteArray; + #else + return null; + #end + } + + /** + **BETA** + + Get the CairoImageSurface associated with this BitmapData object for use with + Cairo software rendering + + @returns The associated CairoImageSurface + **/ + @SuppressWarnings("checkstyle:Dynamic") + @:dox(hide) public function getSurface():#if lime CairoImageSurface #else Dynamic #end + { + #if lime + if (!readable) return null; + + if (__surface == null) + { + __surface = CairoImageSurface.fromImage(image); + } + + return __surface; + #else + return null; + #end + } + + /** + **BETA** + + Get a hardware texture representing this BitmapData instance + + @param context A Context3D instance + @returns A Texture or RectangleTexture instance + **/ + @:dox(hide) public function getTexture(context:Context3D):TextureBase + { + if (!__isValid) return null; + + if (__texture == null || __textureContext != context.__context) + { + __textureContext = context.__context; + __texture = context.createRectangleTexture(width, height, BGRA, false); + + // context.__bindGLTexture2D (__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.NEAREST); + // gl.texParameteri (gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + __textureVersion = -1; + } + + #if lime + #if (js && html5) + ImageCanvasUtil.sync(image, false); + #end + + if (image != null && image.version > __textureVersion) + { + if (__surface != null) + { + __surface.flush(); + } + + var textureImage = image; + + #if (js && html5) + if (#if openfl_power_of_two true || #end (!TextureBase.__supportsBGRA && textureImage.format != RGBA32)) + { + textureImage = textureImage.clone(); + textureImage.format = RGBA32; + // textureImage.buffer.premultiplied = true; + #if openfl_power_of_two + textureImage.powerOfTwo = true; + #end + } + #else + if (#if openfl_power_of_two !textureImage.powerOfTwo || #end (!textureImage.premultiplied && textureImage.transparent)) + { + textureImage = textureImage.clone(); + textureImage.premultiplied = true; + #if openfl_power_of_two + textureImage.powerOfTwo = true; + #end + } + #end + + __texture.__uploadFromImage(textureImage); + + __textureVersion = image.version; + + __textureWidth = textureImage.buffer.width; + __textureHeight = textureImage.buffer.height; + } + + if (!readable && image != null) + { + __surface = null; + image = null; + } + #end + + return __texture; + } + + /** + Generates a vector array from a rectangular region of pixel data. Returns + a Vector object of unsigned integers (a 32-bit unmultiplied pixel value) + for the specified rectangle. + @param rect A rectangular area in the current BitmapData object. + @return A Vector representing the given Rectangle. + @throws TypeError The rect is null. + **/ + public function getVector(rect:Rectangle):Vector + { + var pixels = getPixels(rect); + var length = Std.int(pixels.length / 4); + var result = new Vector(length, true); + + for (i in 0...length) + { + result[i] = pixels.readUnsignedInt(); + } + + return result; + } + + /** + Computes a 256-value binary number histogram of a BitmapData object. This method + returns a Vector object containing four Vector instances (four Vector + objects that contain Float objects). The four Vector instances represent the + red, green, blue and alpha components in order. Each Vector instance contains + 256 values that represent the population count of an individual component value, + from 0 to 255. + @param hRect The area of the BitmapData object to use. + **/ + public function histogram(hRect:Rectangle = null):Array> + { + var rect = hRect != null ? hRect : new Rectangle(0, 0, width, height); + var pixels = getPixels(rect); + var result = [for (i in 0...4) [for (j in 0...256) 0]]; + + for (i in 0...pixels.length) + { + ++result[i % 4][pixels.readUnsignedByte()]; + } + + return result; + } + + /** + Performs pixel-level hit detection between one bitmap image and a point, + rectangle, or other bitmap image. A hit is defined as an overlap of a point or + rectangle over an opaque pixel, or two overlapping opaque pixels. No stretching, + rotation, or other transformation of either object is considered when the hit test + is performed. + + If an image is an opaque image, it is considered a fully opaque rectangle for this + method. Both images must be transparent images to perform pixel-level hit testing + that considers transparency. When you are testing two transparent images, the alpha + threshold parameters control what alpha channel values, from 0 to 255, are + considered opaque. + + @param firstPoint A position of the upper-left corner of the BitmapData image + in an arbitrary coordinate space. The same coordinate space is used in defining + the secondBitmapPoint parameter. + @param firstAlphaThreshold The smallest alpha channel value that is considered + opaque for this hit test. + @param secondObject A Rectangle, Point, Bitmap, or BitmapData object. + @param secondBitmapDataPoint A point that defines a pixel location in the + second BitmapData object. Use this parameter only when the value of `secondObject` + is a BitmapData object. + @param secondAlphaThreshold The smallest alpha channel value that is + considered opaque in the second BitmapData object. Use this parameter only when + the value of `secondObject` is a BitmapData object and both BitmapData objects + are transparent. + @return A value of `true` if a hit occurs; otherwise, `false`. + @throws ArgumentError The `secondObject` parameter is not a Point, Rectangle, + Bitmap, or BitmapData object. + @throws TypeError The `firstPoint` is `null`. + **/ + public function hitTest(firstPoint:Point, firstAlphaThreshold:Int, secondObject:Object, secondBitmapDataPoint:Point = null, + secondAlphaThreshold:Int = 1):Bool + { + if (!readable) return false; + + // #if !openfljs + if ((secondObject is Bitmap)) + { + secondObject = cast(secondObject, Bitmap).__bitmapData; + } + // #end + + if ((secondObject is Point)) + { + var secondPoint:Point = cast secondObject; + + var x = Std.int(secondPoint.x - firstPoint.x); + var y = Std.int(secondPoint.y - firstPoint.y); + + if (rect.contains(x, y)) + { + var pixel = getPixel32(x, y); + + if ((pixel >> 24) & 0xFF > firstAlphaThreshold) + { + return true; + } + } + } + else if ((secondObject is BitmapData)) + { + var secondBitmapData:BitmapData = cast secondObject; + var x, y; + + if (secondBitmapDataPoint == null) + { + x = 0; + y = 0; + } + else + { + x = Math.round(secondBitmapDataPoint.x - firstPoint.x); + y = Math.round(secondBitmapDataPoint.y - firstPoint.y); + } + + var hitRect = Rectangle.__pool.get(); + hitRect.setTo(x, y, secondBitmapData.width, secondBitmapData.height); + + if (rect.intersects(hitRect)) + { + if (x < 0) + { + hitRect.x = 0; + hitRect.width = Math.min(secondBitmapData.width + x, width); + } + else + { + hitRect.width = Math.min(secondBitmapData.width, width - x); + } + + if (y < 0) + { + hitRect.y = 0; + hitRect.height = Math.min(secondBitmapData.height + y, height); + } + else + { + hitRect.height = Math.min(secondBitmapData.height, height - y); + } + + var pixels = getPixels(hitRect); + + hitRect.x = (x < 0) ? -x : 0; + hitRect.y = (y < 0) ? -y : 0; + + var testPixels = secondBitmapData.getPixels(hitRect); + + var length = Std.int(hitRect.width * hitRect.height); + var pixel, testPixel; + + for (i in 0...length) + { + pixel = pixels.readUnsignedInt(); + testPixel = testPixels.readUnsignedInt(); + + if ((pixel >> 24) & 0xFF > firstAlphaThreshold && (testPixel >> 24) & 0xFF > secondAlphaThreshold) + { + Rectangle.__pool.release(hitRect); + return true; + } + } + } + + Rectangle.__pool.release(hitRect); + } + else if ((secondObject is Rectangle)) + { + var secondRectangle = Rectangle.__pool.get(); + secondRectangle.copyFrom(cast secondObject); + secondRectangle.offset(-firstPoint.x, -firstPoint.y); + secondRectangle.__contract(0, 0, width, height); + + if (secondRectangle.width > 0 && secondRectangle.height > 0) + { + var pixels = getPixels(secondRectangle); + var length = Std.int(pixels.length / 4); + var pixel:UInt; + + for (i in 0...length) + { + pixel = pixels.readUnsignedInt(); + + if ((pixel >> 24) & 0xFF > firstAlphaThreshold) + { + Rectangle.__pool.release(secondRectangle); + return true; + } + } + } + + Rectangle.__pool.release(secondRectangle); + } + + return false; + } + + /** + Creates a new BitmapData from Base64-encoded data asynchronously. The data + and (if successful) decoding the data into an image occur in the background. + Progress, completion and error callbacks will be dispatched in the current + thread using callbacks attached to a returned Future object. + + @param base64 Base64-encoded data + @param type The MIME-type for the encoded data ("image/jpeg", etc) + @returns A Future BitmapData + **/ + public static function loadFromBase64(base64:String, type:String):Future + { + #if lime + return Image.loadFromBase64(base64, type).then(function(image) + { + return Future.withValue(BitmapData.fromImage(image)); + }); + #else + return cast Future.withValue(null); + #end + } + + /** + Creates a new BitmapData from haxe.io.Bytes or openfl.utils.ByteArray data + asynchronously. The data and image decoding will occur in the background. + Progress, completion and error callbacks will be dispatched in the current + thread using callbacks attached to a returned Future object. + + The optional `rawAlpha` parameter makes it easier to process images that have alpha + data stored separately. + + @param bytes A haxe.io.Bytes or openfl.utils.ByteArray instance + @param rawAlpha An optional byte array with alpha data + @returns A Future BitmapData + **/ + public static function loadFromBytes(bytes:ByteArray, rawAlpha:ByteArray = null):Future + { + #if lime + return Image.loadFromBytes(bytes).then(function(image) + { + var bitmapData = BitmapData.fromImage(image); + + if (rawAlpha != null) + { + bitmapData.__applyAlpha(rawAlpha); + } + + return Future.withValue(bitmapData); + }); + #else + return cast Future.withValue(null); + #end + } + + /** + Creates a new BitmapData from a file path or web address asynchronously. The file + load and image decoding will occur in the background. + Progress, completion and error callbacks will be dispatched in the current + thread using callbacks attached to a returned Future object. + + @param path A local file path or web address containing an image + @returns A Future BitmapData + **/ + public static function loadFromFile(path:String):Future + { + #if lime + return Image.loadFromFile(path).then(function(image) + { + return Future.withValue(BitmapData.fromImage(image)); + }); + #else + return cast Future.withValue(null); + #end + } + + /** + Locks an image so that any objects that reference the BitmapData object, + such as Bitmap objects, are not updated when this BitmapData object + changes. To improve performance, use this method along with the + `unlock()` method before and after numerous calls to the + `setPixel()` or `setPixel32()` method. + + **/ + public function lock():Void {} + + /** + Performs per-channel blending from a source image to a destination image. For + each channel and each pixel, a new value is computed based on the channel + values of the source and destination pixels. For example, in the red channel, + the new value is computed as follows (where `redSrc` is the red channel value + for a pixel in the source image and `redDest` is the red channel value at the + corresponding pixel of the destination image): + + ```haxe + redDest = [(redSrc * redMultiplier) + (redDest * (256 - redMultiplier))] / 256; + ``` + + The `redMultiplier`, `greenMultiplier`, `blueMultiplier`, and `alphaMultiplier` + values are the multipliers used for each color channel. Use a hexadecimal + value ranging from 0 to 0x100 (256) where 0 specifies the full value from the + destination is used in the result, 0x100 specifies the full value from the + source is used, and numbers in between specify a blend is used (such as 0x80 + for 50%). + + @param sourceBitmapData The input bitmap image to use. The source image can + be a different BitmapData object, or it can refer to the current BitmapData + object. + @param sourceRect A rectangle that defines the area of the source image to use + as input. + @param destPoint The point within the destination image (the current + BitmapData instance) that corresponds to the upper-left corner of the source + rectangle. + @param redMultiplier A hexadecimal uint value by which to multiply the red + channel value. + @param greenMultiplier A hexadecimal uint value by which to multiply the green + channel value. + @param blueMultiplier A hexadecimal uint value by which to multiply the blue + channel value. + @param alphaMultiplier A hexadecimal uint value by which to multiply the alpha + transparency value. + @throws TypeError The `sourceBitmapData`, `sourceRect` or `destPoint` are `null`. + **/ + public function merge(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, redMultiplier:UInt, greenMultiplier:UInt, blueMultiplier:UInt, + alphaMultiplier:UInt):Void + { + #if lime + if (!readable || sourceBitmapData == null || !sourceBitmapData.readable || sourceRect == null || destPoint == null) return; + image.merge(sourceBitmapData.image, sourceRect.__toLimeRectangle(), destPoint.__toLimeVector2(), redMultiplier, greenMultiplier, blueMultiplier, + alphaMultiplier); + #end + } + + /** + Fills an image with pixels representing random noise. + + @param randomSeed The random seed number to use. If you keep all other + parameters the same, you can generate different + pseudo-random results by varying the random seed + value. The noise function is a mapping function, not + a true random-number generation function, so it + creates the same results each time from the same + random seed. + @param low The lowest value to generate for each channel (0 to + 255). + @param high The highest value to generate for each channel (0 to + 255). + @param channelOptions A number that can be a combination of any of the + four color channel values + (`BitmapDataChannel.RED`, + `BitmapDataChannel.BLUE`, + `BitmapDataChannel.GREEN`, and + `BitmapDataChannel.ALPHA`). You can use + the logical OR operator(`|`) to combine + channel values. + @param grayScale A Boolean value. If the value is `true`, + a grayscale image is created by setting all of the + color channels to the same value. The alpha channel + selection is not affected by setting this parameter + to `true`. + + @see [Making textures with noise functions](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/making-textures-with-noise-functions.html) + **/ + public function noise(randomSeed:Int, low:Int = 0, high:Int = 255, channelOptions:Int = 7, grayScale:Bool = false):Void + { + if (!readable) return; + + // Seeded Random Number Generator + var rand:Void->Int = + { + function func():Int + { + randomSeed = randomSeed * 1103515245 + 12345; + return Std.int(Math.abs(randomSeed / 65536)) % 32768; + } + }; + rand(); + + // Range of values to value to. + var range:Int = high - low; + + var redChannel:Bool = ((channelOptions & (1 << 0)) >> 0) == 1; + var greenChannel:Bool = ((channelOptions & (1 << 1)) >> 1) == 1; + var blueChannel:Bool = ((channelOptions & (1 << 2)) >> 2) == 1; + var alphaChannel:Bool = ((channelOptions & (1 << 3)) >> 3) == 1; + + for (y in 0...height) + { + for (x in 0...width) + { + // Default channel colours if all channel options are false. + var red:Int = 0; + var blue:Int = 0; + var green:Int = 0; + var alpha:Int = 255; + + if (grayScale) + { + red = green = blue = low + (rand() % range); + alpha = 255; + } + else + { + if (redChannel) red = low + (rand() % range); + if (greenChannel) green = low + (rand() % range); + if (blueChannel) blue = low + (rand() % range); + if (alphaChannel) alpha = low + (rand() % range); + } + + var rgb:Int = alpha; + rgb = (rgb << 8) + red; + rgb = (rgb << 8) + green; + rgb = (rgb << 8) + blue; + + setPixel32(x, y, rgb); + } + } + } + + /** + Remaps the color channel values in an image that has up to four arrays of + color palette data, one for each channel. + + Flash runtimes use the following steps to generate the resulting image: + + 1. After the red, green, blue, and alpha values are computed, they are added + together using standard 32-bit-integer arithmetic. + 2. The red, green, blue, and alpha channel values of each pixel are extracted + into separate 0 to 255 values. These values are used to look up new color + values in the appropriate array: `redArray`, `greenArray`, `blueArray`, and + `alphaArray`. Each of these four arrays should contain 256 values. + 3. After all four of the new channel values are retrieved, they are combined + into a standard ARGB value that is applied to the pixel. + + Cross-channel effects can be supported with this method. Each input array can + contain full 32-bit values, and no shifting occurs when the values are added + together. This routine does not support per-channel clamping. + + If no array is specified for a channel, the color channel is copied from the + source image to the destination image. + + You can use this method for a variety of effects such as general palette mapping + (taking one channel and converting it to a false color image). You can also use + this method for a variety of advanced color manipulation algorithms, such as + gamma, curves, levels, and quantizing. + + @param sourceBitmapData The input bitmap image to use. The source image can + be a different BitmapData object, or it can refer to the current BitmapData + instance. + @param sourceRect A rectangle that defines the area of the source image to use + as input. + @param destPoint The point within the destination image (the current BitmapData + object) that corresponds to the upper-left corner of the source rectangle. + @param redArray If `redArray` is not `null`, `red = redArray[source red value] else red = source rect value`. + @param greenArray If `greenArray` is not `null`, `green = greenArray[source green value] else green = source green value`. + @param blueArray If `blueArray` is not `null, `blue = blueArray[source blue value] else blue = source blue value`. + @param alphaArray If `alphaArray` is not `null, `alpha = alphaArray[source alpha value] else alpha = source alpha value`. + @throws TypeError The `sourceBitmapData`, `sourceRect` or `destPoint` are `null`. + **/ + public function paletteMap(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, redArray:Array = null, greenArray:Array = null, + blueArray:Array = null, alphaArray:Array = null):Void + { + var sw:Int = Std.int(sourceRect.width); + var sh:Int = Std.int(sourceRect.height); + + var pixels = sourceBitmapData.getPixels(sourceRect); + + var pixelValue:Int, r:Int, g:Int, b:Int, a:Int, color:Int; + + for (i in 0...(sh * sw)) + { + pixelValue = pixels.readUnsignedInt(); + + a = (alphaArray == null) ? pixelValue & 0xFF000000 : alphaArray[(pixelValue >> 24) & 0xFF]; + r = (redArray == null) ? pixelValue & 0x00FF0000 : redArray[(pixelValue >> 16) & 0xFF]; + g = (greenArray == null) ? pixelValue & 0x0000FF00 : greenArray[(pixelValue >> 8) & 0xFF]; + b = (blueArray == null) ? pixelValue & 0x000000FF : blueArray[(pixelValue) & 0xFF]; + + color = a + r + g + b; + + pixels.position = i * 4; + pixels.writeUnsignedInt(color); + } + + pixels.position = 0; + var destRect = Rectangle.__pool.get(); + destRect.setTo(destPoint.x, destPoint.y, sw, sh); + setPixels(destRect, pixels); + Rectangle.__pool.release(destRect); + } + + /** + Generates a Perlin noise image. + + The Perlin noise generation algorithm interpolates and combines + individual random noise functions (called octaves) into a single function + that generates more natural-seeming random noise. Like musical octaves, + each octave function is twice the frequency of the one before it. Perlin + noise has been described as a "fractal sum of noise" because it combines + multiple sets of noise data with different levels of detail. + + You can use Perlin noise functions to simulate natural phenomena and + landscapes, such as wood grain, clouds, and mountain ranges. In most + cases, the output of a Perlin noise function is not displayed directly but + is used to enhance other images and give them pseudo-random + variations. + + Simple digital random noise functions often produce images with harsh, + contrasting points. This kind of harsh contrast is not often found in + nature. The Perlin noise algorithm blends multiple noise functions that + operate at different levels of detail. This algorithm results in smaller + variations among neighboring pixel values. + + @param baseX Frequency to use in the _x_ direction. For + example, to generate a noise that is sized for a 64 + x 128 image, pass 64 for the `baseX` + value. + @param baseY Frequency to use in the _y_ direction. For + example, to generate a noise that is sized for a 64 + x 128 image, pass 128 for the `baseY` + value. + @param numOctaves Number of octaves or individual noise functions to + combine to create this noise. Larger numbers of + octaves create images with greater detail. Larger + numbers of octaves also require more processing + time. + @param randomSeed The random seed number to use. If you keep all other + parameters the same, you can generate different + pseudo-random results by varying the random seed + value. The Perlin noise function is a mapping + function, not a true random-number generation + function, so it creates the same results each time + from the same random seed. + @param stitch A Boolean value. If the value is `true`, + the method attempts to smooth the transition edges + of the image to create seamless textures for tiling + as a bitmap fill. + @param fractalNoise A Boolean value. If the value is `true`, + the method generates fractal noise; otherwise, it + generates turbulence. An image with turbulence has + visible discontinuities in the gradient that can + make it better approximate sharper visual effects + like flames and ocean waves. + @param channelOptions A number that can be a combination of any of the + four color channel values + (`BitmapDataChannel.RED`, + `BitmapDataChannel.BLUE`, + `BitmapDataChannel.GREEN`, and + `BitmapDataChannel.ALPHA`). You can use + the logical OR operator(`|`) to combine + channel values. + @param grayScale A Boolean value. If the value is `true`, + a grayscale image is created by setting each of the + red, green, and blue color channels to identical + values. The alpha channel value is not affected if + this value is set to `true`. + + @see [Making textures with noise functions](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/making-textures-with-noise-functions.html) + **/ + public function perlinNoise(baseX:Float, baseY:Float, numOctaves:UInt, randomSeed:Int, stitch:Bool, fractalNoise:Bool, channelOptions:UInt = 7, + grayScale:Bool = false, offsets:Array = null):Void + { + if (!readable) return; + var noise = new PerlinNoise(randomSeed, numOctaves, channelOptions, grayScale, 0.5, stitch, 0.15); + noise.fill(this, baseX, baseY, 0); + } + + // @:noCompletion @:dox(hide) public function pixelDissolve (sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, randomSeed:Int = 0, numPixels:Int = 0, fillColor:UInt = 0):Int; + + /** + Scrolls an image by a certain(_x_, _y_) pixel amount. Edge + regions outside the scrolling area are left unchanged. + + @param x The amount by which to scroll horizontally. + @param y The amount by which to scroll vertically. + + @see [Scrolling bitmaps](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/scrolling-bitmaps.html) + **/ + public function scroll(x:Int, y:Int):Void + { + if (!readable) return; + image.scroll(x, y); + } + + /** + Sets a single pixel of a BitmapData object. The current alpha channel + value of the image pixel is preserved during this operation. The value of + the RGB color parameter is treated as an unmultiplied color value. + + **Note:** To increase performance, when you use the + `setPixel()` or `setPixel32()` method repeatedly, + call the `lock()` method before you call the + `setPixel()` or `setPixel32()` method, and then call + the `unlock()` method when you have made all pixel changes. + This process prevents objects that reference this BitmapData instance from + updating until you finish making the pixel changes. + + @param x The _x_ position of the pixel whose value changes. + @param y The _y_ position of the pixel whose value changes. + @param color The resulting RGB color for the pixel. + + @see [Manipulating pixels](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/manipulating-pixels.html) + **/ + public function setPixel(x:Int, y:Int, color:Int):Void + { + if (!readable) return; + #if lime + image.setPixel(x, y, color, ARGB32); + #end + } + + /** + Sets the color and alpha transparency values of a single pixel of a + BitmapData object. This method is similar to the `setPixel()` + method; the main difference is that the `setPixel32()` method + takes an ARGB color value that contains alpha channel information. + + All pixels in a BitmapData object are stored as premultiplied color + values. A premultiplied image pixel has the red, green, and blue color + channel values already multiplied by the alpha data. For example, if the + alpha value is 0, the values for the RGB channels are also 0, independent + of their unmultiplied values. This loss of data can cause some problems + when you perform operations. All BitmapData methods take and return + unmultiplied values. The internal pixel representation is converted from + premultiplied to unmultiplied before it is returned as a value. During a + set operation, the pixel value is premultiplied before the raw image pixel + is set. + + **Note:** To increase performance, when you use the + `setPixel()` or `setPixel32()` method repeatedly, + call the `lock()` method before you call the + `setPixel()` or `setPixel32()` method, and then call + the `unlock()` method when you have made all pixel changes. + This process prevents objects that reference this BitmapData instance from + updating until you finish making the pixel changes. + + @param x The _x_ position of the pixel whose value changes. + @param y The _y_ position of the pixel whose value changes. + @param color The resulting ARGB color for the pixel. If the bitmap is + opaque(not transparent), the alpha transparency portion of + this color value is ignored. + + @see [Manipulating pixels](https://books.openfl.org/openfl-developers-guide/working-with-bitmaps/manipulating-pixels.html) + **/ + public function setPixel32(x:Int, y:Int, color:Int):Void + { + if (!readable) return; + #if lime + image.setPixel32(x, y, color, ARGB32); + #end + } + + /** + Converts a byte array into a rectangular region of pixel data. For each + pixel, the `ByteArray.readUnsignedInt()` method is called and + the return value is written into the pixel. If the byte array ends before + the full rectangle is written, the function returns. The data in the byte + array is expected to be 32-bit ARGB pixel values. No seeking is performed + on the byte array before or after the pixels are read. + + @param rect Specifies the rectangular region of the BitmapData + object. + @param inputByteArray A ByteArray object that consists of 32-bit + unmultiplied pixel values to be used in the + rectangular region. + @throws EOFError The `inputByteArray` object does not include + enough data to fill the area of the `rect` + rectangle. The method fills as many pixels as possible + before throwing the exception. + @throws TypeError The rect or inputByteArray are null. + **/ + public function setPixels(rect:Rectangle, byteArray:ByteArray):Void + { + if (!readable || rect == null) return; + + var length = (rect.width * rect.height * 4); + if (byteArray.bytesAvailable < length) throw new Error("End of file was encountered.", 2030); + + #if lime + image.setPixels(rect.__toLimeRectangle(), byteArray, ARGB32, byteArray.endian); + #end + } + + /** + Converts a Vector into a rectangular region of pixel data. For each pixel, + a Vector element is read and written into the BitmapData pixel. The data + in the Vector is expected to be 32-bit ARGB pixel values. + + @param rect Specifies the rectangular region of the BitmapData object. + @throws RangeError The vector array is not large enough to read all the + pixel data. + **/ + public function setVector(rect:Rectangle, inputVector:Vector):Void + { + var byteArray = new ByteArray(); + byteArray.length = inputVector.length * 4; + + for (color in inputVector) + { + byteArray.writeUnsignedInt(color); + } + + byteArray.position = 0; + setPixels(rect, byteArray); + } + + /** + Tests pixel values in an image against a specified threshold and sets + pixels that pass the test to new color values. Using the + `threshold()` method, you can isolate and replace color ranges + in an image and perform other logical operations on image pixels. + + The `threshold()` method's test logic is as follows: + + 1. If `((pixelValue & mask) operation(threshold & mask))`, + then set the pixel to `color`; + 2. Otherwise, if `copySource == true`, then set the pixel to + corresponding pixel value from `sourceBitmap`. + + The `operation` parameter specifies the comparison operator + to use for the threshold test. For example, by using "==" as the + `operation` parameter, you can isolate a specific color value + in an image. Or by using `{operation: "<", mask: 0xFF000000, + threshold: 0x7F000000, color: 0x00000000}`, you can set all + destination pixels to be fully transparent when the source image pixel's + alpha is less than 0x7F. You can use this technique for animated + transitions and other effects. + + @param sourceBitmapData The input bitmap image to use. The source image + can be a different BitmapData object or it can + refer to the current BitmapData instance. + @param sourceRect A rectangle that defines the area of the source + image to use as input. + @param destPoint The point within the destination image(the + current BitmapData instance) that corresponds to + the upper-left corner of the source rectangle. + @param operation One of the following comparison operators, passed + as a String: "<", "<=", ">", ">=", "==", "!=" + @param threshold The value that each pixel is tested against to see + if it meets or exceeds the threshhold. + @param color The color value that a pixel is set to if the + threshold test succeeds. The default value is + 0x00000000. + @param mask The mask to use to isolate a color component. + @param copySource If the value is `true`, pixel values + from the source image are copied to the + destination when the threshold test fails. If the + value is `false`, the source image is + not copied when the threshold test fails. + @return The number of pixels that were changed. + @throws ArgumentError The operation string is not a valid operation + @throws TypeError The sourceBitmapData, sourceRect destPoint or + operation are null. + **/ + public function threshold(sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point, operation:String, threshold:Int, color:Int = 0x00000000, + mask:Int = 0xFFFFFFFF, copySource:Bool = false):Int + { + if (sourceBitmapData == null + || sourceRect == null + || destPoint == null + || sourceRect.x > sourceBitmapData.width + || sourceRect.y > sourceBitmapData.height + || destPoint.x > width + || destPoint.y > height) + { + return 0; + } + + #if lime + return image.threshold(sourceBitmapData.image, sourceRect.__toLimeRectangle(), destPoint.__toLimeVector2(), operation, threshold, color, mask, + copySource, ARGB32); + #else + return 0; + #end + } + + /** + Unlocks an image so that any objects that reference the BitmapData object, + such as Bitmap objects, are updated when this BitmapData object changes. + To improve performance, use this method along with the `lock()` + method before and after numerous calls to the `setPixel()` or + `setPixel32()` method. + + @param changeRect The area of the BitmapData object that has changed. If + you do not specify a value for this parameter, the + entire area of the BitmapData object is considered + changed. + **/ + public function unlock(changeRect:Rectangle = null):Void {} + + @:noCompletion private function __applyAlpha(alpha:ByteArray):Void + { + #if (js && html5) + ImageCanvasUtil.convertToCanvas(image); + ImageCanvasUtil.createImageData(image); + #end + + var data = image.buffer.data; + + for (i in 0...alpha.length) + { + data[i * 4 + 3] = alpha.readUnsignedByte(); + } + + image.version++; + } + + @:noCompletion private function __drawCairo(source:IBitmapDrawable, renderer:CairoRenderer):Void + { + #if lime_cairo + var cairo = renderer.cairo; + + if (source == this) + { + source = clone(); + } + + if (!renderer.__allowSmoothing) cairo.antialias = NONE; + + renderer.__render(source); + + if (!renderer.__allowSmoothing) cairo.antialias = GOOD; + + cairo.target.flush(); + + image.dirty = true; + image.version++; + #end + } + + @:noCompletion private function __drawCanvas(source:IBitmapDrawable, renderer:CanvasRenderer):Void + { + var buffer = image.buffer; + + if (!renderer.__allowSmoothing) renderer.applySmoothing(buffer.__srcContext, false); + + renderer.__render(source); + + if (!renderer.__allowSmoothing) renderer.applySmoothing(buffer.__srcContext, true); + + buffer.__srcContext.setTransform(1, 0, 0, 1, 0, 0); + buffer.__srcImageData = null; + buffer.data = null; + + image.dirty = true; + image.version++; + } + + @:noCompletion private function __drawGL(source:IBitmapDrawable, renderer:OpenGLRenderer):Void + { + var context = renderer.__context3D; + + var cacheRTT = context.__state.renderToTexture; + var cacheRTTDepthStencil = context.__state.renderToTextureDepthStencil; + var cacheRTTAntiAlias = context.__state.renderToTextureAntiAlias; + var cacheRTTSurfaceSelector = context.__state.renderToTextureSurfaceSelector; + + context.setRenderToTexture(getTexture(context), true); + + renderer.__render(source); + + if (cacheRTT != null) + { + context.setRenderToTexture(cacheRTT, cacheRTTDepthStencil, cacheRTTAntiAlias, cacheRTTSurfaceSelector); + } + else + { + context.setRenderToBackBuffer(); + } + } + + @:noCompletion private function __fillRect(rect:Rectangle, color:Int, allowFramebuffer:Bool):Void + { + #if lime + if (rect == null) return; + + if (transparent && (color & 0xFF000000) == 0) + { + color = 0; + } + + if (allowFramebuffer + && __texture != null + && __texture.__glFramebuffer != null + && Lib.current.stage.__renderer.__type == OPENGL) + { + var renderer:OpenGLRenderer = cast Lib.current.stage.__renderer; + var context = renderer.__context3D; + var color:ARGB = (color : ARGB); + var useScissor = !this.rect.equals(rect); + + var cacheRTT = context.__state.renderToTexture; + var cacheRTTDepthStencil = context.__state.renderToTextureDepthStencil; + var cacheRTTAntiAlias = context.__state.renderToTextureAntiAlias; + var cacheRTTSurfaceSelector = context.__state.renderToTextureSurfaceSelector; + + context.setRenderToTexture(__texture); + + if (useScissor) + { + var x = Math.floor(rect.x); + var y = Math.floor(rect.y); + var width = (rect.width > 0 ? Math.ceil(rect.right) - x : 0); + var height = (rect.height > 0 ? Math.ceil(rect.bottom) - y : 0); + #if !openfl_dpi_aware + if (context.__backBufferWantsBestResolution) + { + x = Math.floor(rect.x / context.__stage.window.scale); + y = Math.floor(rect.y / context.__stage.window.scale); + width = (rect.width > 0 ? Math.ceil(rect.right / context.__stage.window.scale) - x : 0); + height = (rect.height > 0 ? Math.ceil(rect.bottom / context.__stage.window.scale) - y : 0); + } + #end + __fillRectRectangle.setTo(x, y, width, height); + context.setScissorRectangle(__fillRectRectangle); + } + + context.__clear(useScissor, color.r / 0xFF, color.g / 0xFF, color.b / 0xFF, transparent ? color.a / 0xFF : 1, 0, 0, Context3DClearMask.COLOR); + + if (useScissor) + { + context.setScissorRectangle(null); + } + + if (cacheRTT != null) + { + context.setRenderToTexture(cacheRTT, cacheRTTDepthStencil, cacheRTTAntiAlias, cacheRTTSurfaceSelector); + } + else + { + context.setRenderToBackBuffer(); + } + } + else if (readable) + { + image.fillRect(rect.__toLimeRectangle(), color, ARGB32); + } + #end + } + + @:noCompletion private inline function __fromBase64(base64:String, type:String):Void + { + #if lime + var image = Image.fromBase64(base64, type); + __fromImage(image); + #end + } + + @:noCompletion private inline function __fromBytes(bytes:ByteArray, rawAlpha:ByteArray = null):Void + { + #if lime + var image = Image.fromBytes(bytes); + __fromImage(image); + + if (rawAlpha != null) + { + __applyAlpha(rawAlpha); + } + #end + } + + @:noCompletion private function __fromFile(path:String):Void + { + #if lime + var image = Image.fromFile(path); + __fromImage(image); + #end + } + + @SuppressWarnings("checkstyle:Dynamic") + @:noCompletion private function __fromImage(image:#if lime Image #else Dynamic #end):Void + { + #if lime + if (image != null && image.buffer != null) + { + this.image = image; + + width = image.width; + height = image.height; + rect = new Rectangle(0, 0, image.width, image.height); + + __textureWidth = width; + __textureHeight = height; + + #if sys + image.format = BGRA32; + image.premultiplied = true; + #end + + readable = true; + __isValid = true; + } + #end + } + + @:noCompletion private function __getBounds(rect:Rectangle, matrix:Matrix):Void + { + var bounds = Rectangle.__pool.get(); + this.rect.__transform(bounds, matrix); + rect.__expand(bounds.x, bounds.y, bounds.width, bounds.height); + Rectangle.__pool.release(bounds); + } + + // @:noCompletion private function __getFramebuffer (context:Context3D, requireStencil:Bool):GLFramebuffer { + // if (__framebuffer == null || __framebufferContext != context.__context) { + // var gl = context.gl; + // var texture = getTexture (context); + // context.__bindGLTexture2D (texture.__textureID); + // __framebufferContext = context.__context; + // __framebuffer = gl.createFramebuffer (); + // context.__bindGLFramebuffer (__framebuffer); + // gl.framebufferTexture2D (gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture.__textureID, 0); + // if (gl.checkFramebufferStatus (gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + // trace (gl.getError ()); + // } + // } + // if (requireStencil && __stencilBuffer == null) { + // var gl = context.gl; + // __stencilBuffer = gl.createRenderbuffer (); + // gl.bindRenderbuffer (gl.RENDERBUFFER, __stencilBuffer); + // gl.renderbufferStorage (gl.RENDERBUFFER, gl.STENCIL_INDEX8, __textureWidth, __textureHeight); + // context.__bindGLFramebuffer (__framebuffer); + // gl.framebufferRenderbuffer (gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, __stencilBuffer); + // if (gl.checkFramebufferStatus (gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + // trace (gl.getError ()); + // } + // gl.bindRenderbuffer (gl.RENDERBUFFER, null); + // } + // return __framebuffer; + // } + @:noCompletion private inline function __loadFromBase64(base64:String, type:String):Future + { + #if lime + return Image.loadFromBase64(base64, type).then(function(image) + { + __fromImage(image); + return Future.withValue(this); + }); + #else + return cast Future.withValue(null); + #end + } + + @:noCompletion private inline function __loadFromBytes(bytes:ByteArray, rawAlpha:ByteArray = null):Future + { + #if lime + return Image.loadFromBytes(bytes).then(function(image) + { + __fromImage(image); + + if (rawAlpha != null) + { + __applyAlpha(rawAlpha); + } + + return Future.withValue(this); + }); + #else + return cast Future.withValue(null); + #end + } + + @:noCompletion private function __loadFromFile(path:String):Future + { + #if lime + return Image.loadFromFile(path).then(function(image) + { + __fromImage(image); + return Future.withValue(this); + }); + #else + return cast Future.withValue(this); + #end + } + + @:noCompletion private function __resize(width:Int, height:Int):Void + { + this.width = width; + this.height = height; + this.rect.width = width; + this.rect.height = height; + + __textureWidth = width; + __textureHeight = height; + } + + @:noCompletion private function __setUVRect(context:Context3D, x:Float, y:Float, width:Float, height:Float):Void + { + var buffer = getVertexBuffer(context); + + if (buffer != null && (width != __uvRect.width || height != __uvRect.height || x != __uvRect.x || y != __uvRect.y)) + { + var gl = context.gl; + + if (__uvRect == null) __uvRect = new Rectangle(); + __uvRect.setTo(x, y, width, height); + + var uvX = __textureWidth > 0 ? x / __textureWidth : 0; + var uvY = __textureHeight > 0 ? y / __textureHeight : 0; + var uvWidth = __textureWidth > 0 ? width / __textureWidth : 0; + var uvHeight = __textureHeight > 0 ? height / __textureHeight : 0; + + __vertexBufferData[0] = width; + __vertexBufferData[1] = height; + __vertexBufferData[3] = uvX + uvWidth; + __vertexBufferData[4] = uvY + uvHeight; + __vertexBufferData[VERTEX_BUFFER_STRIDE + 1] = height; + __vertexBufferData[VERTEX_BUFFER_STRIDE + 3] = uvX; + __vertexBufferData[VERTEX_BUFFER_STRIDE + 4] = uvY + uvHeight; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2] = width; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2 + 3] = uvX + uvWidth; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 2 + 4] = uvY; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 3 + 3] = uvX; + __vertexBufferData[VERTEX_BUFFER_STRIDE * 3 + 4] = uvY; + + __vertexBuffer.uploadFromTypedArray(__vertexBufferData); + } + } + + @:noCompletion private function __sync():Void + { + #if (js && html5) + ImageCanvasUtil.sync(image, false); + #end + } + + @:noCompletion private function __update(transformOnly:Bool, updateChildren:Bool):Void + { + __updateTransforms(); + } + + @:noCompletion private function __updateTransforms(overrideTransform:Matrix = null):Void + { + if (overrideTransform == null) + { + __worldTransform.identity(); + } + else + { + __worldTransform.copyFrom(overrideTransform); + } + + __renderTransform.copyFrom(__worldTransform); + } +} +#else +typedef BitmapData = flash.display.BitmapData; +#end diff --git a/source/openfl/display/DisplayObject.hx b/source/openfl/display/DisplayObject.hx new file mode 100644 index 00000000..a8e8b6a3 --- /dev/null +++ b/source/openfl/display/DisplayObject.hx @@ -0,0 +1,2463 @@ +package openfl.display; + +#if !flash +import openfl.display._internal.IBitmapDrawableType; +import openfl.utils.ObjectPool; +import openfl.utils._internal.Lib; +import openfl.errors.TypeError; +import openfl.events.Event; +import openfl.events.EventDispatcher; +import openfl.events.EventPhase; +import openfl.events.EventType; +import openfl.events.MouseEvent; +import openfl.events.RenderEvent; +import openfl.events.TouchEvent; +import openfl.filters.BitmapFilter; +import openfl.geom.ColorTransform; +import openfl.geom.Matrix; +import openfl.geom.Point; +import openfl.geom.Rectangle; +import openfl.geom.Transform; +import openfl.ui.MouseCursor; +import openfl.Vector; +#if lime +import lime.graphics.cairo.Cairo; +#end +#if (js && html5) +import js.html.CanvasElement; +import js.html.CanvasRenderingContext2D; +import js.html.CSSStyleDeclaration; +#end + +/** + The DisplayObject class is the base class for all objects that can be + placed on the display list. The display list manages all objects displayed + in openfl. Use the DisplayObjectContainer class to arrange the + display objects in the display list. DisplayObjectContainer objects can + have child display objects, while other display objects, such as Shape and + TextField objects, are "leaf" nodes that have only parents and siblings, no + children. + + The DisplayObject class supports basic functionality like the _x_ + and _y_ position of an object, as well as more advanced properties of + the object such as its transformation matrix. + + DisplayObject is an abstract base class; therefore, you cannot call + DisplayObject directly. Invoking `new DisplayObject()` throws an + `ArgumentError` exception. + + All display objects inherit from the DisplayObject class. + + The DisplayObject class itself does not include any APIs for rendering + content onscreen. For that reason, if you want create a custom subclass of + the DisplayObject class, you will want to extend one of its subclasses that + do have APIs for rendering content onscreen, such as the Shape, Sprite, + Bitmap, SimpleButton, TextField, or MovieClip class. + + The DisplayObject class contains several broadcast events. Normally, the + target of any particular event is a specific DisplayObject instance. For + example, the target of an `added` event is the specific + DisplayObject instance that was added to the display list. Having a single + target restricts the placement of event listeners to that target and in + some cases the target's ancestors on the display list. With broadcast + events, however, the target is not a specific DisplayObject instance, but + rather all DisplayObject instances, including those that are not on the + display list. This means that you can add a listener to any DisplayObject + instance to listen for broadcast events. In addition to the broadcast + events listed in the DisplayObject class's Events table, the DisplayObject + class also inherits two broadcast events from the EventDispatcher class: + `activate` and `deactivate`. + + Some properties previously used in the ActionScript 1.0 and 2.0 + MovieClip, TextField, and Button classes (such as `_alpha`, + `_height`, `_name`, `_width`, + `_x`, `_y`, and others) have equivalents in the + OpenFL DisplayObject class that are renamed so that they no + longer begin with the underscore (_) character. + + For more information, see the "Display Programming" chapter of the + _OpenFL Developer's Guide_. + + @event added Dispatched when a display object is added to the + display list. The following methods trigger this + event: + `DisplayObjectContainer.addChild()`, + `DisplayObjectContainer.addChildAt()`. + @event addedToStage Dispatched when a display object is added to the on + stage display list, either directly or through the + addition of a sub tree in which the display object + is contained. The following methods trigger this + event: + `DisplayObjectContainer.addChild()`, + `DisplayObjectContainer.addChildAt()`. + @event enterFrame [broadcast event] Dispatched when the playhead is + entering a new frame. If the playhead is not + moving, or if there is only one frame, this event + is dispatched continuously in conjunction with the + frame rate. This event is a broadcast event, which + means that it is dispatched by all display objects + with a listener registered for this event. + @event exitFrame [broadcast event] Dispatched when the playhead is + exiting the current frame. All frame scripts have + been run. If the playhead is not moving, or if + there is only one frame, this event is dispatched + continuously in conjunction with the frame rate. + This event is a broadcast event, which means that + it is dispatched by all display objects with a + listener registered for this event. + @event frameConstructed [broadcast event] Dispatched after the constructors + of frame display objects have run but before frame + scripts have run. If the playhead is not moving, or + if there is only one frame, this event is + dispatched continuously in conjunction with the + frame rate. This event is a broadcast event, which + means that it is dispatched by all display objects + with a listener registered for this event. + @event removed Dispatched when a display object is about to be + removed from the display list. Two methods of the + DisplayObjectContainer class generate this event: + `removeChild()` and + `removeChildAt()`. + + The following methods of a + DisplayObjectContainer object also generate this + event if an object must be removed to make room for + the new object: `addChild()`, + `addChildAt()`, and + `setChildIndex()`. + @event removedFromStage Dispatched when a display object is about to be + removed from the display list, either directly or + through the removal of a sub tree in which the + display object is contained. Two methods of the + DisplayObjectContainer class generate this event: + `removeChild()` and + `removeChildAt()`. + + The following methods of a + DisplayObjectContainer object also generate this + event if an object must be removed to make room for + the new object: `addChild()`, + `addChildAt()`, and + `setChildIndex()`. + @event render [broadcast event] Dispatched when the display list + is about to be updated and rendered. This event + provides the last opportunity for objects listening + for this event to make changes before the display + list is rendered. You must call the + `invalidate()` method of the Stage + object each time you want a `render` + event to be dispatched. `Render` events + are dispatched to an object only if there is mutual + trust between it and the object that called + `Stage.invalidate()`. This event is a + broadcast event, which means that it is dispatched + by all display objects with a listener registered + for this event. + + **Note:** This event is not dispatched if the + display is not rendering. This is the case when the + content is either minimized or obscured. + + @see [Display programming](https://books.openfl.org/openfl-developers-guide/display-programming/) + @see [Basics of display programming](https://books.openfl.org/openfl-developers-guide/display-programming/basics-of-display-programming.html) + @see [Core display classes](https://books.openfl.org/openfl-developers-guide/display-programming/core-display-classes.html) + @see [Working with display objects](https://books.openfl.org/openfl-developers-guide/display-programming/working-with-display-objects/) + @see [Adding display objects to the display list](https://books.openfl.org/openfl-developers-guide/display-programming/working-with-display-objects/adding-display-objects-to-the-display-list.html) + @see [Traversing the display list](https://books.openfl.org/openfl-developers-guide/display-programming/working-with-display-objects/traversing-the-display-list.html) + @see [Panning and scrolling display objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/panning-and-scrolling-display-objects.html) + @see [Caching display objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/caching-display-objects.html) + @see [Setting an opaque background](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/setting-an-opaque-background.html) + @see [Applying blending modes](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/applying-blending-modes.html) + @see [Adjusting display object colors](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/adjusting-displayobject-colors.html) + @see [Handling events for display objects](https://books.openfl.org/openfl-developers-guide/display-programming/working-with-display-objects/handling-events-for-display-objects.html) + @see [Choosing a display object subclass](https://books.openfl.org/openfl-developers-guide/display-programming/working-with-display-objects/choosing-a-displayobject-subclass.html) + @see [Manipulating display objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/) + @see [Animating objects](https://books.openfl.org/openfl-developers-guide/display-programming/animating-objects.html) + @see [Loading display content dynamically](https://books.openfl.org/openfl-developers-guide/display-programming/loading-display-content-dynamically/) +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(lime.graphics.Image) +@:access(lime.graphics.ImageBuffer) +@:access(openfl.display3D._internal.Context3DState) +@:access(openfl.display._internal.Context3DGraphics) +@:access(openfl.events.Event) +@:access(openfl.display3D.Context3D) +@:access(openfl.display.Bitmap) +@:access(openfl.display.BitmapData) +@:access(openfl.display.DisplayObjectContainer) +@:access(openfl.display.Graphics) +@:access(openfl.display.Shader) +@:access(openfl.display.Stage) +@:access(openfl.filters.BitmapFilter) +@:access(openfl.geom.ColorTransform) +@:access(openfl.geom.Matrix) +@:access(openfl.geom.Rectangle) +@:access(openfl.geom.Transform) +class DisplayObject extends EventDispatcher implements IBitmapDrawable #if (openfl_dynamic && haxe_ver < "4.0.0") implements Dynamic #end +{ + #if (openfl_enable_experimental_update_queue && !dom) + @:noCompletion private static var updateQueue:Array = []; + #end + + @:noCompletion private static var __broadcastEvents:Map> = new Map(); + @:noCompletion private static var __initStage:Stage; + @:noCompletion private static var __instanceCount:Int = 0; + + @:noCompletion + private static #if !js inline #end var __supportDOM:Bool #if !js = false #end; + + @:noCompletion private static var __tempStack:ObjectPool> = new ObjectPool>(function() return + new Vector(), function(stack) stack.length = 0); + + #if false + /** + The current accessibility options for this display object. If you modify the `accessibilityProperties` + property or any of the fields within `accessibilityProperties`, you must call the + `Accessibility.updateProperties()` method to make your changes take effect. + + **Note:** For an object created in the Flash authoring environment, the value of `accessibilityProperties` + is prepopulated with any information you entered in the Accessibility panel for that object. + **/ + // @:noCompletion @:dox(hide) public var accessibilityProperties:openfl.accessibility.AccessibilityProperties; + #end + + /** + Indicates the alpha transparency value of the object specified. Valid + values are 0 (fully transparent) to 1 (fully opaque). The default value is 1. + Display objects with `alpha` set to 0 _are_ active, even though they are invisible. + + @see [Fading objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/fading-objects.html) + **/ + @:keep public var alpha(get, set):Float; + + /** + A value from the BlendMode class that specifies which blend mode to use. A + bitmap can be drawn internally in two ways. If you have a blend mode + enabled or an external clipping mask, the bitmap is drawn by adding a + bitmap-filled square shape to the vector render. If you attempt to set + this property to an invalid value, Flash runtimes set the value to + `BlendMode.NORMAL`. + + The `blendMode` property affects each pixel of the display + object. Each pixel is composed of three constituent colors(red, green, + and blue), and each constituent color has a value between 0x00 and 0xFF. + Flash Player or Adobe AIR compares each constituent color of one pixel in + the movie clip with the corresponding color of the pixel in the + background. For example, if `blendMode` is set to + `BlendMode.LIGHTEN`, Flash Player or Adobe AIR compares the red + value of the display object with the red value of the background, and uses + the lighter of the two as the value for the red component of the displayed + color. + + The following table describes the `blendMode` settings. The + BlendMode class defines string values you can use. The illustrations in + the table show `blendMode` values applied to a circular display + object(2) superimposed on another display object(1). + + ![Square Number 1](/images/blendMode-0a.jpg) ![Circle Number 2](/images/blendMode-0b.jpg) + + | BlendMode Constant | Illustration | Description | + | --- | --- | --- | + | `BlendMode.NORMAL` | ![blend mode NORMAL](/images/blendMode-1.jpg) | The display object appears in front of the background. Pixel values of the display object override those of the background. Where the display object is transparent, the background is visible. | + | `BlendMode.LAYER` | ![blend mode LAYER](/images/blendMode-2.jpg) | Forces the creation of a transparency group for the display object. This means that the display object is pre-composed in a temporary buffer before it is processed further. This is done automatically if the display object is pre-cached using bitmap caching or if the display object is a display object container with at least one child object with a `blendMode` setting other than `BlendMode.NORMAL`. Not supported under GPU rendering. | + | `BlendMode.MULTIPLY` | ![blend mode MULTIPLY](/images/blendMode-3.jpg) | Multiplies the values of the display object constituent colors by the colors of the background color, and then normalizes by dividing by 0xFF, resulting in darker colors. This setting is commonly used for shadows and depth effects.
For example, if a constituent color (such as red) of one pixel in the display object and the corresponding color of the pixel in the background both have the value 0x88, the multiplied result is 0x4840. Dividing by 0xFF yields a value of 0x48 for that constituent color, which is a darker shade than the color of the display object or the color of the background. | + | `BlendMode.SCREEN` | ![blend mode SCREEN](/images/blendMode-4.jpg) | Multiplies the complement (inverse) of the display object color by the complement of the background color, resulting in a bleaching effect. This setting is commonly used for highlights or to remove black areas of the display object. | + | `BlendMode.LIGHTEN` | ![blend mode LIGHTEN](/images/blendMode-5.jpg) | Selects the lighter of the constituent colors of the display object and the color of the background (the colors with the larger values). This setting is commonly used for superimposing type.
For example, if the display object has a pixel with an RGB value of 0xFFCC33, and the background pixel has an RGB value of 0xDDF800, the resulting RGB value for the displayed pixel is 0xFFF833 (because 0xFF > 0xDD, 0xCC < 0xF8, and 0x33 > 0x00 = 33). Not supported under GPU rendering. | + | `BlendMode.DARKEN` | ![blend mode DARKEN](/images/blendMode-6.jpg) | Selects the darker of the constituent colors of the display object and the colors of the background (the colors with the smaller values). This setting is commonly used for superimposing type.
For example, if the display object has a pixel with an RGB value of 0xFFCC33, and the background pixel has an RGB value of 0xDDF800, the resulting RGB value for the displayed pixel is 0xDDCC00 (because 0xFF > 0xDD, 0xCC < 0xF8, and 0x33 > 0x00 = 33). Not supported under GPU rendering. | + | `BlendMode.DIFFERENCE` | ![blend mode DIFFERENCE](/images/blendMode-7.jpg) | Compares the constituent colors of the display object with the colors of its background, and subtracts the darker of the values of the two constituent colors from the lighter value. This setting is commonly used for more vibrant colors.
For example, if the display object has a pixel with an RGB value of 0xFFCC33, and the background pixel has an RGB value of 0xDDF800, the resulting RGB value for the displayed pixel is 0x222C33 (because 0xFF - 0xDD = 0x22, 0xF8 - 0xCC = 0x2C, and 0x33 - 0x00 = 0x33). | + | `BlendMode.ADD` | ![blend mode ADD](/images/blendMode-8.jpg) | Adds the values of the constituent colors of the display object to the colors of its background, applying a ceiling of 0xFF. This setting is commonly used for animating a lightening dissolve between two objects.
For example, if the display object has a pixel with an RGB value of 0xAAA633, and the background pixel has an RGB value of 0xDD2200, the resulting RGB value for the displayed pixel is 0xFFC833 (because 0xAA + 0xDD > 0xFF, 0xA6 + 0x22 = 0xC8, and 0x33 + 0x00 = 0x33). | + | `BlendMode.SUBTRACT` | ![blend mode SUBTRACT](/images/blendMode-9.jpg) | Subtracts the values of the constituent colors in the display object from the values of the background color, applying a floor of 0. This setting is commonly used for animating a darkening dissolve between two objects.
For example, if the display object has a pixel with an RGB value of 0xAA2233, and the background pixel has an RGB value of 0xDDA600, the resulting RGB value for the displayed pixel is 0x338400 (because 0xDD - 0xAA = 0x33, 0xA6 - 0x22 = 0x84, and 0x00 - 0x33 < 0x00). | + | `BlendMode.INVERT` | ![blend mode INVERT](/images/blendMode-10.jpg) | Inverts the background. | + | `BlendMode.ALPHA` | ![blend mode ALPHA](/images/blendMode-11.jpg) | Applies the alpha value of each pixel of the display object to the background. This requires the `blendMode` setting of the parent display object to be set to `BlendMode.LAYER`. For example, in the illustration, the parent display object, which is a white background, has `blendMode = BlendMode.LAYER`. Not supported under GPU rendering. | + | `BlendMode.ERASE` | ![blend mode ERASE](/images/blendMode-12.jpg) | Erases the background based on the alpha value of the display object. This requires the `blendMode` of the parent display object to be set to `BlendMode.LAYER`. For example, in the illustration, the parent display object, which is a white background, has `blendMode = BlendMode.LAYER`. Not supported under GPU rendering. | + | `BlendMode.OVERLAY` | ![blend mode OVERLAY](/images/blendMode-13.jpg) | Adjusts the color of each pixel based on the darkness of the background. If the background is lighter than 50% gray, the display object and background colors are screened, which results in a lighter color. If the background is darker than 50% gray, the colors are multiplied, which results in a darker color. This setting is commonly used for shading effects. Not supported under GPU rendering. | + | `BlendMode.HARDLIGHT` | ![blend mode HARDLIGHT](/images/blendMode-14.jpg) | Adjusts the color of each pixel based on the darkness of the display object. If the display object is lighter than 50% gray, the display object and background colors are screened, which results in a lighter color. If the display object is darker than 50% gray, the colors are multiplied, which results in a darker color. This setting is commonly used for shading effects. Not supported under GPU rendering. | + | `BlendMode.SHADER` | N/A | Adjusts the color using a custom shader routine. The shader that is used is specified as the Shader instance assigned to the blendShader property. Setting the blendShader property of a display object to a Shader instance automatically sets the display object's `blendMode` property to `BlendMode.SHADER`. If the `blendMode` property is set to `BlendMode.SHADER` without first setting the `blendShader` property, the `blendMode` property is set to `BlendMode.NORMAL`. Not supported under GPU rendering. | + + @see [Applying blending modes](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/applying-blending-modes.html) + **/ + public var blendMode(get, set):BlendMode; + + #if false + /** + Sets a shader that is used for blending the foreground and background. When the `blendMode` property is set + to `BlendMode.SHADER`, the specified Shader is used to create the blend mode output for the display object. + + Setting the `blendShader` property of a display object to a Shader instance automatically sets the display + object's `blendMode` property to `BlendMode.SHADER`. If the `blendShader` property is set (which sets the + `blendMode` property to `BlendMode.SHADER`), then the value of the `blendMode` property is changed, the + blend mode can be reset to use the blend shader simply by setting the `blendMode` property to + `BlendMode.SHADER`. The `blendShader` property does not need to be set again except to change the shader + that's used for the blend mode. + + The Shader assigned to the `blendShader` property must specify at least two `image4` inputs. The inputs do + not need to be specified in code using the associated ShaderInput objects' input properties. The background + display object is automatically used as the first input (the input with index 0). The foreground display + object is used as the second input (the input with index 1). A shader used as a blend shader can specify more + than two inputs. In that case, any additional input must be specified by setting its ShaderInput instance's + `input` property. + + When you assign a Shader instance to this property the shader is copied internally. The blend operation uses + that internal copy, not a reference to the original shader. Any changes made to the shader, such as changing + a parameter value, input, or bytecode, are not applied to the copied shader that's used for the blend mode. + **/ + // @:noCompletion @:dox(hide) @:require(flash10) public var blendShader(null, default):Shader; + #end + + /** + All vector data for a display object that has a cached bitmap is drawn + to the bitmap instead of the main display. If + `cacheAsBitmapMatrix` is null or unsupported, the bitmap is + then copied to the main display as unstretched, unrotated pixels snapped + to the nearest pixel boundaries. Pixels are mapped 1 to 1 with the parent + object. If the bounds of the bitmap change, the bitmap is recreated + instead of being stretched. + + If `cacheAsBitmapMatrix` is non-null and supported, the + object is drawn to the off-screen bitmap using that matrix and the + stretched and/or rotated results of that rendering are used to draw the + object to the main display. + + No internal bitmap is created unless the `cacheAsBitmap` + property is set to `true`. + + After you set the `cacheAsBitmap` property to + `true`, the rendering does not change, however the display + object performs pixel snapping automatically. The animation speed can be + significantly faster depending on the complexity of the vector content. + + The `cacheAsBitmap` property is automatically set to + `true` whenever you apply a filter to a display object(when + its `filter` array is not empty), and if a display object has a + filter applied to it, `cacheAsBitmap` is reported as + `true` for that display object, even if you set the property to + `false`. If you clear all filters for a display object, the + `cacheAsBitmap` setting changes to what it was last set to. + + A display object does not use a bitmap even if the + `cacheAsBitmap` property is set to `true` and + instead renders from vector data in the following cases: + + * The bitmap is too large. In AIR 1.5 and Flash Player 10, the maximum + size for a bitmap image is 8,191 pixels in width or height, and the total + number of pixels cannot exceed 16,777,215 pixels.(So, if a bitmap image + is 8,191 pixels wide, it can only be 2,048 pixels high.) In Flash Player 9 + and earlier, the limitation is is 2880 pixels in height and 2,880 pixels + in width. + * The bitmap fails to allocate (out of memory error). + + The `cacheAsBitmap` property is best used with movie clips + that have mostly static content and that do not scale and rotate + frequently. With such movie clips, `cacheAsBitmap` can lead to + performance increases when the movie clip is translated (when its _x_ + and _y_ position is changed). + + @see [Caching display objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/caching-display-objects.html) + **/ + public var cacheAsBitmap(get, set):Bool; + + /** + If non-null, this Matrix object defines how a display object is rendered when `cacheAsBitmap` is set to + `true`. The application uses this matrix as a transformation matrix that is applied when rendering the + bitmap version of the display object. + + _Adobe AIR profile support:_ This feature is supported on mobile devices, but it is not supported on desktop + operating systems. It also has limited support on AIR for TV devices. Specifically, on AIR for TV devices, + supported transformations include scaling and translation, but not rotation and skewing. See + [AIR Profile Support](http://help.adobe.com/en_US/air/build/WS144092a96ffef7cc16ddeea2126bb46b82f-8000.html) + for more information regarding API support across multiple profiles. + + With `cacheAsBitmapMatrix` set, the application retains a cached bitmap image across various 2D + transformations, including translation, rotation, and scaling. If the application uses hardware acceleration, + the object will be stored in video memory as a texture. This allows the GPU to apply the supported + transformations to the object. The GPU can perform these transformations faster than the CPU. + + To use the hardware acceleration, set Rendering to GPU in the General tab of the iPhone Settings dialog box + in Flash Professional CS5. Or set the `renderMode` property to gpu in the application descriptor file. Note + that AIR for TV devices automatically use hardware acceleration if it is available. + + For example, the following code sends an untransformed bitmap representation of the display object to the GPU: + + ```haxe + var matrix:Matrix = new Matrix(); // creates an identity matrix + mySprite.cacheAsBitmapMatrix = matrix; + mySprite.cacheAsBitmap = true; + ``` + + Usually, the identity matrix (`new Matrix()`) suffices. However, you can use another matrix, such as a + scaled-down matrix, to upload a different bitmap to the GPU. For example, the following example applies a + `cacheAsBitmapMatrix` matrix that is scaled by 0.5 on the x and y axes. The bitmap object that the GPU uses + is smaller, however the GPU adjusts its size to match the `transform.matrix` property of the display object: + + ```haxe + var matrix:Matrix = new Matrix(); // creates an identity matrix + matrix.scale(0.5, 0.5); // scales the matrix + mySprite.cacheAsBitmapMatrix = matrix; + mySprite.cacheAsBitmap = true; + ``` + + Generally, you should choose to use a matrix that transforms the display object to the size that it will + appear in the application. For example, if your application displays the bitmap version of the sprite scaled + down by a half, use a matrix that scales down by a half. If you application will display the sprite larger + than its current dimensions, use a matrix that scales up by that factor. + + **Note:** The `cacheAsBitmapMatrix` property is suitable for 2D transformations. If you need to apply + transformations in 3D, you may do so by setting a 3D property of the object and manipulating its + `transform.matrix3D` property. If the application is packaged using GPU mode, this allows the 3D transforms + to be applied to the object by the GPU. The `cacheAsBitmapMatrix` is ignored for 3D objects. + + @see [Caching display objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/caching-display-objects.html) + **/ + public var cacheAsBitmapMatrix(get, set):Matrix; + + /** + An indexed array that contains each filter object currently associated + with the display object. The openfl.filters package contains several + classes that define specific filters you can use. + + Filters can be applied in Flash Professional at design time, or at run + time by using Haxe code. To apply a filter by using Haxe, + you must make a temporary copy of the entire `filters` array, + modify the temporary array, then assign the value of the temporary array + back to the `filters` array. You cannot directly add a new + filter object to the `filters` array. + + To add a filter by using Haxe, perform the following steps + (assume that the target display object is named + `myDisplayObject`): + + 1. Create a new filter object by using the constructor method of your + chosen filter class. + 2. Assign the value of the `myDisplayObject.filters` array + to a temporary array, such as one named `myFilters`. + 3. Add the new filter object to the `myFilters` temporary + array. + 4. Assign the value of the temporary array to the + `myDisplayObject.filters` array. + + If the `filters` array is undefined, you do not need to use + a temporary array. Instead, you can directly assign an array literal that + contains one or more filter objects that you create. The first example in + the Examples section adds a drop shadow filter by using code that handles + both defined and undefined `filters` arrays. + + To modify an existing filter object, you must use the technique of + modifying a copy of the `filters` array: + + 1. Assign the value of the `filters` array to a temporary + array, such as one named `myFilters`. + 2. Modify the property by using the temporary array, + `myFilters`. For example, to set the quality property of the + first filter in the array, you could use the following code: + `myFilters[0].quality = 1;` + 3. Assign the value of the temporary array to the `filters` + array. + + At load time, if a display object has an associated filter, it is + marked to cache itself as a transparent bitmap. From this point forward, + as long as the display object has a valid filter list, the player caches + the display object as a bitmap. This source bitmap is used as a source + image for the filter effects. Each display object usually has two bitmaps: + one with the original unfiltered source display object and another for the + final image after filtering. The final image is used when rendering. As + long as the display object does not change, the final image does not need + updating. + + The openfl.filters package includes classes for filters. For example, to + create a DropShadow filter, you would write: + + @throws ArgumentError When `filters` includes a ShaderFilter + and the shader output type is not compatible with + this operation (the shader must specify a + `pixel4` output). + @throws ArgumentError When `filters` includes a ShaderFilter + and the shader doesn't specify any image input or + the first input is not an `image4` input. + @throws ArgumentError When `filters` includes a ShaderFilter + and the shader specifies an image input that isn't + provided. + @throws ArgumentError When `filters` includes a ShaderFilter, a + ByteArray or Vector. instance as a shader + input, and the `width` and + `height` properties aren't specified for + the ShaderInput object, or the specified values + don't match the amount of data in the input data. + See the `ShaderInput.input` property for + more information. + **/ + public var filters(get, set):Array; + + /** + Indicates the height of the display object, in pixels. The height is + calculated based on the bounds of the content of the display object. When + you set the `height` property, the `scaleY` property + is adjusted accordingly, as shown in the following code: + + Except for TextField and Video objects, a display object with no + content(such as an empty sprite) has a height of 0, even if you try to + set `height` to a different value. + + @see [Manipulating size and scaling objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/manipulating-size-and-scaling-objects.html) + **/ + @:keep public var height(get, set):Float; + + /** + Returns a LoaderInfo object containing information about loading the file + to which this display object belongs. The `loaderInfo` property + is defined only for the root display object of a SWF file or for a loaded + Bitmap (not for a Bitmap that is drawn with Haxe). To find the + `loaderInfo` object associated with the SWF file that contains + a display object named `myDisplayObject`, use + `myDisplayObject.root.loaderInfo`. + + A large SWF file can monitor its download by calling + `this.root.loaderInfo.addEventListener(Event.COMPLETE, + func)`. + **/ + public var loaderInfo(get, never):LoaderInfo; + + /** + The calling display object is masked by the specified `mask` + object. To ensure that masking works when the Stage is scaled, the + `mask` display object must be in an active part of the display + list. The `mask` object itself is not drawn. Set + `mask` to `null` to remove the mask. + + To be able to scale a mask object, it must be on the display list. To + be able to drag a mask Sprite object (by calling its + `startDrag()` method), it must be on the display list. To call + the `startDrag()` method for a mask sprite based on a + `mouseDown` event being dispatched by the sprite, set the + sprite's `buttonMode` property to `true`. + + When display objects are cached by setting the + `cacheAsBitmap` property to `true` an the + `cacheAsBitmapMatrix` property to a Matrix object, both the + mask and the display object being masked must be part of the same cached + bitmap. Thus, if the display object is cached, then the mask must be a + child of the display object. If an ancestor of the display object on the + display list is cached, then the mask must be a child of that ancestor or + one of its descendents. If more than one ancestor of the masked object is + cached, then the mask must be a descendent of the cached container closest + to the masked object in the display list. + + **Note:** A single `mask` object cannot be used to mask + more than one calling display object. When the `mask` is + assigned to a second display object, it is removed as the mask of the + first object, and that object's `mask` property becomes + `null`. + + @see [Masking display objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/masking-display-objects.html) + **/ + public var mask(get, set):DisplayObject; + + /** + Indicates the x coordinate of the mouse or user input device position, in + pixels. + + **Note**: For a DisplayObject that has been rotated, the returned x + coordinate will reflect the non-rotated object. + + @see [Capturing mouse input](https://books.openfl.org/openfl-developers-guide/mouse-input/capturing-mouse-input.html) + **/ + public var mouseX(get, never):Float; + + /** + Indicates the y coordinate of the mouse or user input device position, in + pixels. + + **Note**: For a DisplayObject that has been rotated, the returned y + coordinate will reflect the non-rotated object. + + @see [Capturing mouse input](https://books.openfl.org/openfl-developers-guide/mouse-input/capturing-mouse-input.html) + **/ + public var mouseY(get, never):Float; + + /** + Indicates the instance name of the DisplayObject. The object can be + identified in the child list of its parent display object container by + calling the `getChildByName()` method of the display object + container. + + @throws IllegalOperationError If you are attempting to set this property + on an object that was placed on the timeline + in the Flash authoring tool. + **/ + public var name(get, set):String; + + /** + Specifies whether the display object is opaque with a certain background + color. A transparent bitmap contains alpha channel data and is drawn + transparently. An opaque bitmap has no alpha channel (and renders faster + than a transparent bitmap). If the bitmap is opaque, you specify its own + background color to use. + + If set to a number value, the surface is opaque (not transparent) with + the RGB background color that the number specifies. If set to + `null`(the default value), the display object has a + transparent background. + + The `opaqueBackground` property is intended mainly for use + with the `cacheAsBitmap` property, for rendering optimization. + For display objects in which the `cacheAsBitmap` property is + set to true, setting `opaqueBackground` can improve rendering + performance. + + The opaque background region is _not_ matched when calling the + `hitTestPoint()` method with the `shapeFlag` + parameter set to `true`. + + The opaque background region does not respond to mouse events. + + @see [Setting an opaque background](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/setting-an-opaque-background.html) + **/ + public var opaqueBackground:Null; + + /** + Indicates the DisplayObjectContainer object that contains this display + object. Use the `parent` property to specify a relative path to + display objects that are above the current display object in the display + list hierarchy. + + You can use `parent` to move up multiple levels in the + display list as in the following: + + ```haxe + this.parent.parent.alpha = 20; + ``` + + @throws SecurityError The parent display object belongs to a security + sandbox to which you do not have access. You can + avoid this situation by having the parent movie call + the `Security.allowDomain()` method. + + @see [Traversing the display list](https://books.openfl.org/openfl-developers-guide/display-programming/working-with-display-objects/traversing-the-display-list.html) + **/ + public var parent(default, null):DisplayObjectContainer; + + /** + For a display object in a loaded SWF file, the `root` property + is the top-most display object in the portion of the display list's tree + structure represented by that SWF file. For a Bitmap object representing a + loaded image file, the `root` property is the Bitmap object + itself. For the instance of the main class of the first SWF file loaded, + the `root` property is the display object itself. The + `root` property of the Stage object is the Stage object itself. + The `root` property is set to `null` for any display + object that has not been added to the display list, unless it has been + added to a display object container that is off the display list but that + is a child of the top-most display object in a loaded SWF file. + + For example, if you create a new Sprite object by calling the + `Sprite()` constructor method, its `root` property + is `null` until you add it to the display list (or to a display + object container that is off the display list but that is a child of the + top-most display object in a SWF file). + + For a loaded SWF file, even though the Loader object used to load the + file may not be on the display list, the top-most display object in the + SWF file has its `root` property set to itself. The Loader + object does not have its `root` property set until it is added + as a child of a display object for which the `root` property is + set. + + @see [Traversing the display list](https://books.openfl.org/openfl-developers-guide/display-programming/working-with-display-objects/traversing-the-display-list.html) + **/ + public var root(get, never):DisplayObject; + + /** + Indicates the rotation of the DisplayObject instance, in degrees, from its + original orientation. Values from 0 to 180 represent clockwise rotation; + values from 0 to -180 represent counterclockwise rotation. Values outside + this range are added to or subtracted from 360 to obtain a value within + the range. For example, the statement `my_video.rotation = 450` + is the same as ` my_video.rotation = 90`. + + @see [Rotating objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/rotating-objects.html) + **/ + @:keep public var rotation(get, set):Float; + + #if false + /** + Indicates the x-axis rotation of the DisplayObject instance, in degrees, from its original orientation + relative to the 3D parent container. Values from 0 to 180 represent clockwise rotation; values from 0 to + -180 represent counterclockwise rotation. Values outside this range are added to or subtracted from 360 to + obtain a value within the range. + **/ + // @:noCompletion @:dox(hide) @:require(flash10) public var rotationX:Float; + #end + #if false + /** + Indicates the y-axis rotation of the DisplayObject instance, in degrees, from its original orientation + relative to the 3D parent container. Values from 0 to 180 represent clockwise rotation; values from 0 to + -180 represent counterclockwise rotation. Values outside this range are added to or subtracted from 360 to + obtain a value within the range. + **/ + // @:noCompletion @:dox(hide) @:require(flash10) public var rotationY:Float; + #end + #if false + /** + Indicates the z-axis rotation of the DisplayObject instance, in degrees, from its original orientation + relative to the 3D parent container. Values from 0 to 180 represent clockwise rotation; values from 0 to + -180 represent counterclockwise rotation. Values outside this range are added to or subtracted from 360 to + obtain a value within the range. + **/ + // @:noCompletion @:dox(hide) @:require(flash10) public var rotationZ:Float; + #end + + /** + The current scaling grid that is in effect. If set to `null`, + the entire display object is scaled normally when any scale transformation + is applied. + + When you define the `scale9Grid` property, the display + object is divided into a grid with nine regions based on the + `scale9Grid` rectangle, which defines the center region of the + grid. The eight other regions of the grid are the following areas: + + * The upper-left corner outside of the rectangle + * The area above the rectangle + * The upper-right corner outside of the rectangle + * The area to the left of the rectangle + * The area to the right of the rectangle + * The lower-left corner outside of the rectangle + * The area below the rectangle + * The lower-right corner outside of the rectangle + + You can think of the eight regions outside of the center (defined by + the rectangle) as being like a picture frame that has special rules + applied to it when scaled. + + **Note:** Content that is not rendered through the `graphics` interface + of a display object will not be affected by the `scale9Grid` property. + + When the `scale9Grid` property is set and a display object + is scaled, all text and gradients are scaled normally; however, for other + types of objects the following rules apply: + + * Content in the center region is scaled normally. + * Content in the corners is not scaled. + * Content in the top and bottom regions is scaled horizontally only. + * Content in the left and right regions is scaled vertically only. + * All fills (including bitmaps, video, and gradients) are stretched to + fit their shapes. + + If a display object is rotated, all subsequent scaling is normal(and + the `scale9Grid` property is ignored). + + For example, consider the following display object and a rectangle that + is applied as the display object's `scale9Grid`: + + | | | + | --- | --- | + | ![display object image](/images/scale9Grid-a.jpg)
The display object. | ![display object scale 9 region](/images/scale9Grid-b.jpg)
The red rectangle shows the scale9Grid. | + + When the display object is scaled or stretched, the objects within the rectangle scale normally, but the + objects outside of the rectangle scale according to the `scale9Grid` rules: + + | | | + | --- | --- | + | Scaled to 75%: | ![display object at 75%](/images/scale9Grid-c.jpg) | + | Scaled to 50%: | ![display object at 50%](/images/scale9Grid-d.jpg) | + | Scaled to 25%: | ![display object at 25%](/images/scale9Grid-e.jpg) | + | Stretched horizontally 150%: | ![display stretched 150%](/images/scale9Grid-f.jpg) | + + A common use for setting `scale9Grid` is to set up a display + object to be used as a component, in which edge regions retain the same + width when the component is scaled. + + @throws ArgumentError If you pass an invalid argument to the method. + **/ + public var scale9Grid(get, set):Rectangle; + + /** + Indicates the horizontal scale (percentage) of the object as applied from + the registration point. The default registration point is (0,0). 1.0 + equals 100% scale. + + Scaling the local coordinate system changes the `x` and + `y` property values, which are defined in whole pixels. + + @see [Manipulating size and scaling objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/manipulating-size-and-scaling-objects.html) + **/ + @:keep public var scaleX(get, set):Float; + + /** + Indicates the vertical scale (percentage) of an object as applied from the + registration point of the object. The default registration point is (0,0). + 1.0 is 100% scale. + + Scaling the local coordinate system changes the `x` and + `y` property values, which are defined in whole pixels. + + @see [Manipulating size and scaling objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/manipulating-size-and-scaling-objects.html) + **/ + @:keep public var scaleY(get, set):Float; + + #if false + /** + Indicates the depth scale (percentage) of an object as applied from the registration point of the object. + The default registration point is (0,0). 1.0 is 100% scale. + + Scaling the local coordinate system changes the `x`, `y` and `z` property values, which are defined in whole + pixels. + **/ + // @:noCompletion @:dox(hide) @:require(flash10) public var scaleZ:Float; + #end + + /** + The scroll rectangle bounds of the display object. The display object is + cropped to the size defined by the rectangle, and it scrolls within the + rectangle when you change the `x` and `y` properties + of the `scrollRect` object. + + The properties of the `scrollRect` Rectangle object use the + display object's coordinate space and are scaled just like the overall + display object. The corner bounds of the cropped window on the scrolling + display object are the origin of the display object(0,0) and the point + defined by the width and height of the rectangle. They are not centered + around the origin, but use the origin to define the upper-left corner of + the area. A scrolled display object always scrolls in whole pixel + increments. + + You can scroll an object left and right by setting the `x` + property of the `scrollRect` Rectangle object. You can scroll + an object up and down by setting the `y` property of the + `scrollRect` Rectangle object. If the display object is rotated + 90° and you scroll it left and right, the display object actually scrolls + up and down. + + @see [Panning and scrolling display objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/panning-and-scrolling-display-objects.html) + **/ + public var scrollRect(get, set):Rectangle; + + /** + **BETA** + + Applies a custom Shader object to use when rendering this display object (or its children) when using + hardware rendering. This occurs as a single-pass render on this object only, if visible. In order to + apply a post-process effect to multiple display objects at once, enable `cacheAsBitmap` or use the + `filters` property with a ShaderFilter + **/ + @:beta public var shader(get, set):Shader; + + /** + The Stage of the display object. A Flash runtime application has only one + Stage object. For example, you can create and load multiple display + objects into the display list, and the `stage` property of each + display object refers to the same Stage object (even if the display object + belongs to a loaded SWF file). + + If a display object is not added to the display list, its + `stage` property is set to `null`. + + @see [Traversing the display list](https://books.openfl.org/openfl-developers-guide/display-programming/working-with-display-objects/traversing-the-display-list.html) + **/ + public var stage(default, null):Stage; + + /** + An object with properties pertaining to a display object's matrix, color + transform, and pixel bounds. The specific properties - matrix, + colorTransform, and three read-only properties + (`concatenatedMatrix`, `concatenatedColorTransform`, + and `pixelBounds`) - are described in the entry for the + Transform class. + + Each of the transform object's properties is itself an object. This + concept is important because the only way to set new values for the matrix + or colorTransform objects is to create a new object and copy that object + into the transform.matrix or transform.colorTransform property. + + For example, to increase the `tx` value of a display + object's matrix, you must make a copy of the entire matrix object, then + copy the new object into the matrix property of the transform object: + ` var myMatrix:Matrix = + myDisplayObject.transform.matrix; myMatrix.tx += 10; + myDisplayObject.transform.matrix = myMatrix; ` + + You cannot directly set the `tx` property. The following + code has no effect on `myDisplayObject`: + ` myDisplayObject.transform.matrix.tx += + 10; ` + + You can also copy an entire transform object and assign it to another + display object's transform property. For example, the following code + copies the entire transform object from `myOldDisplayObj` to + `myNewDisplayObj`: + `myNewDisplayObj.transform = myOldDisplayObj.transform;` + + The resulting display object, `myNewDisplayObj`, now has the + same values for its matrix, color transform, and pixel bounds as the old + display object, `myOldDisplayObj`. + + Note that AIR for TV devices use hardware acceleration, if it is + available, for color transforms. + **/ + @:keep public var transform(get, set):Transform; + + /** + Whether or not the display object is visible. Display objects that are not + visible are disabled. For example, if `visible=false` for an + InteractiveObject instance, it cannot be clicked. + **/ + public var visible(get, set):Bool; + + /** + Indicates the width of the display object, in pixels. The width is + calculated based on the bounds of the content of the display object. When + you set the `width` property, the `scaleX` property + is adjusted accordingly, as shown in the following code: + + Except for TextField and Video objects, a display object with no + content(such as an empty sprite) has a width of 0, even if you try to set + `width` to a different value. + + @see [Manipulating size and scaling objects](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/manipulating-size-and-scaling-objects.html) + **/ + @:keep public var width(get, set):Float; + + /** + Indicates the _x_ coordinate of the DisplayObject instance relative + to the local coordinates of the parent DisplayObjectContainer. If the + object is inside a DisplayObjectContainer that has transformations, it is + in the local coordinate system of the enclosing DisplayObjectContainer. + Thus, for a DisplayObjectContainer rotated 90° counterclockwise, the + DisplayObjectContainer's children inherit a coordinate system that is + rotated 90° counterclockwise. The object's coordinates refer to the + registration point position. + + @see [Changing position](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/changing-position.html) + **/ + @:keep public var x(get, set):Float; + + /** + Indicates the _y_ coordinate of the DisplayObject instance relative + to the local coordinates of the parent DisplayObjectContainer. If the + object is inside a DisplayObjectContainer that has transformations, it is + in the local coordinate system of the enclosing DisplayObjectContainer. + Thus, for a DisplayObjectContainer rotated 90° counterclockwise, the + DisplayObjectContainer's children inherit a coordinate system that is + rotated 90° counterclockwise. The object's coordinates refer to the + registration point position. + + @see [Changing position](https://books.openfl.org/openfl-developers-guide/display-programming/manipulating-display-objects/changing-position.html) + **/ + @:keep public var y(get, set):Float; + + /** + If true, the area of this DisplayObject's mask will be inverted. This + is used for "erasing" a portion of the underlying image. + **/ + @:keep public var maskInverted:Bool; + + // @:noCompletion @:dox(hide) @:require(flash10) var z:Float; + @:noCompletion private var __alpha:Float; + @:noCompletion private var __blendMode:BlendMode; + @:noCompletion private var __cacheAsBitmap:Bool; + @:noCompletion private var __cacheAsBitmapMatrix:Matrix; + @:noCompletion private var __cacheBitmap:Bitmap; + @:noCompletion private var __cacheBitmapBackground:Null; + @:noCompletion private var __cacheBitmapColorTransform:ColorTransform; + @:noCompletion private var __cacheBitmapData:BitmapData; + @:noCompletion private var __cacheBitmapData2:BitmapData; + @:noCompletion private var __cacheBitmapData3:BitmapData; + @:noCompletion private var __cacheBitmapMatrix:Matrix; + @:noCompletion private var __cacheBitmapRenderer:DisplayObjectRenderer; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __cairo:#if lime Cairo #else Dynamic #end; + @:noCompletion private var __children:Array; + @:noCompletion private var __customRenderClear:Bool; + @:noCompletion private var __customRenderEvent:RenderEvent; + @:noCompletion private var __drawableType:IBitmapDrawableType; + @:noCompletion private var __filters:Array; + @:noCompletion private var __graphics:Graphics; + @:noCompletion private var __interactive:Bool; + @:noCompletion private var __isCacheBitmapRender:Bool; + @:noCompletion private var __isMask:Bool; + @:noCompletion private var __loaderInfo:LoaderInfo; + @:noCompletion private var __mask:DisplayObject; + @:noCompletion private var __maskTarget:DisplayObject; + @:noCompletion private var __name:String; + @:noCompletion private var __objectTransform:Transform; + @:noCompletion private var __renderable:Bool; + @:noCompletion private var __renderDirty:Bool; + @:noCompletion private var __renderParent:DisplayObject; + @:noCompletion private var __renderTransform:Matrix; + @:noCompletion private var __renderTransformCache:Matrix; + @:noCompletion private var __renderTransformChanged:Bool; + @:noCompletion private var __rotation:Float; + @:noCompletion private var __rotationCosine:Float; + @:noCompletion private var __rotationSine:Float; + @:noCompletion private var __scale9Grid:Rectangle; + @:noCompletion private var __scaleX:Float; + @:noCompletion private var __scaleY:Float; + @:noCompletion private var __scrollRect:Rectangle; + @:noCompletion private var __shader:Shader; + @:noCompletion private var __tempPoint:Point; + @:noCompletion private var __transform:Matrix; + @:noCompletion private var __transformDirty:Bool; + @:noCompletion private var __visible:Bool; + @:noCompletion private var __worldAlpha:Float; + @:noCompletion private var __worldAlphaChanged:Bool; + @:noCompletion private var __worldBlendMode:BlendMode; + @:noCompletion private var __worldClip:Rectangle; + @:noCompletion private var __worldClipChanged:Bool; + @:noCompletion private var __worldColorTransform:ColorTransform; + @:noCompletion private var __worldShader:Shader; + @:noCompletion private var __worldScale9Grid:Rectangle; + @:noCompletion private var __worldTransform:Matrix; + @:noCompletion private var __worldVisible:Bool; + @:noCompletion private var __worldVisibleChanged:Bool; + @:noCompletion private var __worldTransformInvalid:Bool; + @:noCompletion private var __worldZ:Int; + #if (js && html5) + @:noCompletion private var __canvas:CanvasElement; + @:noCompletion private var __context:CanvasRenderingContext2D; + @:noCompletion private var __style:CSSStyleDeclaration; + #end + + #if openfljs + @:noCompletion private static function __init__() + { + untyped Object.defineProperties(DisplayObject.prototype, { + "alpha": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_alpha (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_alpha (v); }") + }, + "blendMode": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_blendMode (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_blendMode (v); }") + }, + "cacheAsBitmap": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_cacheAsBitmap (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_cacheAsBitmap (v); }") + }, + "cacheAsBitmapMatrix": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_cacheAsBitmapMatrix (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_cacheAsBitmapMatrix (v); }") + }, + "filters": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_filters (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_filters (v); }") + }, + "height": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_height (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_height (v); }") + }, + "loaderInfo": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_loaderInfo (); }") + }, + "mask": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_mask (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_mask (v); }") + }, + "mouseX": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_mouseX (); }") + }, + "mouseY": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_mouseY (); }") + }, + "name": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_name (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_name (v); }") + }, + "root": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_root (); }") + }, + "rotation": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_rotation (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_rotation (v); }") + }, + "scaleX": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_scaleX (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_scaleX (v); }") + }, + "scaleY": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_scaleY (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_scaleY (v); }") + }, + "scrollRect": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_scrollRect (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_scrollRect (v); }") + }, + "shader": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_shader (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_shader (v); }") + }, + "transform": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_transform (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_transform (v); }") + }, + "visible": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_visible (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_visible (v); }") + }, + "width": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_width (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_width (v); }") + }, + "x": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_x (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_x (v); }") + }, + "y": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_y (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_y (v); }") + }, + }); + } + #end + + @:noCompletion private function new() + { + super(); + + __drawableType = DISPLAY_OBJECT; + + __alpha = 1; + __blendMode = NORMAL; + __cacheAsBitmap = false; + __transform = new Matrix(); + __visible = true; + + __rotation = 0; + __rotationSine = 0; + __rotationCosine = 1; + __scaleX = 1; + __scaleY = 1; + + __worldAlpha = 1; + __worldBlendMode = NORMAL; + __worldTransform = new Matrix(); + __worldColorTransform = new ColorTransform(); + __renderTransform = new Matrix(); + __worldVisible = true; + + name = "instance" + (++__instanceCount); + + if (__initStage != null) + { + this.stage = __initStage; + __initStage = null; + this.stage.addChild(this); + } + } + + @SuppressWarnings("checkstyle:Dynamic") + public override function addEventListener(type:EventType, listener:T->Void, useCapture:Bool = false, priority:Int = 0, + useWeakReference:Bool = false):Void + { + switch (type) + { + case Event.ACTIVATE, Event.DEACTIVATE, Event.ENTER_FRAME, Event.EXIT_FRAME, Event.FRAME_CONSTRUCTED, Event.RENDER: + if (!__broadcastEvents.exists(type)) + { + __broadcastEvents.set(type, []); + } + + var dispatchers = __broadcastEvents.get(type); + + if (dispatchers.indexOf(this) == -1) + { + dispatchers.push(this); + } + + case RenderEvent.CLEAR_DOM, RenderEvent.RENDER_CAIRO, RenderEvent.RENDER_CANVAS, RenderEvent.RENDER_DOM, RenderEvent.RENDER_OPENGL: + if (__customRenderEvent == null) + { + __customRenderEvent = new RenderEvent(null); + __customRenderEvent.objectColorTransform = new ColorTransform(); + __customRenderEvent.objectMatrix = new Matrix(); + __customRenderClear = true; + } + + default: + } + + super.addEventListener(type, listener, useCapture, priority, useWeakReference); + } + + public override function dispatchEvent(event:Event):Bool + { + if ((event is MouseEvent)) + { + var mouseEvent:MouseEvent = cast event; + mouseEvent.stageX = __getRenderTransform().__transformX(mouseEvent.localX, mouseEvent.localY); + mouseEvent.stageY = __getRenderTransform().__transformY(mouseEvent.localX, mouseEvent.localY); + } + else if ((event is TouchEvent)) + { + var touchEvent:TouchEvent = cast event; + touchEvent.stageX = __getRenderTransform().__transformX(touchEvent.localX, touchEvent.localY); + touchEvent.stageY = __getRenderTransform().__transformY(touchEvent.localX, touchEvent.localY); + } + + event.target = this; + + return __dispatchWithCapture(event); + } + + /** + Returns a rectangle that defines the area of the display object relative + to the coordinate system of the `targetCoordinateSpace` object. + Consider the following code, which shows how the rectangle returned can + vary depending on the `targetCoordinateSpace` parameter that + you pass to the method: + + **Note:** Use the `localToGlobal()` and + `globalToLocal()` methods to convert the display object's local + coordinates to display coordinates, or display coordinates to local + coordinates, respectively. + + The `getBounds()` method is similar to the + `getRect()` method; however, the Rectangle returned by the + `getBounds()` method includes any strokes on shapes, whereas + the Rectangle returned by the `getRect()` method does not. For + an example, see the description of the `getRect()` method. + + @param targetCoordinateSpace The display object that defines the + coordinate system to use. + @return The rectangle that defines the area of the display object relative + to the `targetCoordinateSpace` object's coordinate + system. + **/ + public function getBounds(targetCoordinateSpace:DisplayObject):Rectangle + { + var matrix = Matrix.__pool.get(); + + if (targetCoordinateSpace != null && targetCoordinateSpace != this) + { + matrix.copyFrom(__getWorldTransform()); + + var targetMatrix = Matrix.__pool.get(); + + targetMatrix.copyFrom(targetCoordinateSpace.__getWorldTransform()); + targetMatrix.invert(); + + matrix.concat(targetMatrix); + + Matrix.__pool.release(targetMatrix); + } + else + { + matrix.identity(); + } + + var bounds = new Rectangle(); + __getBounds(bounds, matrix); + + Matrix.__pool.release(matrix); + + return bounds; + } + + /** + Returns a rectangle that defines the boundary of the display object, based + on the coordinate system defined by the `targetCoordinateSpace` + parameter, excluding any strokes on shapes. The values that the + `getRect()` method returns are the same or smaller than those + returned by the `getBounds()` method. + + **Note:** Use `localToGlobal()` and + `globalToLocal()` methods to convert the display object's local + coordinates to Stage coordinates, or Stage coordinates to local + coordinates, respectively. + + @param targetCoordinateSpace The display object that defines the + coordinate system to use. + @return The rectangle that defines the area of the display object relative + to the `targetCoordinateSpace` object's coordinate + system. + **/ + public function getRect(targetCoordinateSpace:DisplayObject):Rectangle + { + // should not account for stroke widths, but is that possible? + return getBounds(targetCoordinateSpace); + } + + /** + Converts the `point` object from the Stage (global) coordinates + to the display object's (local) coordinates. + + To use this method, first create an instance of the Point class. The + _x_ and _y_ values that you assign represent global coordinates + because they relate to the origin(0,0) of the main display area. Then + pass the Point instance as the parameter to the + `globalToLocal()` method. The method returns a new Point object + with _x_ and _y_ values that relate to the origin of the display + object instead of the origin of the Stage. + + @param point An object created with the Point class. The Point object + specifies the _x_ and _y_ coordinates as + properties. + @return A Point object with coordinates relative to the display object. + **/ + public function globalToLocal(pos:Point):Point + { + return __globalToLocal(pos, new Point()); + } + + // @:noCompletion @:dox(hide) @:require(flash10) public function globalToLocal3D (point:Point):Vector3D; + + /** + Evaluates the bounding box of the display object to see if it overlaps or + intersects with the bounding box of the `obj` display object. + + @param obj The display object to test against. + @return `true` if the bounding boxes of the display objects + intersect; `false` if not. + **/ + public function hitTestObject(obj:DisplayObject):Bool + { + if (obj != null && obj.parent != null && parent != null) + { + var currentBounds = getBounds(this); + var targetBounds = obj.getBounds(this); + + return currentBounds.intersects(targetBounds); + } + + return false; + } + + /** + Evaluates the display object to see if it overlaps or intersects with the + point specified by the `x` and `y` parameters. The + `x` and `y` parameters specify a point in the + coordinate space of the Stage, not the display object container that + contains the display object (unless that display object container is the + Stage). + + @param x The _x_ coordinate to test against this object. + @param y The _y_ coordinate to test against this object. + @param shapeFlag Whether to check against the actual pixels of the object + (`true`) or the bounding box + (`false`). + @return `true` if the display object overlaps or intersects + with the specified point; `false` otherwise. + **/ + public function hitTestPoint(x:Float, y:Float, shapeFlag:Bool = false):Bool + { + if (stage != null) + { + return __hitTest(x, y, shapeFlag, null, false, this); + } + else + { + return false; + } + } + + /** + Calling the `invalidate()` method signals to have the current object + redrawn the next time the object is eligible to be rendered. + **/ + public function invalidate():Void + { + __setRenderDirty(); + } + + /** + Converts the `point` object from the display object's (local) + coordinates to the Stage (global) coordinates. + + This method allows you to convert any given _x_ and _y_ + coordinates from values that are relative to the origin(0,0) of a + specific display object (local coordinates) to values that are relative to + the origin of the Stage (global coordinates). + + To use this method, first create an instance of the Point class. The + _x_ and _y_ values that you assign represent local coordinates + because they relate to the origin of the display object. + + You then pass the Point instance that you created as the parameter to + the `localToGlobal()` method. The method returns a new Point + object with _x_ and _y_ values that relate to the origin of the + Stage instead of the origin of the display object. + + @param point The name or identifier of a point created with the Point + class, specifying the _x_ and _y_ coordinates as + properties. + @return A Point object with coordinates relative to the Stage. + **/ + public function localToGlobal(point:Point):Point + { + return __getRenderTransform().transformPoint(point); + } + + // @:noCompletion @:dox(hide) @:require(flash10) public function local3DToGlobal (point3d:Vector3D):Point; + + @SuppressWarnings("checkstyle:Dynamic") + public override function removeEventListener(type:EventType, listener:T->Void, useCapture:Bool = false):Void + { + super.removeEventListener(type, listener, useCapture); + + switch (type) + { + case Event.ACTIVATE, Event.DEACTIVATE, Event.ENTER_FRAME, Event.EXIT_FRAME, Event.FRAME_CONSTRUCTED, Event.RENDER: + if (!hasEventListener(type)) + { + if (__broadcastEvents.exists(type)) + { + __broadcastEvents.get(type).remove(this); + } + } + + case RenderEvent.CLEAR_DOM, RenderEvent.RENDER_CAIRO, RenderEvent.RENDER_CANVAS, RenderEvent.RENDER_DOM, RenderEvent.RENDER_OPENGL: + if (!hasEventListener(RenderEvent.CLEAR_DOM) + && !hasEventListener(RenderEvent.RENDER_CAIRO) + && !hasEventListener(RenderEvent.RENDER_CANVAS) + && !hasEventListener(RenderEvent.RENDER_DOM) + && !hasEventListener(RenderEvent.RENDER_OPENGL)) + { + __customRenderEvent = null; + } + + default: + } + } + + @:noCompletion private static inline function __calculateAbsoluteTransform(local:Matrix, parentTransform:Matrix, target:Matrix):Void + { + target.a = local.a * parentTransform.a + local.b * parentTransform.c; + target.b = local.a * parentTransform.b + local.b * parentTransform.d; + target.c = local.c * parentTransform.a + local.d * parentTransform.c; + target.d = local.c * parentTransform.b + local.d * parentTransform.d; + target.tx = local.tx * parentTransform.a + local.ty * parentTransform.c + parentTransform.tx; + target.ty = local.tx * parentTransform.b + local.ty * parentTransform.d + parentTransform.ty; + } + + @:noCompletion private function __cleanup():Void + { + __cairo = null; + + #if (js && html5) + __canvas = null; + __context = null; + #end + + if (__graphics != null) + { + __graphics.__cleanup(); + } + + if (__cacheBitmap != null) + { + __cacheBitmap.__cleanup(); + __cacheBitmap = null; + } + + if (__cacheBitmapData != null) + { + __cacheBitmapData.dispose(); + __cacheBitmapData = null; + } + } + + @:noCompletion private function __dispatch(event:Event):Bool + { + if (__eventMap != null && hasEventListener(event.type)) + { + var result = super.__dispatchEvent(event); + + if (event.__isCanceled) + { + return true; + } + + return result; + } + + return true; + } + + @:noCompletion private function __dispatchChildren(event:Event):Void {} + + @:noCompletion private override function __dispatchEvent(event:Event):Bool + { + var parent = event.bubbles ? this.parent : null; + var result = super.__dispatchEvent(event); + + if (event.__isCanceled) + { + return true; + } + + if (parent != null && parent != this) + { + event.eventPhase = EventPhase.BUBBLING_PHASE; + + if (event.target == null) + { + event.target = this; + } + + parent.__dispatchEvent(event); + } + + return result; + } + + @:noCompletion private function __dispatchWithCapture(event:Event):Bool + { + if (event.target == null) + { + event.target = this; + } + + if (parent != null) + { + event.eventPhase = CAPTURING_PHASE; + + if (parent == stage) + { + parent.__dispatch(event); + } + else + { + var stack = __tempStack.get(); + var parent = parent; + var i = 0; + + while (parent != null) + { + stack[i] = parent; + parent = parent.parent; + i++; + } + + for (j in 0...i) + { + stack[i - j - 1].__dispatch(event); + } + + __tempStack.release(stack); + } + } + + event.eventPhase = AT_TARGET; + + return __dispatchEvent(event); + } + + @:noCompletion private function __enterFrame(deltaTime:Int):Void {} + + @:noCompletion private function __getBounds(rect:Rectangle, matrix:Matrix):Void + { + if (__graphics != null) + { + __graphics.__getBounds(rect, matrix); + } + } + + @:noCompletion private function __getCursor():MouseCursor + { + return null; + } + + @:noCompletion private function __getFilterBounds(rect:Rectangle, matrix:Matrix):Void + { + __getRenderBounds(rect, matrix); + + if (__filters != null) + { + var extension = Rectangle.__pool.get(); + + for (filter in __filters) + { + extension.__expand(-filter.__leftExtension, + -filter.__topExtension, filter.__leftExtension + + filter.__rightExtension, + filter.__topExtension + + filter.__bottomExtension); + } + + rect.width += extension.width; + rect.height += extension.height; + rect.x += extension.x; + rect.y += extension.y; + + Rectangle.__pool.release(extension); + } + } + + @:noCompletion private function __getInteractive(stack:Array):Bool + { + return false; + } + + @:noCompletion private function __getLocalBounds(rect:Rectangle):Void + { + // var cacheX = __transform.tx; + // var cacheY = __transform.ty; + // __transform.tx = __transform.ty = 0; + + __getBounds(rect, __transform); + + // __transform.tx = cacheX; + // __transform.ty = cacheY; + + rect.x -= __transform.tx; + rect.y -= __transform.ty; + } + + @:noCompletion private function __getRenderBounds(rect:Rectangle, matrix:Matrix):Void + { + if (__scrollRect == null) + { + __getBounds(rect, matrix); + } + else + { + // TODO: Should we have smaller bounds if scrollRect is larger than content? + + var r = Rectangle.__pool.get(); + r.copyFrom(__scrollRect); + r.__transform(r, matrix); + rect.__expand(r.x, r.y, r.width, r.height); + Rectangle.__pool.release(r); + } + } + + @:noCompletion private function __getRenderTransform():Matrix + { + __getWorldTransform(); + return __renderTransform; + } + + @:noCompletion private function __getWorldTransform():Matrix + { + var transformDirty = __transformDirty || __worldTransformInvalid; + + if (transformDirty) + { + var list:Array = []; + var current = this; + + if (parent == null) + { + __update(true, false); + } + else + { + while (current != stage) + { + list.push(current); + current = current.parent; + + if (current == null) break; + } + } + + var i = list.length; + while (--i >= 0) + { + current = list[i]; + current.__update(true, false); + } + } + + return __worldTransform; + } + + @:noCompletion private function __globalToLocal(global:Point, local:Point):Point + { + __getRenderTransform(); + + if (global == local) + { + __renderTransform.__transformInversePoint(global); + } + else + { + local.x = __renderTransform.__transformInverseX(global.x, global.y); + local.y = __renderTransform.__transformInverseY(global.x, global.y); + } + + return local; + } + + @:noCompletion private function __hitTest(x:Float, y:Float, shapeFlag:Bool, stack:Array, interactiveOnly:Bool, hitObject:DisplayObject):Bool + { + if (__graphics != null) + { + if (!hitObject.__visible || __isMask) return false; + + if (mask != null) + { + if (maskInverted && mask.__hitTestMask(x, y)) + { + return false; + } + else if (!mask.__hitTestMask(x, y)) + { + return false; + } + } + + if (__graphics.__hitTest(x, y, shapeFlag, __getRenderTransform())) + { + if (stack != null && !interactiveOnly) + { + stack.push(hitObject); + } + + return true; + } + } + + return false; + } + + @:noCompletion private function __hitTestMask(x:Float, y:Float):Bool + { + if (__graphics != null) + { + if (__graphics.__hitTest(x, y, true, __getRenderTransform())) + { + return true; + } + } + + return false; + } + + @:noCompletion private function __readGraphicsData(graphicsData:Vector, recurse:Bool):Void + { + if (__graphics != null) + { + __graphics.__readGraphicsData(graphicsData); + } + } + + @:noCompletion private function __setParentRenderDirty():Void + { + var renderParent = __renderParent != null ? __renderParent : parent; + if (renderParent != null && !renderParent.__renderDirty) + { + renderParent.__renderDirty = true; + renderParent.__setParentRenderDirty(); + } + } + + #if (openfl_enable_experimental_update_queue && !dom) + @:noCompletion private var _updateQueueFlag:Bool = false; + + @:noCompletion inline private function __setUpdateQueueFlag(add:Bool = true):Void + { + if (add) + { + if (!_updateQueueFlag) + { + _updateQueueFlag = true; + updateQueue.push(this); + } + } + else + { + _updateQueueFlag = false; + updateQueue.remove(this); + } + } + #end + + @:noCompletion private inline function __setRenderDirty():Void + { + if (!__renderDirty) + { + __renderDirty = true; + __setParentRenderDirty(); + } + #if (openfl_enable_experimental_update_queue && !dom) + __setUpdateQueueFlag(); + #end + } + + @:noCompletion private function __setStageReference(stage:Stage):Void + { + this.stage = stage; + } + + @:noCompletion private function __setTransformDirty():Void + { + if (!__transformDirty) + { + __transformDirty = true; + + __setWorldTransformInvalid(); + __setParentRenderDirty(); + } + #if (openfl_enable_experimental_update_queue && !dom) + __setUpdateQueueFlag(); + #end + } + + @:noCompletion private function __setWorldTransformInvalid():Void + { + __worldTransformInvalid = true; + } + + @:noCompletion private function __stopAllMovieClips():Void {} + + @:noCompletion private function __update(transformOnly:Bool, updateChildren:Bool):Void + { + var renderParent = __renderParent != null ? __renderParent : parent; + if (__isMask && renderParent == null) renderParent = __maskTarget; + __renderable = (__visible && __scaleX != 0 && __scaleY != 0 && !__isMask && (renderParent == null || !renderParent.__isMask)); + __updateTransforms(); + + #if (openfl_enable_experimental_update_queue && !dom) + transformOnly = false; + #end + + // if (updateChildren && __transformDirty) { + + __transformDirty = false; + + // } + + __worldTransformInvalid = false; + + if (!transformOnly) + { + if (__supportDOM) + { + __renderTransformChanged = !__renderTransform.equals(__renderTransformCache); + + if (__renderTransformCache == null) + { + __renderTransformCache = __renderTransform.clone(); + } + else + { + __renderTransformCache.copyFrom(__renderTransform); + } + } + + if (renderParent != null) + { + if (__supportDOM) + { + var worldVisible = (renderParent.__worldVisible && __visible); + __worldVisibleChanged = (__worldVisible != worldVisible); + __worldVisible = worldVisible; + } + var worldAlpha = alpha * renderParent.__worldAlpha; + __worldAlphaChanged = (__worldAlpha != worldAlpha); + __worldAlpha = worldAlpha; + + if (__objectTransform != null) + { + __worldColorTransform.__copyFrom(__objectTransform.__colorTransform); + __worldColorTransform.__combine(renderParent.__worldColorTransform); + } + else + { + __worldColorTransform.__copyFrom(renderParent.__worldColorTransform); + } + + if (__blendMode == null || __blendMode == NORMAL) + { + // TODO: Handle multiple blend modes better + __worldBlendMode = renderParent.__worldBlendMode; + } + else + { + __worldBlendMode = __blendMode; + } + + if (__shader == null) + { + __worldShader = renderParent.__shader; + } + else + { + __worldShader = __shader; + } + + if (__scale9Grid == null) + { + __worldScale9Grid = renderParent.__scale9Grid; + } + else + { + __worldScale9Grid = __scale9Grid; + } + } + else + { + __worldAlpha = alpha; + + if (__supportDOM) + { + __worldVisibleChanged = (__worldVisible != __visible); + __worldVisible = __visible; + } + __worldAlphaChanged = (__worldAlpha != alpha); + + if (__objectTransform != null) + { + __worldColorTransform.__copyFrom(__objectTransform.__colorTransform); + } + else + { + __worldColorTransform.__identity(); + } + + __worldBlendMode = __blendMode; + __worldShader = __shader; + __worldScale9Grid = __scale9Grid; + } + + // if (updateChildren && __renderDirty) { + + // __renderDirty = false; + + // } + } + + if (updateChildren && mask != null) + { + mask.__update(transformOnly, true); + } + } + + @:noCompletion private function __updateTransforms(overrideTransform:Matrix = null):Void + { + var overrided = overrideTransform != null; + var local = overrided ? overrideTransform : __transform; + + if (__worldTransform == null) + { + __worldTransform = new Matrix(); + } + + if (__renderTransform == null) + { + __renderTransform = new Matrix(); + } + + var renderParent = __renderParent != null ? __renderParent : parent; + + if (!overrided && parent != null) + { + __calculateAbsoluteTransform(local, parent.__worldTransform, __worldTransform); + } + else + { + __worldTransform.copyFrom(local); + } + + if (!overrided && renderParent != null) + { + __calculateAbsoluteTransform(local, renderParent.__renderTransform, __renderTransform); + } + else + { + __renderTransform.copyFrom(local); + } + + if (__scrollRect != null) + { + __renderTransform.__translateTransformed(-__scrollRect.x, -__scrollRect.y); + } + } + + // Get & Set Methods + @:keep @:noCompletion private function get_alpha():Float + { + return __alpha; + } + + @:keep @:noCompletion private function set_alpha(value:Float):Float + { + if (value > 1.0) value = 1.0; + if (value < 0.0) value = 0.0; + + if (value != __alpha && !cacheAsBitmap) __setRenderDirty(); + return __alpha = value; + } + + @:noCompletion private function get_blendMode():BlendMode + { + return __blendMode; + } + + @:noCompletion private function set_blendMode(value:BlendMode):BlendMode + { + if (value == null) value = NORMAL; + + if (value != __blendMode) __setRenderDirty(); + return __blendMode = value; + } + + @:noCompletion private function get_cacheAsBitmap():Bool + { + return (__filters == null ? __cacheAsBitmap : true); + } + + @:noCompletion private function set_cacheAsBitmap(value:Bool):Bool + { + if (value != __cacheAsBitmap) + { + __setRenderDirty(); + } + + return __cacheAsBitmap = value; + } + + @:noCompletion private function get_cacheAsBitmapMatrix():Matrix + { + return __cacheAsBitmapMatrix; + } + + @:noCompletion private function set_cacheAsBitmapMatrix(value:Matrix):Matrix + { + __setRenderDirty(); + return __cacheAsBitmapMatrix = (value != null ? value.clone() : value); + } + + @:noCompletion private function get_filters():Array + { + if (__filters == null) + { + return new Array(); + } + else + { + return __filters.copy(); + } + } + + @:noCompletion private function set_filters(value:Array):Array + { + if (value != null && value.length > 0) + { + var clonedFilters:Array = []; + + for (filter in value) + { + var clonedFilter:BitmapFilter = filter.clone(); + + clonedFilter.__renderDirty = true; + clonedFilters.push(clonedFilter); + } + + __filters = clonedFilters; + // __updateFilters = true; + __setRenderDirty(); + } + else if (__filters != null) + { + __filters = null; + // __updateFilters = false; + __setRenderDirty(); + } + + return value; + } + + @:keep @:noCompletion private function get_height():Float + { + var rect = Rectangle.__pool.get(); + __getLocalBounds(rect); + var height = rect.height; + Rectangle.__pool.release(rect); + return height; + } + + @:keep @:noCompletion private function set_height(value:Float):Float + { + var rect = Rectangle.__pool.get(); + var matrix = Matrix.__pool.get(); + matrix.identity(); + + __getBounds(rect, matrix); + + if (value != rect.height) + { + scaleY = value / rect.height; + } + else + { + scaleY = 1; + } + + Rectangle.__pool.release(rect); + Matrix.__pool.release(matrix); + + return value; + } + + @:noCompletion private function get_loaderInfo():LoaderInfo + { + if (stage != null) + { + return Lib.current.__loaderInfo; + } + + return null; + } + + @:noCompletion private function get_mask():DisplayObject + { + return __mask; + } + + @:noCompletion private function set_mask(value:DisplayObject):DisplayObject + { + if (value == __mask) + { + return value; + } + + if (value != __mask) + { + __setTransformDirty(); + __setRenderDirty(); + } + + if (__mask != null) + { + __mask.__isMask = false; + __mask.__maskTarget = null; + __mask.__setTransformDirty(); + __mask.__setRenderDirty(); + } + + if (value != null) + { + value.__isMask = true; + value.__maskTarget = this; + value.__setWorldTransformInvalid(); + } + + if (__cacheBitmap != null && __cacheBitmap.mask != value) + { + __cacheBitmap.mask = value; + } + + return __mask = value; + } + + @:noCompletion private function get_mouseX():Float + { + var mouseX = (stage != null ? stage.__mouseX : Lib.current.stage.__mouseX); + var mouseY = (stage != null ? stage.__mouseY : Lib.current.stage.__mouseY); + + return __getRenderTransform().__transformInverseX(mouseX, mouseY); + } + + @:noCompletion private function get_mouseY():Float + { + var mouseX = (stage != null ? stage.__mouseX : Lib.current.stage.__mouseX); + var mouseY = (stage != null ? stage.__mouseY : Lib.current.stage.__mouseY); + + return __getRenderTransform().__transformInverseY(mouseX, mouseY); + } + + @:noCompletion private function get_name():String + { + return __name; + } + + @:noCompletion private function set_name(value:String):String + { + return __name = value; + } + + @:noCompletion private function get_root():DisplayObject + { + if (stage != null) + { + return Lib.current; + } + + return null; + } + + @:keep @:noCompletion private function get_rotation():Float + { + return __rotation; + } + + @:keep @:noCompletion private function set_rotation(value:Float):Float + { + if (value != __rotation) + { + value = value % 360.0; + if (value > 180.0) + { + value -= 360.0; + } + else if (value < -180.0) + { + value += 360.0; + } + + __rotation = value; + var radians = __rotation * (Math.PI / 180); + __rotationSine = Math.sin(radians); + __rotationCosine = Math.cos(radians); + + __transform.a = __rotationCosine * __scaleX; + __transform.b = __rotationSine * __scaleX; + __transform.c = -__rotationSine * __scaleY; + __transform.d = __rotationCosine * __scaleY; + + __setTransformDirty(); + } + + return value; + } + + @:noCompletion private function get_scale9Grid():Rectangle + { + if (__scale9Grid == null) + { + return null; + } + + return __scale9Grid.clone(); + } + + @:noCompletion private function set_scale9Grid(value:Rectangle):Rectangle + { + if (value == null && __scale9Grid == null) return value; + if (value != null && __scale9Grid != null && __scale9Grid.equals(value)) return value; + + if (value != null) + { + if (__scale9Grid == null) __scale9Grid = new Rectangle(); + __scale9Grid.copyFrom(value); + } + else + { + __scale9Grid = null; + } + + __setRenderDirty(); + + return value; + } + + @:keep @:noCompletion private function get_scaleX():Float + { + return __scaleX; + } + + @:keep @:noCompletion private function set_scaleX(value:Float):Float + { + if (value != __scaleX) + { + __scaleX = value; + + if (__transform.b == 0) + { + if (value != __transform.a) __setTransformDirty(); + __transform.a = value; + } + else + { + var a = __rotationCosine * value; + var b = __rotationSine * value; + + if (__transform.a != a || __transform.b != b) + { + __setTransformDirty(); + } + + __transform.a = a; + __transform.b = b; + } + } + + return value; + } + + @:keep @:noCompletion private function get_scaleY():Float + { + return __scaleY; + } + + @:keep @:noCompletion private function set_scaleY(value:Float):Float + { + if (value != __scaleY) + { + __scaleY = value; + + if (__transform.c == 0) + { + if (value != __transform.d) __setTransformDirty(); + __transform.d = value; + } + else + { + var c = -__rotationSine * value; + var d = __rotationCosine * value; + + if (__transform.d != d || __transform.c != c) + { + __setTransformDirty(); + } + + __transform.c = c; + __transform.d = d; + } + } + + return value; + } + + @:noCompletion private function get_scrollRect():Rectangle + { + if (__scrollRect == null) + { + return null; + } + + return __scrollRect.clone(); + } + + @:noCompletion private function set_scrollRect(value:Rectangle):Rectangle + { + if (value == null && __scrollRect == null) return value; + if (value != null && __scrollRect != null && __scrollRect.equals(value)) return value; + + if (value != null) + { + if (__scrollRect == null) __scrollRect = new Rectangle(); + __scrollRect.copyFrom(value); + } + else + { + __scrollRect = null; + } + + __setTransformDirty(); + + if (__supportDOM) + { + __setRenderDirty(); + } + + return value; + } + + @:noCompletion private function get_shader():Shader + { + return __shader; + } + + @:noCompletion private function set_shader(value:Shader):Shader + { + __shader = value; + __setRenderDirty(); + return value; + } + + @:keep @:noCompletion private function get_transform():Transform + { + if (__objectTransform == null) + { + __objectTransform = new Transform(this); + } + + return __objectTransform; + } + + @:keep @:noCompletion private function set_transform(value:Transform):Transform + { + if (value == null) + { + throw new TypeError("Parameter transform must be non-null."); + } + + if (__objectTransform == null) + { + __objectTransform = new Transform(this); + } + + __setTransformDirty(); + __objectTransform.matrix = value.matrix; + + if (!__objectTransform.__colorTransform.__equals(value.__colorTransform, true) + || (!cacheAsBitmap && __objectTransform.__colorTransform.alphaMultiplier != value.__colorTransform.alphaMultiplier)) + { + __objectTransform.__colorTransform.__copyFrom(value.colorTransform); + __setRenderDirty(); + } + + return __objectTransform; + } + + @:noCompletion private function get_visible():Bool + { + return __visible; + } + + @:noCompletion private function set_visible(value:Bool):Bool + { + if (value != __visible) __setRenderDirty(); + return __visible = value; + } + + @:keep @:noCompletion private function get_width():Float + { + var rect = Rectangle.__pool.get(); + __getLocalBounds(rect); + var width = rect.width; + Rectangle.__pool.release(rect); + return width; + } + + @:keep @:noCompletion private function set_width(value:Float):Float + { + var rect = Rectangle.__pool.get(); + var matrix = Matrix.__pool.get(); + matrix.identity(); + + __getBounds(rect, matrix); + + if (value != rect.width) + { + scaleX = value / rect.width; + } + else + { + scaleX = 1; + } + + Rectangle.__pool.release(rect); + Matrix.__pool.release(matrix); + + return value; + } + + @:keep @:noCompletion private function get_x():Float + { + return __transform.tx; + } + + @:keep @:noCompletion private function set_x(value:Float):Float + { + if (value != __transform.tx) __setTransformDirty(); + return __transform.tx = value; + } + + @:keep @:noCompletion private function get_y():Float + { + return __transform.ty; + } + + @:keep @:noCompletion private function set_y(value:Float):Float + { + if (value != __transform.ty) __setTransformDirty(); + return __transform.ty = value; + } +} +#else +typedef DisplayObject = flash.display.DisplayObject; +#end diff --git a/source/openfl/display/DisplayObjectContainer.hx b/source/openfl/display/DisplayObjectContainer.hx new file mode 100644 index 00000000..c43ba8d3 --- /dev/null +++ b/source/openfl/display/DisplayObjectContainer.hx @@ -0,0 +1,1008 @@ +package openfl.display; + +#if !flash +import openfl.errors.ArgumentError; +import openfl.errors.RangeError; +import openfl.errors.TypeError; +import openfl.events.Event; +import openfl.geom.Matrix; +import openfl.geom.Point; +import openfl.geom.Rectangle; +import openfl.Vector; + +/** + The DisplayObjectContainer class is the base class for all objects that can + serve as display object containers on the display list. The display list + manages all objects displayed in the Flash runtimes. Use the + DisplayObjectContainer class to arrange the display objects in the display + list. Each DisplayObjectContainer object has its own child list for + organizing the z-order of the objects. The z-order is the front-to-back + order that determines which object is drawn in front, which is behind, and + so on. + + DisplayObject is an abstract base class; therefore, you cannot call + DisplayObject directly. Invoking `new DisplayObject()` throws an + `ArgumentError` exception. + The DisplayObjectContainer class is an abstract base class for all objects + that can contain child objects. It cannot be instantiated directly; calling + the `new DisplayObjectContainer()` constructor throws an + `ArgumentError` exception. + + For more information, see the "Display Programming" chapter of the + _ActionScript 3.0 Developer's Guide_. +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(openfl.events.Event) +@:access(openfl.display.Graphics) +@:access(openfl.errors.Error) +@:access(openfl.geom.Point) +@:access(openfl.geom.Matrix) +@:access(openfl.geom.Rectangle) +class DisplayObjectContainer extends InteractiveObject +{ + /** + Determines whether or not the children of the object are mouse, or user + input device, enabled. If an object is enabled, a user can interact with + it by using a mouse or user input device. The default is + `true`. + + This property is useful when you create a button with an instance of + the Sprite class(instead of using the SimpleButton class). When you use a + Sprite instance to create a button, you can choose to decorate the button + by using the `addChild()` method to add additional Sprite + instances. This process can cause unexpected behavior with mouse events + because the Sprite instances you add as children can become the target + object of a mouse event when you expect the parent instance to be the + target object. To ensure that the parent instance serves as the target + objects for mouse events, you can set the `mouseChildren` + property of the parent instance to `false`. + + No event is dispatched by setting this property. You must use the + `addEventListener()` method to create interactive + functionality. + **/ + public var mouseChildren:Bool; + + /** + Returns the number of children of this object. + **/ + public var numChildren(get, never):Int; + + /** + Determines whether the children of the object are tab enabled. Enables or + disables tabbing for the children of the object. The default is + `true`. + + **Note:** Do not use the `tabChildren` property with + Flex. Instead, use the + `mx.core.UIComponent.hasFocusableChildren` property. + + @throws IllegalOperationError Calling this property of the Stage object + throws an exception. The Stage object does + not implement this property. + **/ + public var tabChildren(get, set):Bool; + + // @:noCompletion @:dox(hide) public var textSnapshot (default, never):openfl.text.TextSnapshot; + @:noCompletion private var __removedChildren:Vector; + @:noCompletion private var __tabChildren:Bool; + + #if openfljs + @:noCompletion private static function __init__() + { + untyped Object.defineProperty(DisplayObjectContainer.prototype, "numChildren", { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_numChildren (); }") + }); + } + #end + + /** + Calling the `new DisplayObjectContainer()` constructor throws + an `ArgumentError` exception. You _can_, however, call + constructors for the following subclasses of DisplayObjectContainer: + + * `new Loader()` + * `new Sprite()` + * `new MovieClip()` + + **/ + @:noCompletion private function new() + { + super(); + + mouseChildren = true; + __tabChildren = true; + + __children = new Array(); + __removedChildren = new Vector(); + } + + /** + Adds a child DisplayObject instance to this DisplayObjectContainer + instance. The child is added to the front(top) of all other children in + this DisplayObjectContainer instance.(To add a child to a specific index + position, use the `addChildAt()` method.) + + If you add a child object that already has a different display object + container as a parent, the object is removed from the child list of the + other display object container. + + **Note:** The command `stage.addChild()` can cause + problems with a published SWF file, including security problems and + conflicts with other loaded SWF files. There is only one Stage within a + Flash runtime instance, no matter how many SWF files you load into the + runtime. So, generally, objects should not be added to the Stage, + directly, at all. The only object the Stage should contain is the root + object. Create a DisplayObjectContainer to contain all of the items on the + display list. Then, if necessary, add that DisplayObjectContainer instance + to the Stage. + + @param child The DisplayObject instance to add as a child of this + DisplayObjectContainer instance. + @return The DisplayObject instance that you pass in the `child` + parameter. + @throws ArgumentError Throws if the child is the same as the parent. Also + throws if the caller is a child(or grandchild etc.) + of the child being added. + @event added Dispatched when a display object is added to the display + list. + **/ + public function addChild(child:DisplayObject):DisplayObject + { + return addChildAt(child, numChildren); + } + + /** + Adds a child DisplayObject instance to this DisplayObjectContainer + instance. The child is added at the index position specified. An index of + 0 represents the back(bottom) of the display list for this + DisplayObjectContainer object. + + For example, the following example shows three display objects, labeled + a, b, and c, at index positions 0, 2, and 1, respectively: + + ![b over c over a](/images/DisplayObjectContainer_layers.jpg) + + If you add a child object that already has a different display object + container as a parent, the object is removed from the child list of the + other display object container. + + @param child The DisplayObject instance to add as a child of this + DisplayObjectContainer instance. + @param index The index position to which the child is added. If you + specify a currently occupied index position, the child object + that exists at that position and all higher positions are + moved up one position in the child list. + @return The DisplayObject instance that you pass in the `child` + parameter. + @throws ArgumentError Throws if the child is the same as the parent. Also + throws if the caller is a child(or grandchild etc.) + of the child being added. + @throws RangeError Throws if the index position does not exist in the + child list. + @event added Dispatched when a display object is added to the display + list. + **/ + public function addChildAt(child:DisplayObject, index:Int):DisplayObject + { + if (child == null) + { + var error = new TypeError("Error #2007: Parameter child must be non-null."); + error.errorID = 2007; + throw error; + } + else if (child == this) + { + var error = new ArgumentError("Error #2024: An object cannot be added as a child of itself."); + error.errorID = 2024; + throw error; + } + #if ((haxe_ver >= "3.4.0") || !cpp) + else if (child.stage == child) + { + var error = new ArgumentError("Error #3783: A Stage object cannot be added as the child of another object."); + error.errorID = 3783; + throw error; + } + #end + + if (index > __children.length || index < 0) + { + throw "Invalid index position " + index; + } + + if (child.parent == this) + { + if (__children[index] != child) + { + __children.remove(child); + __children.insert(index, child); + + __setRenderDirty(); + } + } + else + { + if (child.parent != null) + { + child.parent.removeChild(child); + } + + __children.insert(index, child); + child.parent = this; + + var addedToStage = (stage != null && child.stage == null); + + if (addedToStage) + { + child.__setStageReference(stage); + } + + child.__setTransformDirty(); + child.__setRenderDirty(); + __setRenderDirty(); + + // #if !openfl_disable_event_pooling + // var event = Event.__pool.get(); + // event.type = Event.ADDED; + // #else + var event = new Event(Event.ADDED); + // #end + event.bubbles = true; + + event.target = child; + + child.__dispatchWithCapture(event); + + // #if !openfl_disable_event_pooling + // Event.__pool.release(event); + // #end + + if (addedToStage) + { + #if openfl_pool_events + event = Event.__pool.get(); + event.type = Event.ADDED_TO_STAGE; + #else + event = new Event(Event.ADDED_TO_STAGE, false, false); + #end + + child.__dispatchWithCapture(event); + child.__dispatchChildren(event); + + #if openfl_pool_events + Event.__pool.release(event); + #end + } + } + + return child; + } + + /** + Indicates whether the security restrictions would cause any display + objects to be omitted from the list returned by calling the + `DisplayObjectContainer.getObjectsUnderPoint()` method with the + specified `point` point. By default, content from one domain + cannot access objects from another domain unless they are permitted to do + so with a call to the `Security.allowDomain()` method. For more + information, related to security, see the Flash Player Developer Center + Topic: [Security](http://www.adobe.com/go/devnet_security_en). + + The `point` parameter is in the coordinate space of the + Stage, which may differ from the coordinate space of the display object + container(unless the display object container is the Stage). You can use + the `globalToLocal()` and the `localToGlobal()` + methods to convert points between these coordinate spaces. + + @param point The point under which to look. + @return `true` if the point contains child display objects with + security restrictions. + **/ + public function areInaccessibleObjectsUnderPoint(point:Point):Bool + { + return false; + } + + /** + Determines whether the specified display object is a child of the + DisplayObjectContainer instance or the instance itself. The search + includes the entire display list including this DisplayObjectContainer + instance. Grandchildren, great-grandchildren, and so on each return + `true`. + + @param child The child object to test. + @return `true` if the `child` object is a child of + the DisplayObjectContainer or the container itself; otherwise + `false`. + **/ + public function contains(child:DisplayObject):Bool + { + while (child != this && child != null) + { + child = child.parent; + } + + return child == this; + } + + /** + Returns the child display object instance that exists at the specified + index. + + @param index The index position of the child object. + @return The child display object at the specified index position. + @throws RangeError Throws if the index does not exist in the child + list. + @throws SecurityError This child display object belongs to a sandbox to + which you do not have access. You can avoid this + situation by having the child movie call + `Security.allowDomain()`. + **/ + public function getChildAt(index:Int):DisplayObject + { + if (index >= 0 && index < __children.length) + { + return __children[index]; + } + + return null; + } + + /** + Returns the child display object that exists with the specified name. If + more that one child display object has the specified name, the method + returns the first object in the child list. + + The `getChildAt()` method is faster than the + `getChildByName()` method. The `getChildAt()` method + accesses a child from a cached array, whereas the + `getChildByName()` method has to traverse a linked list to + access a child. + + @param name The name of the child to return. + @return The child display object with the specified name. + @throws SecurityError This child display object belongs to a sandbox to + which you do not have access. You can avoid this + situation by having the child movie call the + `Security.allowDomain()` method. + **/ + public function getChildByName(name:String):DisplayObject + { + for (child in __children) + { + if (child.name == name) return child; + } + + return null; + } + + /** + Returns the index position of a `child` DisplayObject instance. + + @param child The DisplayObject instance to identify. + @return The index position of the child display object to identify. + @throws ArgumentError Throws if the child parameter is not a child of this + object. + **/ + public function getChildIndex(child:DisplayObject):Int + { + for (i in 0...__children.length) + { + if (__children[i] == child) return i; + } + + return -1; + } + + /** + Returns an array of objects that lie under the specified point and are + children(or grandchildren, and so on) of this DisplayObjectContainer + instance. Any child objects that are inaccessible for security reasons are + omitted from the returned array. To determine whether this security + restriction affects the returned array, call the + `areInaccessibleObjectsUnderPoint()` method. + + The `point` parameter is in the coordinate space of the + Stage, which may differ from the coordinate space of the display object + container(unless the display object container is the Stage). You can use + the `globalToLocal()` and the `localToGlobal()` + methods to convert points between these coordinate spaces. + + @param point The point under which to look. + @return An array of objects that lie under the specified point and are + children(or grandchildren, and so on) of this + DisplayObjectContainer instance. + **/ + public function getObjectsUnderPoint(point:Point):Array + { + var stack = new Array(); + __hitTest(point.x, point.y, false, stack, false, this); + stack.reverse(); + return stack; + } + + /** + Removes the specified `child` DisplayObject instance from the + child list of the DisplayObjectContainer instance. The `parent` + property of the removed child is set to `null` , and the object + is garbage collected if no other references to the child exist. The index + positions of any display objects above the child in the + DisplayObjectContainer are decreased by 1. + + The garbage collector reallocates unused memory space. When a variable + or object is no longer actively referenced or stored somewhere, the + garbage collector sweeps through and wipes out the memory space it used to + occupy if no other references to it exist. + + @param child The DisplayObject instance to remove. + @return The DisplayObject instance that you pass in the `child` + parameter. + @throws ArgumentError Throws if the child parameter is not a child of this + object. + **/ + public function removeChild(child:DisplayObject):DisplayObject + { + if (child != null && child.parent == this) + { + child.__setTransformDirty(); + child.__setRenderDirty(); + __setRenderDirty(); + + var event = new Event(Event.REMOVED, true); + child.__dispatchWithCapture(event); + + if (stage != null) + { + if (child.stage != null && stage.focus == child) + { + stage.focus = null; + } + + var event = new Event(Event.REMOVED_FROM_STAGE, false, false); + child.__dispatchWithCapture(event); + child.__dispatchChildren(event); + child.__setStageReference(null); + } + + child.parent = null; + __children.remove(child); + __removedChildren.push(child); + child.__setTransformDirty(); + } + + return child; + } + + /** + Removes a child DisplayObject from the specified `index` + position in the child list of the DisplayObjectContainer. The + `parent` property of the removed child is set to + `null`, and the object is garbage collected if no other + references to the child exist. The index positions of any display objects + above the child in the DisplayObjectContainer are decreased by 1. + + The garbage collector reallocates unused memory space. When a variable + or object is no longer actively referenced or stored somewhere, the + garbage collector sweeps through and wipes out the memory space it used to + occupy if no other references to it exist. + + @param index The child index of the DisplayObject to remove. + @return The DisplayObject instance that was removed. + @throws RangeError Throws if the index does not exist in the child + list. + @throws SecurityError This child display object belongs to a sandbox to + which the calling object does not have access. You + can avoid this situation by having the child movie + call the `Security.allowDomain()` method. + **/ + public function removeChildAt(index:Int):DisplayObject + { + if (index >= 0 && index < __children.length) + { + return removeChild(__children[index]); + } + + return null; + } + + /** + Removes all `child` DisplayObject instances from the child list of the DisplayObjectContainer + instance. The `parent` property of the removed children is set to `null`, and the objects are + garbage collected if no other references to the children exist. + + The garbage collector reallocates unused memory space. When a variable or object is no + longer actively referenced or stored somewhere, the garbage collector sweeps through and + wipes out the memory space it used to occupy if no other references to it exist. + @param beginIndex The beginning position. A value smaller than 0 throws a `RangeError`. + @param endIndex The ending position. A value smaller than 0 throws a `RangeError`. + **/ + public function removeChildren(beginIndex:Int = 0, endIndex:Int = 0x7FFFFFFF):Void + { + if (endIndex == 0x7FFFFFFF) + { + endIndex = __children.length - 1; + + if (endIndex < 0) + { + return; + } + } + + if (beginIndex > __children.length - 1) + { + return; + } + else if (endIndex < beginIndex || beginIndex < 0 || endIndex > __children.length) + { + throw new RangeError("The supplied index is out of bounds."); + } + + var numRemovals = endIndex - beginIndex; + while (numRemovals >= 0) + { + removeChildAt(beginIndex); + numRemovals--; + } + } + + @:noCompletion private function resolve(fieldName:String):DisplayObject + { + if (__children == null) return null; + + for (child in __children) + { + if (child.name == fieldName) + { + return child; + } + } + + return null; + } + + /** + Changes the position of an existing child in the display object container. + This affects the layering of child objects. For example, the following + example shows three display objects, labeled a, b, and c, at index + positions 0, 1, and 2, respectively: + + ![c over b over a](/images/DisplayObjectContainerSetChildIndex1.jpg) + + When you use the `setChildIndex()` method and specify an + index position that is already occupied, the only positions that change + are those in between the display object's former and new position. All + others will stay the same. If a child is moved to an index LOWER than its + current index, all children in between will INCREASE by 1 for their index + reference. If a child is moved to an index HIGHER than its current index, + all children in between will DECREASE by 1 for their index reference. For + example, if the display object container in the previous example is named + `container`, you can swap the position of the display objects + labeled a and b by calling the following code: + + ```haxe + container.setChildIndex(container.getChildAt(1), 0); + ``` + + This code results in the following arrangement of objects: + + ![c over a over b](/images/DisplayObjectContainerSetChildIndex2.jpg) + + @param child The child DisplayObject instance for which you want to change + the index number. + @param index The resulting index number for the `child` display + object. + @throws ArgumentError Throws if the child parameter is not a child of this + object. + @throws RangeError Throws if the index does not exist in the child + list. + **/ + public function setChildIndex(child:DisplayObject, index:Int):Void + { + if (index >= 0 && index <= __children.length && child.parent == this) + { + __children.remove(child); + __children.insert(index, child); + } + } + + /** + Recursively stops the timeline execution of all MovieClips rooted at this object. + + Child display objects belonging to a sandbox to which the excuting code does not + have access are ignored. + + **Note:** Streaming media playback controlled via a NetStream object will not be + stopped. + **/ + public function stopAllMovieClips():Void + { + __stopAllMovieClips(); + } + + /** + Swaps the z-order (front-to-back order) of the two specified child + objects. All other child objects in the display object container remain in + the same index positions. + + @param child1 The first child object. + @param child2 The second child object. + @throws ArgumentError Throws if either child parameter is not a child of + this object. + **/ + public function swapChildren(child1:DisplayObject, child2:DisplayObject):Void + { + if (child1.parent == this && child2.parent == this) + { + var index1 = __children.indexOf(child1); + var index2 = __children.indexOf(child2); + + __children[index1] = child2; + __children[index2] = child1; + + __setRenderDirty(); + } + } + + /** + Swaps the z-order (front-to-back order) of the child objects at the two + specified index positions in the child list. All other child objects in + the display object container remain in the same index positions. + + @param index1 The index position of the first child object. + @param index2 The index position of the second child object. + @throws RangeError If either index does not exist in the child list. + **/ + public function swapChildrenAt(index1:Int, index2:Int):Void + { + var swap:DisplayObject = __children[index1]; + __children[index1] = __children[index2]; + __children[index2] = swap; + swap = null; + __setRenderDirty(); + } + + @:noCompletion private override function __cleanup():Void + { + super.__cleanup(); + + for (child in __children) + { + child.__cleanup(); + } + + __cleanupRemovedChildren(); + } + + @:noCompletion private inline function __cleanupRemovedChildren():Void + { + for (orphan in __removedChildren) + { + if (orphan.stage == null) + { + orphan.__cleanup(); + } + } + + __removedChildren.length = 0; + } + + @:noCompletion private override function __dispatchChildren(event:Event):Void + { + if (__children != null) + { + for (child in __children) + { + event.target = child; + + if (!child.__dispatchWithCapture(event)) + { + break; + } + + child.__dispatchChildren(event); + } + } + } + + @:noCompletion private override function __enterFrame(deltaTime:Int):Void + { + for (child in __children) + { + child.__enterFrame(deltaTime); + } + } + + @:noCompletion private override function __getBounds(rect:Rectangle, matrix:Matrix):Void + { + super.__getBounds(rect, matrix); + + if (__children.length == 0) return; + + var childWorldTransform = Matrix.__pool.get(); + + for (child in __children) + { + if (child.__scaleX == 0 || child.__scaleY == 0) continue; + + DisplayObject.__calculateAbsoluteTransform(child.__transform, matrix, childWorldTransform); + + child.__getBounds(rect, childWorldTransform); + } + + Matrix.__pool.release(childWorldTransform); + } + + @:noCompletion private override function __getFilterBounds(rect:Rectangle, matrix:Matrix):Void + { + super.__getFilterBounds(rect, matrix); + if (__scrollRect != null) return; + + if (__children.length == 0) return; + + var childWorldTransform = Matrix.__pool.get(); + + for (child in __children) + { + if (child.__scaleX == 0 || child.__scaleY == 0 || child.__isMask) continue; + + DisplayObject.__calculateAbsoluteTransform(child.__transform, matrix, childWorldTransform); + + var childRect = Rectangle.__pool.get(); + + child.__getFilterBounds(childRect, childWorldTransform); + rect.__expand(childRect.x, childRect.y, childRect.width, childRect.height); + + Rectangle.__pool.release(childRect); + } + + Matrix.__pool.release(childWorldTransform); + } + + @:noCompletion private override function __getRenderBounds(rect:Rectangle, matrix:Matrix):Void + { + if (__scrollRect != null) + { + super.__getRenderBounds(rect, matrix); + return; + } + else + { + super.__getBounds(rect, matrix); + } + + if (__children.length == 0) return; + + var childWorldTransform = Matrix.__pool.get(); + + for (child in __children) + { + if (child.__scaleX == 0 || child.__scaleY == 0 || child.__isMask) continue; + + DisplayObject.__calculateAbsoluteTransform(child.__transform, matrix, childWorldTransform); + + child.__getRenderBounds(rect, childWorldTransform); + } + + Matrix.__pool.release(childWorldTransform); + } + + @:noCompletion private override function __hitTest(x:Float, y:Float, shapeFlag:Bool, stack:Array, interactiveOnly:Bool, + hitObject:DisplayObject):Bool + { + if (!hitObject.visible || __isMask || (interactiveOnly && !mouseEnabled && !mouseChildren)) return false; + if (mask != null && !mask.__hitTestMask(x, y)) return false; + + if (__scrollRect != null) + { + var point = Point.__pool.get(); + point.setTo(x, y); + __getRenderTransform().__transformInversePoint(point); + + if (!__scrollRect.containsPoint(point)) + { + Point.__pool.release(point); + return false; + } + + Point.__pool.release(point); + } + + var i = __children.length; + if (interactiveOnly) + { + if (stack == null || !mouseChildren) + { + while (--i >= 0) + { + if (__children[i].__hitTest(x, y, shapeFlag, null, true, cast __children[i])) + { + if (stack != null) + { + stack.push(hitObject); + } + + return true; + } + } + } + else if (stack != null) + { + var length = stack.length; + + var interactive = false; + var hitTest = false; + + while (--i >= 0) + { + interactive = __children[i].__getInteractive(null); + + if (interactive || (mouseEnabled && !hitTest)) + { + if (__children[i].__hitTest(x, y, shapeFlag, stack, true, cast __children[i])) + { + hitTest = true; + + if (interactive && stack.length > length) + { + break; + } + } + } + } + + if (hitTest) + { + stack.insert(length, hitObject); + return true; + } + } + } + else + { + var hitTest = false; + + while (--i >= 0) + { + if (__children[i].__hitTest(x, y, shapeFlag, stack, false, cast __children[i])) + { + hitTest = true; + if (stack == null) break; + } + } + + return hitTest; + } + + return false; + } + + @:noCompletion private override function __hitTestMask(x:Float, y:Float):Bool + { + var i = __children.length; + + while (--i >= 0) + { + if (__children[i].__hitTestMask(x, y)) + { + return true; + } + } + + return false; + } + + @:noCompletion private override function __readGraphicsData(graphicsData:Vector, recurse:Bool):Void + { + super.__readGraphicsData(graphicsData, recurse); + + if (recurse) + { + for (child in __children) + { + child.__readGraphicsData(graphicsData, recurse); + } + } + } + + @:noCompletion private override function __setStageReference(stage:Stage):Void + { + super.__setStageReference(stage); + + if (__children != null) + { + for (child in __children) + { + child.__setStageReference(stage); + } + } + } + + @:noCompletion private override function __setWorldTransformInvalid():Void + { + if (!__worldTransformInvalid) + { + __worldTransformInvalid = true; + + if (__children != null) + { + for (child in __children) + { + child.__setWorldTransformInvalid(); + } + } + } + } + + @:noCompletion private override function __stopAllMovieClips():Void + { + for (child in __children) + { + child.__stopAllMovieClips(); + } + } + + @:noCompletion private override function __tabTest(stack:Array):Void + { + super.__tabTest(stack); + + if (!tabChildren) return; + + var interactive = false; + var interactiveObject:InteractiveObject = null; + + for (child in __children) + { + interactive = child.__getInteractive(null); + + if (interactive) + { + interactiveObject = cast child; + interactiveObject.__tabTest(stack); + } + } + } + + @:noCompletion private override function __update(transformOnly:Bool, updateChildren:Bool):Void + { + super.__update(transformOnly, updateChildren); + + if (updateChildren) + { + for (child in __children) + { + child.__update(transformOnly, true); + } + } + } + + // Get & Set Methods + + @:noCompletion private function get_numChildren():Int + { + return __children.length; + } + + @:noCompletion private function get_tabChildren():Bool + { + return __tabChildren; + } + + @:noCompletion private function set_tabChildren(value:Bool):Bool + { + if (__tabChildren != value) + { + __tabChildren = value; + + dispatchEvent(new Event(Event.TAB_CHILDREN_CHANGE, true, false)); + } + + return __tabChildren; + } +} +#else +typedef DisplayObjectContainer = flash.display.DisplayObjectContainer; +#end diff --git a/source/openfl/display/OpenGLRenderer.hx b/source/openfl/display/OpenGLRenderer.hx new file mode 100644 index 00000000..4732151a --- /dev/null +++ b/source/openfl/display/OpenGLRenderer.hx @@ -0,0 +1,1127 @@ +package openfl.display; + +#if !flash +import openfl.display._internal.Context3DBitmap; +import openfl.display._internal.Context3DBitmapData; +import openfl.display._internal.Context3DDisplayObject; +import openfl.display._internal.Context3DDisplayObjectContainer; +import openfl.display._internal.Context3DGraphics; +import openfl.display._internal.Context3DMaskShader; +import openfl.display._internal.Context3DSimpleButton; +import openfl.display._internal.Context3DTextField; +import openfl.display._internal.Context3DTilemap; +import openfl.display._internal.Context3DVideo; +import openfl.display._internal.ShaderBuffer; +import openfl.utils.ObjectPool; +import openfl.display3D.Context3DClearMask; +import openfl.display3D.Context3D; +import openfl.geom.ColorTransform; +import openfl.geom.Matrix; +import openfl.geom.Rectangle; +#if lime +import lime.graphics.opengl.ext.KHR_debug; +import lime.graphics.WebGLRenderContext; +import lime.math.Matrix4; +#end + +/** + **BETA** + + The OpenGLRenderer API exposes support for OpenGL render instructions within the + `RenderEvent.RENDER_OPENGL` event. +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(lime.graphics.GLRenderContext) +@:access(openfl.display._internal.ShaderBuffer) +@:access(openfl.display3D.Context3D) +@:access(openfl.display.BitmapData) +@:access(openfl.display.DisplayObject) +@:access(openfl.display.Graphics) +@:access(openfl.display.IBitmapDrawable) +@:access(openfl.display.Shader) +@:access(openfl.display.ShaderParameter) +@:access(openfl.display.Stage3D) +@:access(openfl.geom.ColorTransform) +@:access(openfl.geom.Matrix) +@:access(openfl.geom.Rectangle) +@:allow(openfl.display._internal) +@:allow(openfl.display3D.textures) +@:allow(openfl.display3D) +@:allow(openfl.display) +@:allow(openfl.text) +class OpenGLRenderer extends DisplayObjectRenderer +{ + @:noCompletion private static var __alphaValue:Array = [1]; + @:noCompletion private static var __colorMultipliersValue:Array = [0, 0, 0, 0]; + @:noCompletion private static var __colorOffsetsValue:Array = [0, 0, 0, 0]; + @:noCompletion private static var __defaultColorMultipliersValue:Array = [1, 1, 1, 1]; + @:noCompletion private static var __emptyColorValue:Array = [0, 0, 0, 0]; + @:noCompletion private static var __emptyAlphaValue:Array = [1]; + @:noCompletion private static var __hasColorTransformValue:Array = [false]; + @:noCompletion private static var __scissorRectangle:Rectangle = new Rectangle(); + @:noCompletion private static var __textureSizeValue:Array = [0, 0]; + @:noCompletion private static var __currentBlendMode:Map = new Map(); + + /** + The current OpenGL render context + **/ + @SuppressWarnings("checkstyle:Dynamic") + public var gl:#if lime WebGLRenderContext #else Dynamic #end; + + @:noCompletion private static var __staticDefaultDisplayShader:DisplayObjectShader; + @:noCompletion private static var __staticDefaultGraphicsShader:GraphicsShader; + @:noCompletion private static var __staticMaskShader:Context3DMaskShader; + + @:noCompletion private var __context3D:Context3D; + @:noCompletion private var __clipRects:Array; + @:noCompletion private var __currentDisplayShader:Shader; + @:noCompletion private var __currentGraphicsShader:Shader; + @:noCompletion private var __currentRenderTarget:BitmapData; + @:noCompletion private var __currentShader:Shader; + @:noCompletion private var __currentShaderBuffer:ShaderBuffer; + @:noCompletion private var __defaultDisplayShader:DisplayObjectShader; + @:noCompletion private var __defaultGraphicsShader:GraphicsShader; + @:noCompletion private var __defaultRenderTarget:BitmapData; + @:noCompletion private var __defaultShader:Shader; + @:noCompletion private var __displayHeight:Int; + @:noCompletion private var __displayWidth:Int; + @:noCompletion private var __flipped:Bool; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __gl:#if lime WebGLRenderContext #else Dynamic #end; + @:noCompletion private var __height:Int; + @:noCompletion private var __maskShader:Context3DMaskShader; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __matrix:#if lime Matrix4 #else Dynamic #end; + @:noCompletion private var __maskObjects:Array; + @:noCompletion private var __numClipRects:Int; + @:noCompletion private var __offsetX:Int; + @:noCompletion private var __offsetY:Int; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __projection:#if lime Matrix4 #else Dynamic #end; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __projectionFlipped:#if lime Matrix4 #else Dynamic #end; + @:noCompletion private var __scrollRectMasks:ObjectPool; + @:noCompletion private var __softwareRenderer:DisplayObjectRenderer; + @:noCompletion private var __stencilReference:Int; + @:noCompletion private var __tempRect:Rectangle; + @:noCompletion private var __updatedStencil:Bool; + @:noCompletion private var __upscaled:Bool; + @:noCompletion private var __values:Array; + @:noCompletion private var __width:Int; + + @:noCompletion private function new(context:Context3D, defaultRenderTarget:BitmapData = null) + { + super(); + + __context3D = context; + __context = context.__context; + + gl = context.__context.webgl; + __gl = gl; + + this.__defaultRenderTarget = defaultRenderTarget; + this.__flipped = (__defaultRenderTarget == null); + + if (Graphics.maxTextureWidth == null) + { + Graphics.maxTextureWidth = Graphics.maxTextureHeight = __gl.getParameter(__gl.MAX_TEXTURE_SIZE); + } + + #if lime + __matrix = new Matrix4(); + #end + + __values = new Array(); + + #if gl_debug + var ext:KHR_debug = __gl.getExtension("KHR_debug"); + if (ext != null) + { + gl.enable(ext.DEBUG_OUTPUT); + gl.enable(ext.DEBUG_OUTPUT_SYNCHRONOUS); + } + #end + + #if (js && html5) + __softwareRenderer = new CanvasRenderer(null); + #else + __softwareRenderer = new CairoRenderer(null); + #end + + #if lime + __type = OPENGL; + #end + + __setBlendMode(NORMAL); + __context3D.__setGLBlend(true); + + __clipRects = new Array(); + __maskObjects = new Array(); + __numClipRects = 0; + #if lime + __projection = new Matrix4(); + __projectionFlipped = new Matrix4(); + #end + __stencilReference = 0; + __tempRect = new Rectangle(); + + if (__staticDefaultDisplayShader == null) __staticDefaultDisplayShader = new DisplayObjectShader(); + if (__staticDefaultGraphicsShader == null) __staticDefaultGraphicsShader = new GraphicsShader(); + if (__staticMaskShader == null) __staticMaskShader = new Context3DMaskShader(); + + __defaultDisplayShader = __staticDefaultDisplayShader; + __defaultGraphicsShader = __staticDefaultGraphicsShader; + __defaultShader = __defaultDisplayShader; + + __initShader(__defaultShader); + + __scrollRectMasks = new ObjectPool(function() return new Shape()); + __maskShader = __staticMaskShader; + } + + /** + Applies an alpha value to the active shader, if compatible with OpenFL core shaders + **/ + public function applyAlpha(alpha:Float):Void + { + __alphaValue[0] = alpha * __worldAlpha; + + if (__currentShaderBuffer != null) + { + __currentShaderBuffer.addFloatOverride("openfl_Alpha", __alphaValue); + } + else if (__currentShader != null) + { + if (__currentShader.__alpha != null) __currentShader.__alpha.value = __alphaValue; + } + } + + /** + Binds a BitmapData object as the first active texture of the current active shader, + if compatible with OpenFL core shaders + **/ + public function applyBitmapData(bitmapData:BitmapData, smooth:Bool, repeat:Bool = false):Void + { + if (__currentShaderBuffer != null) + { + if (bitmapData != null) + { + __textureSizeValue[0] = bitmapData.__textureWidth; + __textureSizeValue[1] = bitmapData.__textureHeight; + + __currentShaderBuffer.addFloatOverride("openfl_TextureSize", __textureSizeValue); + } + } + else if (__currentShader != null) + { + if (__currentShader.__bitmap != null) + { + __currentShader.__bitmap.input = bitmapData; + __currentShader.__bitmap.filter = (smooth && __allowSmoothing) ? LINEAR : NEAREST; + __currentShader.__bitmap.mipFilter = MIPNONE; + __currentShader.__bitmap.wrap = repeat ? REPEAT : CLAMP; + } + + if (__currentShader.__texture != null) + { + __currentShader.__texture.input = bitmapData; + __currentShader.__texture.filter = (smooth && __allowSmoothing) ? LINEAR : NEAREST; + __currentShader.__texture.mipFilter = MIPNONE; + __currentShader.__texture.wrap = repeat ? REPEAT : CLAMP; + } + + if (__currentShader.__textureSize != null) + { + if (bitmapData != null) + { + __textureSizeValue[0] = bitmapData.__textureWidth; + __textureSizeValue[1] = bitmapData.__textureHeight; + + __currentShader.__textureSize.value = __textureSizeValue; + } + else + { + __currentShader.__textureSize.value = null; + } + } + } + } + + /** + Applies a color transform value to the active shader, if compatible with OpenFL + core shaders + **/ + public function applyColorTransform(colorTransform:ColorTransform):Void + { + var enabled = (colorTransform != null && !colorTransform.__isDefault(true)); + applyHasColorTransform(enabled); + + if (enabled) + { + colorTransform.__setArrays(__colorMultipliersValue, __colorOffsetsValue); + + if (__currentShaderBuffer != null) + { + __currentShaderBuffer.addFloatOverride("openfl_ColorMultiplier", __colorMultipliersValue); + __currentShaderBuffer.addFloatOverride("openfl_ColorOffset", __colorOffsetsValue); + } + else if (__currentShader != null) + { + if (__currentShader.__colorMultiplier != null) __currentShader.__colorMultiplier.value = __colorMultipliersValue; + if (__currentShader.__colorOffset != null) __currentShader.__colorOffset.value = __colorOffsetsValue; + } + } + else + { + if (__currentShaderBuffer != null) + { + __currentShaderBuffer.addFloatOverride("openfl_ColorMultiplier", __emptyColorValue); + __currentShaderBuffer.addFloatOverride("openfl_ColorOffset", __emptyColorValue); + } + else if (__currentShader != null) + { + if (__currentShader.__colorMultiplier != null) __currentShader.__colorMultiplier.value = __emptyColorValue; + if (__currentShader.__colorOffset != null) __currentShader.__colorOffset.value = __emptyColorValue; + } + } + } + + /** + Applies the "has color transform" uniform value for the active shader, if + compatible with OpenFL core shaders + **/ + public function applyHasColorTransform(enabled:Bool):Void + { + __hasColorTransformValue[0] = enabled; + + if (__currentShaderBuffer != null) + { + __currentShaderBuffer.addBoolOverride("openfl_HasColorTransform", __hasColorTransformValue); + } + else if (__currentShader != null) + { + if (__currentShader.__hasColorTransform != null) __currentShader.__hasColorTransform.value = __hasColorTransformValue; + } + } + + /** + Applies render matrix to the active shader, if compatible with OpenFL core shaders + **/ + public function applyMatrix(matrix:Array):Void + { + if (__currentShaderBuffer != null) + { + __currentShaderBuffer.addFloatOverride("openfl_Matrix", matrix); + } + else if (__currentShader != null) + { + if (__currentShader.__matrix != null) __currentShader.__matrix.value = matrix; + } + } + + /** + Converts an OpenFL two-dimensional matrix to a compatible 3D matrix for use with + OpenGL rendering. Repeated calls to this method will return the same object with + new values, so it will need to be cloned if the result must be cached + **/ + @SuppressWarnings("checkstyle:Dynamic") + public function getMatrix(transform:Matrix):#if lime Matrix4 #else Dynamic #end + { + if (gl != null) + { + var values = __getMatrix(transform, AUTO); + + for (i in 0...16) + { + __matrix[i] = values[i]; + } + + return __matrix; + } + else + { + __matrix.identity(); + __matrix[0] = transform.a; + __matrix[1] = transform.b; + __matrix[4] = transform.c; + __matrix[5] = transform.d; + __matrix[12] = transform.tx; + __matrix[13] = transform.ty; + + return __matrix; + } + } + + /** + Sets the current active shader, which automatically unbinds the previous shader + if it was bound using an OpenFL Shader object + **/ + public function setShader(shader:Shader):Void + { + __currentShaderBuffer = null; + + if (__currentShader == shader) return; + + if (__currentShader != null) + { + // TODO: Integrate cleanup with Context3D + // __currentShader.__disable (); + } + + if (shader == null) + { + __currentShader = null; + __context3D.setProgram(null); + // __context3D.__flushGLProgram (); + return; + } + else + { + __currentShader = shader; + __initShader(shader); + __context3D.setProgram(shader.program); + __context3D.__flushGLProgram(); + // __context3D.__flushGLTextures (); + __currentShader.__enable(); + __context3D.__state.shader = shader; + } + } + + /** + Updates the current OpenGL viewport using the current OpenFL stage coordinates + **/ + public function setViewport():Void + { + __gl.viewport(__offsetX, __offsetY, __displayWidth, __displayHeight); + } + + /** + Updates the current active shader with cached alpha, color transform, + bitmap data and other uniform or attribute values. This should be called in advance + of rendering + **/ + public function updateShader():Void + { + if (__currentShader != null) + { + if (__currentShader.__position != null) __currentShader.__position.__useArray = true; + if (__currentShader.__textureCoord != null) __currentShader.__textureCoord.__useArray = true; + __context3D.setProgram(__currentShader.program); + __context3D.__flushGLProgram(); + __context3D.__flushGLTextures(); + __currentShader.__update(); + } + } + + /** + Updates the active shader to expect an alpha array, if the current shader + is compatible with OpenFL core shaders + **/ + public function useAlphaArray():Void + { + if (__currentShader != null) + { + if (__currentShader.__alpha != null) __currentShader.__alpha.__useArray = true; + } + } + + /** + Updates the active shader to expect a color transform array, if the current shader + is compatible with OpenFL core shaders + **/ + public function useColorTransformArray():Void + { + if (__currentShader != null) + { + if (__currentShader.__colorMultiplier != null) __currentShader.__colorMultiplier.__useArray = true; + if (__currentShader.__colorOffset != null) __currentShader.__colorOffset.__useArray = true; + } + } + + @:noCompletion private function __cleanup():Void + { + if (__stencilReference > 0) + { + __stencilReference = 0; + __context3D.setStencilActions(); + __context3D.setStencilReferenceValue(0, 0, 0); + } + + if (__numClipRects > 0) + { + __numClipRects = 0; + __scissorRect(); + } + } + + @:noCompletion private override function __clear():Void + { + if (__stage == null || __stage.__transparent) + { + __context3D.clear(0, 0, 0, 0, 0, 0, Context3DClearMask.COLOR); + } + else + { + __context3D.clear(__stage.__colorSplit[0], __stage.__colorSplit[1], __stage.__colorSplit[2], 1, 0, 0, Context3DClearMask.COLOR); + } + + __cleared = true; + } + + @:noCompletion private function __clearShader():Void + { + if (__currentShader != null) + { + if (__currentShaderBuffer == null) + { + if (__currentShader.__bitmap != null) __currentShader.__bitmap.input = null; + } + else + { + __currentShaderBuffer.clearOverride(); + } + + if (__currentShader.__texture != null) __currentShader.__texture.input = null; + if (__currentShader.__textureSize != null) __currentShader.__textureSize.value = null; + if (__currentShader.__hasColorTransform != null) __currentShader.__hasColorTransform.value = null; + if (__currentShader.__position != null) __currentShader.__position.value = null; + if (__currentShader.__matrix != null) __currentShader.__matrix.value = null; + __currentShader.__clearUseArray(); + } + } + + @:noCompletion private function __copyShader(other:OpenGLRenderer):Void + { + __currentShader = other.__currentShader; + __currentShaderBuffer = other.__currentShaderBuffer; + __currentDisplayShader = other.__currentDisplayShader; + __currentGraphicsShader = other.__currentGraphicsShader; + + // __gl.glProgram = other.__gl.glProgram; + } + + @:noCompletion private function __getMatrix(transform:Matrix, pixelSnapping:PixelSnapping):Array + { + var _matrix = Matrix.__pool.get(); + _matrix.copyFrom(transform); + _matrix.concat(__worldTransform); + + if (pixelSnapping == ALWAYS + || (pixelSnapping == AUTO + && _matrix.b == 0 + && _matrix.c == 0 + && (_matrix.a < 1.001 && _matrix.a > 0.999) + && (_matrix.d < 1.001 && _matrix.d > 0.999))) + { + _matrix.tx = Math.round(_matrix.tx); + _matrix.ty = Math.round(_matrix.ty); + } + + __matrix.identity(); + __matrix[0] = _matrix.a; + __matrix[1] = _matrix.b; + __matrix[4] = _matrix.c; + __matrix[5] = _matrix.d; + __matrix[12] = _matrix.tx; + __matrix[13] = _matrix.ty; + __matrix.append(__flipped ? __projectionFlipped : __projection); + + for (i in 0...16) + { + __values[i] = __matrix[i]; + } + + Matrix.__pool.release(_matrix); + + return __values; + } + + @:noCompletion private function __initShader(shader:Shader):Shader + { + if (shader != null) + { + // TODO: Change of GL context? + + if (shader.__context == null) + { + shader.__context = __context3D; + shader.__init(); + } + + // currentShader = shader; + return shader; + } + + return __defaultShader; + } + + @:noCompletion private function __initDisplayShader(shader:Shader):Shader + { + if (shader != null) + { + // TODO: Change of GL context? + + if (shader.__context == null) + { + shader.__context = __context3D; + shader.__init(); + } + + // currentShader = shader; + return shader; + } + + return __defaultDisplayShader; + } + + @:noCompletion private function __initGraphicsShader(shader:Shader):Shader + { + if (shader != null) + { + // TODO: Change of GL context? + + if (shader.__context == null) + { + shader.__context = __context3D; + shader.__init(); + } + + // currentShader = shader; + return shader; + } + + return __defaultGraphicsShader; + } + + @:noCompletion private function __initShaderBuffer(shaderBuffer:ShaderBuffer):Shader + { + if (shaderBuffer != null) + { + return __initGraphicsShader(shaderBuffer.shader); + } + + return __defaultGraphicsShader; + } + + @:noCompletion private override function __popMask(maskee:DisplayObject = null):Void + { + if (__stencilReference == 0) return; + + var mask = __maskObjects.pop(); + + if (__stencilReference > 1) + { + __context3D.setStencilActions(FRONT_AND_BACK, EQUAL, DECREMENT_SATURATE, DECREMENT_SATURATE, KEEP); + __context3D.setStencilReferenceValue(__stencilReference, 0xFF, 0xFF); + __context3D.setColorMask(false, false, false, false); + + __renderDrawableMask(mask); + if (maskee == null || !maskee.maskInverted) + { + __stencilReference--; + } + + __context3D.setStencilActions(FRONT_AND_BACK, EQUAL, KEEP, KEEP, KEEP); + __context3D.setStencilReferenceValue(__stencilReference, 0xFF, 0); + __context3D.setColorMask(true, true, true, true); + } + else + { + __stencilReference = 0; + __context3D.setStencilActions(); + __context3D.setStencilReferenceValue(0, 0, 0); + } + } + + @:noCompletion private override function __popMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void + { + if (object.__mask != null) + { + __popMask(object); + } + + if (handleScrollRect && object.__scrollRect != null) + { + if (object.__renderTransform.b != 0 || object.__renderTransform.c != 0) + { + __scrollRectMasks.release(cast __maskObjects[__maskObjects.length - 1]); + __popMask(); + } + else + { + __popMaskRect(); + } + } + } + + @:noCompletion private override function __popMaskRect():Void + { + if (__numClipRects > 0) + { + __numClipRects--; + + if (__numClipRects > 0) + { + __scissorRect(__clipRects[__numClipRects - 1]); + } + else + { + __scissorRect(); + } + } + } + + @:noCompletion private override function __pushMask(mask:DisplayObject, maskee:DisplayObject = null):Void + { + if (__stencilReference == 0) + { + __context3D.clear(0, 0, 0, 0, 0, 0, Context3DClearMask.STENCIL); + __updatedStencil = true; + } + + __context3D.setStencilActions(FRONT_AND_BACK, EQUAL, INCREMENT_SATURATE, KEEP, KEEP); + __context3D.setStencilReferenceValue(__stencilReference, 0xFF, 0xFF); + __context3D.setColorMask(false, false, false, false); + + __renderDrawableMask(mask); + __maskObjects.push(mask); + if (maskee == null || !maskee.maskInverted) + { + __stencilReference++; + } + + __context3D.setStencilActions(FRONT_AND_BACK, EQUAL, KEEP, KEEP, KEEP); + __context3D.setStencilReferenceValue(__stencilReference, 0xFF, 0); + __context3D.setColorMask(true, true, true, true); + } + + @:noCompletion private override function __pushMaskObject(object:DisplayObject, handleScrollRect:Bool = true):Void + { + if (handleScrollRect && object.__scrollRect != null) + { + if (object.__renderTransform.b != 0 || object.__renderTransform.c != 0) + { + var shape = __scrollRectMasks.get(); + shape.graphics.clear(); + shape.graphics.beginFill(0x00FF00); + shape.graphics.drawRect(object.__scrollRect.x, object.__scrollRect.y, object.__scrollRect.width, object.__scrollRect.height); + shape.__renderTransform.copyFrom(object.__renderTransform); + __pushMask(shape); + } + else + { + __pushMaskRect(object.__scrollRect, object.__renderTransform); + } + } + + if (object.__mask != null) + { + __pushMask(object.__mask, object); + } + } + + @:noCompletion private override function __pushMaskRect(rect:Rectangle, transform:Matrix):Void + { + // TODO: Handle rotation? + + if (__numClipRects == __clipRects.length) + { + __clipRects[__numClipRects] = new Rectangle(); + } + + var _matrix = Matrix.__pool.get(); + _matrix.copyFrom(transform); + _matrix.concat(__worldTransform); + + var clipRect = __clipRects[__numClipRects]; + rect.__transform(clipRect, _matrix); + + if (__numClipRects > 0) + { + var parentClipRect = __clipRects[__numClipRects - 1]; + clipRect.__contract(parentClipRect.x, parentClipRect.y, parentClipRect.width, parentClipRect.height); + } + + if (clipRect.height < 0) + { + clipRect.height = 0; + } + + if (clipRect.width < 0) + { + clipRect.width = 0; + } + + Matrix.__pool.release(_matrix); + + __scissorRect(clipRect); + __numClipRects++; + } + + @:noCompletion private override function __render(object:IBitmapDrawable):Void + { + __context3D.setColorMask(true, true, true, true); + __context3D.setCulling(NONE); + __context3D.setDepthTest(false, ALWAYS); + __context3D.setStencilActions(); + __context3D.setStencilReferenceValue(0, 0, 0); + __context3D.setScissorRectangle(null); + + __blendMode = null; + if (__defaultRenderTarget == null) + { + OpenGLRenderer.__currentBlendMode.remove(__context3D); + } + __setBlendMode(NORMAL); + + if (__defaultRenderTarget == null) + { + if (__context3D.__backBufferWantsBestResolution) + { + __scissorRectangle.setTo(__offsetX / __pixelRatio, __offsetY / __pixelRatio, __displayWidth / __pixelRatio, __displayHeight / __pixelRatio); + } + else + { + __scissorRectangle.setTo(__offsetX, __offsetY, __displayWidth, __displayHeight); + } + __context3D.setScissorRectangle(__scissorRectangle); + + __upscaled = (__worldTransform.a != 1 || __worldTransform.d != 1); + + __renderDrawable(object); + + // TODO: Handle this in Context3D as a viewport? + + if (__offsetX > 0 || __offsetY > 0) + { + // __context3D.__setGLScissorTest (true); + + if (__offsetX > 0) + { + // __gl.scissor (0, 0, __offsetX, __height); + __scissorRectangle.setTo(0, 0, __offsetX, __height); + __context3D.setScissorRectangle(__scissorRectangle); + + __context3D.__flushGL(); + __gl.clearColor(0, 0, 0, 1); + __gl.clear(__gl.COLOR_BUFFER_BIT); + // __context3D.clear (0, 0, 0, 1, 0, 0, Context3DClearMask.COLOR); + + // __gl.scissor (__offsetX + __displayWidth, 0, __width, __height); + __scissorRectangle.setTo(__offsetX + __displayWidth, 0, __width, __height); + __context3D.setScissorRectangle(__scissorRectangle); + + __context3D.__flushGL(); + __gl.clearColor(0, 0, 0, 1); + __gl.clear(__gl.COLOR_BUFFER_BIT); + // __context3D.clear (0, 0, 0, 1, 0, 0, Context3DClearMask.COLOR); + } + + if (__offsetY > 0) + { + // __gl.scissor (0, 0, __width, __offsetY); + __scissorRectangle.setTo(0, 0, __width, __offsetY); + __context3D.setScissorRectangle(__scissorRectangle); + + __context3D.__flushGL(); + __gl.clearColor(0, 0, 0, 1); + __gl.clear(__gl.COLOR_BUFFER_BIT); + // __context3D.clear (0, 0, 0, 1, 0, 0, Context3DClearMask.COLOR); + + // __gl.scissor (0, __offsetY + __displayHeight, __width, __height); + __scissorRectangle.setTo(0, __offsetY + __displayHeight, __width, __height); + __context3D.setScissorRectangle(__scissorRectangle); + + __context3D.__flushGL(); + __gl.clearColor(0, 0, 0, 1); + __gl.clear(__gl.COLOR_BUFFER_BIT); + // __context3D.clear (0, 0, 0, 1, 0, 0, Context3DClearMask.COLOR); + } + + __context3D.setScissorRectangle(null); + } + } + else + { + if (__context3D.__backBufferWantsBestResolution) + { + __scissorRectangle.setTo(__offsetX / __pixelRatio, __offsetY / __pixelRatio, __displayWidth / __pixelRatio, __displayHeight / __pixelRatio); + } + else + { + __scissorRectangle.setTo(__offsetX, __offsetY, __displayWidth, __displayHeight); + } + __context3D.setScissorRectangle(__scissorRectangle); + // __gl.viewport (__offsetX, __offsetY, __displayWidth, __displayHeight); + + // __upscaled = (__worldTransform.a != 1 || __worldTransform.d != 1); + + // TODO: Cleaner approach? + + var cacheMask = object.__mask; + var cacheScrollRect = object.__scrollRect; + object.__mask = null; + object.__scrollRect = null; + + __renderDrawable(object); + + object.__mask = cacheMask; + object.__scrollRect = cacheScrollRect; + } + + __context3D.present(); + } + + @:noCompletion private function __renderDrawable(object:IBitmapDrawable):Void + { + if (object == null) return; + + switch (object.__drawableType) + { + case BITMAP_DATA: + Context3DBitmapData.renderDrawable(cast object, this); + case STAGE, SPRITE: + Context3DDisplayObjectContainer.renderDrawable(cast object, this); + case BITMAP: + Context3DBitmap.renderDrawable(cast object, this); + case SHAPE: + Context3DDisplayObject.renderDrawable(cast object, this); + case SIMPLE_BUTTON: + Context3DSimpleButton.renderDrawable(cast object, this); + case TEXT_FIELD: + Context3DTextField.renderDrawable(cast object, this); + case VIDEO: + Context3DVideo.renderDrawable(cast object, this); + case TILEMAP: + Context3DTilemap.renderDrawable(cast object, this); + default: + } + } + + @:noCompletion private function __renderDrawableMask(object:IBitmapDrawable):Void + { + if (object == null) return; + + switch (object.__drawableType) + { + case BITMAP_DATA: + Context3DBitmapData.renderDrawableMask(cast object, this); + case STAGE, SPRITE: + Context3DDisplayObjectContainer.renderDrawableMask(cast object, this); + case BITMAP: + Context3DBitmap.renderDrawableMask(cast object, this); + case SHAPE: + Context3DDisplayObject.renderDrawableMask(cast object, this); + case SIMPLE_BUTTON: + Context3DSimpleButton.renderDrawableMask(cast object, this); + case TEXT_FIELD: + Context3DTextField.renderDrawableMask(cast object, this); + case VIDEO: + Context3DVideo.renderDrawableMask(cast object, this); + case TILEMAP: + Context3DTilemap.renderDrawableMask(cast object, this); + default: + } + } + + @:noCompletion private function __renderFilterPass(source:BitmapData, shader:Shader, smooth:Bool, clear:Bool = true):Void + { + if (source == null || shader == null) return; + if (__defaultRenderTarget == null) return; + + var cacheRTT = __context3D.__state.renderToTexture; + var cacheRTTDepthStencil = __context3D.__state.renderToTextureDepthStencil; + var cacheRTTAntiAlias = __context3D.__state.renderToTextureAntiAlias; + var cacheRTTSurfaceSelector = __context3D.__state.renderToTextureSurfaceSelector; + + __context3D.setRenderToTexture(__defaultRenderTarget.getTexture(__context3D), false); + + if (clear) + { + __context3D.clear(0, 0, 0, 0, 0, 0, Context3DClearMask.COLOR); + } + + var shader = __initShader(shader); + setShader(shader); + applyAlpha(1); + applyBitmapData(source, smooth); + applyColorTransform(null); + applyMatrix(__getMatrix(source.__renderTransform, AUTO)); + updateShader(); + + var vertexBuffer = source.getVertexBuffer(__context3D); + if (shader.__position != null) __context3D.setVertexBufferAt(shader.__position.index, vertexBuffer, 0, FLOAT_3); + if (shader.__textureCoord != null) __context3D.setVertexBufferAt(shader.__textureCoord.index, vertexBuffer, 3, FLOAT_2); + var indexBuffer = source.getIndexBuffer(__context3D); + __context3D.drawTriangles(indexBuffer); + + if (cacheRTT != null) + { + __context3D.setRenderToTexture(cacheRTT, cacheRTTDepthStencil, cacheRTTAntiAlias, cacheRTTSurfaceSelector); + } + else + { + __context3D.setRenderToBackBuffer(); + } + + __clearShader(); + } + + @:noCompletion private override function __resize(width:Int, height:Int):Void + { + __width = width; + __height = height; + + var w = (__defaultRenderTarget == null) ? __stage.stageWidth : __defaultRenderTarget.width; + var h = (__defaultRenderTarget == null) ? __stage.stageHeight : __defaultRenderTarget.height; + + __offsetX = __defaultRenderTarget == null ? Math.round(__worldTransform.__transformX(0, 0)) : 0; + __offsetY = __defaultRenderTarget == null ? Math.round(__worldTransform.__transformY(0, 0)) : 0; + __displayWidth = __defaultRenderTarget == null ? Math.round(__worldTransform.__transformX(w, 0) - __offsetX) : w; + __displayHeight = __defaultRenderTarget == null ? Math.round(__worldTransform.__transformY(0, h) - __offsetY) : h; + + __projection.createOrtho(0, __displayWidth + __offsetX * 2, 0, __displayHeight + __offsetY * 2, -1000, 1000); + __projectionFlipped.createOrtho(0, __displayWidth + __offsetX * 2, __displayHeight + __offsetY * 2, 0, -1000, 1000); + } + + @:noCompletion private function __resumeClipAndMask(childRenderer:OpenGLRenderer):Void + { + if (__stencilReference > 0) + { + __context3D.setStencilActions(FRONT_AND_BACK, EQUAL, KEEP, KEEP, KEEP); + __context3D.setStencilReferenceValue(__stencilReference, 0xFF, 0); + } + else + { + __context3D.setStencilActions(); + __context3D.setStencilReferenceValue(0, 0, 0); + } + + if (__numClipRects > 0) + { + __scissorRect(__clipRects[__numClipRects - 1]); + } + else + { + __scissorRect(); + } + } + + @:noCompletion private function __scissorRect(clipRect:Rectangle = null):Void + { + if (clipRect != null) + { + var x:Float = Math.floor(clipRect.x); + var y:Float = Math.floor(clipRect.y); + var width:Float = (clipRect.width > 0 ? Math.ceil(clipRect.right) - x : 0); + var height:Float = (clipRect.height > 0 ? Math.ceil(clipRect.bottom) - y : 0); + #if !openfl_dpi_aware + if (__context3D.__backBufferWantsBestResolution) + { + var uv = 1.5 / __pixelRatio; + x = clipRect.x / __pixelRatio; + y = clipRect.y / __pixelRatio; + width = (clipRect.width > 0 ? (clipRect.right / __pixelRatio) - x + uv : 0); + height = (clipRect.height > 0 ? (clipRect.bottom / __pixelRatio) - y + uv : 0); + } + #end + + if (width < 0) width = 0; + if (height < 0) height = 0; + + // __scissorRectangle.setTo (x, __flipped ? __height - y - height : y, width, height); + __scissorRectangle.setTo(x, y, width, height); + __context3D.setScissorRectangle(__scissorRectangle); + } + else + { + __context3D.setScissorRectangle(null); + } + } + + @:noCompletion private override function __setBlendMode(value:BlendMode):Void + { + if (__overrideBlendMode != null) value = __overrideBlendMode; + if (OpenGLRenderer.__currentBlendMode.get(__context3D) == value) + { + __blendMode = value; + return; + } + OpenGLRenderer.__currentBlendMode.set(__context3D, value); + + __blendMode = value; + + switch (value) + { + case ADD: + __context3D.setBlendFactors(ONE, ONE); + + case MULTIPLY: + __context3D.setBlendFactors(DESTINATION_COLOR, ONE_MINUS_SOURCE_ALPHA); + + case SCREEN: + __context3D.setBlendFactors(ONE, ONE_MINUS_SOURCE_COLOR); + + case SUBTRACT: + __context3D.setBlendFactors(ONE, ONE); + __context3D.__setGLBlendEquation(__gl.FUNC_REVERSE_SUBTRACT); + + case ERASE: + __context3D.setBlendFactors(ZERO, ONE_MINUS_SOURCE_ALPHA); + + #if desktop + case DARKEN: + __context3D.setBlendFactors(ONE, ONE); + __context3D.__setGLBlendEquation(0x8007); // GL_MIN + + case LIGHTEN: + __context3D.setBlendFactors(ONE, ONE); + __context3D.__setGLBlendEquation(0x8008); // GL_MAX + #end + + default: + __context3D.setBlendFactors(ONE, ONE_MINUS_SOURCE_ALPHA); + } + } + + @:noCompletion private function __setRenderTarget(renderTarget:BitmapData):Void + { + __defaultRenderTarget = renderTarget; + __flipped = (renderTarget == null); + + if (renderTarget != null) + { + __resize(renderTarget.width, renderTarget.height); + } + } + + @:noCompletion private function __setShaderBuffer(shaderBuffer:ShaderBuffer):Void + { + setShader(shaderBuffer.shader); + __currentShaderBuffer = shaderBuffer; + } + + @:noCompletion private function __suspendClipAndMask():Void + { + if (__stencilReference > 0) + { + __context3D.setStencilActions(); + __context3D.setStencilReferenceValue(0, 0, 0); + } + + if (__numClipRects > 0) + { + __scissorRect(); + } + } + + @:noCompletion private function __updateShaderBuffer(bufferOffset:Int):Void + { + if (__currentShader != null && __currentShaderBuffer != null) + { + __currentShader.__updateFromBuffer(__currentShaderBuffer, bufferOffset); + } + } +} +#else +typedef OpenGLRenderer = Dynamic; +#end diff --git a/source/openfl/display/Stage.hx b/source/openfl/display/Stage.hx new file mode 100644 index 00000000..aadbecdc --- /dev/null +++ b/source/openfl/display/Stage.hx @@ -0,0 +1,3817 @@ +package openfl.display; + +#if !flash +import haxe.CallStack; +import haxe.ds.ArraySort; +import openfl.utils._internal.Log; +import openfl.utils._internal.TouchData; +import openfl.display3D.Context3D; +import openfl.display.Application as OpenFLApplication; +import openfl.errors.IllegalOperationError; +import openfl.events.Event; +import openfl.events.EventDispatcher; +import openfl.events.EventPhase; +import openfl.events.FocusEvent; +import openfl.events.FullScreenEvent; +import openfl.events.KeyboardEvent; +import openfl.events.MouseEvent; +import openfl.events.TextEvent; +import openfl.events.TouchEvent; +import openfl.events.UncaughtErrorEvent; +import openfl.events.UncaughtErrorEvents; +import openfl.geom.Matrix; +import openfl.geom.Point; +import openfl.geom.Rectangle; +import openfl.geom.Transform; +import openfl.text.TextField; +import openfl.ui.GameInput; +import openfl.ui.Keyboard; +import openfl.ui.Mouse; +import openfl.ui.MouseCursor; +#if lime +import lime.app.Application; +import lime.app.IModule; +import lime.graphics.RenderContext; +import lime.graphics.RenderContextType; +import lime.ui.Touch; +import lime.ui.Gamepad; +import lime.ui.GamepadAxis; +import lime.ui.GamepadButton; +import lime.ui.KeyCode; +import lime.ui.KeyModifier; +import lime.ui.MouseCursor as LimeMouseCursor; +import lime.ui.MouseWheelMode; +import lime.ui.Window; +#end +#if hxtelemetry +import openfl.profiler.Telemetry; +#end +#if gl_stats +import openfl.display._internal.stats.Context3DStats; +#end +#if (js && html5) +import js.html.Element; +import js.Browser; +#elseif js +typedef Element = Dynamic; +#end + +/** + The Stage class represents the main drawing area. + + For SWF content running in the browser (in Flash® Player), + the Stage represents the entire area where Flash content is shown. For + content running in AIR on desktop operating systems, each NativeWindow + object has a corresponding Stage object. + + The Stage object is not globally accessible. You need to access it + through the `stage` property of a DisplayObject instance. + + The Stage class has several ancestor classes - DisplayObjectContainer, + InteractiveObject, DisplayObject, and EventDispatcher - from which it + inherits properties and methods. Many of these properties and methods are + either inapplicable to Stage objects, or require security checks when + called on a Stage object. The properties and methods that require security + checks are documented as part of the Stage class. + + In addition, the following inherited properties are inapplicable to + Stage objects. If you try to set them, an IllegalOperationError is thrown. + These properties may always be read, but since they cannot be set, they + will always contain default values. + + + * `accessibilityProperties` + * `alpha` + * `blendMode` + * `cacheAsBitmap` + * `contextMenu` + * `filters` + * `focusRect` + * `loaderInfo` + * `mask` + * `mouseEnabled` + * `name` + * `opaqueBackground` + * `rotation` + * `scale9Grid` + * `scaleX` + * `scaleY` + * `scrollRect` + * `tabEnabled` + * `tabIndex` + * `transform` + * `visible` + * `x` + * `y` + + + Some events that you might expect to be a part of the Stage class, such + as `enterFrame`, `exitFrame`, + `frameConstructed`, and `render`, cannot be Stage + events because a reference to the Stage object cannot be guaranteed to + exist in every situation where these events are used. Because these events + cannot be dispatched by the Stage object, they are instead dispatched by + every DisplayObject instance, which means that you can add an event + listener to any DisplayObject instance to listen for these events. These + events, which are part of the DisplayObject class, are called broadcast + events to differentiate them from events that target a specific + DisplayObject instance. Two other broadcast events, `activate` + and `deactivate`, belong to DisplayObject's superclass, + EventDispatcher. The `activate` and `deactivate` + events behave similarly to the DisplayObject broadcast events, except that + these two events are dispatched not only by all DisplayObject instances, + but also by all EventDispatcher instances and instances of other + EventDispatcher subclasses. For more information on broadcast events, see + the DisplayObject class. + + @event fullScreen Dispatched when the Stage object enters, or + leaves, full-screen mode. A change in + full-screen mode can be initiated through + Haxe code, or the user invoking a keyboard + shortcut, or if the current focus leaves the + full-screen window. + @event mouseLeave Dispatched by the Stage object when the + pointer moves out of the stage area. If the + mouse button is pressed, the event is not + dispatched. + @event orientationChange Dispatched by the Stage object when the stage + orientation changes. + + Orientation changes can occur when the + user rotates the device, opens a slide-out + keyboard, or when the + `setAspectRatio()` is called. + + **Note:** If the + `autoOrients` property is + `false`, then the stage + orientation does not change when a device is + rotated. Thus, StageOrientationEvents are + only dispatched for device rotation when + `autoOrients` is + `true`. + @event orientationChanging Dispatched by the Stage object when the stage + orientation begins changing. + + **Important:** orientationChanging + events are not dispatched on Android + devices. + + **Note:** If the + `autoOrients` property is + `false`, then the stage + orientation does not change when a device is + rotated. Thus, StageOrientationEvents are + only dispatched for device rotation when + `autoOrients` is + `true`. + @event resize Dispatched when the `scaleMode` + property of the Stage object is set to + `StageScaleMode.NO_SCALE` and the + SWF file is resized. + @event stageVideoAvailability Dispatched by the Stage object when the state + of the stageVideos property changes. +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(openfl.display3D.Context3D) +@:access(openfl.display.DisplayObjectRenderer) +@:access(openfl.display.LoaderInfo) +@:access(openfl.display.Sprite) +@:access(openfl.display.Stage3D) +@:access(openfl.events.Event) +@:access(openfl.events.UncaughtErrorEvents) +@:access(openfl.geom.Matrix) +@:access(openfl.geom.Point) +@:access(openfl.ui.GameInput) +@:access(openfl.ui.Keyboard) +@:access(openfl.ui.Mouse) +@:access(lime.ui.Window) +class Stage extends DisplayObjectContainer #if lime implements IModule #end +{ + /** + A value from the StageAlign class that specifies the alignment of the + stage in Flash Player or the browser. The following are valid values: + + The `align` property is only available to an object that is + in the same security sandbox as the Stage owner (the main SWF file). To + avoid this, the Stage owner can grant permission to the domain of the + calling object by calling the `Security.allowDomain()` method + or the `Security.alowInsecureDomain()` method. For more + information, see the "Security" chapter in the _OpenFL + Developer's Guide_. + **/ + public var align:StageAlign; + + /** + Specifies whether this stage allows the use of the full screen mode + **/ + public var allowsFullScreen(default, null):Bool; + + /** + Specifies whether this stage allows the use of the full screen with text input mode + **/ + public var allowsFullScreenInteractive(default, null):Bool; + + /** + The associated Lime Application instance. + **/ + public var application(default, null):Application; + + // @:noCompletion @:dox(hide) @:require(flash15) public var browserZoomFactor (default, null):Float; + + /** + The window background color. + **/ + public var color(get, set):Null; + + #if false + /** + Controls Flash runtime color correction for displays. Color correction + works only if the main monitor is assigned a valid ICC color profile, + which specifies the device's particular color attributes. By default, + the Flash runtime tries to match the color correction of its host + (usually a browser). + Use the `Stage.colorCorrectionSupport` property to determine if color + correction is available on the current system and the default state. . + If color correction is available, all colors on the stage are assumed + to be in the sRGB color space, which is the most standard color space. + Source profiles of input devices are not considered during color + correction. No input color correction is applied; only the stage + output is mapped to the main monitor's ICC color profile. + + In general, the benefits of activating color management include + predictable and consistent color, better conversion, accurate proofing + and more efficient cross-media output. Be aware, though, that color + management does not provide perfect conversions due to devices having + a different gamut from each other or original images. Nor does color + management eliminate the need for custom or edited profiles. Color + profiles are dependent on browsers, operating systems (OS), OS + extensions, output devices, and application support. + + Applying color correction degrades the Flash runtime performance. A + Flash runtime's color correction is document style color correction + because all SWF movies are considered documents with implicit sRGB + profiles. Use the `Stage.colorCorrectionSupport` property to tell the + Flash runtime to correct colors when displaying the SWF file + (document) to the display color space. Flash runtimes only compensates + for differences between monitors, not for differences between input + devices (camera/scanner/etc.). + + The three possible values are strings with corresponding constants in + the openfl.display.ColorCorrection class: + + * `"default"`: Use the same color correction as the host system. + * `"on"`: Always perform color correction. + * `"off"`: Never perform color correction. + **/ + // @:noCompletion @:dox(hide) @:require(flash10) public var colorCorrection:openfl.display.ColorCorrection; + #end + #if false + /** + Specifies whether the Flash runtime is running on an operating system + that supports color correction and whether the color profile of the + main (primary) monitor can be read and understood by the Flash + runtime. This property also returns the default state of color + correction on the host system (usually the browser). Currently the + return values can be: + The three possible values are strings with corresponding constants in + the openfl.display.ColorCorrectionSupport class: + + * `"unsupported"`: Color correction is not available. + * `"defaultOn"`: Always performs color correction. + * `"defaultOff"`: Never performs color correction. + **/ + // @:noCompletion @:dox(hide) @:require(flash10) public var colorCorrectionSupport (default, null):openfl.display.ColorCorrectionSupport; + #end + + /** + Specifies the effective pixel scaling factor of the stage. This + value is 1 on standard screens and HiDPI (Retina display) + screens. When the stage is rendered on HiDPI screens the pixel + resolution is doubled; even if the stage scaling mode is set to + `StageScaleMode.NO_SCALE`. `Stage.stageWidth` and `Stage.stageHeight` + continue to be reported in classic pixel units. + **/ + public var contentsScaleFactor(get, never):Float; + + /** + **BETA** + + The current Context3D the default display renderer. + + This property is supported only when using hardware rendering. + **/ + public var context3D(default, null):Context3D; + + // @:noCompletion @:dox(hide) @:require(flash11) public var displayContextInfo (default, null):String; + + /** + A value from the StageDisplayState class that specifies which display + state to use. The following are valid values: + + * `StageDisplayState.FULL_SCREEN` Sets the OpenFL application to expand + the stage over the user's entire screen, with keyboard input disabled. + * `StageDisplayState.FULL_SCREEN_INTERACTIVE` Sets the OpenFL + application to expand the stage over the user's entire screen, with + keyboard input allowed. (Not available for content running in Adobe + Flash Player.) + * `StageDisplayState.NORMAL` Sets the OpenFL application back to + the standard stage display mode. + + + The scaling behavior of the movie in full-screen mode is determined by + the `scaleMode` setting(set using the + `Stage.scaleMode` property or the SWF file's `embed` + tag settings in the HTML file). If the `scaleMode` property is + set to `noScale` while the application transitions to + full-screen mode, the Stage `width` and `height` + properties are updated, and the Stage dispatches a `resize` + event. If any other scale mode is set, the stage and its contents are + scaled to fill the new screen dimensions. The Stage object retains its + original `width` and `height` values and does not + dispatch a `resize` event. + + The following restrictions apply to SWF files that play within an HTML + page(not those using the stand-alone Flash Player or not running in the + AIR runtime): + + + * To enable full-screen mode, add the `allowFullScreen` + parameter to the `object` and `embed` tags in the + HTML page that includes the SWF file, with `allowFullScreen` + set to `"true"`, as shown in the following example: + * Full-screen mode is initiated in response to a mouse click or key + press by the user; the movie cannot change `Stage.displayState` + without user input. Flash runtimes restrict keyboard input in full-screen + mode. Acceptable keys include keyboard shortcuts that terminate + full-screen mode and non-printing keys such as arrows, space, Shift, and + Tab keys. Keyboard shortcuts that terminate full-screen mode are: Escape + (Windows, Linux, and Mac), Control+W(Windows), Command+W(Mac), and + Alt+F4. + + A Flash runtime dialog box appears over the movie when users enter + full-screen mode to inform the users they are in full-screen mode and that + they can press the Escape key to end full-screen mode. + + * Starting with Flash Player 9.0.115.0, full-screen works the same in + windowless mode as it does in window mode. If you set the Window Mode + (`wmode` in the HTML) to Opaque Windowless + (`opaque`) or Transparent Windowless + (`transparent`), full-screen can be initiated, but the + full-screen window will always be opaque. + + These restrictions are _not_ present for SWF content running in + the stand-alone Flash Player or in AIR. AIR supports an interactive + full-screen mode which allows keyboard input. + + For AIR content running in full-screen mode, the system screen saver + and power saving options are disabled while video content is playing and + until either the video stops or full-screen mode is exited. + + On Linux, setting `displayState` to + `StageDisplayState.FULL_SCREEN` or + `StageDisplayState.FULL_SCREEN_INTERACTIVE` is an asynchronous + operation. + + @throws SecurityError Calling the `displayState` property of a + Stage object throws an exception for any caller that + is not in the same security sandbox as the Stage + owner(the main SWF file). To avoid this, the Stage + owner can grant permission to the domain of the + caller by calling the + `Security.allowDomain()` method or the + `Security.allowInsecureDomain()` method. + For more information, see the "Security" chapter in + the _OpenFL Developer's Guide_. + Trying to set the `displayState` property + while the settings dialog is displayed, without a + user response, or if the `param` or + `embed` HTML tag's + `allowFullScreen` attribute is not set to + `true` throws a security error. + **/ + public var displayState(get, set):StageDisplayState; + + #if commonjs + /** + The parent HTML element where this Stage is embedded. + **/ + public var element:Element; + #end + + /** + The interactive object with keyboard focus; or `null` if focus + is not set or if the focused object belongs to a security sandbox to which + the calling object does not have access. + + @throws Error Throws an error if focus cannot be set to the target. + **/ + public var focus(get, set):InteractiveObject; + + /** + Gets and sets the frame rate of the stage. The frame rate is defined as + frames per second. By default the rate is set to the frame rate of the + first SWF file loaded. Valid range for the frame rate is from 0.01 to 1000 + frames per second. + + **Note:** An application might not be able to follow high frame rate + settings, either because the target platform is not fast enough or the + player is synchronized to the vertical blank timing of the display device + (usually 60 Hz on LCD devices). In some cases, a target platform might + also choose to lower the maximum frame rate if it anticipates high CPU + usage. + + For content running in Adobe AIR, setting the `frameRate` + property of one Stage object changes the frame rate for all Stage objects + (used by different NativeWindow objects). + + @throws SecurityError Calling the `frameRate` property of a + Stage object throws an exception for any caller that + is not in the same security sandbox as the Stage + owner(the main SWF file). To avoid this, the Stage + owner can grant permission to the domain of the + caller by calling the + `Security.allowDomain()` method or the + `Security.allowInsecureDomain()` method. + For more information, see the "Security" chapter in + the _OpenFL Developer's Guide_. + **/ + public var frameRate(get, set):Float; + + /** + Returns the height of the monitor that will be used when going to full + screen size, if that state is entered immediately. If the user has + multiple monitors, the monitor that's used is the monitor that most of + the stage is on at the time. + **Note**: If the user has the opportunity to move the browser from one + monitor to another between retrieving the value and going to full + screen size, the value could be incorrect. If you retrieve the value + in an event handler that sets `Stage.displayState` to + `StageDisplayState.FULL_SCREEN`, the value will be correct. + + This is the pixel height of the monitor and is the same as the stage + height would be if `Stage.align` is set to `StageAlign.TOP_LEFT` and + `Stage.scaleMode` is set to `StageScaleMode.NO_SCALE`. + **/ + public var fullScreenHeight(get, never):UInt; + + /** + Sets OpenFL to scale a specific region of the stage to + full-screen mode. If available, the OpenFL scales in hardware, + which uses the graphics and video card on a user's computer, and + generally displays content more quickly than software scaling. + When this property is set to a valid rectangle and the `displayState` + property is set to full-screen mode, OpenFL scales the + specified area. The actual Stage size in pixels within Haxe + does not change. OpenFL enforces a minimum limit for the + size of the rectangle to accommodate the standard "Press Esc to exit + full-screen mode" message. This limit is usually around 260 by 30 + pixels but can vary on platform and OpenFL version. + + This property can only be set when the OpenFL is not in + full-screen mode. To use this property correctly, set this property + first, then set the `displayState` property to full-screen mode, as + shown in the code examples. + + To enable scaling, set the `fullScreenSourceRect` property to a + rectangle object: + + ```haxe + // valid, will enable hardware scaling + stage.fullScreenSourceRect = new Rectangle(0,0,320,240); + ``` + + To disable scaling, set `fullScreenSourceRect=null`. + + ```haxe + stage.fullScreenSourceRect = null; + ``` + + The end user also can select within Flash Player Display Settings to + turn off hardware scaling, which is enabled by default. For more + information, see www.adobe.com/go/display_settings. + **/ + public var fullScreenSourceRect(get, set):Rectangle; + + /** + Returns the width of the monitor that will be used when going to full + screen size, if that state is entered immediately. If the user has + multiple monitors, the monitor that's used is the monitor that most of + the stage is on at the time. + **Note**: If the user has the opportunity to move the browser from one + monitor to another between retrieving the value and going to full + screen size, the value could be incorrect. If you retrieve the value + in an event handler that sets `Stage.displayState` to + `StageDisplayState.FULL_SCREEN`, the value will be correct. + + This is the pixel width of the monitor and is the same as the stage + width would be if `Stage.align` is set to `StageAlign.TOP_LEFT` and + `Stage.scaleMode` is set to `StageScaleMode.NO_SCALE`. + **/ + public var fullScreenWidth(get, never):UInt; + + // @:noCompletion @:dox(hide) @:require(flash11_2) public var mouseLock:Bool; + + /** + A value from the StageQuality class that specifies which rendering quality + is used. The following are valid values: + + * `StageQuality.LOW` - Low rendering quality. Graphics are + not anti-aliased, and bitmaps are not smoothed, but runtimes still use + mip-mapping. + * `StageQuality.MEDIUM` - Medium rendering quality. + Graphics are anti-aliased using a 2 x 2 pixel grid, bitmap smoothing is + dependent on the `Bitmap.smoothing` setting. Runtimes use + mip-mapping. This setting is suitable for movies that do not contain + text. + * `StageQuality.HIGH` - High rendering quality. Graphics + are anti-aliased using a 4 x 4 pixel grid, and bitmap smoothing is + dependent on the `Bitmap.smoothing` setting. Runtimes use + mip-mapping. This is the default rendering quality setting that Flash + Player uses. + * `StageQuality.BEST` - Very high rendering quality. + Graphics are anti-aliased using a 4 x 4 pixel grid. If + `Bitmap.smoothing` is `true` the runtime uses a high + quality downscale algorithm that produces fewer artifacts(however, using + `StageQuality.BEST` with `Bitmap.smoothing` set to + `true` slows performance significantly and is not a recommended + setting). + + + Higher quality settings produce better rendering of scaled bitmaps. + However, higher quality settings are computationally more expensive. In + particular, when rendering scaled video, using higher quality settings can + reduce the frame rate. + + In the desktop profile of Adobe AIR, `quality` can be set to + `StageQuality.BEST` or `StageQuality.HIGH`(and the + default value is `StageQuality.HIGH`). Attempting to set it to + another value has no effect (and the property remains unchanged). In the + moble profile of AIR, all four quality settings are available. The default + value on mobile devices is `StageQuality.MEDIUM`. + + For content running in Adobe AIR, setting the `quality` + property of one Stage object changes the rendering quality for all Stage + objects(used by different NativeWindow objects). + **_Note:_** The operating system draws the device fonts, which are + therefore unaffected by the `quality` property. + + @throws SecurityError Calling the `quality` property of a Stage + object throws an exception for any caller that is + not in the same security sandbox as the Stage owner + (the main SWF file). To avoid this, the Stage owner + can grant permission to the domain of the caller by + calling the `Security.allowDomain()` + method or the + `Security.allowInsecureDomain()` method. + For more information, see the "Security" chapter in + the _OpenFL Developer's Guide_. + **/ + public var quality(get, set):StageQuality; + + /** + A value from the StageScaleMode class that specifies which scale mode to + use. The following are valid values: + + * `StageScaleMode.EXACT_FIT` - The entire application is + visible in the specified area without trying to preserve the original + aspect ratio. Distortion can occur, and the application may appear + stretched or compressed. + * `StageScaleMode.SHOW_ALL` - The entire application is + visible in the specified area without distortion while maintaining the + original aspect ratio of the application. Borders can appear on two sides + of the application. + * `StageScaleMode.NO_BORDER` - The entire application fills + the specified area, without distortion but possibly with some cropping, + while maintaining the original aspect ratio of the application. + * `StageScaleMode.NO_SCALE` - The entire application is + fixed, so that it remains unchanged even as the size of the player window + changes. Cropping might occur if the player window is smaller than the + content. + + + @throws SecurityError Calling the `scaleMode` property of a + Stage object throws an exception for any caller that + is not in the same security sandbox as the Stage + owner(the main SWF file). To avoid this, the Stage + owner can grant permission to the domain of the + caller by calling the + `Security.allowDomain()` method or the + `Security.allowInsecureDomain()` method. + For more information, see the "Security" chapter in + the _OpenFL Developer's Guide_. + **/ + public var scaleMode(get, set):StageScaleMode; + + /** + Specifies whether to show or hide the default items in the Flash + runtime context menu. + If the `showDefaultContextMenu` property is set to `true` (the + default), all context menu items appear. If the + `showDefaultContextMenu` property is set to `false`, only the Settings + and About... menu items appear. + + @throws SecurityError Calling the `showDefaultContextMenu` property of + a Stage object throws an exception for any + caller that is not in the same security sandbox + as the Stage owner (the main SWF file). To avoid + this, the Stage owner can grant permission to + the domain of the caller by calling the + `Security.allowDomain()` method or the + `Security.allowInsecureDomain()` method. For + more information, see the "Security" chapter in + the _OpenFL Developer's Guide_. + **/ + public var showDefaultContextMenu:Bool; + + /** + The area of the stage that is currently covered by the software + keyboard. + The area has a size of zero (0,0,0,0) when the soft keyboard is not + visible. + + When the keyboard opens, the `softKeyboardRect` is set at the time the + softKeyboardActivate event is dispatched. If the keyboard changes size + while open, the runtime updates the `softKeyboardRect` property and + dispatches an additional softKeyboardActivate event. + + **Note:** On Android, the area covered by the keyboard is estimated + when the operating system does not provide the information necessary + to determine the exact area. This problem occurs in fullscreen mode + and also when the keyboard opens in response to an InteractiveObject + receiving focus or invoking the `requestSoftKeyboard()` method. + **/ + public var softKeyboardRect:Rectangle; + + /** + A list of Stage3D objects available for displaying 3-dimensional content. + + You can use only a limited number of Stage3D objects at a time. The number of + available Stage3D objects depends on the platform and on the available hardware. + + A Stage3D object draws in front of a StageVideo object and behind the OpenFL + display list. + **/ + public var stage3Ds(default, null):Vector; + + /** + Specifies whether or not objects display a glowing border when they have + focus. + + @throws SecurityError Calling the `stageFocusRect` property of + a Stage object throws an exception for any caller + that is not in the same security sandbox as the + Stage owner (the main SWF file). To avoid this, the + Stage owner can grant permission to the domain of + the caller by calling the + `Security.allowDomain()` method or the + `Security.allowInsecureDomain()` method. + For more information, see the "Security" chapter in + the _OpenFL Developer's Guide_. + **/ + public var stageFocusRect:Bool; + + /** + The current height, in pixels, of the Stage. + + If the value of the `Stage.scaleMode` property is set to + `StageScaleMode.NO_SCALE` when the user resizes the window, the + Stage content maintains its size while the `stageHeight` + property changes to reflect the new height size of the screen area + occupied by the SWF file.(In the other scale modes, the + `stageHeight` property always reflects the original height of + the SWF file.) You can add an event listener for the `resize` + event and then use the `stageHeight` property of the Stage + class to determine the actual pixel dimension of the resized Flash runtime + window. The event listener allows you to control how the screen content + adjusts when the user resizes the window. + + Air for TV devices have slightly different behavior than desktop + devices when you set the `stageHeight` property. If the + `Stage.scaleMode` property is set to + `StageScaleMode.NO_SCALE` and you set the + `stageHeight` property, the stage height does not change until + the next frame of the SWF. + + **Note:** In an HTML page hosting the SWF file, both the + `object` and `embed` tags' `height` + attributes must be set to a percentage (such as `100%`), not + pixels. If the settings are generated by JavaScript code, the + `height` parameter of the `AC_FL_RunContent() ` + method must be set to a percentage, too. This percentage is applied to the + `stageHeight` value. + + @throws SecurityError Calling the `stageHeight` property of a + Stage object throws an exception for any caller that + is not in the same security sandbox as the Stage + owner(the main SWF file). To avoid this, the Stage + owner can grant permission to the domain of the + caller by calling the + `Security.allowDomain()` method or the + `Security.allowInsecureDomain()` method. + For more information, see the "Security" chapter in + the _OpenFL Developer's Guide_. + **/ + public var stageHeight(default, null):Int; + + #if false + /** + A list of StageVideo objects available for playing external videos. + You can use only a limited number of StageVideo objects at a time. + When a SWF begins to run, the number of available StageVideo objects + depends on the platform and on available hardware. + + To use a StageVideo object, assign a member of the `stageVideos` + Vector object to a StageVideo variable. + + All StageVideo objects are displayed on the stage behind any display + objects. The StageVideo objects are displayed on the stage in the + order they appear in the `stageVideos` Vector object. For example, if + the `stageVideos` Vector object contains three entries: + + 1. The StageVideo object in the 0 index of the `stageVideos` Vector + object is displayed behind all StageVideo objects. + 2. The StageVideo object at index 1 is displayed in front of the + StageVideo object at index 0. + 3. The StageVideo object at index 2 is displayed in front of the + StageVideo object at index 1. + + Use the `StageVideo.depth` property to change this ordering. + + **Note:** AIR for TV devices support only one StageVideo object. + **/ + // @:noCompletion @:dox(hide) @:require(flash10_2) public var stageVideos (default, null):Vector; + #end + + /** + Specifies the current width, in pixels, of the Stage. + + If the value of the `Stage.scaleMode` property is set to + `StageScaleMode.NO_SCALE` when the user resizes the window, the + Stage content maintains its defined size while the `stageWidth` + property changes to reflect the new width size of the screen area occupied + by the SWF file.(In the other scale modes, the `stageWidth` + property always reflects the original width of the SWF file.) You can add + an event listener for the `resize` event and then use the + `stageWidth` property of the Stage class to determine the + actual pixel dimension of the resized Flash runtime window. The event + listener allows you to control how the screen content adjusts when the + user resizes the window. + + Air for TV devices have slightly different behavior than desktop + devices when you set the `stageWidth` property. If the + `Stage.scaleMode` property is set to + `StageScaleMode.NO_SCALE` and you set the + `stageWidth` property, the stage width does not change until + the next frame of the SWF. + + **Note:** In an HTML page hosting the SWF file, both the + `object` and `embed` tags' `width` + attributes must be set to a percentage (such as `100%`), not + pixels. If the settings are generated by JavaScript code, the + `width` parameter of the `AC_FL_RunContent() ` + method must be set to a percentage, too. This percentage is applied to the + `stageWidth` value. + + @throws SecurityError Calling the `stageWidth` property of a + Stage object throws an exception for any caller that + is not in the same security sandbox as the Stage + owner (the main SWF file). To avoid this, the Stage + owner can grant permission to the domain of the + caller by calling the + `Security.allowDomain()` method or the + `Security.allowInsecureDomain()` method. + For more information, see the "Security" chapter in + the _OpenFL Developer's Guide_. + **/ + public var stageWidth(default, null):Int; + + /** + The associated Lime Window instance for this Stage. + **/ + public var window(default, null):Window; + + #if (sys && (!flash_doc_gen || air_doc_gen)) + /** + + **/ + public var nativeWindow(default, null):openfl.display.NativeWindow; + #end + + /** + Indicates whether GPU compositing is available and in use. The + `wmodeGPU` value is `true` _only_ when all three of the following + conditions exist: + * GPU compositing has been requested. + * GPU compositing is available. + * GPU compositing is in use. + + Specifically, the `wmodeGPU` property indicates one of the following: + + 1. GPU compositing has not been requested or is unavailable. In this + case, the `wmodeGPU` property value is `false`. + 2. GPU compositing has been requested (if applicable and available), + but the environment is operating in "fallback mode" (not optimal + rendering) due to limitations of the content. In this case, the + `wmodeGPU` property value is `true`. + 3. GPU compositing has been requested (if applicable and available), + and the environment is operating in the best mode. In this case, the + `wmodeGPU` property value is also `true`. + + In other words, the `wmodeGPU` property identifies the capability and + state of the rendering environment. For runtimes that do not support + GPU compositing, such as AIR 1.5.2, the value is always `false`, + because (as stated above) the value is `true` only when GPU + compositing has been requested, is available, and is in use. + + The `wmodeGPU` property is useful to determine, at runtime, whether or + not GPU compositing is in use. The value of `wmodeGPU` indicates if + your content is going to be scaled by hardware, or not, so you can + present graphics at the correct size. You can also determine if you're + rendering in a fast path or not, so that you can adjust your content + complexity accordingly. + + For Flash Player in a browser, GPU compositing can be requested by the + value of `gpu` for the `wmode` HTML parameter in the page hosting the + SWF file. For other configurations, GPU compositing can be requested + in the header of a SWF file (set using SWF authoring tools). + + However, the `wmodeGPU` property does not identify the current + rendering performance. Even if GPU compositing is "in use" the + rendering process might not be operating in the best mode. To adjust + your content for optimal rendering, use a Flash runtime debugger + version, and set the `DisplayGPUBlendsetting` in your mm.cfg file. + + **Note:** This property is always `false` when referenced from + Haxe code that runs before the runtime performs its first rendering + pass. For example, if you examine `wmodeGPU` from a script in Frame 1 + of Adobe Flash Professional, and your SWF file is the first SWF file + loaded in a new instance of the runtime, then the `wmodeGPU` value is + `false`. To get an accurate value, wait until at least one rendering + pass has occurred. If you write an event listener for the `exitFrame` + event of any `DisplayObject`, the `wmodeGPU` value at is the correct + value. + **/ + #if false + // @:noCompletion @:dox(hide) @:require(flash10_1) public var wmodeGPU (default, null):Bool; + #end + @:noCompletion private var __cacheFocus:InteractiveObject; + @:noCompletion private var __clearBeforeRender:Bool; + @:noCompletion private var __color:Int; + @:noCompletion private var __colorSplit:Array; + @:noCompletion private var __colorString:String; + @:noCompletion private var __contentsScaleFactor:Float; + @:noCompletion private var __currentTabOrderIndex:Int; + #if (commonjs && !nodejs) + @:noCompletion private var __cursor:LimeMouseCursor; + #end + @:noCompletion private var __deltaTime:Int; + @:noCompletion private var __dirty:Bool; + @:noCompletion private var __displayMatrix:Matrix; + @:noCompletion private var __displayRect:Rectangle; + @:noCompletion private var __displayState:StageDisplayState; + @:noCompletion private var __dragBounds:Rectangle; + @:noCompletion private var __dragObject:Sprite; + @:noCompletion private var __dragOffsetX:Float; + @:noCompletion private var __dragOffsetY:Float; + @:noCompletion private var __focus:InteractiveObject; + @:noCompletion private var __forceRender:Bool; + @:noCompletion private var __fullscreen:Bool; + @:noCompletion private var __fullScreenSourceRect:Rectangle; + @:noCompletion private var __invalidated:Bool; + @:noCompletion private var __lastClickTime:Int; + @:noCompletion private var __lastClickTarget:InteractiveObject; + @:noCompletion private var __logicalWidth:Int; + @:noCompletion private var __logicalHeight:Int; + @:noCompletion private var __macKeyboard:Bool; + @:noCompletion private var __mouseDownLeft:InteractiveObject; + @:noCompletion private var __mouseDownMiddle:InteractiveObject; + @:noCompletion private var __mouseDownRight:InteractiveObject; + @:noCompletion private var __mouseOutStack:Array; + @:noCompletion private var __mouseOverTarget:InteractiveObject; + @:noCompletion private var __mouseX:Float; + @:noCompletion private var __mouseY:Float; + @:noCompletion private var __pendingMouseEvent:Bool; + @:noCompletion private var __pendingMouseX:Int; + @:noCompletion private var __pendingMouseY:Int; + @:noCompletion private var __quality:StageQuality; + @:noCompletion private var __renderer:DisplayObjectRenderer; + @:noCompletion private var __rendering:Bool; + @:noCompletion private var __rollOutStack:Array; + @:noCompletion private var __scaleMode:StageScaleMode; + @:noCompletion private var __stack:Array; + @:noCompletion private var __touchData:Map; + @:noCompletion private var __transparent:Bool; + @:noCompletion private var __uncaughtErrorEvents:UncaughtErrorEvents; + @:noCompletion private var __wasDirty:Bool; + @:noCompletion private var __wasFullscreen:Bool; + #if lime + @:noCompletion private var __primaryTouch:Touch; + #end + + #if openfljs + @:noCompletion private static function __init__() + { + untyped Object.defineProperties(Stage.prototype, { + "color": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_color (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_color (v); }") + }, + "contentsScaleFactor": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_contentsScaleFactor (); }") + }, + "displayState": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_displayState (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_displayState (v); }") + }, + "focus": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_focus (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_focus (v); }") + }, + "frameRate": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_frameRate (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_frameRate (v); }") + }, + "fullScreenHeight": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_fullScreenHeight (); }") + }, + "fullScreenWidth": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_fullScreenWidth (); }") + }, + "quality": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_quality (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_quality (v); }") + }, + "scaleMode": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_scaleMode (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_scaleMode (v); }") + }, + }); + } + #end + + public function new(#if commonjs width:Dynamic = 0, height:Dynamic = 0, color:Null = null, documentClass:Class = null, + windowAttributes:Dynamic = null #else window:Window, color:Null = null #end) + { + #if hxtelemetry + Telemetry.__initialize(); + #end + + super(); + + __drawableType = STAGE; + this.name = null; + + __color = 0xFFFFFFFF; + __colorSplit = [0xFF, 0xFF, 0xFF]; + __colorString = "#FFFFFF"; + __contentsScaleFactor = 1; + __currentTabOrderIndex = 0; + __deltaTime = 0; + __displayState = NORMAL; + __mouseX = 0; + __mouseY = 0; + __lastClickTime = 0; + __logicalWidth = 0; + __logicalHeight = 0; + __displayMatrix = new Matrix(); + __displayRect = new Rectangle(); + __renderDirty = true; + + stage3Ds = new Vector(); + for (i in 0...#if mobile 2 #else 4 #end) + { + stage3Ds.push(new Stage3D(this)); + } + + this.stage = this; + + align = StageAlign.TOP_LEFT; + allowsFullScreen = true; + allowsFullScreenInteractive = true; + __quality = StageQuality.HIGH; + __scaleMode = StageScaleMode.NO_SCALE; + showDefaultContextMenu = true; + softKeyboardRect = new Rectangle(); + stageFocusRect = true; + + #if mac + __macKeyboard = true; + #elseif (js && html5) + __macKeyboard = untyped #if haxe4 js.Syntax.code #else __js__ #end ("/AppleWebKit/.test (navigator.userAgent) && /Mobile\\/\\w+/.test (navigator.userAgent) || /Mac/.test (navigator.platform)"); + #end + + __clearBeforeRender = true; + __forceRender = false; + __stack = []; + __rollOutStack = []; + __mouseOutStack = []; + __touchData = new Map(); + + if (Lib.current.__loaderInfo == null) + { + Lib.current.__loaderInfo = LoaderInfo.create(null); + Lib.current.__loaderInfo.content = Lib.current; + } + + // TODO: Do not rely on Lib.current + __uncaughtErrorEvents = Lib.current.__loaderInfo.uncaughtErrorEvents; + + #if commonjs + if (windowAttributes == null) windowAttributes = {}; + var app:OpenFLApplication = null; + + if (!Math.isNaN(width)) + { + var resizable = (width == 0 && width == 0); + + #if (js && html5) + if (windowAttributes.element != null) + { + element = windowAttributes.element; + } + else + { + element = Browser.document.createElement("div"); + } + + if (resizable) + { + element.style.width = "100%"; + element.style.height = "100%"; + } + #else + element = null; + #end + + windowAttributes.width = width; + windowAttributes.height = height; + windowAttributes.element = element; + windowAttributes.resizable = resizable; + + windowAttributes.stage = this; + + if (!Reflect.hasField(windowAttributes, "context")) windowAttributes.context = {}; + var contextAttributes = windowAttributes.context; + if (Reflect.hasField(windowAttributes, "renderer")) + { + var type = Std.string(windowAttributes.renderer); + if (type == "webgl1") + { + contextAttributes.type = RenderContextType.WEBGL; + contextAttributes.version = "1"; + } + else if (type == "webgl2") + { + contextAttributes.type = RenderContextType.WEBGL; + contextAttributes.version = "2"; + } + else + { + Reflect.setField(contextAttributes, "type", windowAttributes.renderer); + } + } + if (!Reflect.hasField(contextAttributes, "stencil")) contextAttributes.stencil = true; + if (!Reflect.hasField(contextAttributes, "depth")) contextAttributes.depth = true; + if (!Reflect.hasField(contextAttributes, "background")) contextAttributes.background = null; + + app = new OpenFLApplication(); + window = app.createWindow(windowAttributes); + + this.color = color; + } + else + { + this.window = cast width; + this.color = height; + } + #else + this.application = window.application; + this.window = window; + this.color = color; + #end + + __contentsScaleFactor = window.scale; + __wasFullscreen = window.fullscreen; + + __resize(); + + if (Lib.current.stage == null) + { + stage.addChild(Lib.current); + } + + #if commonjs + if (documentClass != null) + { + DisplayObject.__initStage = this; + var sprite:Sprite = cast Type.createInstance(documentClass, []); + // addChild (sprite); // done by init stage + sprite.dispatchEvent(new Event(Event.ADDED_TO_STAGE, false, false)); + } + + if (app != null) + { + app.addModule(this); + app.exec(); + } + #end + } + + /** + Calling the `invalidate()` method signals Flash runtimes to + alert display objects on the next opportunity it has to render the display + list(for example, when the playhead advances to a new frame). After you + call the `invalidate()` method, when the display list is next + rendered, the Flash runtime sends a `render` event to each + display object that has registered to listen for the `render` + event. You must call the `invalidate()` method each time you + want the Flash runtime to send `render` events. + + The `render` event gives you an opportunity to make changes + to the display list immediately before it is actually rendered. This lets + you defer updates to the display list until the latest opportunity. This + can increase performance by eliminating unnecessary screen updates. + + The `render` event is dispatched only to display objects + that are in the same security domain as the code that calls the + `stage.invalidate()` method, or to display objects from a + security domain that has been granted permission via the + `Security.allowDomain()` method. + + **/ + public override function invalidate():Void + { + __invalidated = true; + + // TODO: Should this not mark as dirty? + __renderDirty = true; + } + + // @:noCompletion @:dox(hide) public function isFocusInaccessible ():Bool; + public override function localToGlobal(pos:Point):Point + { + return pos.clone(); + } + + @SuppressWarnings("checkstyle:Dynamic") + @:noCompletion private function __broadcastEvent(event:Event):Void + { + if (DisplayObject.__broadcastEvents.exists(event.type)) + { + var dispatchers = DisplayObject.__broadcastEvents.get(event.type); + + for (dispatcher in dispatchers) + { + // TODO: Way to resolve dispatching occurring if object not on stage + // and there are multiple stage objects running in HTML5? + + if (dispatcher.stage == this || dispatcher.stage == null) + { + if (__uncaughtErrorEvents.__enabled) + { + try + { + dispatcher.__dispatch(event); + } + catch (e:Dynamic) + { + __handleError(e); + } + } + else + { + dispatcher.__dispatch(event); + } + } + } + } + } + + @:noCompletion private function __createRenderer():Void + { + #if lime + var windowWidth = Std.int(window.width * window.scale); + var windowHeight = Std.int(window.height * window.scale); + + switch (window.context.type) + { + case OPENGL, OPENGLES, WEBGL: + #if (!disable_cffi && (!html5 || !canvas)) + context3D = new Context3D(this); + #if openfl_dpi_aware + context3D.configureBackBuffer(windowWidth, windowHeight, 0, true, true, true); + #else + context3D.configureBackBuffer(stageWidth, stageHeight, 0, true, true, true); + #end + context3D.present(); + __renderer = new OpenGLRenderer(context3D); + #end + + case CANVAS: + #if (js && html5) + __renderer = new CanvasRenderer(window.context.canvas2D); + #end + + case DOM: + #if (js && html5) + __renderer = new DOMRenderer(window.context.dom); + #end + + case CAIRO: + #if lime_cairo + __renderer = new CairoRenderer(window.context.cairo); + #end + + default: + } + + if (__renderer != null) + { + __renderer.__allowSmoothing = (quality != LOW); + __renderer.__pixelRatio = #if openfl_disable_hdpi 1 #else window.scale #end; + __renderer.__worldTransform = __displayMatrix; + __renderer.__stage = this; + + #if (js && html5 && dom && !openfl_disable_hdpi) + __renderer.__pixelRatio = Browser.window.devicePixelRatio; + #end + + __renderer.__resize(windowWidth, windowHeight); + } + #end + } + + @SuppressWarnings(["checkstyle:Dynamic", "checkstyle:LeftCurly"]) + @:noCompletion private override function __dispatchEvent(event:Event):Bool + { + var result:Bool; + if (__uncaughtErrorEvents.__enabled) + { + try + { + result = super.__dispatchEvent(event); + } + catch (e:Dynamic) + { + __handleError(e); + result = false; + } + } + else + { + result = super.__dispatchEvent(event); + } + return result; + } + + @:noCompletion private function __dispatchPendingMouseEvent():Void + { + if (__pendingMouseEvent) + { + __onMouse(MouseEvent.MOUSE_MOVE, __pendingMouseX, __pendingMouseY, 0); + __pendingMouseEvent = false; + } + } + + @SuppressWarnings(["checkstyle:Dynamic", "checkstyle:LeftCurly"]) + @:noCompletion private function __dispatchStack(event:Event, stack:Array):Void + { + // TODO: Prevent repetition + if (__uncaughtErrorEvents.__enabled) + { + try + { + var target:DisplayObject; + var length = stack.length; + + if (length == 0) + { + event.eventPhase = EventPhase.AT_TARGET; + target = cast event.target; + target.__dispatch(event); + } + else + { + event.eventPhase = EventPhase.CAPTURING_PHASE; + event.target = stack[stack.length - 1]; + + for (i in 0...length - 1) + { + stack[i].__dispatch(event); + + if (event.__isCanceled) + { + return; + } + } + + event.eventPhase = EventPhase.AT_TARGET; + target = cast event.target; + target.__dispatch(event); + + if (event.__isCanceled) + { + return; + } + + if (event.bubbles) + { + event.eventPhase = EventPhase.BUBBLING_PHASE; + var i = length - 2; + + while (i >= 0) + { + stack[i].__dispatch(event); + + if (event.__isCanceled) + { + return; + } + + i--; + } + } + } + } + catch (e:Dynamic) + { + __handleError(e); + } + } + else + { + var target:DisplayObject; + var length = stack.length; + + if (length == 0) + { + event.eventPhase = EventPhase.AT_TARGET; + target = cast event.target; + target.__dispatch(event); + } + else + { + event.eventPhase = EventPhase.CAPTURING_PHASE; + event.target = stack[stack.length - 1]; + + for (i in 0...length - 1) + { + stack[i].__dispatch(event); + + if (event.__isCanceled) + { + return; + } + } + + event.eventPhase = EventPhase.AT_TARGET; + target = cast event.target; + target.__dispatch(event); + + if (event.__isCanceled) + { + return; + } + + if (event.bubbles) + { + event.eventPhase = EventPhase.BUBBLING_PHASE; + var i = length - 2; + + while (i >= 0) + { + stack[i].__dispatch(event); + + if (event.__isCanceled) + { + return; + } + + i--; + } + } + } + } + } + + @SuppressWarnings("checkstyle:Dynamic") + @:noCompletion private function __dispatchTarget(target:EventDispatcher, event:Event):Bool + { + if (__uncaughtErrorEvents.__enabled) + { + try + { + return target.__dispatchEvent(event); + } + catch (e:Dynamic) + { + __handleError(e); + return false; + } + } + else + { + return target.__dispatchEvent(event); + } + } + + @:noCompletion private function __drag(mouse:Point):Void + { + var parent = __dragObject.parent; + if (parent != null) + { + parent.__getWorldTransform().__transformInversePoint(mouse); + } + + var x = mouse.x + __dragOffsetX; + var y = mouse.y + __dragOffsetY; + + if (__dragBounds != null) + { + if (x < __dragBounds.x) + { + x = __dragBounds.x; + } + else if (x > __dragBounds.right) + { + x = __dragBounds.right; + } + + if (y < __dragBounds.y) + { + y = __dragBounds.y; + } + else if (y > __dragBounds.bottom) + { + y = __dragBounds.bottom; + } + } + + __dragObject.x = x; + __dragObject.y = y; + } + + @:noCompletion private override function __getInteractive(stack:Array):Bool + { + if (stack != null) + { + stack.push(this); + } + + return true; + } + + @:noCompletion private override function __globalToLocal(global:Point, local:Point):Point + { + if (global != local) + { + local.copyFrom(global); + } + + return local; + } + + @SuppressWarnings("checkstyle:Dynamic") + @:noCompletion private function __handleError(e:Dynamic):Void + { + var event = new UncaughtErrorEvent(UncaughtErrorEvent.UNCAUGHT_ERROR, true, true, e); + + Lib.current.__loaderInfo.uncaughtErrorEvents.dispatchEvent(event); + + if (!event.__preventDefault) + { + // #if mobile + Log.println(CallStack.toString(CallStack.exceptionStack())); + Log.println(Std.string(e)); + // #end + + #if (cpp && !cppia) + untyped __cpp__("throw e"); + #elseif neko + neko.Lib.rethrow(e); + #elseif js + try + { + #if (haxe >= "4.1.0") + var exc = e; + #else + var exc = @:privateAccess haxe.CallStack.lastException; + #end + if (exc != null && Reflect.hasField(exc, "stack") && exc.stack != null && exc.stack != "") + { + untyped #if haxe4 js.Syntax.code #else __js__ #end ("console.log")(exc.stack); + e.stack = exc.stack; + } + else + { + var msg = CallStack.toString(CallStack.callStack()); + untyped #if haxe4 js.Syntax.code #else __js__ #end ("console.log")(msg); + } + } + catch (e2:Dynamic) {} + untyped #if haxe4 js.Syntax.code #else __js__ #end ("throw e"); + #elseif cs + throw e; + // cs.Lib.rethrow (e); + #elseif hl + hl.Api.rethrow(e); + #else + throw e; + #end + } + } + + #if lime + @:noCompletion private function __onKey(type:String, keyCode:KeyCode, modifier:KeyModifier):Void + { + __dispatchPendingMouseEvent(); + + MouseEvent.__altKey = modifier.altKey; + MouseEvent.__commandKey = modifier.metaKey; + MouseEvent.__controlKey = modifier.ctrlKey && !modifier.metaKey; + MouseEvent.__ctrlKey = modifier.ctrlKey; + MouseEvent.__shiftKey = modifier.shiftKey; + + var stack = new Array(); + + if (__focus == null) + { + __getInteractive(stack); + } + else + { + __focus.__getInteractive(stack); + } + + if (stack.length > 0) + { + var keyLocation = Keyboard.__getKeyLocation(keyCode); + var keyCode = Keyboard.__convertKeyCode(keyCode); + var charCode = Keyboard.__getCharCode(keyCode, modifier.shiftKey, modifier.capsLock); + + if (type == KeyboardEvent.KEY_UP && (keyCode == Keyboard.SPACE || keyCode == Keyboard.ENTER) && (__focus is Sprite)) + { + var sprite = cast(__focus, Sprite); + if (sprite.buttonMode && sprite.focusRect == true) + { + var localPoint = Point.__pool.get(); + var targetPoint = Point.__pool.get(); + targetPoint.x = __mouseX; + targetPoint.y = __mouseY; + + #if openfl_pool_events + var clickEvent = MouseEvent.__pool.get(); + clickEvent.type = MouseEvent.CLICK; + clickEvent.stageX = __mouseX; + clickEvent.stageY = __mouseY; + var local = sprite.__globalToLocal(targetPoint, localPoint); + clickEvent.localX = local.x; + clickEvent.localY = local.y; + clickEvent.target = sprite; + #else + var clickEvent = MouseEvent.__create(MouseEvent.CLICK, 0, 0, __mouseX, __mouseY, sprite.__globalToLocal(targetPoint, localPoint), sprite); + #end + + __dispatchStack(clickEvent, stack); + + if (clickEvent.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + #if openfl_pool_events + MouseEvent.__pool.release(clickEvent); + #end + + Point.__pool.release(targetPoint); + Point.__pool.release(localPoint); + } + } + + // Flash Player events are not cancelable, should we make only some events (like APP_CONTROL_BACK) cancelable? + + var event = new KeyboardEvent(type, true, true, charCode, keyCode, keyLocation, + __macKeyboard ? (modifier.ctrlKey || modifier.metaKey) : modifier.ctrlKey, modifier.altKey, modifier.shiftKey, modifier.ctrlKey, + modifier.metaKey); + + stack.reverse(); + __dispatchStack(event, stack); + + if (event.__preventDefault) + { + if (type == KeyboardEvent.KEY_DOWN) + { + window.onKeyDown.cancel(); + } + else + { + window.onKeyUp.cancel(); + } + } + else + { + if (type == KeyboardEvent.KEY_DOWN && keyCode == Keyboard.TAB) + { + var tabStack = new Array(); + + __tabTest(tabStack); + + var nextIndex = -1; + var nextObject:InteractiveObject = null; + var nextOffset = modifier.shiftKey ? -1 : 1; + + if (tabStack.length > 1) + { + ArraySort.sort(tabStack, function(a, b) + { + return a.tabIndex - b.tabIndex; + }); + + if (tabStack[tabStack.length - 1].tabIndex != -1) + { + // if some tabIndices aren't equal to -1, remove all + // of the ones that are + var i = 0; + while (i < tabStack.length) + { + if (tabStack[i].tabIndex > -1) + { + if (i > 0) tabStack.splice(0, i); + break; + } + + i++; + } + } + + if (focus != null) + { + var current = focus; + var index = tabStack.indexOf(current); + while (index == -1 && current != null) + { + // if the current focus is not in the tab stack, + // try to find the nearest object in the display + // list that is in the stack + var currentParent = current.parent; + if (currentParent != null && currentParent.tabChildren) + { + var currentIndex = currentParent.getChildIndex(current); + if (currentIndex == -1) + { + current = currentParent; + continue; + } + var i = currentIndex + nextOffset; + while (modifier.shiftKey ? (i >= 0) : (i < currentParent.numChildren)) + { + var sibling = currentParent.getChildAt(i); + if ((sibling is InteractiveObject)) + { + var interactiveSibling = cast(sibling, InteractiveObject); + index = tabStack.indexOf(interactiveSibling); + if (index != -1) + { + nextOffset = 0; + break; + } + } + i += nextOffset; + } + } + else if (modifier.shiftKey) + { + index = tabStack.indexOf(currentParent); + if (index != -1) nextOffset = 0; + } + current = currentParent; + } + + if (index < 0) nextIndex = 0; + else + nextIndex = index + nextOffset; + } + else + { + nextIndex = __currentTabOrderIndex; + } + } + else if (tabStack.length == 1) + { + nextObject = tabStack[0]; + + if (focus == nextObject) nextObject = null; + } + + var cancelTab = nextIndex >= 0 && nextIndex < tabStack.length; + if (tabStack.length == 1 || tabStack.length == 0 && focus != null) + { + nextIndex = 0; + } + else if (tabStack.length > 1) + { + if (nextIndex < 0) nextIndex += tabStack.length; + + nextIndex %= tabStack.length; + nextObject = tabStack[nextIndex]; + + if (nextObject == focus) + { + nextIndex += nextOffset; + + if (nextIndex < 0) nextIndex += tabStack.length; + + nextIndex %= tabStack.length; + nextObject = tabStack[nextIndex]; + } + } + + var focusEvent:FocusEvent = null; + + if (focus != null) + { + focusEvent = new FocusEvent(FocusEvent.KEY_FOCUS_CHANGE, true, true, nextObject, modifier.shiftKey, 0); + + stack = []; + + focus.__getInteractive(stack); + stack.reverse(); + + __dispatchStack(focusEvent, stack); + + if (focusEvent.isDefaultPrevented()) + { + window.onKeyDown.cancel(); + } + } + + if (focusEvent == null || !focusEvent.isDefaultPrevented()) + { + __currentTabOrderIndex = nextIndex; + if (nextObject != null) focus = nextObject; + if (cancelTab) + { + // ensure that the html5 target does not lose focus + // to the browser every time that tab is pressed + window.onKeyDown.cancel(); + } + + // TODO: handle border around focus + } + } + else if (type == KeyboardEvent.KEY_DOWN + && focus != null + && !#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (focus, TextField)) + { + var ctrlKey = (__macKeyboard ? (modifier.ctrlKey || modifier.metaKey) : modifier.ctrlKey); + if (ctrlKey && !modifier.altKey && !modifier.shiftKey) + { + switch (keyCode) + { + case Keyboard.C: + // flash docs say that bubbles and cancelable + // are false, but they're actually true + var copyEvent = new Event(Event.COPY, true, true); + focus.dispatchEvent(copyEvent); + case Keyboard.X: + var cutEvent = new Event(Event.CUT, true, true); + focus.dispatchEvent(cutEvent); + case Keyboard.V: + var pasteEvent = new Event(Event.PASTE, true, true); + focus.dispatchEvent(pasteEvent); + case Keyboard.A: + var selectAllEvent = new Event(Event.SELECT_ALL, true, true); + focus.dispatchEvent(selectAllEvent); + } + } + } + + // TODO: handle arrow keys changing the focus + } + + if (event.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + } + } + #end + + #if lime + @:noCompletion private function __onLimeCreateWindow(window:Window):Void + { + if (this.window != window) return; + + window.onActivate.add(__onLimeWindowActivate.bind(window)); + window.onClose.add(__onLimeWindowClose.bind(window), false, -9000); + window.onDeactivate.add(__onLimeWindowDeactivate.bind(window)); + window.onDropFile.add(__onLimeWindowDropFile.bind(window)); + window.onEnter.add(__onLimeWindowEnter.bind(window)); + window.onExpose.add(__onLimeWindowExpose.bind(window)); + window.onFocusIn.add(__onLimeWindowFocusIn.bind(window)); + window.onFocusOut.add(__onLimeWindowFocusOut.bind(window)); + window.onFullscreen.add(__onLimeWindowFullscreen.bind(window)); + window.onKeyDown.add(__onLimeKeyDown.bind(window)); + window.onKeyUp.add(__onLimeKeyUp.bind(window)); + window.onLeave.add(__onLimeWindowLeave.bind(window)); + window.onMinimize.add(__onLimeWindowMinimize.bind(window)); + window.onMouseDown.add(__onLimeMouseDown.bind(window)); + window.onMouseMove.add(__onLimeMouseMove.bind(window)); + window.onMouseMoveRelative.add(__onLimeMouseMoveRelative.bind(window)); + window.onMouseUp.add(__onLimeMouseUp.bind(window)); + window.onMouseWheel.add(__onLimeMouseWheel.bind(window)); + window.onMove.add(__onLimeWindowMove.bind(window)); + window.onRender.add(__onLimeRender); + window.onRenderContextLost.add(__onLimeRenderContextLost); + window.onRenderContextRestored.add(__onLimeRenderContextRestored); + window.onResize.add(__onLimeWindowResize.bind(window)); + window.onRestore.add(__onLimeWindowRestore.bind(window)); + window.onTextEdit.add(__onLimeTextEdit.bind(window)); + window.onTextInput.add(__onLimeTextInput.bind(window)); + + __onLimeWindowCreate(window); + } + + @:noCompletion private function __onLimeGamepadAxisMove(gamepad:Gamepad, axis:GamepadAxis, value:Float):Void + { + if (__uncaughtErrorEvents.__enabled) + { + try + { + GameInput.__onGamepadAxisMove(gamepad, axis, value); + } + catch (e:Dynamic) + { + __handleError(e); + } + } + else + { + GameInput.__onGamepadAxisMove(gamepad, axis, value); + } + } + + @:noCompletion private function __onLimeGamepadButtonDown(gamepad:Gamepad, button:GamepadButton):Void + { + if (__uncaughtErrorEvents.__enabled) + { + try + { + GameInput.__onGamepadButtonDown(gamepad, button); + } + catch (e:Dynamic) + { + __handleError(e); + } + } + else + { + GameInput.__onGamepadButtonDown(gamepad, button); + } + } + + @:noCompletion private function __onLimeGamepadButtonUp(gamepad:Gamepad, button:GamepadButton):Void + { + if (__uncaughtErrorEvents.__enabled) + { + try + { + GameInput.__onGamepadButtonUp(gamepad, button); + } + catch (e:Dynamic) + { + __handleError(e); + } + } + else + { + GameInput.__onGamepadButtonUp(gamepad, button); + } + } + + @:noCompletion private function __onLimeGamepadConnect(gamepad:Gamepad):Void + { + if (__uncaughtErrorEvents.__enabled) + { + try + { + GameInput.__onGamepadConnect(gamepad); + } + catch (e:Dynamic) + { + __handleError(e); + } + } + else + { + GameInput.__onGamepadConnect(gamepad); + } + + gamepad.onAxisMove.add(__onLimeGamepadAxisMove.bind(gamepad)); + gamepad.onButtonDown.add(__onLimeGamepadButtonDown.bind(gamepad)); + gamepad.onButtonUp.add(__onLimeGamepadButtonUp.bind(gamepad)); + gamepad.onDisconnect.add(__onLimeGamepadDisconnect.bind(gamepad)); + } + + @:noCompletion private function __onLimeGamepadDisconnect(gamepad:Gamepad):Void + { + if (__uncaughtErrorEvents.__enabled) + { + try + { + GameInput.__onGamepadDisconnect(gamepad); + } + catch (e:Dynamic) + { + __handleError(e); + } + } + else + { + GameInput.__onGamepadDisconnect(gamepad); + } + } + + @:noCompletion private function __onLimeKeyDown(window:Window, keyCode:KeyCode, modifier:KeyModifier):Void + { + if (this.window == null || this.window != window) return; + + __onKey(KeyboardEvent.KEY_DOWN, keyCode, modifier); + } + + @:noCompletion private function __onLimeKeyUp(window:Window, keyCode:KeyCode, modifier:KeyModifier):Void + { + if (this.window == null || this.window != window) return; + + __onKey(KeyboardEvent.KEY_UP, keyCode, modifier); + } + + @:noCompletion private function __onLimeModuleExit(code:Int):Void + { + if (window != null) + { + var event:Event = null; + + #if openfl_pool_events + event = Event.__pool.get(); + event.type = Event.DEACTIVATE; + #else + event = new Event(Event.DEACTIVATE); + #end + + __broadcastEvent(event); + + #if openfl_pool_events + Event.__pool.release(event); + #end + } + } + + @:noCompletion private function __onLimeMouseDown(window:Window, x:Float, y:Float, button:Int):Void + { + if (this.window == null || this.window != window) return; + + __dispatchPendingMouseEvent(); + + var type = switch (button) + { + case 1: MouseEvent.MIDDLE_MOUSE_DOWN; + case 2: MouseEvent.RIGHT_MOUSE_DOWN; + default: MouseEvent.MOUSE_DOWN; + } + + __onMouse(type, Std.int(x * window.scale), Std.int(y * window.scale), button); + + if (!showDefaultContextMenu && button == 2) + { + window.onMouseDown.cancel(); + } + } + + @:noCompletion private function __onLimeMouseMove(window:Window, x:Float, y:Float):Void + { + if (this.window == null || this.window != window) return; + + #if openfl_always_dispatch_mouse_events + __onMouse(MouseEvent.MOUSE_MOVE, Std.int(x * window.scale), Std.int(y * window.scale), 0); + #else + __pendingMouseEvent = true; + __pendingMouseX = Std.int(x * window.scale); + __pendingMouseY = Std.int(y * window.scale); + #end + } + + @:noCompletion private function __onLimeMouseMoveRelative(window:Window, x:Float, y:Float):Void + { + // if (this.window == null || this.window != window) return; + } + + @:noCompletion private function __onLimeMouseUp(window:Window, x:Float, y:Float, button:Int):Void + { + if (this.window == null || this.window != window) return; + + __dispatchPendingMouseEvent(); + + var type = switch (button) + { + case 1: MouseEvent.MIDDLE_MOUSE_UP; + case 2: MouseEvent.RIGHT_MOUSE_UP; + default: MouseEvent.MOUSE_UP; + } + + __onMouse(type, Std.int(x * window.scale), Std.int(y * window.scale), button); + + if (!showDefaultContextMenu && button == 2) + { + window.onMouseUp.cancel(); + } + } + + @:noCompletion private function __onLimeMouseWheel(window:Window, deltaX:Float, deltaY:Float, deltaMode:MouseWheelMode):Void + { + if (this.window == null || this.window != window) return; + + __dispatchPendingMouseEvent(); + + if (deltaMode == PIXELS) + { + __onMouseWheel(Std.int(deltaX * window.scale), Std.int(deltaY * window.scale), deltaMode); + } + else + { + __onMouseWheel(Std.int(deltaX), Std.int(deltaY), deltaMode); + } + } + + @:noCompletion private function __renderAfterEvent():Void + { + #if (cpp || hl || neko) + // TODO: should Lime have a public API to force rendering? + window.__backend.render(); + #end + var cancelled = __render(window.context); + #if (cpp || hl || neko) + if (!cancelled) + { + window.__backend.contextFlip(); + } + #end + } + + @:noCompletion private function __render(context:RenderContext):Bool + { + var cancelled = false; + + var event:Event = null; + + var shouldRender = #if !openfl_disable_display_render (__renderer != null #if !openfl_always_render && (__renderDirty || __forceRender) #end) #else false #end; + + if (__invalidated && shouldRender) + { + __invalidated = false; + + #if openfl_pool_events + event = Event.__pool.get(); + event.type = Event.RENDER; + #else + event = new Event(Event.RENDER); + #end + + __broadcastEvent(event); + + #if openfl_pool_events + Event.__pool.release(event); + #end + } + + #if hxtelemetry + var stack = Telemetry.__unwindStack(); + Telemetry.__startTiming(TelemetryCommandName.RENDER); + #end + + #if (openfl_enable_experimental_update_queue && !dom) + __updateQueue(false, true); + #else + __update(false, true); + #end + + #if lime + if (__renderer != null) + { + if (context3D != null) + { + for (stage3D in stage3Ds) + { + context3D.__renderStage3D(stage3D); + } + + #if !openfl_disable_display_render + if (context3D.__present) shouldRender = true; + #end + } + + if (shouldRender) + { + if (__renderer.__type == CAIRO) + { + #if lime_cairo + cast(__renderer, CairoRenderer).cairo = context.cairo; + #end + } + + if (context3D == null) + { + __renderer.__clear(); + } + + __renderer.__render(this); + } + else if (context3D == null) + { + cancelled = true; + } + + if (context3D != null) + { + if (!context3D.__present) + { + cancelled = true; + } + else + { + if (!__renderer.__cleared) + { + __renderer.__clear(); + } + + context3D.__present = false; + context3D.__cleared = false; + } + } + + __renderer.__cleared = false; + } + #end + + #if hxtelemetry + Telemetry.__endTiming(TelemetryCommandName.RENDER); + Telemetry.__rewindStack(stack); + #end + + return cancelled; + } + + @:noCompletion private function __onLimeRender(context:RenderContext):Void + { + if (__rendering) return; + __rendering = true; + + #if hxtelemetry + Telemetry.__advanceFrame(); + #end + + #if gl_stats + Context3DStats.resetDrawCalls(); + #end + + var event:Event = null; + + #if openfl_pool_events + event = Event.__pool.get(); + event.type = Event.ENTER_FRAME; + + __broadcastEvent(event); + + Event.__pool.release(event); + event = Event.__pool.get(); + event.type = Event.FRAME_CONSTRUCTED; + + __broadcastEvent(event); + + Event.__pool.release(event); + event = Event.__pool.get(); + event.type = Event.EXIT_FRAME; + + __broadcastEvent(event); + + Event.__pool.release(event); + #else + __broadcastEvent(new Event(Event.ENTER_FRAME)); + __broadcastEvent(new Event(Event.FRAME_CONSTRUCTED)); + __broadcastEvent(new Event(Event.EXIT_FRAME)); + #end + + __renderable = true; + __enterFrame(__deltaTime); + __deltaTime = 0; + + var cancelled = __render(context); + if (cancelled) + { + window.onRender.cancel(); + } + + __rendering = false; + } + + @:noCompletion private function __onLimeRenderContextLost():Void + { + __renderer = null; + context3D = null; + + for (stage3D in stage3Ds) + { + stage3D.__lostContext(); + } + } + + @:noCompletion private function __onLimeRenderContextRestored(context:RenderContext):Void + { + __createRenderer(); + + for (stage3D in stage3Ds) + { + stage3D.__restoreContext(); + } + } + + @:noCompletion private function __onLimeTextEdit(window:Window, text:String, start:Int, length:Int):Void + { + // if (this.window == null || this.window != window) return; + } + + @:noCompletion private function __onLimeTextInput(window:Window, text:String):Void + { + if (this.window == null || this.window != window) return; + + var stack = new Array(); + + if (__focus == null) + { + __getInteractive(stack); + } + else + { + __focus.__getInteractive(stack); + } + + var event = new TextEvent(TextEvent.TEXT_INPUT, true, true, text); + if (stack.length > 0) + { + stack.reverse(); + __dispatchStack(event, stack); + } + else + { + __dispatchEvent(event); + } + + if (event.isDefaultPrevented()) + { + window.onTextInput.cancel(); + } + } + + @:noCompletion private function __onLimeTouchCancel(touch:Touch):Void + { + // TODO: Should we handle this differently? + var isPrimaryTouchPoint = __primaryTouch == touch; + if (isPrimaryTouchPoint) + { + __primaryTouch = null; + } + + __onTouch(TouchEvent.TOUCH_END, touch, isPrimaryTouchPoint); + } + + @:noCompletion private function __onLimeTouchMove(touch:Touch):Void + { + __onTouch(TouchEvent.TOUCH_MOVE, touch, __primaryTouch == touch); + } + + @:noCompletion private function __onLimeTouchEnd(touch:Touch):Void + { + var isPrimaryTouchPoint = __primaryTouch == touch; + if (isPrimaryTouchPoint) + { + __primaryTouch = null; + } + + __onTouch(TouchEvent.TOUCH_END, touch, isPrimaryTouchPoint); + } + + @:noCompletion private function __onLimeTouchStart(touch:Touch):Void + { + if (__primaryTouch == null) + { + __primaryTouch = touch; + } + + __onTouch(TouchEvent.TOUCH_BEGIN, touch, __primaryTouch == touch); + } + + @:noCompletion private function __onLimeUpdate(deltaTime:Int):Void + { + __deltaTime = deltaTime; + + __dispatchPendingMouseEvent(); + } + + @:noCompletion private function __onLimeWindowActivate(window:Window):Void + { + if (this.window == null || this.window != window) return; + + // __broadcastEvent (new Event (Event.ACTIVATE)); + } + + @:noCompletion private function __onLimeWindowClose(window:Window):Void + { + if (this.window == window) + { + this.window = null; + } + + __primaryTouch = null; + + var event:Event = null; + + #if openfl_pool_events + event = Event.__pool.get(); + event.type = Event.DEACTIVATE; + #else + event = new Event(Event.DEACTIVATE); + #end + + __broadcastEvent(event); + + #if openfl_pool_events + Event.__pool.release(event); + #end + } + + @:noCompletion private function __onLimeWindowCreate(window:Window):Void + { + if (this.window == null || this.window != window) return; + + if (window.context != null) + { + __createRenderer(); + } + } + + @:noCompletion private function __onLimeWindowDeactivate(window:Window):Void + { + if (this.window == null || this.window != window) return; + + // __primaryTouch = null; + // __broadcastEvent (new Event (Event.DEACTIVATE)); + } + + @:noCompletion private function __onLimeWindowDropFile(window:Window, file:String):Void {} + + @:noCompletion private function __onLimeWindowEnter(window:Window):Void + { + // if (this.window == null || this.window != window) return; + } + + @:noCompletion private function __onLimeWindowExpose(window:Window):Void + { + if (this.window == null || this.window != window) return; + + __renderDirty = true; + } + + @:noCompletion private function __onLimeWindowFocusIn(window:Window):Void + { + if (this.window == null || this.window != window) return; + + #if !desktop + // TODO: Is this needed? + __renderDirty = true; + #end + + var event:Event = null; + + #if openfl_pool_events + event = Event.__pool.get(); + event.type = Event.ACTIVATE; + #else + event = new Event(Event.ACTIVATE); + #end + + __broadcastEvent(event); + + #if openfl_pool_events + Event.__pool.release(event); + #end + + #if !desktop + focus = __cacheFocus; + #end + } + + @:noCompletion private function __onLimeWindowFocusOut(window:Window):Void + { + if (this.window == null || this.window != window) return; + + __primaryTouch = null; + + var event:Event = null; + + #if openfl_pool_events + event = Event.__pool.get(); + event.type = Event.DEACTIVATE; + #else + event = new Event(Event.DEACTIVATE); + #end + + __broadcastEvent(event); + + #if openfl_pool_events + Event.__pool.release(event); + #end + + var currentFocus = focus; + focus = null; + __cacheFocus = currentFocus; + + MouseEvent.__altKey = false; + MouseEvent.__commandKey = false; + MouseEvent.__ctrlKey = false; + MouseEvent.__shiftKey = false; + } + + @:noCompletion private function __onLimeWindowFullscreen(window:Window):Void + { + if (this.window == null || this.window != window) return; + + __resize(); + + if (!__wasFullscreen) + { + __wasFullscreen = true; + if (__displayState == NORMAL) __displayState = FULL_SCREEN_INTERACTIVE; + __dispatchEvent(new FullScreenEvent(FullScreenEvent.FULL_SCREEN, false, false, true, true)); + } + } + + @:noCompletion private function __onLimeWindowLeave(window:Window):Void + { + if (this.window == null || this.window != window || MouseEvent.__buttonDown) return; + + __dispatchPendingMouseEvent(); + + var event:Event = null; + + #if openfl_pool_events + event = Event.__pool.get(); + event.type = Event.MOUSE_LEAVE; + #else + event = new Event(Event.MOUSE_LEAVE); + #end + + __dispatchEvent(event); + + #if openfl_pool_events + Event.__pool.release(event); + #end + } + + @:noCompletion private function __onLimeWindowMinimize(window:Window):Void + { + if (this.window == null || this.window != window) return; + + // __primaryTouch = null; + // __broadcastEvent (new Event (Event.DEACTIVATE)); + } + + @:noCompletion private function __onLimeWindowMove(window:Window, x:Float, y:Float):Void + { + // if (this.window == null || this.window != window) return; + } + + @:noCompletion private function __onLimeWindowResize(window:Window, width:Int, height:Int):Void + { + if (this.window == null || this.window != window) return; + + __resize(); + + #if android + // workaround for newer behavior + __forceRender = true; + Lib.setTimeout(function() + { + __forceRender = false; + }, 500); + #end + + if (__wasFullscreen && !window.fullscreen) + { + __wasFullscreen = false; + __displayState = NORMAL; + __dispatchEvent(new FullScreenEvent(FullScreenEvent.FULL_SCREEN, false, false, false, true)); + } + } + + @:noCompletion private function __onLimeWindowRestore(window:Window):Void + { + if (this.window == null || this.window != window) return; + + if (__wasFullscreen && !window.fullscreen) + { + __wasFullscreen = false; + __displayState = NORMAL; + __dispatchEvent(new FullScreenEvent(FullScreenEvent.FULL_SCREEN, false, false, false, true)); + } + } + #end + + @:noCompletion private function __onMouse(type:String, x:Float, y:Float, button:Int):Void + { + if (button > 2) return; + + var targetPoint = Point.__pool.get(); + targetPoint.setTo(x, y); + __displayMatrix.__transformInversePoint(targetPoint); + + __mouseX = targetPoint.x; + __mouseY = targetPoint.y; + + var stack:Array = []; + var target:InteractiveObject = null; + + if (__hitTest(__mouseX, __mouseY, true, stack, true, this)) + { + target = cast stack[stack.length - 1]; + } + else + { + target = this; + stack = [this]; + } + + if (target == null) target = this; + + var clickType:String = null; + var supportsClickCount = false; + + switch (type) + { + case MouseEvent.MOUSE_DOWN: + if (focus != null) + { + if (focus != target) + { + var focusEvent = new FocusEvent(FocusEvent.MOUSE_FOCUS_CHANGE, true, true, target, false, 0); + focus.dispatchEvent(focusEvent); + + if (!focusEvent.isDefaultPrevented()) + { + if (target.__allowMouseFocus()) + { + focus = target; + } + else + { + focus = null; + } + } + } + } + else + { + if (target.__allowMouseFocus()) + { + focus = target; + } + else + { + focus = null; + } + } + + __mouseDownLeft = target; + if (__lastClickTarget != target) + { + // the target has changed since the previous click + // so we can't double-click the old target anymore + __lastClickTarget = null; + __lastClickTime = 0; + } + MouseEvent.__buttonDown = true; + supportsClickCount = true; + + case MouseEvent.MIDDLE_MOUSE_DOWN: + __mouseDownMiddle = target; + supportsClickCount = true; + + case MouseEvent.RIGHT_MOUSE_DOWN: + __mouseDownRight = target; + supportsClickCount = true; + + case MouseEvent.MOUSE_UP: + if (__mouseDownLeft != null) + { + MouseEvent.__buttonDown = false; + + if (__mouseDownLeft == target) + { + clickType = MouseEvent.CLICK; + } + else + { + var event:MouseEvent = null; + + #if openfl_pool_events + event = MouseEvent.__pool.get(); + event.type = MouseEvent.RELEASE_OUTSIDE; + event.stageX = __mouseX; + event.stageY = __mouseY; + event.localX = __mouseX; + event.localY = __mouseY; + event.target = this; + event.clickCount = 0; + #else + event = MouseEvent.__create(MouseEvent.RELEASE_OUTSIDE, 1, 0, __mouseX, __mouseY, new Point(__mouseX, __mouseY), this); + #end + + __mouseDownLeft.dispatchEvent(event); + + #if openfl_pool_events + MouseEvent.__pool.release(event); + #end + } + + __mouseDownLeft = null; + } + supportsClickCount = true; + + case MouseEvent.MIDDLE_MOUSE_UP: + if (__mouseDownMiddle == target) + { + clickType = MouseEvent.MIDDLE_CLICK; + } + + __mouseDownMiddle = null; + supportsClickCount = true; + + case MouseEvent.RIGHT_MOUSE_UP: + if (__mouseDownRight == target) + { + clickType = MouseEvent.RIGHT_CLICK; + } + + __mouseDownRight = null; + supportsClickCount = true; + + default: + } + + var localPoint = Point.__pool.get(); + var event:MouseEvent = null; + + var clickCount = #if (lime >= "8.1.0") supportsClickCount ? window.clickCount : 0 #else 0 #end; + #if openfl_pool_events + event = MouseEvent.__pool.get(); + event.type = type; + event.stageX = __mouseX; + event.stageY = __mouseY; + var local = target.__globalToLocal(targetPoint, localPoint); + event.localX = local.x; + event.localY = local.y; + event.target = target; + event.clickCount = clickCount; + #else + event = MouseEvent.__create(type, button, clickCount, __mouseX, __mouseY, target.__globalToLocal(targetPoint, localPoint), target); + #end + + __dispatchStack(event, stack); + + if (event.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + #if openfl_pool_events + MouseEvent.__pool.release(event); + #end + + if (clickType != null) + { + #if openfl_pool_events + event = MouseEvent.__pool.get(); + event.type = clickType; + event.stageX = __mouseX; + event.stageY = __mouseY; + var local = target.__globalToLocal(targetPoint, localPoint); + event.localX = local.x; + event.localY = local.y; + event.target = target; + event.clickCount = 0; + #else + event = MouseEvent.__create(clickType, button, 0, __mouseX, __mouseY, target.__globalToLocal(targetPoint, localPoint), target); + #end + + __dispatchStack(event, stack); + + if (event.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + #if openfl_pool_events + MouseEvent.__pool.release(event); + #end + + if (type == MouseEvent.MOUSE_UP) + { + if (target.doubleClickEnabled) + { + var currentTime = Lib.getTimer(); + if (currentTime - __lastClickTime < 500 && target == __lastClickTarget) + { + #if openfl_pool_events + event = MouseEvent.__pool.get(); + event.type = MouseEvent.DOUBLE_CLICK; + event.stageX = __mouseX; + event.stageY = __mouseY; + var local = target.__globalToLocal(targetPoint, localPoint); + event.localX = local.x; + event.localY = local.y; + event.target = target; + event.clickCount = 0; + #else + event = MouseEvent.__create(MouseEvent.DOUBLE_CLICK, button, 0, __mouseX, __mouseY, target.__globalToLocal(targetPoint, localPoint), + target); + #end + + __dispatchStack(event, stack); + + if (event.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + #if openfl_pool_events + MouseEvent.__pool.release(event); + #end + + __lastClickTime = 0; + __lastClickTarget = null; + } + else + { + // it's been too long since the previous click, + // or the target has changed since the previous click + __lastClickTarget = target; + __lastClickTime = currentTime; + } + } + else + { + // if the current target can't be double-clicked, clear the + // old value so that it doesn't become a memory leak + __lastClickTarget = null; + __lastClickTime = 0; + } + } + } + + if (Mouse.__cursor == MouseCursor.AUTO && !Mouse.__hidden) + { + var cursor:MouseCursor = null; + + if (__mouseDownLeft != null) + { + cursor = __mouseDownLeft.__getCursor(); + } + else + { + for (target in stack) + { + cursor = target.__getCursor(); + + if (cursor != null && window != null) + { + window.cursor = cursor; + break; + } + } + } + + if (cursor == null && window != null) + { + window.cursor = ARROW; + } + } + + var event:MouseEvent; + + if (target != __mouseOverTarget) + { + if (__mouseOverTarget != null) + { + #if openfl_pool_events + event = MouseEvent.__pool.get(); + event.type = MouseEvent.MOUSE_OUT; + event.stageX = __mouseX; + event.stageY = __mouseY; + var local = __mouseOverTarget.__globalToLocal(targetPoint, localPoint); + event.localX = local.x; + event.localY = local.y; + event.target = __mouseOverTarget; + event.clickCount = 0; + #else + event = MouseEvent.__create(MouseEvent.MOUSE_OUT, button, 0, __mouseX, __mouseY, __mouseOverTarget.__globalToLocal(targetPoint, localPoint), + cast __mouseOverTarget); + #end + + __dispatchStack(event, __mouseOutStack); + + if (event.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + #if openfl_pool_events + MouseEvent.__pool.release(cast event); + #end + } + } + + var item, i = 0; + while (i < __rollOutStack.length) + { + item = __rollOutStack[i]; + if (stack.indexOf(item) == -1) + { + __rollOutStack.remove(item); + + #if openfl_pool_events + event = MouseEvent.__pool.get(); + event.type = MouseEvent.ROLL_OUT; + event.stageX = __mouseX; + event.stageY = __mouseY; + var local = __mouseOverTarget.__globalToLocal(targetPoint, localPoint); + event.localX = local.x; + event.localY = local.y; + event.target = item; + event.clickCount = 0; + #else + event = MouseEvent.__create(MouseEvent.ROLL_OUT, button, 0, __mouseX, __mouseY, __mouseOverTarget.__globalToLocal(targetPoint, localPoint), + cast item); + #end + event.bubbles = false; + + __dispatchTarget(item, event); + + if (event.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + #if openfl_pool_events + MouseEvent.__pool.release(cast event); + #end + } + else + { + i++; + } + } + + for (item in stack) + { + if (__rollOutStack.indexOf(item) == -1 && __mouseOverTarget != null) + { + if (item.hasEventListener(MouseEvent.ROLL_OVER)) + { + #if openfl_pool_events + var mouseEvent = MouseEvent.__pool.get(); + mouseEvent.type = MouseEvent.ROLL_OVER; + mouseEvent.stageX = __mouseX; + mouseEvent.stageY = __mouseY; + var local = __mouseOverTarget.__globalToLocal(targetPoint, localPoint); + mouseEvent.localX = local.x; + mouseEvent.localY = local.y; + mouseEvent.target = item; + event = mouseEvent; + event.clickCount = 0; + #else + event = MouseEvent.__create(MouseEvent.ROLL_OVER, button, 0, __mouseX, __mouseY, + __mouseOverTarget.__globalToLocal(targetPoint, localPoint), cast item); + #end + event.bubbles = false; + + __dispatchTarget(item, event); + + if (event.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + #if openfl_pool_events + MouseEvent.__pool.release(cast event); + #end + } + + if (item.hasEventListener(MouseEvent.ROLL_OUT) || item.hasEventListener(MouseEvent.ROLL_OVER)) + { + __rollOutStack.push(item); + } + } + } + + if (target != __mouseOverTarget) + { + if (target != null) + { + #if openfl_pool_events + var mouseEvent = MouseEvent.__pool.get(); + mouseEvent.type = MouseEvent.MOUSE_OVER; + mouseEvent.stageX = __mouseX; + mouseEvent.stageY = __mouseY; + var local = target.__globalToLocal(targetPoint, localPoint); + mouseEvent.localX = local.x; + mouseEvent.localY = local.y; + mouseEvent.target = target; + event = mouseEvent; + event.clickCount = 0; + #else + event = MouseEvent.__create(MouseEvent.MOUSE_OVER, button, 0, __mouseX, __mouseY, target.__globalToLocal(targetPoint, localPoint), cast target); + #end + + __dispatchStack(event, stack); + + if (event.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + #if openfl_pool_events + MouseEvent.__pool.release(cast event); + #end + } + + __mouseOverTarget = target; + __mouseOutStack = stack; + } + + if (__dragObject != null) + { + __drag(targetPoint); + + var dropTarget:DisplayObject = null; + + if (__mouseOverTarget == __dragObject) + { + var cacheMouseEnabled = __dragObject.mouseEnabled; + var cacheMouseChildren = __dragObject.mouseChildren; + + __dragObject.mouseEnabled = false; + __dragObject.mouseChildren = false; + + var stack:Array = []; + + if (__hitTest(__mouseX, __mouseY, true, stack, true, this)) + { + dropTarget = stack[stack.length - 1]; + } + + __dragObject.mouseEnabled = cacheMouseEnabled; + __dragObject.mouseChildren = cacheMouseChildren; + } + else if (__mouseOverTarget != this) + { + dropTarget = __mouseOverTarget; + } + + __dragObject.dropTarget = dropTarget; + } + + Point.__pool.release(targetPoint); + Point.__pool.release(localPoint); + } + + #if lime + @:noCompletion private function __onMouseWheel(deltaX:Float, deltaY:Float, deltaMode:MouseWheelMode):Void + { + var x = __mouseX; + var y = __mouseY; + + var stack:Array = []; + var target:InteractiveObject = null; + + if (__hitTest(__mouseX, __mouseY, true, stack, true, this)) + { + target = cast stack[stack.length - 1]; + } + else + { + target = this; + stack = [this]; + } + + if (target == null) target = this; + var targetPoint = Point.__pool.get(); + targetPoint.setTo(x, y); + __displayMatrix.__transformInversePoint(targetPoint); + var delta = Std.int(deltaY); + + var event = MouseEvent.__create(MouseEvent.MOUSE_WHEEL, 0, 0, __mouseX, __mouseY, target.__globalToLocal(targetPoint, targetPoint), target, delta); + event.cancelable = true; + __dispatchStack(event, stack); + if (event.isDefaultPrevented()) window.onMouseWheel.cancel(); + + if (event.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + Point.__pool.release(targetPoint); + } + #end + + #if lime + @:noCompletion private function __onTouch(type:String, touch:Touch, isPrimaryTouchPoint:Bool):Void + { + var targetPoint = Point.__pool.get(); + targetPoint.setTo(Math.round(touch.x * window.width * window.scale), Math.round(touch.y * window.height * window.scale)); + __displayMatrix.__transformInversePoint(targetPoint); + + var touchX = targetPoint.x; + var touchY = targetPoint.y; + + var stack:Array = []; + var target:InteractiveObject = null; + + if (__hitTest(touchX, touchY, false, stack, true, this)) + { + target = cast stack[stack.length - 1]; + } + else + { + target = this; + stack = [this]; + } + + if (target == null) target = this; + + var touchId:Int = touch.id; + var touchData:TouchData = null; + + if (__touchData.exists(touchId)) + { + touchData = __touchData.get(touchId); + } + else + { + touchData = TouchData.__pool.get(); + touchData.reset(); + touchData.touch = touch; + __touchData.set(touchId, touchData); + } + + var touchType:String = null; + var releaseTouchData:Bool = false; + + switch (type) + { + case TouchEvent.TOUCH_BEGIN: + touchData.touchDownTarget = target; + + case TouchEvent.TOUCH_END: + if (touchData.touchDownTarget == target) + { + touchType = TouchEvent.TOUCH_TAP; + } + + touchData.touchDownTarget = null; + releaseTouchData = true; + + default: + } + + var localPoint = Point.__pool.get(); + var touchEvent = TouchEvent.__create(type, null, touchX, touchY, target.__globalToLocal(targetPoint, localPoint), cast target); + touchEvent.touchPointID = touchId; + touchEvent.isPrimaryTouchPoint = isPrimaryTouchPoint; + touchEvent.pressure = touch.pressure; + + __dispatchStack(touchEvent, stack); + + if (touchEvent.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + + if (touchType != null) + { + touchEvent = TouchEvent.__create(touchType, null, touchX, touchY, target.__globalToLocal(targetPoint, localPoint), cast target); + touchEvent.touchPointID = touchId; + touchEvent.isPrimaryTouchPoint = isPrimaryTouchPoint; + touchEvent.pressure = touch.pressure; + + __dispatchStack(touchEvent, stack); + + if (touchEvent.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + } + + var touchOverTarget = touchData.touchOverTarget; + + if (target != touchOverTarget && touchOverTarget != null) + { + touchEvent = TouchEvent.__create(TouchEvent.TOUCH_OUT, null, touchX, touchY, touchOverTarget.__globalToLocal(targetPoint, localPoint), + cast touchOverTarget); + touchEvent.touchPointID = touchId; + touchEvent.isPrimaryTouchPoint = isPrimaryTouchPoint; + touchEvent.pressure = touch.pressure; + + __dispatchTarget(touchOverTarget, touchEvent); + + if (touchEvent.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + } + + var touchOutStack = touchData.rollOutStack; + var item, i = 0; + while (i < touchOutStack.length) + { + item = touchOutStack[i]; + if (stack.indexOf(item) == -1) + { + touchOutStack.remove(item); + + touchEvent = TouchEvent.__create(TouchEvent.TOUCH_ROLL_OUT, null, touchX, touchY, touchOverTarget.__globalToLocal(targetPoint, localPoint), + cast touchOverTarget); + touchEvent.touchPointID = touchId; + touchEvent.isPrimaryTouchPoint = isPrimaryTouchPoint; + touchEvent.bubbles = false; + touchEvent.pressure = touch.pressure; + + __dispatchTarget(item, touchEvent); + + if (touchEvent.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + } + else + { + i++; + } + } + + for (item in stack) + { + if (touchOutStack.indexOf(item) == -1) + { + if (item.hasEventListener(TouchEvent.TOUCH_ROLL_OVER)) + { + touchEvent = TouchEvent.__create(TouchEvent.TOUCH_ROLL_OVER, null, touchX, touchY, + touchOverTarget.__globalToLocal(targetPoint, localPoint), cast item); + touchEvent.touchPointID = touchId; + touchEvent.isPrimaryTouchPoint = isPrimaryTouchPoint; + touchEvent.bubbles = false; + touchEvent.pressure = touch.pressure; + + __dispatchTarget(item, touchEvent); + + if (touchEvent.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + } + + if (item.hasEventListener(TouchEvent.TOUCH_ROLL_OUT)) + { + touchOutStack.push(item); + } + } + } + + if (target != touchOverTarget) + { + if (target != null) + { + touchEvent = TouchEvent.__create(TouchEvent.TOUCH_OVER, null, touchX, touchY, target.__globalToLocal(targetPoint, localPoint), cast target); + touchEvent.touchPointID = touchId; + touchEvent.isPrimaryTouchPoint = isPrimaryTouchPoint; + touchEvent.bubbles = true; + touchEvent.pressure = touch.pressure; + + __dispatchTarget(target, touchEvent); + + if (touchEvent.__updateAfterEventFlag) + { + __renderAfterEvent(); + } + } + + touchData.touchOverTarget = target; + } + + Point.__pool.release(targetPoint); + Point.__pool.release(localPoint); + + if (releaseTouchData) + { + __touchData.remove(touchId); + touchData.reset(); + TouchData.__pool.release(touchData); + } + } + #end + + #if lime + @:noCompletion private function __registerLimeModule(application:Application):Void + { + application.onCreateWindow.add(__onLimeCreateWindow); + application.onUpdate.add(__onLimeUpdate); + application.onExit.add(__onLimeModuleExit, false, 0); + + for (gamepad in Gamepad.devices) + { + __onLimeGamepadConnect(gamepad); + } + + Gamepad.onConnect.add(__onLimeGamepadConnect); + Touch.onStart.add(__onLimeTouchStart); + Touch.onMove.add(__onLimeTouchMove); + Touch.onEnd.add(__onLimeTouchEnd); + Touch.onCancel.add(__onLimeTouchCancel); + } + #end + + @:noCompletion private function __resize():Void + { + var cacheWidth = stageWidth; + var cacheHeight = stageHeight; + + var windowWidth = Std.int(window.width * window.scale); + var windowHeight = Std.int(window.height * window.scale); + + __displayMatrix.identity(); + + // Assuming `fullScreenSourceRect` ignores `stageScaleMode` + + if (fullScreenSourceRect != null && window.fullscreen) + { + // Should stageWidth / stageHeight be changed? + + stageWidth = Std.int(fullScreenSourceRect.width); + stageHeight = Std.int(fullScreenSourceRect.height); + + var displayScaleX = windowWidth / stageWidth; + var displayScaleY = windowHeight / stageHeight; + + __displayMatrix.translate(-fullScreenSourceRect.x, -fullScreenSourceRect.y); + __displayMatrix.scale(displayScaleX, displayScaleY); + + __displayRect.setTo(fullScreenSourceRect.left, fullScreenSourceRect.right, fullScreenSourceRect.top, fullScreenSourceRect.bottom); + } + else + { + if (__logicalWidth == 0 || __logicalHeight == 0 || scaleMode == NO_SCALE || windowWidth == 0 || windowHeight == 0) + { + #if openfl_dpi_aware + stageWidth = windowWidth; + stageHeight = windowHeight; + #else + stageWidth = Math.round(windowWidth / window.scale); + stageHeight = Math.round(windowHeight / window.scale); + + __displayMatrix.scale(window.scale, window.scale); + #end + + __displayRect.setTo(0, 0, stageWidth, stageHeight); + } + else + { + stageWidth = __logicalWidth; + stageHeight = __logicalHeight; + + switch (scaleMode) + { + case EXACT_FIT: + var displayScaleX = windowWidth / stageWidth; + var displayScaleY = windowHeight / stageHeight; + + __displayMatrix.scale(displayScaleX, displayScaleY); + __displayRect.setTo(0, 0, stageWidth, stageHeight); + + case NO_BORDER: + var scaleX = windowWidth / stageWidth; + var scaleY = windowHeight / stageHeight; + + var scale = Math.max(scaleX, scaleY); + + var scaledWidth = stageWidth * scale; + var scaledHeight = stageHeight * scale; + + var visibleWidth = stageWidth - Math.round((scaledWidth - windowWidth) / scale); + var visibleHeight = stageHeight - Math.round((scaledHeight - windowHeight) / scale); + var visibleX = Math.round((stageWidth - visibleWidth) / 2); + var visibleY = Math.round((stageHeight - visibleHeight) / 2); + + __displayMatrix.translate(-visibleX, -visibleY); + __displayMatrix.scale(scale, scale); + + __displayRect.setTo(visibleX, visibleY, visibleWidth, visibleHeight); + + default: // SHOW_ALL + + var scaleX = windowWidth / stageWidth; + var scaleY = windowHeight / stageHeight; + + var scale = Math.min(scaleX, scaleY); + + var scaledWidth = stageWidth * scale; + var scaledHeight = stageHeight * scale; + + var visibleWidth = stageWidth - Math.round((scaledWidth - windowWidth) / scale); + var visibleHeight = stageHeight - Math.round((scaledHeight - windowHeight) / scale); + var visibleX = Math.round((stageWidth - visibleWidth) / 2); + var visibleY = Math.round((stageHeight - visibleHeight) / 2); + + __displayMatrix.translate(-visibleX, -visibleY); + __displayMatrix.scale(scale, scale); + + __displayRect.setTo(visibleX, visibleY, visibleWidth, visibleHeight); + } + } + } + + if (context3D != null) + { + #if openfl_dpi_aware + context3D.configureBackBuffer(windowWidth, windowHeight, 0, true, true, true); + #else + context3D.configureBackBuffer(stageWidth, stageHeight, 0, true, true, true); + #end + } + + for (stage3D in stage3Ds) + { + stage3D.__resize(windowWidth, windowHeight); + } + + if (__renderer != null) + { + __renderer.__resize(windowWidth, windowHeight); + } + + __renderDirty = true; + + if (stageWidth != cacheWidth || stageHeight != cacheHeight) + { + __setTransformDirty(); + + var event:Event = null; + + #if openfl_pool_events + event = Event.__pool.get(); + event.type = Event.RESIZE; + #else + event = new Event(Event.RESIZE); + #end + + __dispatchEvent(event); + + #if openfl_pool_events + Event.__pool.release(event); + #end + } + } + + @:noCompletion private function __setLogicalSize(width:Int, height:Int):Void + { + __logicalWidth = width; + __logicalHeight = height; + + __resize(); + } + + @:noCompletion private function __startDrag(sprite:Sprite, lockCenter:Bool, bounds:Rectangle):Void + { + if (bounds == null) + { + __dragBounds = null; + } + else + { + __dragBounds = new Rectangle(); + + var right = bounds.right; + var bottom = bounds.bottom; + __dragBounds.x = right < bounds.x ? right : bounds.x; + __dragBounds.y = bottom < bounds.y ? bottom : bounds.y; + __dragBounds.width = Math.abs(bounds.width); + __dragBounds.height = Math.abs(bounds.height); + } + + __dragObject = sprite; + + if (__dragObject != null) + { + if (lockCenter) + { + __dragOffsetX = 0; + __dragOffsetY = 0; + } + else + { + var mouse = Point.__pool.get(); + mouse.setTo(mouseX, mouseY); + var parent = __dragObject.parent; + + if (parent != null) + { + parent.__getWorldTransform().__transformInversePoint(mouse); + } + + __dragOffsetX = __dragObject.x - mouse.x; + __dragOffsetY = __dragObject.y - mouse.y; + Point.__pool.release(mouse); + } + } + } + + @:noCompletion private function __stopDrag(sprite:Sprite):Void + { + __dragBounds = null; + __dragObject = null; + } + + @:noCompletion private function __unregisterLimeModule(application:Application):Void + { + #if lime + application.onCreateWindow.remove(__onLimeCreateWindow); + application.onUpdate.remove(__onLimeUpdate); + application.onExit.remove(__onLimeModuleExit); + + Gamepad.onConnect.remove(__onLimeGamepadConnect); + Touch.onStart.remove(__onLimeTouchStart); + Touch.onMove.remove(__onLimeTouchMove); + Touch.onEnd.remove(__onLimeTouchEnd); + Touch.onCancel.remove(__onLimeTouchCancel); + #end + } + + #if (openfl_enable_experimental_update_queue && !dom) + @:noCompletion private function __updateQueue(transformOnly:Bool, updateChildren:Bool):Void + { + var updateFix:Array = []; + var updateQueue = DisplayObject.updateQueue; + while (updateQueue.length != 0) + { + var displayObject = updateQueue.shift(); + var parentDisplayObject = displayObject.parent; + if (parentDisplayObject != null && parentDisplayObject.__updateRequired == true && parentDisplayObject != this) + { + parentDisplayObject.__update(transformOnly, false); + parentDisplayObject.__updateRequired = false; + updateFix.push(parentDisplayObject); + } + + displayObject.__update(transformOnly, updateChildren); + displayObject._updateQueueFlag = false; + } + + for (i in 0...updateFix.length) + { + updateFix[i].__updateRequired = true; + } + } + #else + @:noCompletion private override function __update(transformOnly:Bool, updateChildren:Bool):Void + { + if (transformOnly) + { + if (__transformDirty) + { + super.__update(true, updateChildren); + + if (updateChildren) + { + __transformDirty = false; + // __dirty = true; + } + } + } + else + { + if (__transformDirty || __renderDirty) + { + super.__update(false, updateChildren); + + if (updateChildren) + { + // #if dom + if (DisplayObject.__supportDOM) + { + __wasDirty = true; + } + + // #end + + // __dirty = false; + } + } + /* + #if dom + **/ + else if (!__renderDirty && __wasDirty) + { + // If we were dirty last time, we need at least one more + // update in order to clear "changed" properties + + super.__update(false, updateChildren); + + if (updateChildren) + { + __wasDirty = false; + } + } + /* + #end + **/ + } + } + #end + + // Get & Set Methods + @:noCompletion private function get_color():Null + { + return __color; + } + + @:noCompletion private function set_color(value:Null):Null + { + if (value == null) + { + __transparent = true; + value = 0x000000; + } + else + { + __transparent = false; + } + + if (__color != value) + { + var r = (value & 0xFF0000) >>> 16; + var g = (value & 0x00FF00) >>> 8; + var b = (value & 0x0000FF); + + __colorSplit[0] = r / 0xFF; + __colorSplit[1] = g / 0xFF; + __colorSplit[2] = b / 0xFF; + __colorString = "#" + StringTools.hex(value & 0xFFFFFF, 6); + __renderDirty = true; + __color = (0xFF << 24) | (value & 0xFFFFFF); + } + + return value; + } + + @:noCompletion private function get_contentsScaleFactor():Float + { + return __contentsScaleFactor; + } + + @:noCompletion private function get_displayState():StageDisplayState + { + return __displayState; + } + + @:noCompletion private function set_displayState(value:StageDisplayState):StageDisplayState + { + if (window != null) + { + switch (value) + { + case NORMAL: + if (window.fullscreen) + { + // window.minimized = false; + window.fullscreen = false; + } + + default: + if (!window.fullscreen) + { + // window.minimized = false; + window.fullscreen = true; + } + } + } + + return __displayState = value; + } + + @:noCompletion private function get_focus():InteractiveObject + { + return __focus; + } + + @:noCompletion private function set_focus(value:InteractiveObject):InteractiveObject + { + if (value != __focus || (value == null && __cacheFocus != null)) + { + var oldFocus = __focus; + __focus = value; + __cacheFocus = value; + + if (oldFocus != null) + { + var event = new FocusEvent(FocusEvent.FOCUS_OUT, true, false, value, false, 0); + var stack = new Array(); + oldFocus.__getInteractive(stack); + stack.reverse(); + __dispatchStack(event, stack); + } + + if (value != null) + { + var event = new FocusEvent(FocusEvent.FOCUS_IN, true, false, oldFocus, false, 0); + var stack = new Array(); + value.__getInteractive(stack); + stack.reverse(); + __dispatchStack(event, stack); + } + } + + return value; + } + + @:noCompletion private function get_frameRate():Float + { + if (window != null) + { + return window.frameRate; + } + + return 0; + } + + @:noCompletion private function set_frameRate(value:Float):Float + { + if (window != null) + { + return window.frameRate = value; + } + + return value; + } + + @:noCompletion private function get_fullScreenHeight():UInt + { + return Math.ceil(window.display.currentMode.height * window.scale); + } + + @:noCompletion private function get_fullScreenSourceRect():Rectangle + { + return __fullScreenSourceRect == null ? null : __fullScreenSourceRect.clone(); + } + + @:noCompletion private function set_fullScreenSourceRect(value:Rectangle):Rectangle + { + if (value == null) + { + if (__fullScreenSourceRect != null) + { + __fullScreenSourceRect = null; + __resize(); + } + } + else if (!value.equals(__fullScreenSourceRect)) + { + __fullScreenSourceRect = value.clone(); + __resize(); + } + + return value; + } + + @:noCompletion private function get_fullScreenWidth():UInt + { + return Math.ceil(window.display.currentMode.width * window.scale); + } + + @:noCompletion private override function set_height(value:Float):Float + { + return this.height; + } + + @:noCompletion private override function get_mouseX():Float + { + return __mouseX; + } + + @:noCompletion private override function get_mouseY():Float + { + return __mouseY; + } + + @:noCompletion private function get_quality():StageQuality + { + return __quality; + } + + @:noCompletion private function set_quality(value:StageQuality):StageQuality + { + __quality = value; + + if (__renderer != null) + { + __renderer.__allowSmoothing = (quality != LOW); + } + + return value; + } + + @:noCompletion private override function set_rotation(value:Float):Float + { + return 0; + } + + @:noCompletion private function get_scaleMode():StageScaleMode + { + return __scaleMode; + } + + @:noCompletion private function set_scaleMode(value:StageScaleMode):StageScaleMode + { + if (value != __scaleMode) + { + __scaleMode = value; + __resize(); + } + + return value; + } + + @:noCompletion private override function set_scaleX(value:Float):Float + { + return 0; + } + + @:noCompletion private override function set_scaleY(value:Float):Float + { + return 0; + } + + @:noCompletion private override function get_tabEnabled():Bool + { + return false; + } + + @:noCompletion private override function set_tabEnabled(value:Bool):Bool + { + throw new IllegalOperationError("Error: The Stage class does not implement this property or method."); + } + + @:noCompletion private override function get_tabIndex():Int + { + return -1; + } + + @:noCompletion private override function set_tabIndex(value:Int):Int + { + throw new IllegalOperationError("Error: The Stage class does not implement this property or method."); + } + + @:noCompletion private override function set_transform(value:Transform):Transform + { + return this.transform; + } + + @:noCompletion private override function set_width(value:Float):Float + { + return this.width; + } + + @:noCompletion private override function set_x(value:Float):Float + { + return 0; + } + + @:noCompletion private override function set_y(value:Float):Float + { + return 0; + } +} +#else +typedef Stage = flash.display.Stage; +#end diff --git a/source/openfl/display/_internal/CairoGraphics.hx b/source/openfl/display/_internal/CairoGraphics.hx new file mode 100644 index 00000000..624cf7c1 --- /dev/null +++ b/source/openfl/display/_internal/CairoGraphics.hx @@ -0,0 +1,1561 @@ +package openfl.display._internal; + +#if !flash +import openfl.display._internal.DrawCommandBuffer; +import openfl.display._internal.DrawCommandReader; +import openfl.display.BitmapData; +import openfl.display.CairoRenderer; +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.Vector; +#if lime +import lime.graphics.cairo.Cairo; +import lime.graphics.cairo.CairoExtend; +import lime.graphics.cairo.CairoFilter; +import lime.graphics.cairo.CairoImageSurface; +import lime.graphics.cairo.CairoPattern; +import lime.math.Matrix3; +import lime.math.Vector2; +#end + +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(openfl.display.DisplayObject) +@:access(openfl.display.BitmapData) +@:access(openfl.display.Graphics) +@:access(openfl.geom.Matrix) +@:access(openfl.geom.Point) +@:access(openfl.geom.Rectangle) +@SuppressWarnings("checkstyle:FieldDocComment") +class CairoGraphics +{ + #if lime_cairo + private static var SIN45:Float = 0.70710678118654752440084436210485; + private static var TAN22:Float = 0.4142135623730950488016887242097; + private static var allowSmoothing:Bool; + private static var bitmapFill:BitmapData; + private static var bitmapRepeat:Bool; + private static var bounds:Rectangle; + private static var cairo:Cairo; + private static var fillCommands:DrawCommandBuffer = new DrawCommandBuffer(); + 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 hitTesting:Bool; + private static var inversePendingMatrix:Matrix; + private static var pendingMatrix:Matrix; + private static var strokeCommands:DrawCommandBuffer = new DrawCommandBuffer(); + private static var strokePattern:CairoPattern; + private static var tempMatrix3 = new Matrix3(); + private static var worldAlpha:Float; + + private static function closePath(strokeBefore:Bool = false):Void + { + if (strokePattern == null) + { + return; + } + + if (!strokeBefore) + { + cairo.closePath(); + } + + cairo.source = strokePattern; + if (!hitTesting) cairo.strokePreserve(); + + if (strokeBefore) + { + cairo.closePath(); + } + + cairo.newPath(); + } + + private static function createGradientPattern(type:GradientType, colors:Array, alphas:Array, ratios:Array, matrix:Matrix, + spreadMethod:SpreadMethod, interpolationMethod:InterpolationMethod, focalPointRatio:Float):CairoPattern + { + var pattern:CairoPattern = null, + point:Point = null, + point2:Point = null, + releaseMatrix = false; + + if (matrix == null) + { + matrix = Matrix.__pool.get(); + releaseMatrix = true; + } + + switch (type) + { + case RADIAL: + point = Point.__pool.get(); + point.setTo(1638.4, 0); + matrix.__transformPoint(point); + + var x = matrix.tx + graphics.__bounds.x; + var y = matrix.ty + graphics.__bounds.y; + + pattern = CairoPattern.createRadial(x, y, 0, x, y, Math.abs((point.x - matrix.tx) / 2)); + + case LINEAR: + point = Point.__pool.get(); + point.setTo(-819.2, 0); + matrix.__transformPoint(point); + + point2 = Point.__pool.get(); + point2.setTo(819.2, 0); + matrix.__transformPoint(point2); + + point.x += graphics.__bounds.x; + point2.x += graphics.__bounds.x; + point.y += graphics.__bounds.y; + point2.y += graphics.__bounds.y; + + pattern = CairoPattern.createLinear(point.x, point.y, point2.x, point2.y); + } + + var rgb:Int, alpha:Float, r:Float, g:Float, b:Float, ratio:Float; + + for (i in 0...colors.length) + { + rgb = colors[i]; + alpha = alphas[i]; + r = ((rgb & 0xFF0000) >>> 16) / 0xFF; + g = ((rgb & 0x00FF00) >>> 8) / 0xFF; + b = (rgb & 0x0000FF) / 0xFF; + + ratio = ratios[i] / 0xFF; + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; + + pattern.addColorStopRGBA(ratio, r, g, b, alpha); + } + + if (point != null) Point.__pool.release(point); + if (point2 != null) Point.__pool.release(point2); + if (releaseMatrix) Matrix.__pool.release(matrix); + + 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, smooth:Bool):CairoPattern + { + var pattern = CairoPattern.createForSurface(bitmapFill.getSurface()); + pattern.filter = (smooth && allowSmoothing) ? CairoFilter.GOOD : CairoFilter.NEAREST; + + if (bitmapRepeat) + { + pattern.extend = CairoExtend.REPEAT; + } + + fillPatternMatrix = matrix; + + return pattern; + } + + private static function drawRoundRect(x:Float, y:Float, width:Float, height:Float, ellipseWidth:Float, ellipseHeight:Null):Void + { + if (ellipseHeight == null) ellipseHeight = ellipseWidth; + + ellipseWidth *= 0.5; + ellipseHeight *= 0.5; + + if (ellipseWidth > width / 2) ellipseWidth = width / 2; + if (ellipseHeight > height / 2) ellipseHeight = height / 2; + + var xe = x + width, + ye = y + height, + cx1 = -ellipseWidth + (ellipseWidth * SIN45), + cx2 = -ellipseWidth + (ellipseWidth * TAN22), + cy1 = -ellipseHeight + (ellipseHeight * SIN45), + cy2 = -ellipseHeight + (ellipseHeight * TAN22); + + cairo.moveTo(xe, ye - ellipseHeight); + quadraticCurveTo(xe, ye + cy2, xe + cx1, ye + cy1); + quadraticCurveTo(xe + cx2, ye, xe - ellipseWidth, ye); + cairo.lineTo(x + ellipseWidth, ye); + quadraticCurveTo(x - cx2, ye, x - cx1, ye + cy1); + quadraticCurveTo(x, ye + cy2, x, ye - ellipseHeight); + cairo.lineTo(x, y + ellipseHeight); + quadraticCurveTo(x, y - cy2, x - cx1, y - cy1); + quadraticCurveTo(x - cx2, y, x + ellipseWidth, y); + cairo.lineTo(xe - ellipseWidth, y); + quadraticCurveTo(xe + cx2, y, xe + cx1, y - cy1); + quadraticCurveTo(xe, y - cy2, xe, y + ellipseHeight); + cairo.lineTo(xe, ye - ellipseHeight); + } + + private static function endFill():Void + { + cairo.newPath(); + playCommands(fillCommands, false); + fillCommands.clear(); + } + + private static function endStroke():Void + { + cairo.newPath(); + playCommands(strokeCommands, true); + cairo.closePath(); + strokeCommands.clear(); + } + #end + + public static function hitTest(graphics:Graphics, x:Float, y:Float):Bool + { + #if lime_cairo + CairoGraphics.graphics = graphics; + bounds = graphics.__bounds; + + if (graphics.__commands.length == 0 || bounds == null || bounds.width == 0 || bounds.height == 0 || !bounds.contains(x, y)) + { + CairoGraphics.graphics = null; + return false; + } + else + { + hitTesting = true; + + x -= bounds.x; + y -= bounds.y; + + if (graphics.__cairo == null) + { + var bitmap = new BitmapData(Math.floor(Math.max(1, bounds.width)), Math.floor(Math.max(1, bounds.height)), true, 0); + var surface = bitmap.getSurface(); + graphics.__cairo = new Cairo(surface); + // graphics.__bitmap = bitmap; + } + + cairo = graphics.__cairo; + + fillCommands.clear(); + strokeCommands.clear(); + + hasFill = false; + hasStroke = false; + + fillPattern = null; + strokePattern = null; + + cairo.newPath(); + cairo.fillRule = EVEN_ODD; + + var data = new DrawCommandReader(graphics.__commands); + + for (type in graphics.__commands.types) + { + switch (type) + { + case CUBIC_CURVE_TO: + var c = data.readCubicCurveTo(); + fillCommands.cubicCurveTo(c.controlX1, c.controlY1, c.controlX2, c.controlY2, c.anchorX, c.anchorY); + strokeCommands.cubicCurveTo(c.controlX1, c.controlY1, c.controlX2, c.controlY2, c.anchorX, c.anchorY); + + case CURVE_TO: + var c = data.readCurveTo(); + fillCommands.curveTo(c.controlX, c.controlY, c.anchorX, c.anchorY); + strokeCommands.curveTo(c.controlX, c.controlY, c.anchorX, c.anchorY); + + case LINE_TO: + var c = data.readLineTo(); + fillCommands.lineTo(c.x, c.y); + strokeCommands.lineTo(c.x, c.y); + + case MOVE_TO: + var c = data.readMoveTo(); + fillCommands.moveTo(c.x, c.y); + strokeCommands.moveTo(c.x, c.y); + + case LINE_STYLE: + var c = data.readLineStyle(); + strokeCommands.lineStyle(c.thickness, c.color, 1, c.pixelHinting, c.scaleMode, c.caps, c.joints, c.miterLimit); + + case LINE_GRADIENT_STYLE: + var c = data.readLineGradientStyle(); + strokeCommands.lineGradientStyle(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + + case LINE_BITMAP_STYLE: + var c = data.readLineBitmapStyle(); + strokeCommands.lineBitmapStyle(c.bitmap, c.matrix, c.repeat, c.smooth); + + case END_FILL: + data.readEndFill(); + endFill(); + + if (hasFill && cairo.inFill(x, y)) + { + data.destroy(); + CairoGraphics.graphics = null; + return true; + } + + endStroke(); + + if (hasStroke && cairo.inStroke(x, y)) + { + data.destroy(); + CairoGraphics.graphics = null; + return true; + } + + hasFill = false; + bitmapFill = null; + + case BEGIN_BITMAP_FILL, BEGIN_FILL, BEGIN_GRADIENT_FILL, BEGIN_SHADER_FILL: + endFill(); + + if (hasFill && cairo.inFill(x, y)) + { + data.destroy(); + CairoGraphics.graphics = null; + return true; + } + + endStroke(); + + if (hasStroke && cairo.inStroke(x, y)) + { + data.destroy(); + CairoGraphics.graphics = null; + return true; + } + + if (type == BEGIN_BITMAP_FILL) + { + var c = data.readBeginBitmapFill(); + fillCommands.beginBitmapFill(c.bitmap, c.matrix, c.repeat, c.smooth); + strokeCommands.beginBitmapFill(c.bitmap, c.matrix, c.repeat, c.smooth); + } + else if (type == BEGIN_GRADIENT_FILL) + { + var c = data.readBeginGradientFill(); + fillCommands.beginGradientFill(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + strokeCommands.beginGradientFill(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + } + else if (type == BEGIN_SHADER_FILL) + { + var c = data.readBeginShaderFill(); + fillCommands.beginShaderFill(c.shaderBuffer); + strokeCommands.beginShaderFill(c.shaderBuffer); + } + else + { + var c = data.readBeginFill(); + fillCommands.beginFill(c.color, 1); + strokeCommands.beginFill(c.color, 1); + } + + case DRAW_CIRCLE: + var c = data.readDrawCircle(); + fillCommands.drawCircle(c.x, c.y, c.radius); + strokeCommands.drawCircle(c.x, c.y, c.radius); + + case DRAW_ELLIPSE: + var c = data.readDrawEllipse(); + fillCommands.drawEllipse(c.x, c.y, c.width, c.height); + strokeCommands.drawEllipse(c.x, c.y, c.width, c.height); + + case DRAW_RECT: + var c = data.readDrawRect(); + fillCommands.drawRect(c.x, c.y, c.width, c.height); + strokeCommands.drawRect(c.x, c.y, c.width, c.height); + + case DRAW_ROUND_RECT: + var c = data.readDrawRoundRect(); + fillCommands.drawRoundRect(c.x, c.y, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + strokeCommands.drawRoundRect(c.x, c.y, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + + case WINDING_EVEN_ODD: + data.readWindingEvenOdd(); + cairo.fillRule = EVEN_ODD; + + case WINDING_NON_ZERO: + data.readWindingNonZero(); + cairo.fillRule = WINDING; + + default: + data.skip(type); + } + } + + var hitTest = false; + + if (fillCommands.length > 0) + { + endFill(); + } + + if (hasFill && cairo.inFill(x, y)) + { + hitTest = true; + } + + if (strokeCommands.length > 0) + { + endStroke(); + } + + if (hasStroke && cairo.inStroke(x, y)) + { + hitTest = true; + } + + data.destroy(); + + CairoGraphics.graphics = null; + return hitTest; + } + #end + + return false; + } + + #if lime_cairo + 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):NormalizedUVT + { + 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; + } + } + + if (!skipT) + { + return {max: max, uvt: uvt}; + } + + var result = new Vector(); + + for (t in 1...len + 1) + { + if (skipT && t % 3 == 0) + { + continue; + } + + result.push(uvt[t - 1]); + } + + return {max: max, uvt: result}; + } + + private static function playCommands(commands:DrawCommandBuffer, stroke:Bool = false):Void + { + if (commands.length == 0) return; + + 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; + var setStart = false; + + cairo.fillRule = EVEN_ODD; + cairo.antialias = SUBPIXEL; + + var hasPath:Bool = false; + + var data = new DrawCommandReader(commands); + + var x, + y, + width, + height, + kappa = .5522848, + ox, + oy, + xe, + ye, + xm, + ym, + r, + g, + b; + + for (type in commands.types) + { + switch (type) + { + case CUBIC_CURVE_TO: + var c = data.readCubicCurveTo(); + hasPath = true; + cairo.curveTo(c.controlX1 + - offsetX, c.controlY1 + - offsetY, c.controlX2 + - offsetX, c.controlY2 + - offsetY, c.anchorX + - offsetX, + c.anchorY + - offsetY); + + case CURVE_TO: + var c = data.readCurveTo(); + hasPath = true; + quadraticCurveTo(c.controlX - offsetX, c.controlY - offsetY, c.anchorX - offsetX, c.anchorY - offsetY); + + case DRAW_CIRCLE: + var c = data.readDrawCircle(); + hasPath = true; + cairo.moveTo(c.x - offsetX + c.radius, c.y - offsetY); + cairo.arc(c.x - offsetX, c.y - offsetY, c.radius, 0, Math.PI * 2); + + case DRAW_RECT: + var c = data.readDrawRect(); + hasPath = true; + cairo.rectangle(c.x - offsetX, c.y - offsetY, c.width, c.height); + + case DRAW_ELLIPSE: + var c = data.readDrawEllipse(); + hasPath = true; + + x = c.x; + y = c.y; + width = c.width; + height = c.height; + + x -= offsetX; + y -= offsetY; + + 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 DRAW_ROUND_RECT: + var c = data.readDrawRoundRect(); + hasPath = true; + drawRoundRect(c.x - offsetX, c.y - offsetY, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + + case LINE_TO: + var c = data.readLineTo(); + hasPath = true; + cairo.lineTo(c.x - offsetX, c.y - offsetY); + + positionX = c.x; + positionY = c.y; + + if (positionX == startX && positionY == startY) + { + closeGap = true; + } + + case MOVE_TO: + var c = data.readMoveTo(); + cairo.moveTo(c.x - offsetX, c.y - offsetY); + + positionX = c.x; + positionY = c.y; + + if (setStart && c.x != startX && c.y != startY) + { + closeGap = true; + } + + startX = c.x; + startY = c.y; + setStart = true; + + case LINE_STYLE: + var c = data.readLineStyle(); + if (stroke && hasStroke) + { + closePath(true); + } + + cairo.moveTo(positionX - offsetX, positionY - offsetY); + + if (c.thickness == null) + { + hasStroke = false; + } + else + { + hasStroke = true; + + cairo.lineWidth = (c.thickness > 0 ? c.thickness : 1); + + if (c.joints == null) + { + cairo.lineJoin = ROUND; + } + else + { + cairo.lineJoin = switch (c.joints) + { + case MITER: MITER; + case BEVEL: BEVEL; + default: ROUND; + } + } + + if (c.caps == null) + { + cairo.lineCap = ROUND; + } + else + { + cairo.lineCap = switch (c.caps) + { + case NONE: BUTT; + case SQUARE: SQUARE; + default: ROUND; + } + } + + cairo.miterLimit = c.miterLimit; + + r = ((c.color & 0xFF0000) >>> 16) / 0xFF; + g = ((c.color & 0x00FF00) >>> 8) / 0xFF; + b = (c.color & 0x0000FF) / 0xFF; + + if (c.alpha == 1) + { + strokePattern = CairoPattern.createRGB(r, g, b); + } + else + { + strokePattern = CairoPattern.createRGBA(r, g, b, c.alpha); + } + } + + case LINE_GRADIENT_STYLE: + var c = data.readLineGradientStyle(); + if (stroke && hasStroke) + { + closePath(true); + } + + cairo.moveTo(positionX - offsetX, positionY - offsetY); + strokePattern = createGradientPattern(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + + hasStroke = true; + + case LINE_BITMAP_STYLE: + var c = data.readLineBitmapStyle(); + if (stroke && hasStroke) + { + closePath(true); + } + + cairo.moveTo(positionX - offsetX, positionY - offsetY); + strokePattern = createImagePattern(c.bitmap, c.matrix, c.repeat, c.smooth); + + hasStroke = true; + + case BEGIN_BITMAP_FILL: + var c = data.readBeginBitmapFill(); + fillPattern = createImagePattern(c.bitmap, c.matrix, c.repeat, c.smooth); + + bitmapFill = c.bitmap; + bitmapRepeat = c.repeat; + + hasFill = true; + + case BEGIN_FILL: + var c = data.readBeginFill(); + if (c.alpha < 0.005) + { + hasFill = false; + } + else + { + if (fillPattern != null) + { + fillPatternMatrix = null; + } + + fillPattern = CairoPattern.createRGBA(((c.color & 0xFF0000) >>> 16) / 0xFF, ((c.color & 0x00FF00) >>> 8) / 0xFF, + (c.color & 0x0000FF) / 0xFF, c.alpha); + hasFill = true; + } + + bitmapFill = null; + + case BEGIN_GRADIENT_FILL: + var c = data.readBeginGradientFill(); + if (fillPattern != null) + { + fillPatternMatrix = null; + } + + fillPattern = createGradientPattern(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + + hasFill = true; + bitmapFill = null; + + case BEGIN_SHADER_FILL: + var c = data.readBeginShaderFill(); + var shaderBuffer = c.shaderBuffer; + + if (shaderBuffer.inputCount > 0) + { + fillPattern = createImagePattern(shaderBuffer.inputs[0], null, shaderBuffer.inputWrap[0] != CLAMP, + shaderBuffer.inputFilter[0] != NEAREST); + + bitmapFill = shaderBuffer.inputs[0]; + bitmapRepeat = false; + + hasFill = true; + } + + case DRAW_QUADS: + var cacheExtend = fillPattern.extend; + fillPattern.extend = CairoExtend.NONE; + + var c = data.readDrawQuads(); + var rects = c.rects; + var indices = c.indices; + var transforms = c.transforms; + + var hasIndices = (indices != null); + var transformABCD = false, transformXY = false; + + var length = hasIndices ? indices.length : Math.floor(rects.length / 4); + if (length == 0) return; + + if (transforms != null) + { + if (transforms.length >= length * 6) + { + transformABCD = true; + transformXY = true; + } + else if (transforms.length >= length * 4) + { + transformABCD = true; + } + else if (transforms.length >= length * 2) + { + transformXY = true; + } + } + + var tileRect = Rectangle.__pool.get(); + var tileTransform = Matrix.__pool.get(); + + var sourceRect = (bitmapFill != null) ? bitmapFill.rect : null; + tempMatrix3.identity(); + + var transform = graphics.__renderTransform; + // var roundPixels = renderer.__roundPixels; + var alpha = CairoGraphics.worldAlpha; + + var ri, ti; + + for (i in 0...length) + { + ri = (hasIndices ? (indices[i] * 4) : i * 4); + if (ri < 0) continue; + tileRect.setTo(rects[ri], rects[ri + 1], rects[ri + 2], rects[ri + 3]); + + if (tileRect.width <= 0 || tileRect.height <= 0) + { + continue; + } + + if (transformABCD && transformXY) + { + ti = i * 6; + tileTransform.setTo(transforms[ti], transforms[ti + 1], transforms[ti + 2], transforms[ti + 3], transforms[ti + 4], + transforms[ti + 5]); + } + else if (transformABCD) + { + ti = i * 4; + tileTransform.setTo(transforms[ti], transforms[ti + 1], transforms[ti + 2], transforms[ti + 3], tileRect.x, tileRect.y); + } + else if (transformXY) + { + ti = i * 2; + tileTransform.tx = transforms[ti]; + tileTransform.ty = transforms[ti + 1]; + } + else + { + tileTransform.tx = tileRect.x; + tileTransform.ty = tileRect.y; + } + + tileTransform.tx += positionX - offsetX; + tileTransform.ty += positionY - offsetY; + tileTransform.concat(transform); + + // if (roundPixels) { + + // tileTransform.tx = Math.round (tileTransform.tx); + // tileTransform.ty = Math.round (tileTransform.ty); + + // } + + cairo.matrix = tileTransform.__toMatrix3(); + + tempMatrix3.tx = tileRect.x; + tempMatrix3.ty = tileRect.y; + fillPattern.matrix = tempMatrix3; + cairo.source = fillPattern; + + if (tileRect != sourceRect) + { + cairo.save(); + + cairo.newPath(); + cairo.rectangle(0, 0, tileRect.width, tileRect.height); + cairo.clip(); + } + + if (!hitTesting) + { + if (alpha == 1) + { + cairo.paint(); + } + else + { + cairo.paintWithAlpha(alpha); + } + } + + if (tileRect != sourceRect) + { + cairo.restore(); + } + } + + Rectangle.__pool.release(tileRect); + Matrix.__pool.release(tileTransform); + + cairo.matrix = graphics.__renderTransform.__toMatrix3(); + fillPattern.extend = cacheExtend; + + case DRAW_TRIANGLES: + var c = data.readDrawTriangles(); + var v = c.vertices; + var ind = c.indices; + var uvt:Vector = c.uvtData; + var colorFill = bitmapFill == null; + + if (colorFill && uvt != null) + { + break; + } + + var width = 0; + var height = 0; + var currentMatrix = graphics.__renderTransform.__toMatrix3(); + + if (!colorFill) + { + // TODO move this to Graphics? + + if (uvt == null) + { + uvt = new Vector(); + + for (i in 0...(Std.int(v.length / 2))) + { + uvt.push(v[i * 2] - offsetX / bitmapFill.width); + uvt.push(v[i * 2 + 1] - offsetY / bitmapFill.height); + } + } + + var skipT = c.uvtData.length != v.length; + var normalizedUVT = normalizeUVT(uvt, 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; + } + } + + 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; + + cairo.antialias = NONE; + + 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] - offsetX; + y1 = v[iay] - offsetY; + x2 = v[ibx] - offsetX; + y2 = v[iby] - offsetY; + x3 = v[icx] - offsetX; + y3 = v[icy] - offsetY; + + switch (c.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.source = fillPattern; + if (!hitTesting) cairo.fillPreserve(); + i += 3; + continue; + } + + cairo.matrix = graphics.__renderTransform.__toMatrix3(); + // cairo.identityMatrix(); + // cairo.resetClip(); + + 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; + } + + cairo.newPath(); + cairo.moveTo(x1, y1); + cairo.lineTo(x2, y2); + cairo.lineTo(x3, y3); + cairo.closePath(); + // cairo.clip (); + + x1 *= currentMatrix.a; + x2 *= currentMatrix.a; + x3 *= currentMatrix.a; + y1 *= currentMatrix.d; + y2 *= currentMatrix.d; + y3 *= currentMatrix.d; + + 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; + + tempMatrix3.setTo(t1, t2, t3, t4, dx, dy); + cairo.matrix = tempMatrix3; + cairo.source = fillPattern; + if (!hitTesting) cairo.fill(); + + i += 3; + } + + cairo.matrix = graphics.__renderTransform.__toMatrix3(); + + case WINDING_EVEN_ODD: + data.readWindingEvenOdd(); + cairo.fillRule = EVEN_ODD; + + case WINDING_NON_ZERO: + data.readWindingNonZero(); + cairo.fillRule = WINDING; + + default: + data.skip(type); + } + } + + data.destroy(); + + if (hasPath) + { + if (stroke && hasStroke) + { + if (hasFill) + { + if (positionX != startX || positionY != startY) + { + cairo.lineTo(startX - offsetX, startY - offsetY); + closeGap = true; + } + + if (closeGap) closePath(true); + } + else if (closeGap && positionX == startX && positionY == startY) + { + closePath(true); + } + + cairo.source = strokePattern; + if (!hitTesting) cairo.strokePreserve(); + } + + if (!stroke && hasFill) + { + cairo.translate(-bounds.x, -bounds.y); + + if (fillPatternMatrix != null) + { + var matrix = Matrix.__pool.get(); + matrix.copyFrom(fillPatternMatrix); + matrix.invert(); + + if (pendingMatrix != null) + { + matrix.concat(pendingMatrix); + } + + fillPattern.matrix = matrix.__toMatrix3(); + + Matrix.__pool.release(matrix); + } + + cairo.source = fillPattern; + + if (pendingMatrix != null) + { + cairo.transform(pendingMatrix.__toMatrix3()); + if (!hitTesting) cairo.fillPreserve(); + cairo.transform(inversePendingMatrix.__toMatrix3()); + } + else + { + if (!hitTesting) 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); + } + #end + + public static function render(graphics:Graphics, renderer:CairoRenderer):Void + { + #if lime_cairo + CairoGraphics.graphics = graphics; + CairoGraphics.allowSmoothing = renderer.__allowSmoothing; + CairoGraphics.worldAlpha = renderer.__getAlpha(graphics.__owner.__worldAlpha); + + #if (openfl_disable_hdpi || openfl_disable_hdpi_graphics) + var pixelRatio = 1; + #else + var pixelRatio = renderer.__pixelRatio; + #end + + graphics.__update(renderer.__worldTransform, pixelRatio); + + if (!graphics.__softwareDirty || graphics.__managed) + { + CairoGraphics.graphics = null; + return; + } + + bounds = graphics.__bounds; + + var width = graphics.__width; + var height = graphics.__height; + + if (!graphics.__visible || graphics.__commands.length == 0 || bounds == null || width < 1 || height < 1) + { + graphics.__cairo = null; + graphics.__bitmap = null; + } + else + { + hitTesting = false; + var needsUpscaling = false; + + if (graphics.__cairo != null) + { + var surface:CairoImageSurface = cast graphics.__cairo.target; + + if (width > surface.width || height > surface.height) + { + graphics.__cairo = null; + needsUpscaling = true; + } + } + + if (graphics.__cairo == null || graphics.__bitmap == null) + { + var bitmap = needsUpscaling ? new BitmapData(Std.int(width * 1.25), Std.int(height * 1.25), true, 0) : new BitmapData(width, height, true, 0); + var surface = bitmap.getSurface(); + graphics.__cairo = new Cairo(surface); + graphics.__bitmap = bitmap; + } + + cairo = graphics.__cairo; + + renderer.__setBlendModeCairo(cairo, NORMAL); + renderer.applyMatrix(graphics.__renderTransform, cairo); + + cairo.setOperator(CLEAR); + cairo.paint(); + cairo.setOperator(OVER); + + fillCommands.clear(); + strokeCommands.clear(); + + hasFill = false; + hasStroke = false; + + fillPattern = null; + strokePattern = null; + + var hasLineStyle = false; + var initStrokeX = 0.0; + var initStrokeY = 0.0; + + var data = new DrawCommandReader(graphics.__commands); + + for (type in graphics.__commands.types) + { + switch (type) + { + case CUBIC_CURVE_TO: + var c = data.readCubicCurveTo(); + fillCommands.cubicCurveTo(c.controlX1, c.controlY1, c.controlX2, c.controlY2, c.anchorX, c.anchorY); + + if (hasLineStyle) + { + strokeCommands.cubicCurveTo(c.controlX1, c.controlY1, c.controlX2, c.controlY2, c.anchorX, c.anchorY); + } + else + { + initStrokeX = c.anchorX; + initStrokeY = c.anchorY; + } + + case CURVE_TO: + var c = data.readCurveTo(); + fillCommands.curveTo(c.controlX, c.controlY, c.anchorX, c.anchorY); + + if (hasLineStyle) + { + strokeCommands.curveTo(c.controlX, c.controlY, c.anchorX, c.anchorY); + } + else + { + initStrokeX = c.anchorX; + initStrokeY = c.anchorY; + } + + case LINE_TO: + var c = data.readLineTo(); + fillCommands.lineTo(c.x, c.y); + + if (hasLineStyle) + { + strokeCommands.lineTo(c.x, c.y); + } + else + { + initStrokeX = c.x; + initStrokeY = c.y; + } + + case MOVE_TO: + var c = data.readMoveTo(); + fillCommands.moveTo(c.x, c.y); + + if (hasLineStyle) + { + strokeCommands.moveTo(c.x, c.y); + } + else + { + initStrokeX = c.x; + initStrokeY = c.y; + } + + case END_FILL: + data.readEndFill(); + endFill(); + endStroke(); + hasFill = false; + hasLineStyle = false; + bitmapFill = null; + initStrokeX = 0; + initStrokeY = 0; + + case LINE_GRADIENT_STYLE: + var c = data.readLineGradientStyle(); + + if (!hasLineStyle && (initStrokeX != 0 || initStrokeY != 0)) + { + strokeCommands.moveTo(initStrokeX, initStrokeY); + initStrokeX = 0; + initStrokeY = 0; + } + + hasLineStyle = true; + strokeCommands.lineGradientStyle(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + + case LINE_BITMAP_STYLE: + var c = data.readLineBitmapStyle(); + + if (!hasLineStyle && (initStrokeX != 0 || initStrokeY != 0)) + { + strokeCommands.moveTo(initStrokeX, initStrokeY); + initStrokeX = 0; + initStrokeY = 0; + } + + hasLineStyle = true; + strokeCommands.lineBitmapStyle(c.bitmap, c.matrix, c.repeat, c.smooth); + + case LINE_STYLE: + var c = data.readLineStyle(); + + if (!hasLineStyle && c.thickness != null) + { + if (initStrokeX != 0 || initStrokeY != 0) + { + strokeCommands.moveTo(initStrokeX, initStrokeY); + initStrokeX = 0; + initStrokeY = 0; + } + } + + hasLineStyle = c.thickness != null; + strokeCommands.lineStyle(c.thickness, c.color, c.alpha, c.pixelHinting, c.scaleMode, c.caps, c.joints, c.miterLimit); + + case BEGIN_BITMAP_FILL, BEGIN_FILL, BEGIN_GRADIENT_FILL, BEGIN_SHADER_FILL: + endFill(); + endStroke(); + + if (type == BEGIN_BITMAP_FILL) + { + var c = data.readBeginBitmapFill(); + fillCommands.beginBitmapFill(c.bitmap, c.matrix, c.repeat, c.smooth); + strokeCommands.beginBitmapFill(c.bitmap, c.matrix, c.repeat, c.smooth); + } + else if (type == BEGIN_GRADIENT_FILL) + { + var c = data.readBeginGradientFill(); + fillCommands.beginGradientFill(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + strokeCommands.beginGradientFill(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + } + else if (type == BEGIN_SHADER_FILL) + { + var c = data.readBeginShaderFill(); + fillCommands.beginShaderFill(c.shaderBuffer); + strokeCommands.beginShaderFill(c.shaderBuffer); + } + else + { + var c = data.readBeginFill(); + fillCommands.beginFill(c.color, c.alpha); + strokeCommands.beginFill(c.color, c.alpha); + } + + case DRAW_CIRCLE: + var c = data.readDrawCircle(); + fillCommands.drawCircle(c.x, c.y, c.radius); + + if (hasLineStyle) + { + strokeCommands.drawCircle(c.x, c.y, c.radius); + } + + case DRAW_ELLIPSE: + var c = data.readDrawEllipse(); + fillCommands.drawEllipse(c.x, c.y, c.width, c.height); + + if (hasLineStyle) + { + strokeCommands.drawEllipse(c.x, c.y, c.width, c.height); + } + + case DRAW_RECT: + var c = data.readDrawRect(); + fillCommands.drawRect(c.x, c.y, c.width, c.height); + + if (hasLineStyle) + { + strokeCommands.drawRect(c.x, c.y, c.width, c.height); + } + + case DRAW_ROUND_RECT: + var c = data.readDrawRoundRect(); + fillCommands.drawRoundRect(c.x, c.y, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + + if (hasLineStyle) + { + strokeCommands.drawRoundRect(c.x, c.y, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + } + + case DRAW_QUADS: + var c = data.readDrawQuads(); + fillCommands.drawQuads(c.rects, c.indices, c.transforms); + + case DRAW_TRIANGLES: + var c = data.readDrawTriangles(); + fillCommands.drawTriangles(c.vertices, c.indices, c.uvtData, c.culling); + + case OVERRIDE_BLEND_MODE: + var c = data.readOverrideBlendMode(); + renderer.__setBlendModeCairo(cairo, c.blendMode); + + case WINDING_EVEN_ODD: + data.readWindingEvenOdd(); + fillCommands.windingEvenOdd(); + + case WINDING_NON_ZERO: + data.readWindingNonZero(); + fillCommands.windingNonZero(); + + default: + data.skip(type); + } + } + + if (fillCommands.length > 0) + { + endFill(); + } + + if (strokeCommands.length > 0) + { + endStroke(); + } + + data.destroy(); + + graphics.__bitmap.image.dirty = true; + graphics.__bitmap.image.version++; + } + + graphics.__softwareDirty = false; + graphics.__dirty = false; + CairoGraphics.graphics = null; + #end + } + + public static function renderMask(graphics:Graphics, renderer:CairoRenderer):Void + { + #if lime_cairo + if (graphics.__commands.length != 0) + { + cairo = renderer.cairo; + + var positionX = 0.0; + var positionY = 0.0; + + var offsetX = 0; + var offsetY = 0; + + var data = new DrawCommandReader(graphics.__commands); + + var x, y, width, height, kappa = .5522848, ox, oy, xe, ye, xm, ym; + + for (type in graphics.__commands.types) + { + switch (type) + { + case CUBIC_CURVE_TO: + var c = data.readCubicCurveTo(); + cairo.curveTo(c.controlX1 + - offsetX, c.controlY1 + - offsetY, c.controlX2 + - offsetX, c.controlY2 + - offsetY, c.anchorX + - offsetX, + c.anchorY + - offsetY); + positionX = c.anchorX; + positionY = c.anchorY; + + case CURVE_TO: + var c = data.readCurveTo(); + quadraticCurveTo(c.controlX - offsetX, c.controlY - offsetY, c.anchorX - offsetX, c.anchorY - offsetY); + positionX = c.anchorX; + positionY = c.anchorY; + + case DRAW_CIRCLE: + var c = data.readDrawCircle(); + cairo.arc(c.x - offsetX, c.y - offsetY, c.radius, 0, Math.PI * 2); + + case DRAW_ELLIPSE: + var c = data.readDrawEllipse(); + + x = c.x; + y = c.y; + width = c.width; + height = c.height; + + x -= offsetX; + y -= offsetY; + + 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 DRAW_RECT: + var c = data.readDrawRect(); + cairo.rectangle(c.x - offsetX, c.y - offsetY, c.width, c.height); + + case DRAW_ROUND_RECT: + var c = data.readDrawRoundRect(); + drawRoundRect(c.x - offsetX, c.y - offsetY, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + + case LINE_TO: + var c = data.readLineTo(); + cairo.lineTo(c.x - offsetX, c.y - offsetY); + positionX = c.x; + positionY = c.y; + + case MOVE_TO: + var c = data.readMoveTo(); + cairo.moveTo(c.x - offsetX, c.y - offsetY); + positionX = c.x; + positionY = c.y; + + default: + data.skip(type); + } + } + + data.destroy(); + } + #end + } +} + +private typedef NormalizedUVT = +{ + max:Float, + uvt:Vector +} +#end diff --git a/source/openfl/display/_internal/CanvasGraphics.hx b/source/openfl/display/_internal/CanvasGraphics.hx new file mode 100644 index 00000000..4de98151 --- /dev/null +++ b/source/openfl/display/_internal/CanvasGraphics.hx @@ -0,0 +1,1599 @@ +package openfl.display._internal; + +#if !flash +import openfl.display.BitmapData; +import openfl.display.CanvasRenderer; +import openfl.display.CapsStyle; +import openfl.display._internal.DrawCommandBuffer; +import openfl.display._internal.DrawCommandReader; +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.Vector; +#if lime +import lime._internal.graphics.ImageCanvasUtil; // TODO +#end +#if (js && html5) +import js.html.CanvasElement; +import js.html.CanvasGradient; +import js.html.CanvasPattern; +import js.html.CanvasRenderingContext2D; +import js.html.CanvasWindingRule; +import js.Browser; +#end + +@:access(openfl.display.DisplayObject) +@:access(openfl.display.BitmapData) +@:access(openfl.display.Graphics) +@:access(openfl.geom.Matrix) +@:access(openfl.geom.Point) +@:access(openfl.geom.Rectangle) +@SuppressWarnings("checkstyle:FieldDocComment") +class CanvasGraphics +{ + private static inline var SIN45:Float = 0.70710678118654752440084436210485; + private static inline var TAN22:Float = 0.4142135623730950488016887242097; + private static var allowSmoothing:Bool; + private static var bitmapFill:BitmapData; + private static var bitmapStroke:BitmapData; + private static var bitmapRepeat:Bool; + private static var bounds:Rectangle; + private static var fillCommands:DrawCommandBuffer = new DrawCommandBuffer(); + private static var graphics:Graphics; + private static var hasFill:Bool; + private static var hasStroke:Bool; + private static var hitTesting:Bool; + private static var inversePendingMatrix:Matrix; + private static var pendingMatrix:Matrix; + private static var strokeCommands:DrawCommandBuffer = new DrawCommandBuffer(); + @SuppressWarnings("checkstyle:Dynamic") private static var windingRule:#if (js && html5) CanvasWindingRule #else Dynamic #end; + private static var worldAlpha:Float; + #if (js && html5) + private static var context:CanvasRenderingContext2D; + private static var hitTestCanvas:CanvasElement; + private static var hitTestContext:CanvasRenderingContext2D; + #end + + #if (js && html5) + private static function __init__():Void + { + hitTestCanvas = Browser.supported ? cast Browser.document.createElement("canvas") : null; + hitTestContext = Browser.supported ? hitTestCanvas.getContext("2d") : null; + } + #end + + private static function closePath(strokeBefore:Bool = false):Void + { + #if (js && html5) + if (context.strokeStyle == null) + { + return; + } + + if (!strokeBefore) + { + context.closePath(); + } + + context.stroke(); + + if (strokeBefore) + { + context.closePath(); + } + + context.beginPath(); + #end + } + + @SuppressWarnings("checkstyle:Dynamic") + private static function createBitmapFill(bitmap:BitmapData, bitmapRepeat:Bool, smooth:Bool):#if (js && html5) CanvasPattern #else Dynamic #end + { + #if (js && html5) + ImageCanvasUtil.convertToCanvas(bitmap.image); + setSmoothing(smooth); + return context.createPattern(bitmap.image.src, bitmapRepeat ? "repeat" : "no-repeat"); + #else + return null; + #end + } + + @SuppressWarnings("checkstyle:Dynamic") + private static function createGradientPattern(type:GradientType, colors:Array, alphas:Array, ratios:Array, matrix:Matrix, + spreadMethod:SpreadMethod, interpolationMethod:InterpolationMethod, focalPointRatio:Float):#if (js && html5) CanvasPattern #else Void #end + { + #if (js && html5) + var gradientFill:CanvasGradient = null, + point:Point = null, + point2:Point = null, + releaseMatrix = false; + + if (matrix == null) + { + matrix = Matrix.__pool.get(); + matrix.identity(); + releaseMatrix = true; + } + + switch (type) + { + case RADIAL: + var radius = 819.2; + focalPointRatio = focalPointRatio > 1.0 ? 1.0 : focalPointRatio < -1.0 ? -1.0 : focalPointRatio; + gradientFill = context.createRadialGradient(radius * focalPointRatio, 0, 0, 0, 0, radius); + + pendingMatrix = matrix.clone(); + inversePendingMatrix = matrix.clone(); + inversePendingMatrix.invert(); + + case LINEAR: + gradientFill = context.createLinearGradient(-819.2, 0, 819.2, 0); + + pendingMatrix = matrix.clone(); + inversePendingMatrix = matrix.clone(); + inversePendingMatrix.invert(); + } + + var rgb:Int, alpha:Float, r:Float, g:Float, b:Float, ratio:Float; + + for (i in 0...colors.length) + { + rgb = colors[i]; + alpha = alphas[i]; + r = (rgb & 0xFF0000) >>> 16; + g = (rgb & 0x00FF00) >>> 8; + b = (rgb & 0x0000FF); + + ratio = ratios[i] / 0xFF; + if (ratio < 0) ratio = 0; + if (ratio > 1) ratio = 1; + + gradientFill.addColorStop(ratio, "rgba(" + r + ", " + g + ", " + b + ", " + alpha + ")"); + } + + if (point != null) Point.__pool.release(point); + if (point2 != null) Point.__pool.release(point2); + if (releaseMatrix) Matrix.__pool.release(matrix); + + return cast(gradientFill); + #end + } + + private static function createTempPatternCanvas(bitmap:BitmapData, repeat:Bool, width:Int, height:Int):#if (js && html5) CanvasElement #else Void #end + { + // TODO: Don't create extra canvas elements like this + + #if (js && html5) + var canvas:CanvasElement = cast Browser.document.createElement("canvas"); + var context = canvas.getContext("2d"); + + 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(); + if (!hitTesting) context.fill(windingRule); + return canvas; + #end + } + + private static function drawRoundRect(x:Float, y:Float, width:Float, height:Float, ellipseWidth:Float, ellipseHeight:Null):Void + { + #if (js && html5) + if (ellipseHeight == null) ellipseHeight = ellipseWidth; + + ellipseWidth *= 0.5; + ellipseHeight *= 0.5; + + if (ellipseWidth > width / 2) ellipseWidth = width / 2; + if (ellipseHeight > height / 2) ellipseHeight = height / 2; + + var xe = x + width, + ye = y + height, + cx1 = -ellipseWidth + (ellipseWidth * SIN45), + cx2 = -ellipseWidth + (ellipseWidth * TAN22), + cy1 = -ellipseHeight + (ellipseHeight * SIN45), + cy2 = -ellipseHeight + (ellipseHeight * TAN22); + + context.moveTo(xe, ye - ellipseHeight); + context.quadraticCurveTo(xe, ye + cy2, xe + cx1, ye + cy1); + context.quadraticCurveTo(xe + cx2, ye, xe - ellipseWidth, ye); + context.lineTo(x + ellipseWidth, ye); + context.quadraticCurveTo(x - cx2, ye, x - cx1, ye + cy1); + context.quadraticCurveTo(x, ye + cy2, x, ye - ellipseHeight); + context.lineTo(x, y + ellipseHeight); + context.quadraticCurveTo(x, y - cy2, x - cx1, y - cy1); + context.quadraticCurveTo(x - cx2, y, x + ellipseWidth, y); + context.lineTo(xe - ellipseWidth, y); + context.quadraticCurveTo(xe + cx2, y, xe + cx1, y - cy1); + context.quadraticCurveTo(xe, y - cy2, xe, y + ellipseHeight); + context.lineTo(xe, ye - ellipseHeight); + #end + } + + private static function endFill():Void + { + #if (js && html5) + context.beginPath(); + playCommands(fillCommands, false); + fillCommands.clear(); + #end + } + + private static function endStroke():Void + { + #if (js && html5) + context.beginPath(); + playCommands(strokeCommands, true); + context.closePath(); + strokeCommands.clear(); + #end + } + + public static function hitTest(graphics:Graphics, x:Float, y:Float):Bool + { + #if (js && html5) + bounds = graphics.__bounds; + CanvasGraphics.graphics = graphics; + + if (graphics.__commands.length == 0 || bounds == null || bounds.width <= 0 || bounds.height <= 0) + { + CanvasGraphics.graphics = null; + return false; + } + else + { + hitTesting = true; + + var transform = graphics.__renderTransform; + + var px = transform.__transformX(x, y); + var py = transform.__transformY(x, y); + + x = px; + y = py; + + x -= transform.__transformX(bounds.x, bounds.y); + y -= transform.__transformY(bounds.x, bounds.y); + + var cacheCanvas = graphics.__canvas; + var cacheContext = graphics.__context; + graphics.__canvas = hitTestCanvas; + graphics.__context = hitTestContext; + + context = graphics.__context; + context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + + fillCommands.clear(); + strokeCommands.clear(); + + hasFill = false; + hasStroke = false; + bitmapFill = null; + bitmapRepeat = false; + + windingRule = CanvasWindingRule.EVENODD; + + var data = new DrawCommandReader(graphics.__commands); + + for (type in graphics.__commands.types) + { + switch (type) + { + case CUBIC_CURVE_TO: + var c = data.readCubicCurveTo(); + fillCommands.cubicCurveTo(c.controlX1, c.controlY1, c.controlX2, c.controlY2, c.anchorX, c.anchorY); + strokeCommands.cubicCurveTo(c.controlX1, c.controlY1, c.controlX2, c.controlY2, c.anchorX, c.anchorY); + + case CURVE_TO: + var c = data.readCurveTo(); + fillCommands.curveTo(c.controlX, c.controlY, c.anchorX, c.anchorY); + strokeCommands.curveTo(c.controlX, c.controlY, c.anchorX, c.anchorY); + + case LINE_TO: + var c = data.readLineTo(); + fillCommands.lineTo(c.x, c.y); + strokeCommands.lineTo(c.x, c.y); + + case MOVE_TO: + var c = data.readMoveTo(); + fillCommands.moveTo(c.x, c.y); + strokeCommands.moveTo(c.x, c.y); + + case LINE_GRADIENT_STYLE: + var c = data.readLineGradientStyle(); + strokeCommands.lineGradientStyle(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + + case LINE_BITMAP_STYLE: + var c = data.readLineBitmapStyle(); + strokeCommands.lineBitmapStyle(c.bitmap, c.matrix, c.repeat, c.smooth); + + case LINE_STYLE: + var c = data.readLineStyle(); + strokeCommands.lineStyle(c.thickness, c.color, 1, c.pixelHinting, c.scaleMode, c.caps, c.joints, c.miterLimit); + + case END_FILL: + data.readEndFill(); + endFill(); + + if (hasFill && context.isPointInPath(x, y, windingRule)) + { + data.destroy(); + graphics.__canvas = cacheCanvas; + graphics.__context = cacheContext; + CanvasGraphics.graphics = null; + return true; + } + + endStroke(); + + if (hasStroke && (context : Dynamic).isPointInStroke(x, y)) + { + data.destroy(); + graphics.__canvas = cacheCanvas; + graphics.__context = cacheContext; + CanvasGraphics.graphics = null; + return true; + } + + hasFill = false; + bitmapFill = null; + + case BEGIN_BITMAP_FILL, BEGIN_FILL, BEGIN_GRADIENT_FILL, BEGIN_SHADER_FILL: + endFill(); + + if (hasFill && context.isPointInPath(x, y, windingRule)) + { + data.destroy(); + graphics.__canvas = cacheCanvas; + graphics.__context = cacheContext; + CanvasGraphics.graphics = null; + return true; + } + + endStroke(); + + if (hasStroke && (context : Dynamic).isPointInStroke(x, y)) + { + data.destroy(); + graphics.__canvas = cacheCanvas; + graphics.__context = cacheContext; + CanvasGraphics.graphics = null; + return true; + } + + if (type == BEGIN_BITMAP_FILL) + { + var c = data.readBeginBitmapFill(); + fillCommands.beginBitmapFill(c.bitmap, c.matrix, c.repeat, c.smooth); + strokeCommands.beginBitmapFill(c.bitmap, c.matrix, c.repeat, c.smooth); + } + else if (type == BEGIN_GRADIENT_FILL) + { + var c = data.readBeginGradientFill(); + fillCommands.beginGradientFill(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + strokeCommands.beginGradientFill(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + } + else if (type == BEGIN_SHADER_FILL) + { + var c = data.readBeginShaderFill(); + fillCommands.beginShaderFill(c.shaderBuffer); + strokeCommands.beginShaderFill(c.shaderBuffer); + } + else + { + var c = data.readBeginFill(); + fillCommands.beginFill(c.color, 1); + strokeCommands.beginFill(c.color, 1); + } + + case DRAW_CIRCLE: + var c = data.readDrawCircle(); + fillCommands.drawCircle(c.x, c.y, c.radius); + strokeCommands.drawCircle(c.x, c.y, c.radius); + + case DRAW_ELLIPSE: + var c = data.readDrawEllipse(); + fillCommands.drawEllipse(c.x, c.y, c.width, c.height); + strokeCommands.drawEllipse(c.x, c.y, c.width, c.height); + + case DRAW_RECT: + var c = data.readDrawRect(); + fillCommands.drawRect(c.x, c.y, c.width, c.height); + strokeCommands.drawRect(c.x, c.y, c.width, c.height); + + case DRAW_ROUND_RECT: + var c = data.readDrawRoundRect(); + fillCommands.drawRoundRect(c.x, c.y, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + strokeCommands.drawRoundRect(c.x, c.y, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + + case WINDING_EVEN_ODD: + windingRule = CanvasWindingRule.EVENODD; + + case WINDING_NON_ZERO: + windingRule = CanvasWindingRule.NONZERO; + + default: + data.skip(type); + } + } + + var hitTest = false; + + if (fillCommands.length > 0) + { + endFill(); + } + + if (hasFill && context.isPointInPath(x, y, windingRule)) + { + hitTest = true; + } + + if (strokeCommands.length > 0) + { + endStroke(); + } + + if (hasStroke && (context : Dynamic).isPointInStroke(x, y)) + { + hitTest = true; + } + + data.destroy(); + + graphics.__canvas = cacheCanvas; + graphics.__context = cacheContext; + CanvasGraphics.graphics = null; + return hitTest; + } + #end + + return false; + } + + private static inline function isCCW(x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float):Bool + { + return ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)) < 0; + } + + private static function normalizeUVT(uvt:Vector, skipT:Bool = false):NormalizedUVT + { + 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; + } + } + + if (!skipT) + { + return {max: max, uvt: uvt}; + } + + var result = new Vector(); + + for (t in 1...len + 1) + { + if (skipT && t % 3 == 0) + { + continue; + } + + result.push(uvt[t - 1]); + } + + return {max: max, uvt: result}; + } + + private static function playCommands(commands:DrawCommandBuffer, stroke:Bool = false):Void + { + #if (js && html5) + 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; + var setStart = false; + + windingRule = CanvasWindingRule.EVENODD; + setSmoothing(true); + + var hasPath:Bool = false; + + var data = new DrawCommandReader(commands); + + var x, + y, + width, + height, + kappa = .5522848, + ox, + oy, + xe, + ye, + xm, + ym, + r, + g, + b; + var optimizationUsed, + canOptimizeMatrix, + st:Float, + sr:Float, + sb:Float, + sl:Float, + stl = null, + sbr = null; + + for (type in commands.types) + { + switch (type) + { + case CUBIC_CURVE_TO: + var c = data.readCubicCurveTo(); + hasPath = true; + context.bezierCurveTo(c.controlX1 + - offsetX, c.controlY1 + - offsetY, c.controlX2 + - offsetX, c.controlY2 + - offsetY, c.anchorX + - offsetX, + c.anchorY + - offsetY); + + case CURVE_TO: + var c = data.readCurveTo(); + hasPath = true; + context.quadraticCurveTo(c.controlX - offsetX, c.controlY - offsetY, c.anchorX - offsetX, c.anchorY - offsetY); + + case DRAW_CIRCLE: + var c = data.readDrawCircle(); + hasPath = true; + context.moveTo(c.x - offsetX + c.radius, c.y - offsetY); + context.arc(c.x - offsetX, c.y - offsetY, c.radius, 0, Math.PI * 2, true); + + case DRAW_ELLIPSE: + var c = data.readDrawEllipse(); + hasPath = true; + x = c.x; + y = c.y; + width = c.width; + height = c.height; + x -= offsetX; + y -= offsetY; + + 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 DRAW_ROUND_RECT: + var c = data.readDrawRoundRect(); + hasPath = true; + drawRoundRect(c.x - offsetX, c.y - offsetY, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + + case LINE_TO: + var c = data.readLineTo(); + hasPath = true; + context.lineTo(c.x - offsetX, c.y - offsetY); + + positionX = c.x; + positionY = c.y; + + if (positionX == startX && positionY == startY) + { + closeGap = true; + } + + case MOVE_TO: + var c = data.readMoveTo(); + context.moveTo(c.x - offsetX, c.y - offsetY); + + positionX = c.x; + positionY = c.y; + + if (setStart && c.x != startX && c.y != startY) + { + closeGap = true; + } + + startX = c.x; + startY = c.y; + setStart = true; + + case LINE_STYLE: + var c = data.readLineStyle(); + if (stroke && hasStroke) + { + closePath(true); + } + + context.moveTo(positionX - offsetX, positionY - offsetY); + + if (c.thickness == null) + { + hasStroke = false; + } + else + { + context.lineWidth = (c.thickness > 0 ? c.thickness : 1); + + context.lineJoin = (c.joints == null ? "round" : Std.string(c.joints).toLowerCase()); + context.lineCap = (c.caps == null ? "round" : switch (c.caps) + { + case CapsStyle.NONE: "butt"; + default: Std.string(c.caps).toLowerCase(); + }); + + context.miterLimit = c.miterLimit; + + if (c.alpha == 1) + { + context.strokeStyle = "#" + StringTools.hex(c.color & 0x00FFFFFF, 6); + } + else + { + r = (c.color & 0xFF0000) >>> 16; + g = (c.color & 0x00FF00) >>> 8; + b = (c.color & 0x0000FF); + + context.strokeStyle = "rgba(" + r + ", " + g + ", " + b + ", " + c.alpha + ")"; + } + + setSmoothing(true); + hasStroke = true; + } + + case LINE_GRADIENT_STYLE: + var c = data.readLineGradientStyle(); + if (stroke && hasStroke) + { + closePath(true); + } + + context.moveTo(positionX - offsetX, positionY - offsetY); + context.strokeStyle = createGradientPattern(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + + setSmoothing(true); + hasStroke = true; + + case LINE_BITMAP_STYLE: + var c = data.readLineBitmapStyle(); + if (stroke && hasStroke) + { + closePath(true); + } + + context.moveTo(positionX - offsetX, positionY - offsetY); + context.strokeStyle = createBitmapFill(c.bitmap, c.repeat, c.smooth); + + hasStroke = true; + + case BEGIN_BITMAP_FILL: + var c = data.readBeginBitmapFill(); + bitmapFill = c.bitmap; + context.fillStyle = createBitmapFill(c.bitmap, c.repeat, c.smooth); + hasFill = true; + + if (c.matrix != null) + { + pendingMatrix = c.matrix; + inversePendingMatrix = c.matrix.clone(); + inversePendingMatrix.invert(); + } + else + { + pendingMatrix = null; + inversePendingMatrix = null; + } + + case BEGIN_FILL: + var c = data.readBeginFill(); + if (c.alpha < 0.005) + { + hasFill = false; + } + else + { + if (c.alpha == 1) + { + context.fillStyle = "#" + StringTools.hex(c.color & 0xFFFFFF, 6); + } + else + { + r = (c.color & 0xFF0000) >>> 16; + g = (c.color & 0x00FF00) >>> 8; + b = (c.color & 0x0000FF); + + context.fillStyle = "rgba(" + r + ", " + g + ", " + b + ", " + c.alpha + ")"; + } + + bitmapFill = null; + setSmoothing(true); + hasFill = true; + } + + case BEGIN_GRADIENT_FILL: + var c = data.readBeginGradientFill(); + context.fillStyle = createGradientPattern(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + + bitmapFill = null; + setSmoothing(true); + hasFill = true; + + case BEGIN_SHADER_FILL: + var c = data.readBeginShaderFill(); + var shaderBuffer = c.shaderBuffer; + + if (shaderBuffer.inputCount > 0) + { + bitmapFill = shaderBuffer.inputs[0]; + context.fillStyle = createBitmapFill(bitmapFill, shaderBuffer.inputWrap[0] != CLAMP, shaderBuffer.inputFilter[0] != NEAREST); + hasFill = true; + + pendingMatrix = null; + inversePendingMatrix = null; + } + + case DRAW_QUADS: + var c = data.readDrawQuads(); + var rects = c.rects; + var indices = c.indices; + var transforms = c.transforms; + + var hasIndices = (indices != null); + var transformABCD = false, transformXY = false; + + var length = hasIndices ? indices.length : Math.floor(rects.length / 4); + if (length == 0) return; + + if (transforms != null) + { + if (transforms.length >= length * 6) + { + transformABCD = true; + transformXY = true; + } + else if (transforms.length >= length * 4) + { + transformABCD = true; + } + else if (transforms.length >= length * 2) + { + transformXY = true; + } + } + + var tileRect = Rectangle.__pool.get(); + var tileTransform = Matrix.__pool.get(); + + var transform = graphics.__renderTransform; + // var roundPixels = renderer.__roundPixels; + var alpha = CanvasGraphics.worldAlpha; + + var ri, ti; + + context.save(); // TODO: Restore transform without save/restore + + for (i in 0...length) + { + ri = (hasIndices ? (indices[i] * 4) : i * 4); + if (ri < 0) continue; + tileRect.setTo(rects[ri], rects[ri + 1], rects[ri + 2], rects[ri + 3]); + + if (tileRect.width <= 0 || tileRect.height <= 0) + { + continue; + } + + if (transformABCD && transformXY) + { + ti = i * 6; + tileTransform.setTo(transforms[ti], transforms[ti + 1], transforms[ti + 2], transforms[ti + 3], transforms[ti + 4], + transforms[ti + 5]); + } + else if (transformABCD) + { + ti = i * 4; + tileTransform.setTo(transforms[ti], transforms[ti + 1], transforms[ti + 2], transforms[ti + 3], tileRect.x, tileRect.y); + } + else if (transformXY) + { + ti = i * 2; + tileTransform.tx = transforms[ti]; + tileTransform.ty = transforms[ti + 1]; + } + else + { + tileTransform.tx = tileRect.x; + tileTransform.ty = tileRect.y; + } + + tileTransform.tx += positionX - offsetX; + tileTransform.ty += positionY - offsetY; + tileTransform.concat(transform); + + // if (roundPixels) { + + // tileTransform.tx = Math.round (tileTransform.tx); + // tileTransform.ty = Math.round (tileTransform.ty); + + // } + + context.setTransform(tileTransform.a, tileTransform.b, tileTransform.c, tileTransform.d, tileTransform.tx, tileTransform.ty); + + if (bitmapFill != null) + { + context.drawImage(bitmapFill.image.src, tileRect.x, tileRect.y, tileRect.width, tileRect.height, 0, 0, tileRect.width, + tileRect.height); + } + else + { + context.fillRect(0, 0, tileRect.width, tileRect.height); + } + } + + Rectangle.__pool.release(tileRect); + Matrix.__pool.release(tileTransform); + + context.restore(); + + case DRAW_TRIANGLES: + var c = data.readDrawTriangles(); + + var v = c.vertices; + var ind = c.indices; + var uvt = c.uvtData; + var pattern:CanvasElement = null; + var colorFill = bitmapFill == null; + + if (colorFill && uvt != null) + { + break; + } + + if (!colorFill) + { + // TODO move this to Graphics? + + if (uvt == null) + { + uvt = new Vector(); + + for (i in 0...(Std.int(v.length / 2))) + { + uvt.push(v[i * 2] - offsetX / bitmapFill.width); + uvt.push(v[i * 2 + 1] - offsetY / bitmapFill.height); + } + } + + var skipT = uvt.length != v.length; + var normalizedUVT = normalizeUVT(uvt, skipT); + var maxUVT = normalizedUVT.max; + uvt = normalizedUVT.uvt; + + if (maxUVT > 1) + { + pattern = createTempPatternCanvas(bitmapFill, bitmapRepeat, Std.int(bounds.width), Std.int(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; + + 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] - offsetX; + y1 = v[iay] - offsetY; + x2 = v[ibx] - offsetX; + y2 = v[iby] - offsetY; + x3 = v[icx] - offsetX; + y3 = v[icy] - offsetY; + + switch (c.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) + { + context.beginPath(); + context.moveTo(x1, y1); + context.lineTo(x2, y2); + context.lineTo(x3, y3); + context.closePath(); + if (!hitTesting) context.fill(windingRule); + i += 3; + continue; + } + + 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; + context.restore(); + continue; + } + + context.save(); + context.beginPath(); + context.moveTo(x1, y1); + context.lineTo(x2, y2); + context.lineTo(x3, y3); + context.closePath(); + context.clip(); + + 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, pattern.width, pattern.height); + context.restore(); + + i += 3; + } + + case DRAW_RECT: + var c = data.readDrawRect(); + optimizationUsed = false; + + if (bitmapFill != null && !hitTesting) + { + st = 0; + sr = 0; + sb = 0; + sl = 0; + + canOptimizeMatrix = true; + + if (pendingMatrix != null) + { + if (pendingMatrix.b != 0 || pendingMatrix.c != 0) + { + canOptimizeMatrix = false; + } + else + { + if (stl == null) stl = Point.__pool.get(); + if (sbr == null) sbr = Point.__pool.get(); + + stl.setTo(c.x, c.y); + inversePendingMatrix.__transformPoint(stl); + + sbr.setTo(c.x + c.width, c.y + c.height); + inversePendingMatrix.__transformPoint(sbr); + + st = stl.y; + sl = stl.x; + sb = sbr.y; + sr = sbr.x; + } + } + else + { + st = c.y; + sl = c.x; + sb = c.y + c.height; + sr = c.x + c.width; + } + + if (canOptimizeMatrix && st >= 0 && sl >= 0 && sr <= bitmapFill.width && sb <= bitmapFill.height) + { + optimizationUsed = true; + if (!hitTesting) context.drawImage(bitmapFill.image.src, sl, st, sr - sl, sb - st, c.x - offsetX, c.y - offsetY, c.width, c.height); + } + } + + if (!optimizationUsed) + { + hasPath = true; + context.rect(c.x - offsetX, c.y - offsetY, c.width, c.height); + } + + case WINDING_EVEN_ODD: + windingRule = CanvasWindingRule.EVENODD; + + case WINDING_NON_ZERO: + windingRule = CanvasWindingRule.NONZERO; + + default: + data.skip(type); + } + } + + if (stl != null) Point.__pool.release(stl); + if (sbr != null) Point.__pool.release(sbr); + + data.destroy(); + + if (hasPath) + { + if (stroke && hasStroke) + { + if (hasFill) + { + if (positionX != startX || positionY != startY) + { + context.lineTo(startX - offsetX, startY - offsetY); + closeGap = true; + } + + if (closeGap) closePath(true); + } + else if (closeGap && positionX == startX && positionY == startY) + { + closePath(true); + } + + if (!hitTesting) context.stroke(); + } + + if (!stroke) + { + if (hasFill || bitmapFill != null) + { + context.translate(-bounds.x, -bounds.y); + + if (pendingMatrix != null) + { + context.transform(pendingMatrix.a, pendingMatrix.b, pendingMatrix.c, pendingMatrix.d, pendingMatrix.tx, pendingMatrix.ty); + if (!hitTesting) context.fill(windingRule); + context.transform(inversePendingMatrix.a, inversePendingMatrix.b, inversePendingMatrix.c, inversePendingMatrix.d, + inversePendingMatrix.tx, inversePendingMatrix.ty); + } + else + { + if (!hitTesting) context.fill(windingRule); + } + + context.translate(bounds.x, bounds.y); + context.closePath(); + } + } + } + #end + } + + public static function render(graphics:Graphics, renderer:CanvasRenderer):Void + { + #if (js && html5) + #if (openfl_disable_hdpi || openfl_disable_hdpi_graphics) + var pixelRatio = 1; + #else + var pixelRatio = renderer.__pixelRatio; + #end + + graphics.__update(renderer.__worldTransform, pixelRatio); + + if (graphics.__softwareDirty) + { + hitTesting = false; + + CanvasGraphics.graphics = graphics; + CanvasGraphics.allowSmoothing = renderer.__allowSmoothing; + CanvasGraphics.worldAlpha = renderer.__getAlpha(graphics.__owner.__worldAlpha); + bounds = graphics.__bounds; + + var width = graphics.__width; + var height = graphics.__height; + + if (!graphics.__visible || graphics.__commands.length == 0 || bounds == null || width < 1 || height < 1) + { + graphics.__canvas = null; + graphics.__context = null; + graphics.__bitmap = null; + } + else + { + if (graphics.__canvas == null) + { + graphics.__canvas = cast Browser.document.createElement("canvas"); + graphics.__context = graphics.__canvas.getContext("2d"); + } + + context = graphics.__context; + var transform = graphics.__renderTransform; + var canvas = graphics.__canvas; + + var scale = renderer.__pixelRatio; + var scaledWidth = Std.int(width * scale); + var scaledHeight = Std.int(height * scale); + + renderer.__setBlendModeContext(context, NORMAL); + + if (renderer.__isDOM) + { + if (canvas.width == scaledWidth && canvas.height == scaledHeight) + { + context.clearRect(0, 0, scaledWidth, scaledHeight); + } + else + { + canvas.width = scaledWidth; + canvas.height = scaledHeight; + canvas.style.width = width + "px"; + canvas.style.height = height + "px"; + } + + var transform = graphics.__renderTransform; + context.setTransform(transform.a * scale, transform.b * scale, transform.c * scale, transform.d * scale, transform.tx * scale, + transform.ty * scale); + } + else + { + if (canvas.width == scaledWidth && canvas.height == scaledHeight) + { + context.closePath(); + context.setTransform(1, 0, 0, 1, 0, 0); + context.clearRect(0, 0, scaledWidth, scaledHeight); + } + else + { + canvas.width = width; + canvas.height = height; + } + + context.setTransform(transform.a, transform.b, transform.c, transform.d, transform.tx, transform.ty); + } + + fillCommands.clear(); + strokeCommands.clear(); + + hasFill = false; + hasStroke = false; + bitmapFill = null; + bitmapRepeat = false; + + var hasLineStyle = false; + var initStrokeX = 0.0; + var initStrokeY = 0.0; + + windingRule = CanvasWindingRule.EVENODD; + + var data = new DrawCommandReader(graphics.__commands); + + for (type in graphics.__commands.types) + { + switch (type) + { + case CUBIC_CURVE_TO: + var c = data.readCubicCurveTo(); + fillCommands.cubicCurveTo(c.controlX1, c.controlY1, c.controlX2, c.controlY2, c.anchorX, c.anchorY); + + if (hasLineStyle) + { + strokeCommands.cubicCurveTo(c.controlX1, c.controlY1, c.controlX2, c.controlY2, c.anchorX, c.anchorY); + } + else + { + initStrokeX = c.anchorX; + initStrokeY = c.anchorY; + } + + case CURVE_TO: + var c = data.readCurveTo(); + fillCommands.curveTo(c.controlX, c.controlY, c.anchorX, c.anchorY); + + if (hasLineStyle) + { + strokeCommands.curveTo(c.controlX, c.controlY, c.anchorX, c.anchorY); + } + else + { + initStrokeX = c.anchorX; + initStrokeY = c.anchorY; + } + + case LINE_TO: + var c = data.readLineTo(); + fillCommands.lineTo(c.x, c.y); + + if (hasLineStyle) + { + strokeCommands.lineTo(c.x, c.y); + } + else + { + initStrokeX = c.x; + initStrokeY = c.y; + } + + case MOVE_TO: + var c = data.readMoveTo(); + fillCommands.moveTo(c.x, c.y); + + if (hasLineStyle) + { + strokeCommands.moveTo(c.x, c.y); + } + else + { + initStrokeX = c.x; + initStrokeY = c.y; + } + + case END_FILL: + data.readEndFill(); + endFill(); + endStroke(); + hasFill = false; + hasLineStyle = false; + bitmapFill = null; + initStrokeX = 0; + initStrokeY = 0; + + case LINE_GRADIENT_STYLE: + var c = data.readLineGradientStyle(); + + if (!hasLineStyle && (initStrokeX != 0 || initStrokeY != 0)) + { + strokeCommands.moveTo(initStrokeX, initStrokeY); + initStrokeX = 0; + initStrokeY = 0; + } + + hasLineStyle = true; + strokeCommands.lineGradientStyle(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + + case LINE_BITMAP_STYLE: + var c = data.readLineBitmapStyle(); + + if (!hasLineStyle && (initStrokeX != 0 || initStrokeY != 0)) + { + strokeCommands.moveTo(initStrokeX, initStrokeY); + initStrokeX = 0; + initStrokeY = 0; + } + + hasLineStyle = true; + strokeCommands.lineBitmapStyle(c.bitmap, c.matrix, c.repeat, c.smooth); + + case LINE_STYLE: + var c = data.readLineStyle(); + + if (!hasLineStyle && c.thickness != null) + { + if (initStrokeX != 0 || initStrokeY != 0) + { + strokeCommands.moveTo(initStrokeX, initStrokeY); + initStrokeX = 0; + initStrokeY = 0; + } + } + + hasLineStyle = c.thickness != null; + strokeCommands.lineStyle(c.thickness, c.color, c.alpha, c.pixelHinting, c.scaleMode, c.caps, c.joints, c.miterLimit); + + case BEGIN_BITMAP_FILL, BEGIN_FILL, BEGIN_GRADIENT_FILL, BEGIN_SHADER_FILL: + endFill(); + endStroke(); + + if (type == BEGIN_BITMAP_FILL) + { + var c = data.readBeginBitmapFill(); + fillCommands.beginBitmapFill(c.bitmap, c.matrix, c.repeat, c.smooth); + strokeCommands.beginBitmapFill(c.bitmap, c.matrix, c.repeat, c.smooth); + } + else if (type == BEGIN_GRADIENT_FILL) + { + var c = data.readBeginGradientFill(); + fillCommands.beginGradientFill(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + strokeCommands.beginGradientFill(c.type, c.colors, c.alphas, c.ratios, c.matrix, c.spreadMethod, c.interpolationMethod, + c.focalPointRatio); + } + else if (type == BEGIN_SHADER_FILL) + { + var c = data.readBeginShaderFill(); + fillCommands.beginShaderFill(c.shaderBuffer); + strokeCommands.beginShaderFill(c.shaderBuffer); + } + else + { + var c = data.readBeginFill(); + fillCommands.beginFill(c.color, c.alpha); + strokeCommands.beginFill(c.color, c.alpha); + } + + case DRAW_CIRCLE: + var c = data.readDrawCircle(); + fillCommands.drawCircle(c.x, c.y, c.radius); + + if (hasLineStyle) + { + strokeCommands.drawCircle(c.x, c.y, c.radius); + } + + case DRAW_ELLIPSE: + var c = data.readDrawEllipse(); + fillCommands.drawEllipse(c.x, c.y, c.width, c.height); + + if (hasLineStyle) + { + strokeCommands.drawEllipse(c.x, c.y, c.width, c.height); + } + + case DRAW_RECT: + var c = data.readDrawRect(); + fillCommands.drawRect(c.x, c.y, c.width, c.height); + + if (hasLineStyle) + { + strokeCommands.drawRect(c.x, c.y, c.width, c.height); + } + + case DRAW_ROUND_RECT: + var c = data.readDrawRoundRect(); + fillCommands.drawRoundRect(c.x, c.y, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + + if (hasLineStyle) + { + strokeCommands.drawRoundRect(c.x, c.y, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + } + + case DRAW_QUADS: + var c = data.readDrawQuads(); + fillCommands.drawQuads(c.rects, c.indices, c.transforms); + + case DRAW_TRIANGLES: + var c = data.readDrawTriangles(); + fillCommands.drawTriangles(c.vertices, c.indices, c.uvtData, c.culling); + + case OVERRIDE_BLEND_MODE: + var c = data.readOverrideBlendMode(); + renderer.__setBlendModeContext(context, c.blendMode); + + case WINDING_EVEN_ODD: + data.readWindingEvenOdd(); + fillCommands.windingEvenOdd(); + windingRule = CanvasWindingRule.EVENODD; + + case WINDING_NON_ZERO: + data.readWindingNonZero(); + fillCommands.windingNonZero(); + windingRule = CanvasWindingRule.NONZERO; + + default: + data.skip(type); + } + } + + if (fillCommands.length > 0) + { + endFill(); + } + + if (strokeCommands.length > 0) + { + endStroke(); + } + + data.destroy(); + graphics.__bitmap = BitmapData.fromCanvas(graphics.__canvas); + } + + graphics.__softwareDirty = false; + graphics.__dirty = false; + CanvasGraphics.graphics = null; + } + #end + } + + public static function renderMask(graphics:Graphics, renderer:CanvasRenderer):Void + { + #if (js && html5) + // TODO: Move to normal render method, browsers appear to support more than + // one path in clipping now + + if (graphics.__commands.length != 0) + { + context = cast renderer.context; + + var positionX = 0.0; + var positionY = 0.0; + + var offsetX = 0; + var offsetY = 0; + + var data = new DrawCommandReader(graphics.__commands); + + var x, y, width, height, kappa = .5522848, ox, oy, xe, ye, xm, ym; + + for (type in graphics.__commands.types) + { + switch (type) + { + case CUBIC_CURVE_TO: + var c = data.readCubicCurveTo(); + context.bezierCurveTo(c.controlX1 + - offsetX, c.controlY1 + - offsetY, c.controlX2 + - offsetX, c.controlY2 + - offsetY, c.anchorX + - offsetX, + c.anchorY + - offsetY); + positionX = c.anchorX; + positionY = c.anchorY; + + case CURVE_TO: + var c = data.readCurveTo(); + context.quadraticCurveTo(c.controlX - offsetX, c.controlY - offsetY, c.anchorX - offsetX, c.anchorY - offsetY); + positionX = c.anchorX; + positionY = c.anchorY; + + case DRAW_CIRCLE: + var c = data.readDrawCircle(); + context.arc(c.x - offsetX, c.y - offsetY, c.radius, 0, Math.PI * 2, true); + + case DRAW_ELLIPSE: + var c = data.readDrawEllipse(); + x = c.x; + y = c.y; + width = c.width; + height = c.height; + x -= offsetX; + y -= offsetY; + + 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 (); + 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); + // closePath (false); + + case DRAW_RECT: + var c = data.readDrawRect(); + context.beginPath(); + context.rect(c.x - offsetX, c.y - offsetY, c.width, c.height); + context.closePath(); + + case DRAW_ROUND_RECT: + var c = data.readDrawRoundRect(); + drawRoundRect(c.x - offsetX, c.y - offsetY, c.width, c.height, c.ellipseWidth, c.ellipseHeight); + + case LINE_TO: + var c = data.readLineTo(); + context.lineTo(c.x - offsetX, c.y - offsetY); + positionX = c.x; + positionY = c.y; + + case MOVE_TO: + var c = data.readMoveTo(); + context.moveTo(c.x - offsetX, c.y - offsetY); + positionX = c.x; + positionY = c.y; + + default: + data.skip(type); + } + } + + data.destroy(); + } + #end + } + + private static function setSmoothing(smooth:Bool):Void + { + #if (js && html5) + if (!allowSmoothing) + { + smooth = false; + } + + if (context.imageSmoothingEnabled != smooth) + { + context.imageSmoothingEnabled = smooth; + } + #end + } +} + +private typedef NormalizedUVT = +{ + max:Float, + uvt:Vector +} +#end diff --git a/source/openfl/display/_internal/DrawCommandType.hx b/source/openfl/display/_internal/DrawCommandType.hx new file mode 100644 index 00000000..bf40facd --- /dev/null +++ b/source/openfl/display/_internal/DrawCommandType.hx @@ -0,0 +1,32 @@ +package openfl.display._internal; + +#if !flash +#if (haxe_ver >= 4.0) enum #else @:enum #end abstract DrawCommandType(Int) + +{ + var BEGIN_BITMAP_FILL; + var BEGIN_FILL; + var BEGIN_GRADIENT_FILL; + var BEGIN_SHADER_FILL; + var CUBIC_CURVE_TO; + var CURVE_TO; + var DRAW_CIRCLE; + var DRAW_ELLIPSE; + var DRAW_QUADS; + var DRAW_RECT; + var DRAW_ROUND_RECT; + var DRAW_TILES; + var DRAW_TRIANGLES; + var END_FILL; + var LINE_BITMAP_STYLE; + var LINE_GRADIENT_STYLE; + var LINE_STYLE; + var LINE_TO; + var MOVE_TO; + var OVERRIDE_BLEND_MODE; + var OVERRIDE_MATRIX; + var WINDING_EVEN_ODD; + var WINDING_NON_ZERO; + var UNKNOWN; +} +#end diff --git a/source/openfl/display3D/textures/TextureBase.hx b/source/openfl/display3D/textures/TextureBase.hx new file mode 100644 index 00000000..0838d35f --- /dev/null +++ b/source/openfl/display3D/textures/TextureBase.hx @@ -0,0 +1,435 @@ +package openfl.display3D.textures; + +#if !flash +import openfl.display3D._internal.GLFramebuffer; +import openfl.display3D._internal.GLRenderbuffer; +import openfl.display3D._internal.GLTexture; +import openfl.display3D._internal.ATFGPUFormat; +import openfl.display._internal.SamplerState; +import openfl.display.BitmapData; +import openfl.events.EventDispatcher; +import openfl.errors.Error; +import openfl.utils._internal.Log; +#if lime +import lime._internal.graphics.ImageCanvasUtil; +import lime.graphics.Image; +import lime.graphics.RenderContext; +#end + +/** + The TextureBase class is the base class for Context3D texture objects. + + **Note:** You cannot create your own texture classes using TextureBase. To add + functionality to a texture class, extend either Texture or CubeTexture instead. +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(openfl.display._internal.SamplerState) +@:access(openfl.display3D.Context3D) +@:access(openfl.display.BitmapData) +@:access(openfl.display.Stage) +class TextureBase extends EventDispatcher +{ + @:noCompletion private static var __compressedFormats:Map; + @:noCompletion private static var __compressedFormatsAlpha:Map; + @:noCompletion private static var __supportsBGRA:Null = null; + @:noCompletion private static var __textureFormat:Int; + @:noCompletion private static var __textureInternalFormat:Int; + + @:noCompletion private var __alphaTexture:TextureBase; + // private var __compressedMemoryUsage:Int; + @:noCompletion private var __context:Context3D; + @:noCompletion private var __format:Int; + @:noCompletion private var __glDepthRenderbuffer:GLRenderbuffer; + @:noCompletion private var __glFramebuffer:GLFramebuffer; + @:noCompletion private var __glStencilRenderbuffer:GLRenderbuffer; + @:noCompletion private var __height:Int; + @:noCompletion private var __internalFormat:Int; + // private var __memoryUsage:Int; + @:noCompletion private var __optimizeForRenderToTexture:Bool; + // private var __outputTextureMemoryUsage:Bool = false; + @:noCompletion private var __samplerState:SamplerState; + @:noCompletion private var __streamingLevels:Int; + @SuppressWarnings("checkstyle:Dynamic") @:noCompletion private var __textureContext:#if lime RenderContext #else Dynamic #end; + @:noCompletion private var __textureID:GLTexture; + @:noCompletion private var __textureTarget:Int; + @:noCompletion private var __width:Int; + + @:noCompletion private function new(context:Context3D) + { + super(); + + __context = context; + var gl = __context.gl; + // __textureTarget = target; + + __textureID = gl.createTexture(); + __textureContext = __context.__context; + + if (__supportsBGRA == null) + { + __textureInternalFormat = gl.RGBA; + + var bgraExtension:Dynamic = null; + #if (!js || !html5) + bgraExtension = gl.getExtension("EXT_bgra"); + if (bgraExtension == null) bgraExtension = gl.getExtension("EXT_texture_format_BGRA8888"); + if (bgraExtension == null) bgraExtension = gl.getExtension("APPLE_texture_format_BGRA8888"); + #end + + if (bgraExtension != null) + { + __supportsBGRA = true; + __textureFormat = bgraExtension.BGRA_EXT; + + #if (lime && !ios && !tvos) + if (context.__context.type == OPENGLES) + { + __textureInternalFormat = bgraExtension.BGRA_EXT; + } + #end + } + else + { + __supportsBGRA = false; + __textureFormat = gl.RGBA; + } + + __compressedFormats = new Map(); + __compressedFormatsAlpha = new Map(); + + #if (js && html5) + var dxtExtension = gl.getExtension("WEBGL_compressed_texture_s3tc"); + var etc1Extension = gl.getExtension("WEBGL_compressed_texture_etc1"); + // WEBGL_compressed_texture_pvrtc is not available on iOS Safari + var pvrtcExtension = gl.getExtension("WEBKIT_WEBGL_compressed_texture_pvrtc"); + #else + var dxtExtension = gl.getExtension("EXT_texture_compression_s3tc"); + var etc1Extension = gl.getExtension("OES_compressed_ETC1_RGB8_texture"); + var pvrtcExtension = gl.getExtension("IMG_texture_compression_pvrtc"); + #end + + if (dxtExtension != null) + { + __compressedFormats[ATFGPUFormat.DXT] = dxtExtension.COMPRESSED_RGBA_S3TC_DXT1_EXT; + __compressedFormatsAlpha[ATFGPUFormat.DXT] = dxtExtension.COMPRESSED_RGBA_S3TC_DXT5_EXT; + } + + if (etc1Extension != null) + { + #if (js && html5) + __compressedFormats[ATFGPUFormat.ETC1] = etc1Extension.COMPRESSED_RGB_ETC1_WEBGL; + __compressedFormatsAlpha[ATFGPUFormat.ETC1] = etc1Extension.COMPRESSED_RGB_ETC1_WEBGL; + #else + __compressedFormats[ATFGPUFormat.ETC1] = etc1Extension.ETC1_RGB8_OES; + __compressedFormatsAlpha[ATFGPUFormat.ETC1] = etc1Extension.ETC1_RGB8_OES; + #end + } + + if (pvrtcExtension != null) + { + __compressedFormats[ATFGPUFormat.PVRTC] = pvrtcExtension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + __compressedFormatsAlpha[ATFGPUFormat.PVRTC] = pvrtcExtension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + } + } + + __internalFormat = __textureInternalFormat; + __format = __textureFormat; + + // __memoryUsage = 0; + // __compressedMemoryUsage = 0; + } + + /** + Frees all GPU resources associated with this texture. After disposal, calling + `upload()` or rendering with this object fails. + **/ + public function dispose():Void + { + var gl = __context.gl; + + if (__alphaTexture != null) + { + __alphaTexture.dispose(); + __alphaTexture = null; + } + + if (__textureID != null) + { + gl.deleteTexture(__textureID); + __textureID = null; + } + + if (__glFramebuffer != null) + { + gl.deleteFramebuffer(__glFramebuffer); + __glFramebuffer = null; + } + + if (__glDepthRenderbuffer != null) + { + gl.deleteRenderbuffer(__glDepthRenderbuffer); + __glDepthRenderbuffer = null; + } + + if (__glStencilRenderbuffer != null) + { + gl.deleteRenderbuffer(__glStencilRenderbuffer); + __glStencilRenderbuffer = null; + } + } + + @SuppressWarnings("checkstyle:Dynamic") + @:noCompletion private function __getGLFramebuffer(enableDepthAndStencil:Bool, antiAlias:Int, surfaceSelector:Int):GLFramebuffer + { + var gl = __context.gl; + + if (__glFramebuffer == null) + { + __glFramebuffer = gl.createFramebuffer(); + __context.__bindGLFramebuffer(__glFramebuffer); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, __textureID, 0); + + if (__context.__enableErrorChecking) + { + var code = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + + if (code != gl.FRAMEBUFFER_COMPLETE) + { + Log.warn('Error: Context3D.setRenderToTexture status:${code} width:${__width} height:${__height}'); + } + } + } + + if (enableDepthAndStencil && __glDepthRenderbuffer == null) + { + __context.__bindGLFramebuffer(__glFramebuffer); + + if (Context3D.__glDepthStencil != 0) + { + __glDepthRenderbuffer = gl.createRenderbuffer(); + __glStencilRenderbuffer = __glDepthRenderbuffer; + + gl.bindRenderbuffer(gl.RENDERBUFFER, __glDepthRenderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, Context3D.__glDepthStencil, __width, __height); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, __glDepthRenderbuffer); + } + else + { + __glDepthRenderbuffer = gl.createRenderbuffer(); + __glStencilRenderbuffer = gl.createRenderbuffer(); + + gl.bindRenderbuffer(gl.RENDERBUFFER, __glDepthRenderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, __width, __height); + gl.bindRenderbuffer(gl.RENDERBUFFER, __glStencilRenderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, __width, __height); + + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, __glDepthRenderbuffer); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, __glStencilRenderbuffer); + } + + if (__context.__enableErrorChecking) + { + var code = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + + if (code != gl.FRAMEBUFFER_COMPLETE) + { + Log.warn('Error: Context3D.setRenderToTexture status:${code} width:${__width} height:${__height}'); + } + } + + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + } + + return __glFramebuffer; + } + + #if lime + @:noCompletion private function __getImage(bitmapData:BitmapData):Image + { + var image = bitmapData.image; + + if (!bitmapData.__isValid || image == null) + { + return null; + } + + #if (js && html5) + ImageCanvasUtil.sync(image, false); + #end + + #if (js && html5) + var gl = __context.gl; + + if (image.type != DATA && !image.premultiplied) + { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1); + } + else if (!image.premultiplied && image.transparent) + { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0); + image = image.clone(); + image.premultiplied = true; + } + + // TODO: Some way to support BGRA on WebGL? + + if (image.format != RGBA32) + { + image = image.clone(); + image.format = RGBA32; + image.buffer.premultiplied = true; + #if openfl_power_of_two + image.powerOfTwo = true; + #end + } + #else + if (#if openfl_power_of_two !image.powerOfTwo || #end (!image.premultiplied && image.transparent)) + { + image = image.clone(); + image.premultiplied = true; + #if openfl_power_of_two + image.powerOfTwo = true; + #end + } + #end + + return image; + } + #end + + @:noCompletion private function __getTexture():GLTexture + { + return __textureID; + } + + @:noCompletion private function __setSamplerState(state:SamplerState):Bool + { + if (!state.equals(__samplerState)) + { + var gl = __context.gl; + + if (__textureTarget == __context.gl.TEXTURE_CUBE_MAP) __context.__bindGLTextureCubeMap(__textureID); + else + { + __context.__bindGLTexture2D(__textureID); + if (state.mipfilter != MIPNONE) + { + gl.generateMipmap(__textureTarget); + state.mipmapGenerated = true; + } + } + + var wrapModeS = 0, wrapModeT = 0; + + switch (state.wrap) + { + case CLAMP: + wrapModeS = gl.CLAMP_TO_EDGE; + wrapModeT = gl.CLAMP_TO_EDGE; + case CLAMP_U_REPEAT_V: + wrapModeS = gl.CLAMP_TO_EDGE; + wrapModeT = gl.REPEAT; + case REPEAT: + wrapModeS = gl.REPEAT; + wrapModeT = gl.REPEAT; + case REPEAT_U_CLAMP_V: + wrapModeS = gl.REPEAT; + wrapModeT = gl.CLAMP_TO_EDGE; + default: + throw new Error("wrap bad enum"); + } + + var magFilter = 0, minFilter = 0; + + switch (state.filter) + { + case NEAREST: + magFilter = gl.NEAREST; + default: + magFilter = gl.LINEAR; + } + + switch (state.mipfilter) + { + case MIPLINEAR: + minFilter = state.filter == NEAREST ? gl.NEAREST_MIPMAP_LINEAR : gl.LINEAR_MIPMAP_LINEAR; + case MIPNEAREST: + minFilter = state.filter == NEAREST ? gl.NEAREST_MIPMAP_NEAREST : gl.LINEAR_MIPMAP_NEAREST; + case Context3DMipFilter.MIPNONE: + minFilter = state.filter == NEAREST ? gl.NEAREST : gl.LINEAR; + default: + throw new Error("mipfiter bad enum"); + } + + gl.texParameteri(__textureTarget, gl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(__textureTarget, gl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri(__textureTarget, gl.TEXTURE_WRAP_S, wrapModeS); + gl.texParameteri(__textureTarget, gl.TEXTURE_WRAP_T, wrapModeT); + gl.texParameterf(__textureTarget, 34049, state.lodBias); // GL_TEXTURE_LOD_BIAS + + + if (__samplerState == null) __samplerState = state.clone(); + __samplerState.copyFrom(state); + + return true; + } + + return false; + } + + #if lime + @:noCompletion private function __uploadFromImage(image:Image):Void + { + var gl = __context.gl; + var internalFormat, format; + + if (__textureTarget != gl.TEXTURE_2D) return; + + if (image.buffer.bitsPerPixel == 1) + { + internalFormat = gl.ALPHA; + format = gl.ALPHA; + } + else + { + internalFormat = TextureBase.__textureInternalFormat; + format = TextureBase.__textureFormat; + } + + __context.__bindGLTexture2D(__textureID); + + #if (js && html5) + if (image.type != DATA && !image.premultiplied) + { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1); + } + else if (!image.premultiplied && image.transparent) + { + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1); + // gl.pixelStorei (gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 0); + // textureImage = textureImage.clone (); + // textureImage.premultiplied = true; + } + + if (image.type == DATA) + { + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, image.buffer.width, image.buffer.height, 0, format, gl.UNSIGNED_BYTE, image.data); + } + else + { + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, format, gl.UNSIGNED_BYTE, image.src); + } + #else + gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, image.buffer.width, image.buffer.height, 0, format, gl.UNSIGNED_BYTE, image.data); + #end + + __context.__bindGLTexture2D(null); + } + #end +} +#else +typedef TextureBase = flash.display3D.textures.TextureBase; +#end diff --git a/source/openfl/geom/PerspectiveProjection.hx b/source/openfl/geom/PerspectiveProjection.hx new file mode 100644 index 00000000..644a859d --- /dev/null +++ b/source/openfl/geom/PerspectiveProjection.hx @@ -0,0 +1,147 @@ +package openfl.geom; + +#if !flash +/** + The `PerspectiveProjection` class provides an easy way to assign or modify the perspective + transformations of a display object and all of its children. For more complex or custom + perspective transformations, use the `Matrix3D` class. While the `PerspectiveProjection` class + provides basic three-dimensional presentation properties, the `Matrix3D` class provides more + detailed control over the three-dimensional presentation of display objects. + + Projection is a way of representing a three-dimensional object in a two-dimensional space, + like a cube projected onto a computer screen. Perspective projection uses a viewing frustum + (a rectangular pyramid) to model and project a three-dimensional world and its objects on the screen. + The viewing frustum becomes increasingly wider as it moves further from the origin of the viewpoint. + The origin of the viewpoint could be a camera or the eyes of an observer facing the screen. + The projected perspective produces the illusion of three dimensions with depth and distance, + where the objects closer to the screen appear larger than the objects farther from the screen. + + ![Frustum viewing area](/images/frustum.jpg) + + A default `PerspectiveProjection` object is a framework defined for perspective transformation of + the root object, based on the field of view and aspect ratio (dimensions) of the stage. + The projection center, the vanishing point, is set to the center of the stage, which means the + three-dimensional display objects disappear toward the center of the stage as they move + back in the z axis. The default viewpoint is at point (0,0) looking down the positive z axis. + The y-axis points down toward the bottom of the screen. You can gain access to the root display + object's perspective projection settings and change the field of view and projection center + properties of the perspectiveProjection property through the root object's `DisplayObject.transform` + property. + + You can also set a different perspective projection setting for a display object through the parent's + perspective projection. First, create a `PerspectiveProjection` object and set its `fieldOfView` and + projectionCenter properties. Next, assign the `PerspectiveProjection` object to the parent display + object using the `DisplayObject.transform` property. The specified projection matrix and transformation + will then apply to all the display object's three-dimensional children. +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +class PerspectiveProjection +{ + @SuppressWarnings("checkstyle:FieldDocComment") + @:dox(hide) @:noCompletion public static inline var TO_RADIAN:Float = 0.01745329251994329577; // Math.PI / 180 + + /** + Specifies an angle, as a degree between 0 and 180, for the field of view in three dimensions. + This value determines how strong the perspective transformation and distortion apply to a + three-dimensional display object with a non-zero z-coordinate. + + A degree close to 0 means that the screen's two-dimensional x- and y-coordinates are roughly + the same as the three-dimensional x-, y-, and z-coordinates with little or no distortion. + In other words, for a small angle, a display object moving down the z axis appears to stay + near the same size and moves little. + + A value close to 180 degrees results in a fisheye lens effect: positions with a z value smaller + than 0 are magnified, while positions with a z value larger than 0 are minimized. With a + large angle, a display object moving down the z axis appears to change size quickly and moves + a great distance. If the field of view is set to 0 or 180, nothing is seen on the screen. + **/ + public var fieldOfView(get, set):Float; + + /** + The distance between the eye or the viewpoint's origin (0,0,0) and the display object located + in the z axis. During the perspective transformation, the `focalLength` is calculated dynamically + using the angle of the field of view and the stage's aspect ratio (stage width divided by stage height). + **/ + public var focalLength:Float; + + /** + A two-dimensional point representing the center of the projection, the vanishing point for the display object. + + The projectionCenter property is an offset to the default registration point that is the upper left of the stage, + point (0,0). The default projection transformation center is in the middle of the stage, which means the + three-dimensional display objects disappear toward the center of the stage as they move backwards in the z axis. + **/ + public var projectionCenter:Point; + + @:noCompletion private var __fieldOfView:Float; + @:noCompletion private var matrix3D:Matrix3D; + + #if openfljs + @:noCompletion private static function __init__() + { + untyped Object.defineProperty(PerspectiveProjection.prototype, "fieldOfView", { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_fieldOfView (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_fieldOfView (v); }") + }); + } + #end + + /** + Creates an instance of a PerspectiveProjection object. + **/ + public function new() + { + __fieldOfView = 0; + this.focalLength = 0; + + matrix3D = new Matrix3D(); + projectionCenter = new Point(#if !openfl_unit_testing Lib.current.stage.stageWidth / 2, Lib.current.stage.stageHeight / 2 #end); + } + + /** + Returns the underlying Matrix3D object of the display object. + + A display object, like the root object, can have a `PerspectiveProjection` object without needing a `Matrix3D` + property defined for its transformations. In fact, use either a `PerspectiveProjection` or a `Matrix3D` object + to specify the perspective transformation. If when using the `PerspectiveProjection` object, a `Matrix3D` + object was needed, the `toMatrix3D()` method can retrieve the underlying `Matrix3D` object of the display object. + For example, the `toMatrix3D()` method can be used with the `Utils3D.projectVectors()` method. + @return The underlying `Matrix3D` object. + **/ + public function toMatrix3D():Matrix3D + { + if (#if neko __fieldOfView == null || #end projectionCenter == null) return null; + + var _mp = matrix3D.rawData; + _mp[0] = focalLength; + _mp[5] = focalLength; + _mp[12] = ((projectionCenter.x * 2) / Lib.current.stage.stageWidth) - 1; + _mp[13] = ((projectionCenter.y * 2) / Lib.current.stage.stageHeight) - 1; + _mp[11] = 1.0; + _mp[15] = 0; + + // matrix3D.rawData = [357.0370178222656,0,0,0,0,357.0370178222656,0,0,0,0,1,1,0,0,0,0]; + return matrix3D; + } + + // Getters & Setters + @:noCompletion private function get_fieldOfView():Float + { + return __fieldOfView; + } + + @:noCompletion private function set_fieldOfView(fieldOfView:Float):Float + { + __fieldOfView = fieldOfView * TO_RADIAN; + + this.focalLength = 250.0 * (1.0 / Math.tan(__fieldOfView * 0.5)); + + return __fieldOfView; + } +} +#else +typedef PerspectiveProjection = flash.geom.PerspectiveProjection; +#end diff --git a/source/openfl/ui/Keyboard.hx b/source/openfl/ui/Keyboard.hx new file mode 100644 index 00000000..3aa949ee --- /dev/null +++ b/source/openfl/ui/Keyboard.hx @@ -0,0 +1,984 @@ +package openfl.ui; + +#if !flash +#if lime +import lime.ui.KeyCode; +#end + +/** + The Keyboard class is used to build an interface that can be controlled by + a user with a standard keyboard. You can use the methods and properties of + the Keyboard class without using a constructor. The properties of the + Keyboard class are constants representing the keys that are most commonly + used to control games. + + @see [Capturing keyboard input](https://books.openfl.org/openfl-developers-guide/keyboard-input/capturing-keyboard-input.html) + @see `openfl.events.KeyboardEvent` +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:final class Keyboard +{ + /** + Constant associated with the key code value for the 0 key(48). + **/ + public static inline var NUMBER_0:Int = 48; + + /** + Constant associated with the key code value for the 1 key(49). + **/ + public static inline var NUMBER_1:Int = 49; + + /** + Constant associated with the key code value for the 2 key(50). + **/ + public static inline var NUMBER_2:Int = 50; + + /** + Constant associated with the key code value for the 3 key(51). + **/ + public static inline var NUMBER_3:Int = 51; + + /** + Constant associated with the key code value for the 4 key(52). + **/ + public static inline var NUMBER_4:Int = 52; + + /** + Constant associated with the key code value for the 5 key(53). + **/ + public static inline var NUMBER_5:Int = 53; + + /** + Constant associated with the key code value for the 6 key(54). + **/ + public static inline var NUMBER_6:Int = 54; + + /** + Constant associated with the key code value for the 7 key(55). + **/ + public static inline var NUMBER_7:Int = 55; + + /** + Constant associated with the key code value for the 8 key(56). + **/ + public static inline var NUMBER_8:Int = 56; + + /** + Constant associated with the key code value for the 9 key(57). + **/ + public static inline var NUMBER_9:Int = 57; + + /** + Constant associated with the key code value for the A key(65). + **/ + public static inline var A:Int = 65; + + /** + Constant associated with the key code value for the B key(66). + **/ + public static inline var B:Int = 66; + + /** + Constant associated with the key code value for the C key(67). + **/ + public static inline var C:Int = 67; + + /** + Constant associated with the key code value for the D key(68). + **/ + public static inline var D:Int = 68; + + /** + Constant associated with the key code value for the E key(69). + **/ + public static inline var E:Int = 69; + + /** + Constant associated with the key code value for the F key(70). + **/ + public static inline var F:Int = 70; + + /** + Constant associated with the key code value for the G key(71). + **/ + public static inline var G:Int = 71; + + /** + Constant associated with the key code value for the H key(72). + **/ + public static inline var H:Int = 72; + + /** + Constant associated with the key code value for the I key(73). + **/ + public static inline var I:Int = 73; + + /** + Constant associated with the key code value for the J key(74). + **/ + public static inline var J:Int = 74; + + /** + Constant associated with the key code value for the K key(75). + **/ + public static inline var K:Int = 75; + + /** + Constant associated with the key code value for the L key(76). + **/ + public static inline var L:Int = 76; + + /** + Constant associated with the key code value for the M key(77). + **/ + public static inline var M:Int = 77; + + /** + Constant associated with the key code value for the N key(78). + **/ + public static inline var N:Int = 78; + + /** + Constant associated with the key code value for the O key(79). + **/ + public static inline var O:Int = 79; + + /** + Constant associated with the key code value for the P key(80). + **/ + public static inline var P:Int = 80; + + /** + Constant associated with the key code value for the Q key(81). + **/ + public static inline var Q:Int = 81; + + /** + Constant associated with the key code value for the R key(82). + **/ + public static inline var R:Int = 82; + + /** + Constant associated with the key code value for the S key(83). + **/ + public static inline var S:Int = 83; + + /** + Constant associated with the key code value for the T key(84). + **/ + public static inline var T:Int = 84; + + /** + Constant associated with the key code value for the U key(85). + **/ + public static inline var U:Int = 85; + + /** + Constant associated with the key code value for the V key(85). + **/ + public static inline var V:Int = 86; + + /** + Constant associated with the key code value for the W key(87). + **/ + public static inline var W:Int = 87; + + /** + Constant associated with the key code value for the X key(88). + **/ + public static inline var X:Int = 88; + + /** + Constant associated with the key code value for the Y key(89). + **/ + public static inline var Y:Int = 89; + + /** + Constant associated with the key code value for the Z key(90). + **/ + public static inline var Z:Int = 90; + + /** + Constant associated with the key code value for the number 0 key on the + number pad(96). + **/ + public static inline var NUMPAD_0:Int = 96; + + /** + Constant associated with the key code value for the number 1 key on the + number pad(97). + **/ + public static inline var NUMPAD_1:Int = 97; + + /** + Constant associated with the key code value for the number 2 key on the + number pad(98). + **/ + public static inline var NUMPAD_2:Int = 98; + + /** + Constant associated with the key code value for the number 3 key on the + number pad(99). + **/ + public static inline var NUMPAD_3:Int = 99; + + /** + Constant associated with the key code value for the number 4 key on the + number pad(100). + **/ + public static inline var NUMPAD_4:Int = 100; + + /** + Constant associated with the key code value for the number 5 key on the + number pad(101). + **/ + public static inline var NUMPAD_5:Int = 101; + + /** + Constant associated with the key code value for the number 6 key on the + number pad(102). + **/ + public static inline var NUMPAD_6:Int = 102; + + /** + Constant associated with the key code value for the number 7 key on the + number pad(103). + **/ + public static inline var NUMPAD_7:Int = 103; + + /** + Constant associated with the key code value for the number 8 key on the + number pad(104). + **/ + public static inline var NUMPAD_8:Int = 104; + + /** + Constant associated with the key code value for the number 9 key on the + number pad(105). + **/ + public static inline var NUMPAD_9:Int = 105; + + /** + Constant associated with the key code value for the multiplication key on + the number pad(106). + **/ + public static inline var NUMPAD_MULTIPLY:Int = 106; + + /** + Constant associated with the key code value for the addition key on the + number pad(107). + **/ + public static inline var NUMPAD_ADD:Int = 107; + + /** + Constant associated with the key code value for the Enter key on the + number pad(108). + **/ + public static inline var NUMPAD_ENTER:Int = 108; + + /** + Constant associated with the key code value for the subtraction key on the + number pad(109). + **/ + public static inline var NUMPAD_SUBTRACT:Int = 109; + + /** + Constant associated with the key code value for the decimal key on the + number pad(110). + **/ + public static inline var NUMPAD_DECIMAL:Int = 110; + + /** + Constant associated with the key code value for the division key on the + number pad(111). + **/ + public static inline var NUMPAD_DIVIDE:Int = 111; + + /** + Constant associated with the key code value for the F1 key(112). + **/ + public static inline var F1:Int = 112; + + /** + Constant associated with the key code value for the F2 key(113). + **/ + public static inline var F2:Int = 113; + + /** + Constant associated with the key code value for the F3 key(114). + **/ + public static inline var F3:Int = 114; + + /** + Constant associated with the key code value for the F4 key(115). + **/ + public static inline var F4:Int = 115; + + /** + Constant associated with the key code value for the F5 key(116). + **/ + public static inline var F5:Int = 116; + + /** + Constant associated with the key code value for the F6 key(117). + **/ + public static inline var F6:Int = 117; + + /** + Constant associated with the key code value for the F7 key(118). + **/ + public static inline var F7:Int = 118; + + /** + Constant associated with the key code value for the F8 key(119). + **/ + public static inline var F8:Int = 119; + + /** + Constant associated with the key code value for the F9 key(120). + **/ + public static inline var F9:Int = 120; + + /** + Constant associated with the key code value for the F10 key(121). + **/ + public static inline var F10:Int = 121; // F10 is used by browser. + + /** + Constant associated with the key code value for the F11 key(122). + **/ + public static inline var F11:Int = 122; + + /** + Constant associated with the key code value for the F12 key(123). + **/ + public static inline var F12:Int = 123; + + /** + Constant associated with the key code value for the F13 key(124). + **/ + public static inline var F13:Int = 124; + + /** + Constant associated with the key code value for the F14 key(125). + **/ + public static inline var F14:Int = 125; + + /** + Constant associated with the key code value for the F15 key(126). + **/ + public static inline var F15:Int = 126; + + /** + Constant associated with the key code value for the Backspace key(8). + **/ + public static inline var BACKSPACE:Int = 8; + + /** + Constant associated with the key code value for the Tab key(9). + **/ + public static inline var TAB:Int = 9; + + /** + Constant associated with the key code value for the Alternate (Option) key + (18). + **/ + public static inline var ALTERNATE:Int = 18; + + /** + Constant associated with the key code value for the Enter key(13). + **/ + public static inline var ENTER:Int = 13; + + /** + Constant associated with the Mac command key (15). This constant is + currently only used for setting menu key equivalents. + **/ + public static inline var COMMAND:Int = 15; + + /** + Constant associated with the key code value for the Shift key(16). + **/ + public static inline var SHIFT:Int = 16; + + /** + Constant associated with the key code value for the Control key(17). + **/ + public static inline var CONTROL:Int = 17; + + @SuppressWarnings("checkstyle:FieldDocComment") + @:noCompletion @:dox(hide) public static inline var BREAK:Int = 19; + + /** + Constant associated with the key code value for the Caps Lock key(20). + **/ + public static inline var CAPS_LOCK:Int = 20; + + /** + Constant associated with the pseudo-key code for the the number pad(21). + Use to set numpad modifier on key equivalents + **/ + public static inline var NUMPAD:Int = 21; + + /** + Constant associated with the key code value for the Escape key(27). + **/ + public static inline var ESCAPE:Int = 27; + + /** + Constant associated with the key code value for the Spacebar(32). + **/ + public static inline var SPACE:Int = 32; + + /** + Constant associated with the key code value for the Page Up key(33). + **/ + public static inline var PAGE_UP:Int = 33; + + /** + Constant associated with the key code value for the Page Down key(34). + **/ + public static inline var PAGE_DOWN:Int = 34; + + /** + Constant associated with the key code value for the End key(35). + **/ + public static inline var END:Int = 35; + + /** + Constant associated with the key code value for the Home key(36). + **/ + public static inline var HOME:Int = 36; + + /** + Constant associated with the key code value for the Left Arrow key(37). + **/ + public static inline var LEFT:Int = 37; + + /** + Constant associated with the key code value for the Right Arrow key(39). + **/ + public static inline var RIGHT:Int = 39; + + /** + Constant associated with the key code value for the Up Arrow key(38). + **/ + public static inline var UP:Int = 38; + + /** + Constant associated with the key code value for the Down Arrow key(40). + **/ + public static inline var DOWN:Int = 40; + + /** + Constant associated with the key code value for the Insert key(45). + **/ + public static inline var INSERT:Int = 45; + + /** + Constant associated with the key code value for the Delete key(46). + **/ + public static inline var DELETE:Int = 46; + + @SuppressWarnings("checkstyle:FieldDocComment") + @:noCompletion @:dox(hide) public static inline var NUMLOCK:Int = 144; + + /** + Constant associated with the key code value for the ; key(186). + **/ + public static inline var SEMICOLON:Int = 186; + + /** + Constant associated with the key code value for the:Int = key(187). + **/ + public static inline var EQUAL:Int = 187; + + /** + Constant associated with the key code value for the , key(188). + **/ + public static inline var COMMA:Int = 188; + + /** + Constant associated with the key code value for the - key(189). + **/ + public static inline var MINUS:Int = 189; + + /** + Constant associated with the key code value for the . key(190). + **/ + public static inline var PERIOD:Int = 190; + + /** + Constant associated with the key code value for the / key(191). + **/ + public static inline var SLASH:Int = 191; + + /** + Constant associated with the key code value for the ` key(192). + **/ + public static inline var BACKQUOTE:Int = 192; + + /** + Constant associated with the key code value for the [ key(219). + **/ + public static inline var LEFTBRACKET:Int = 219; + + /** + Constant associated with the key code value for the \ key(220). + **/ + public static inline var BACKSLASH:Int = 220; + + /** + Constant associated with the key code value for the ] key(221). + **/ + public static inline var RIGHTBRACKET:Int = 221; + + /** + Constant associated with the key code value for the ' key(222). + **/ + public static inline var QUOTE:Int = 222; + + /** + Specifies whether the Caps Lock key is activated (`true`) or + not (`false`). + **/ + public static var capsLock(default, null):Bool; + + /** + Specifies whether the Num Lock key is activated (`true`) or not + (`false`). + **/ + public static var numLock(default, null):Bool; + + /** + Specifies whether the last key pressed is accessible by other SWF files. + By default, security restrictions prevent code from a SWF file in one + domain from accessing a keystroke generated from a SWF file in another + domain. + + @return The value `true` if the last key pressed can be + accessed. If access is not permitted, this method returns + `false`. + **/ + public static function isAccessible():Bool + { + // default browser security restrictions are always enforced + return false; + } + + #if lime + @:noCompletion private static inline function __convertKeyCode(key:KeyCode):Int + { + return switch (key) + { + case KeyCode.BACKSPACE: Keyboard.BACKSPACE; + case KeyCode.TAB: Keyboard.TAB; + case KeyCode.RETURN: Keyboard.ENTER; + case KeyCode.ESCAPE: Keyboard.ESCAPE; + case KeyCode.SPACE: Keyboard.SPACE; + case KeyCode.EXCLAMATION: Keyboard.NUMBER_1; + case KeyCode.QUOTE: Keyboard.QUOTE; + case KeyCode.HASH: Keyboard.NUMBER_3; + case KeyCode.DOLLAR: Keyboard.NUMBER_4; + case KeyCode.PERCENT: Keyboard.NUMBER_5; + case KeyCode.AMPERSAND: Keyboard.NUMBER_7; + case KeyCode.SINGLE_QUOTE: Keyboard.QUOTE; + case KeyCode.LEFT_PARENTHESIS: Keyboard.NUMBER_9; + case KeyCode.RIGHT_PARENTHESIS: Keyboard.NUMBER_0; + case KeyCode.ASTERISK: Keyboard.NUMBER_8; + // case KeyCode.PLUS: 0x2B; + case KeyCode.COMMA: Keyboard.COMMA; + case KeyCode.MINUS: Keyboard.MINUS; + case KeyCode.PERIOD: Keyboard.PERIOD; + case KeyCode.SLASH: Keyboard.SLASH; + case KeyCode.NUMBER_0: Keyboard.NUMBER_0; + case KeyCode.NUMBER_1: Keyboard.NUMBER_1; + case KeyCode.NUMBER_2: Keyboard.NUMBER_2; + case KeyCode.NUMBER_3: Keyboard.NUMBER_3; + case KeyCode.NUMBER_4: Keyboard.NUMBER_4; + case KeyCode.NUMBER_5: Keyboard.NUMBER_5; + case KeyCode.NUMBER_6: Keyboard.NUMBER_6; + case KeyCode.NUMBER_7: Keyboard.NUMBER_7; + case KeyCode.NUMBER_8: Keyboard.NUMBER_8; + case KeyCode.NUMBER_9: Keyboard.NUMBER_9; + case KeyCode.COLON: Keyboard.SEMICOLON; + case KeyCode.SEMICOLON: Keyboard.SEMICOLON; + case KeyCode.LESS_THAN: 60; + case KeyCode.EQUALS: Keyboard.EQUAL; + case KeyCode.GREATER_THAN: Keyboard.PERIOD; + case KeyCode.QUESTION: Keyboard.SLASH; + case KeyCode.AT: Keyboard.NUMBER_2; + case KeyCode.LEFT_BRACKET: Keyboard.LEFTBRACKET; + case KeyCode.BACKSLASH: Keyboard.BACKSLASH; + case KeyCode.RIGHT_BRACKET: Keyboard.RIGHTBRACKET; + case KeyCode.CARET: Keyboard.NUMBER_6; + case KeyCode.UNDERSCORE: Keyboard.MINUS; + case KeyCode.GRAVE: Keyboard.BACKQUOTE; + case KeyCode.A: Keyboard.A; + case KeyCode.B: Keyboard.B; + case KeyCode.C: Keyboard.C; + case KeyCode.D: Keyboard.D; + case KeyCode.E: Keyboard.E; + case KeyCode.F: Keyboard.F; + case KeyCode.G: Keyboard.G; + case KeyCode.H: Keyboard.H; + case KeyCode.I: Keyboard.I; + case KeyCode.J: Keyboard.J; + case KeyCode.K: Keyboard.K; + case KeyCode.L: Keyboard.L; + case KeyCode.M: Keyboard.M; + case KeyCode.N: Keyboard.N; + case KeyCode.O: Keyboard.O; + case KeyCode.P: Keyboard.P; + case KeyCode.Q: Keyboard.Q; + case KeyCode.R: Keyboard.R; + case KeyCode.S: Keyboard.S; + case KeyCode.T: Keyboard.T; + case KeyCode.U: Keyboard.U; + case KeyCode.V: Keyboard.V; + case KeyCode.W: Keyboard.W; + case KeyCode.X: Keyboard.X; + case KeyCode.Y: Keyboard.Y; + case KeyCode.Z: Keyboard.Z; + case KeyCode.DELETE: Keyboard.DELETE; + case KeyCode.CAPS_LOCK: Keyboard.CAPS_LOCK; + case KeyCode.F1: Keyboard.F1; + case KeyCode.F2: Keyboard.F2; + case KeyCode.F3: Keyboard.F3; + case KeyCode.F4: Keyboard.F4; + case KeyCode.F5: Keyboard.F5; + case KeyCode.F6: Keyboard.F6; + case KeyCode.F7: Keyboard.F7; + case KeyCode.F8: Keyboard.F8; + case KeyCode.F9: Keyboard.F9; + case KeyCode.F10: Keyboard.F10; + case KeyCode.F11: Keyboard.F11; + case KeyCode.F12: Keyboard.F12; + case KeyCode.PRINT_SCREEN: 301; + case KeyCode.SCROLL_LOCK: 145; + case KeyCode.PAUSE: Keyboard.BREAK; + case KeyCode.INSERT: Keyboard.INSERT; + case KeyCode.HOME: Keyboard.HOME; + case KeyCode.PAGE_UP: Keyboard.PAGE_UP; + case KeyCode.END: Keyboard.END; + case KeyCode.PAGE_DOWN: Keyboard.PAGE_DOWN; + case KeyCode.RIGHT: Keyboard.RIGHT; + case KeyCode.LEFT: Keyboard.LEFT; + case KeyCode.DOWN: Keyboard.DOWN; + case KeyCode.UP: Keyboard.UP; + case KeyCode.NUM_LOCK: Keyboard.NUMLOCK; + case KeyCode.NUMPAD_DIVIDE: Keyboard.NUMPAD_DIVIDE; + case KeyCode.NUMPAD_MULTIPLY: Keyboard.NUMPAD_MULTIPLY; + case KeyCode.NUMPAD_MINUS: Keyboard.NUMPAD_SUBTRACT; + case KeyCode.NUMPAD_PLUS: Keyboard.NUMPAD_ADD; + case KeyCode.NUMPAD_ENTER: #if openfl_numpad_enter Keyboard.NUMPAD_ENTER #else Keyboard.ENTER #end; + case KeyCode.NUMPAD_1: Keyboard.NUMPAD_1; + case KeyCode.NUMPAD_2: Keyboard.NUMPAD_2; + case KeyCode.NUMPAD_3: Keyboard.NUMPAD_3; + case KeyCode.NUMPAD_4: Keyboard.NUMPAD_4; + case KeyCode.NUMPAD_5: Keyboard.NUMPAD_5; + case KeyCode.NUMPAD_6: Keyboard.NUMPAD_6; + case KeyCode.NUMPAD_7: Keyboard.NUMPAD_7; + case KeyCode.NUMPAD_8: Keyboard.NUMPAD_8; + case KeyCode.NUMPAD_9: Keyboard.NUMPAD_9; + case KeyCode.NUMPAD_0: Keyboard.NUMPAD_0; + case KeyCode.NUMPAD_PERIOD: Keyboard.NUMPAD_DECIMAL; + case KeyCode.APPLICATION: 302; + // case KeyCode.POWER: 0x40000066; + // case KeyCode.NUMPAD_EQUALS: 0x40000067; + case KeyCode.F13: Keyboard.F13; + case KeyCode.F14: Keyboard.F14; + case KeyCode.F15: Keyboard.F15; + // case KeyCode.F16: 0x4000006B; + // case KeyCode.F17: 0x4000006C; + // case KeyCode.F18: 0x4000006D; + // case KeyCode.F19: 0x4000006E; + // case KeyCode.F20: 0x4000006F; + // case KeyCode.F21: 0x40000070; + // case KeyCode.F22: 0x40000071; + // case KeyCode.F23: 0x40000072; + // case KeyCode.F24: 0x40000073; + // case KeyCode.EXECUTE: 0x40000074; + // case KeyCode.HELP: 0x40000075; + // case KeyCode.MENU: 0x40000076; + // case KeyCode.SELECT: 0x40000077; + // case KeyCode.STOP: 0x40000078; + // case KeyCode.AGAIN: 0x40000079; + // case KeyCode.UNDO: 0x4000007A; + // case KeyCode.CUT: 0x4000007B; + // case KeyCode.COPY: 0x4000007C; + // case KeyCode.PASTE: 0x4000007D; + // case KeyCode.FIND: 0x4000007E; + // case KeyCode.MUTE: 0x4000007F; + // case KeyCode.VOLUME_UP: 0x40000080; + // case KeyCode.VOLUME_DOWN: 0x40000081; + // case KeyCode.NUMPAD_COMMA: 0x40000085; + ////case KeyCode.NUMPAD_EQUALS_AS400: 0x40000086; + // case KeyCode.ALT_ERASE: 0x40000099; + // case KeyCode.SYSTEM_REQUEST: 0x4000009A; + // case KeyCode.CANCEL: 0x4000009B; + // case KeyCode.CLEAR: 0x4000009C; + // case KeyCode.PRIOR: 0x4000009D; + case KeyCode.RETURN2: Keyboard.ENTER; + // case KeyCode.SEPARATOR: 0x4000009F; + // case KeyCode.OUT: 0x400000A0; + // case KeyCode.OPER: 0x400000A1; + // case KeyCode.CLEAR_AGAIN: 0x400000A2; + // case KeyCode.CRSEL: 0x400000A3; + // case KeyCode.EXSEL: 0x400000A4; + // case KeyCode.NUMPAD_00: 0x400000B0; + // case KeyCode.NUMPAD_000: 0x400000B1; + // case KeyCode.THOUSAND_SEPARATOR: 0x400000B2; + // case KeyCode.DECIMAL_SEPARATOR: 0x400000B3; + // case KeyCode.CURRENCY_UNIT: 0x400000B4; + // case KeyCode.CURRENCY_SUBUNIT: 0x400000B5; + // case KeyCode.NUMPAD_LEFT_PARENTHESIS: 0x400000B6; + // case KeyCode.NUMPAD_RIGHT_PARENTHESIS: 0x400000B7; + // case KeyCode.NUMPAD_LEFT_BRACE: 0x400000B8; + // case KeyCode.NUMPAD_RIGHT_BRACE: 0x400000B9; + // case KeyCode.NUMPAD_TAB: 0x400000BA; + // case KeyCode.NUMPAD_BACKSPACE: 0x400000BB; + // case KeyCode.NUMPAD_A: 0x400000BC; + // case KeyCode.NUMPAD_B: 0x400000BD; + // case KeyCode.NUMPAD_C: 0x400000BE; + // case KeyCode.NUMPAD_D: 0x400000BF; + // case KeyCode.NUMPAD_E: 0x400000C0; + // case KeyCode.NUMPAD_F: 0x400000C1; + // case KeyCode.NUMPAD_XOR: 0x400000C2; + // case KeyCode.NUMPAD_POWER: 0x400000C3; + // case KeyCode.NUMPAD_PERCENT: 0x400000C4; + // case KeyCode.NUMPAD_LESS_THAN: 0x400000C5; + // case KeyCode.NUMPAD_GREATER_THAN: 0x400000C6; + // case KeyCode.NUMPAD_AMPERSAND: 0x400000C7; + // case KeyCode.NUMPAD_DOUBLE_AMPERSAND: 0x400000C8; + // case KeyCode.NUMPAD_VERTICAL_BAR: 0x400000C9; + // case KeyCode.NUMPAD_DOUBLE_VERTICAL_BAR: 0x400000CA; + // case KeyCode.NUMPAD_COLON: 0x400000CB; + // case KeyCode.NUMPAD_HASH: 0x400000CC; + // case KeyCode.NUMPAD_SPACE: 0x400000CD; + // case KeyCode.NUMPAD_AT: 0x400000CE; + // case KeyCode.NUMPAD_EXCLAMATION: 0x400000CF; + // case KeyCode.NUMPAD_MEM_STORE: 0x400000D0; + // case KeyCode.NUMPAD_MEM_RECALL: 0x400000D1; + // case KeyCode.NUMPAD_MEM_CLEAR: 0x400000D2; + // case KeyCode.NUMPAD_MEM_ADD: 0x400000D3; + // case KeyCode.NUMPAD_MEM_SUBTRACT: 0x400000D4; + // case KeyCode.NUMPAD_MEM_MULTIPLY: 0x400000D5; + // case KeyCode.NUMPAD_MEM_DIVIDE: 0x400000D6; + // case KeyCode.NUMPAD_PLUS_MINUS: 0x400000D7; + // case KeyCode.NUMPAD_CLEAR: 0x400000D8; + // case KeyCode.NUMPAD_CLEAR_ENTRY: 0x400000D9; + // case KeyCode.NUMPAD_BINARY: 0x400000DA; + // case KeyCode.NUMPAD_OCTAL: 0x400000DB; + case KeyCode.NUMPAD_DECIMAL: Keyboard.NUMPAD_DECIMAL; + // case KeyCode.NUMPAD_HEXADECIMAL: 0x400000DD; + case KeyCode.LEFT_CTRL: Keyboard.CONTROL; + case KeyCode.LEFT_SHIFT: Keyboard.SHIFT; + case KeyCode.LEFT_ALT: Keyboard.ALTERNATE; + case KeyCode.LEFT_META: Keyboard.COMMAND; + case KeyCode.RIGHT_CTRL: Keyboard.CONTROL; + case KeyCode.RIGHT_SHIFT: Keyboard.SHIFT; + case KeyCode.RIGHT_ALT: Keyboard.ALTERNATE; + case KeyCode.RIGHT_META: Keyboard.COMMAND; + // case KeyCode.MODE: 0x40000101; + // case KeyCode.AUDIO_NEXT: 0x40000102; + // case KeyCode.AUDIO_PREVIOUS: 0x40000103; + // case KeyCode.AUDIO_STOP: 0x40000104; + // case KeyCode.AUDIO_PLAY: 0x40000105; + // case KeyCode.AUDIO_MUTE: 0x40000106; + // case KeyCode.MEDIA_SELECT: 0x40000107; + // case KeyCode.WWW: 0x40000108; + // case KeyCode.MAIL: 0x40000109; + // case KeyCode.CALCULATOR: 0x4000010A; + // case KeyCode.COMPUTER: 0x4000010B; + // case KeyCode.APP_CONTROL_SEARCH: 0x4000010C; + // case KeyCode.APP_CONTROL_HOME: 0x4000010D; + // case KeyCode.APP_CONTROL_BACK: 0x4000010E; + // case KeyCode.APP_CONTROL_FORWARD: 0x4000010F; + // case KeyCode.APP_CONTROL_STOP: 0x40000110; + // case KeyCode.APP_CONTROL_REFRESH: 0x40000111; + // case KeyCode.APP_CONTROL_BOOKMARKS: 0x40000112; + // case KeyCode.BRIGHTNESS_DOWN: 0x40000113; + // case KeyCode.BRIGHTNESS_UP: 0x40000114; + // case KeyCode.DISPLAY_SWITCH: 0x40000115; + // case KeyCode.BACKLIGHT_TOGGLE: 0x40000116; + // case KeyCode.BACKLIGHT_DOWN: 0x40000117; + // case KeyCode.BACKLIGHT_UP: 0x40000118; + // case KeyCode.EJECT: 0x40000119; + // case KeyCode.SLEEP: 0x4000011A; + default: cast key; + } + } + #end + + @:noCompletion private static function __getCharCode(key:Int, shift:Bool = false, capLock:Bool = false):Int + { + if (capLock) + { + if (!shift) + { + if (key >= Keyboard.A && key <= Keyboard.Z) + { + return key; + } + } + else + { + if (key >= Keyboard.A && key <= Keyboard.Z) + { + return key + 32; + } + } + } + if (!shift) + { + switch (key) + { + case Keyboard.BACKSPACE: + return 8; + case Keyboard.TAB: + return 9; + case Keyboard.ENTER: + return 13; + case Keyboard.ESCAPE: + return 27; + case Keyboard.SPACE: + return 32; + case Keyboard.SEMICOLON: + return 59; + case Keyboard.EQUAL: + return 61; + case Keyboard.COMMA: + return 44; + case Keyboard.MINUS: + return 45; + case Keyboard.PERIOD: + return 46; + case Keyboard.SLASH: + return 47; + case Keyboard.BACKQUOTE: + return 96; + case Keyboard.LEFTBRACKET: + return 91; + case Keyboard.BACKSLASH: + return 92; + case Keyboard.RIGHTBRACKET: + return 93; + case Keyboard.QUOTE: + return 39; + } + + if (key >= Keyboard.NUMBER_0 && key <= Keyboard.NUMBER_9) + { + return key - Keyboard.NUMBER_0 + 48; + } + + if (key >= Keyboard.A && key <= Keyboard.Z) + { + return key + 32; + } + } + else + { + switch (key) + { + case Keyboard.NUMBER_0: + return 41; + case Keyboard.NUMBER_1: + return 33; + case Keyboard.NUMBER_2: + return 64; + case Keyboard.NUMBER_3: + return 35; + case Keyboard.NUMBER_4: + return 36; + case Keyboard.NUMBER_5: + return 37; + case Keyboard.NUMBER_6: + return 94; + case Keyboard.NUMBER_7: + return 38; + case Keyboard.NUMBER_8: + return 42; + case Keyboard.NUMBER_9: + return 40; + case Keyboard.SEMICOLON: + return 58; + case Keyboard.EQUAL: + return 43; + case Keyboard.COMMA: + return 60; + case Keyboard.MINUS: + return 95; + case Keyboard.PERIOD: + return 62; + case Keyboard.SLASH: + return 63; + case Keyboard.BACKQUOTE: + return 126; + case Keyboard.LEFTBRACKET: + return 123; + case Keyboard.BACKSLASH: + return 124; + case Keyboard.RIGHTBRACKET: + return 125; + case Keyboard.QUOTE: + return 34; + } + + if (key >= Keyboard.A && key <= Keyboard.Z) + { + return key; + } + } + + if (key >= Keyboard.NUMPAD_0 && key <= Keyboard.NUMPAD_9) + { + return key - Keyboard.NUMPAD_0 + 48; + } + + switch (key) + { + case Keyboard.NUMPAD_MULTIPLY: + return 42; + case Keyboard.NUMPAD_ADD: + return 43; + case Keyboard.NUMPAD_ENTER: + return 44; + case Keyboard.NUMPAD_DECIMAL: + return 45; + case Keyboard.NUMPAD_DIVIDE: + return 46; + case Keyboard.DELETE: + return 127; + case Keyboard.ENTER: + return 13; + case Keyboard.BACKSPACE: + return 8; + } + + return 0; + } + + #if lime + @:noCompletion private static inline function __getKeyLocation(key:KeyCode):KeyLocation + { + return switch (key) + { + case KeyCode.LEFT_CTRL, KeyCode.LEFT_SHIFT, KeyCode.LEFT_ALT, KeyCode.LEFT_META: KeyLocation.LEFT; + case KeyCode.RIGHT_CTRL, KeyCode.RIGHT_SHIFT, KeyCode.RIGHT_ALT, KeyCode.RIGHT_META: KeyLocation.RIGHT; + case KeyCode.NUMPAD_DIVIDE, KeyCode.NUMPAD_MULTIPLY, KeyCode.NUMPAD_MINUS, KeyCode.NUMPAD_PLUS, KeyCode.NUMPAD_ENTER, KeyCode.NUMPAD_1, + KeyCode.NUMPAD_2, KeyCode.NUMPAD_3, KeyCode.NUMPAD_4, KeyCode.NUMPAD_5, KeyCode.NUMPAD_6, KeyCode.NUMPAD_7, KeyCode.NUMPAD_8, + KeyCode.NUMPAD_9, KeyCode.NUMPAD_0, KeyCode.NUMPAD_PERIOD, KeyCode.NUMPAD_DECIMAL: + KeyLocation.NUM_PAD; + default: KeyLocation.STANDARD; + } + } + #end +} +#else +typedef Keyboard = flash.ui.Keyboard; +#end