// ================================================================================================= // // Starling Framework // Copyright Gamua GmbH. All Rights Reserved. // // This program is free software. You can redistribute and/or modify it // in accordance with the terms of the accompanying license agreement. // // ================================================================================================= package starling.textures { import flash.display3D.textures.TextureBase; import flash.errors.IllegalOperationError; import flash.geom.Matrix; import flash.geom.Rectangle; import flash.utils.Dictionary; import starling.core.Starling; import starling.display.BlendMode; import starling.display.DisplayObject; import starling.display.Image; import starling.filters.FragmentFilter; import starling.rendering.Painter; import starling.rendering.RenderState; import starling.utils.execute; /** A RenderTexture is a dynamic texture onto which you can draw any display object. * *
After creating a render texture, just call the drawObject method to render
* an object directly onto the texture. The object will be drawn onto the texture at its current
* position, adhering its current rotation, scale and alpha properties.
Drawing is done very efficiently, as it is happening directly in graphics memory. After * you have drawn objects onto the texture, the performance will be just like that of a normal * texture — no matter how many objects you have drawn.
* *If you draw lots of objects at once, it is recommended to bundle the drawing calls in
* a block via the drawBundled method, like shown below. That will speed it up
* immensely, allowing you to draw hundreds of objects very quickly.
* renderTexture.drawBundled(function():void
* {
* for (var i:int=0; i<numDrawings; ++i)
* {
* image.rotation = (2 * Math.PI / numDrawings) * i;
* renderTexture.draw(image);
* }
* });
*
*
* To erase parts of a render texture, you can use any display object like a "rubber" by
* setting its blending mode to BlendMode.ERASE. To wipe it completely clean,
* use the clear method.
Older devices may require double buffering to support persistent render textures. Thus,
* you should disable the persistent parameter in the constructor if you only
* need to make one draw operation on the texture. The static useDoubleBuffering
* property allows you to customize if new textures will be created with or without double
* buffering.
Unfortunately, render textures are wiped clean when the render context is lost.
* This means that you need to manually recreate all their contents in such a case.
* One way to do that is by using the root.onRestore callback, like here:
For example, a drawing app would need to store information about all draw operations
* when they occur, and then recreate them inside onRestore on a context loss
* (preferably using drawBundled instead).
However, there is one problem: when that callback is executed, it's very likely that
* not all of your textures are already available, since they need to be restored, too (and
* that might take a while). You probably loaded your textures with the "AssetManager".
* In that case, you can listen to its TEXTURES_RESTORED event instead:
[Note that this time, there is no need to call clear, because that's the
* default behavior of onRestore, anyway — and we didn't modify that.]
Non-persistent textures can be used more efficiently on older devices; on modern
* hardware, it does not make a difference. For more information, have a look at the
* documentation of the useDoubleBuffering property.
draw together in a block. This avoids buffer
* switches and allows you to draw multiple objects into a non-persistent texture.
* Note that the 'antiAliasing' setting provided here overrides those provided in
* individual 'draw' calls.
*
* @param drawingBlock a callback with the form: function():void;* @param antiAliasing Values range from 0 (no antialiasing) to 4 (best quality). * Beginning with AIR 22, this feature is supported on all platforms * (except for software rendering mode). */ public function drawBundled(drawingBlock:Function, antiAliasing:int=0):void { renderBundled(drawingBlock, null, null, 1.0, antiAliasing); } private function render(object:DisplayObject, matrix:Matrix=null, alpha:Number=1.0):void { var painter:Painter = Starling.painter; var state:RenderState = painter.state; var wasCacheEnabled:Boolean = painter.cacheEnabled; var filter:FragmentFilter = object.filter; var mask:DisplayObject = object.mask; painter.cacheEnabled = false; painter.pushState(); state.alpha = object.alpha * alpha; state.setModelviewMatricesToIdentity(); state.blendMode = object.blendMode == BlendMode.AUTO ? BlendMode.NORMAL : object.blendMode; if (matrix) state.transformModelviewMatrix(matrix); else state.transformModelviewMatrix(object.transformationMatrix); if (mask) painter.drawMask(mask); if (filter) filter.render(painter); else object.render(painter); if (mask) painter.eraseMask(mask); painter.popState(); painter.cacheEnabled = wasCacheEnabled; } private function renderBundled(renderBlock:Function, object:DisplayObject=null, matrix:Matrix=null, alpha:Number=1.0, antiAliasing:int=0):void { var painter:Painter = Starling.painter; var state:RenderState = painter.state; if (!Starling.current.contextValid) return; // switch buffers if (isDoubleBuffered) { var tmpTexture:Texture = _activeTexture; _activeTexture = _bufferTexture; _bufferTexture = tmpTexture; _helperImage.texture = _bufferTexture; } painter.pushState(); var rootTexture:Texture = _activeTexture.root; state.setProjectionMatrix(0, 0, rootTexture.width, rootTexture.height, width, height); // limit drawing to relevant area sClipRect.setTo(0, 0, _activeTexture.width, _activeTexture.height); state.clipRect = sClipRect; state.setRenderTarget(_activeTexture, true, antiAliasing); painter.prepareToDraw(); if (isDoubleBuffered || !isPersistent || !_bufferReady) painter.clear(); // draw buffer if (isDoubleBuffered && _bufferReady) _helperImage.render(painter); else _bufferReady = true; try { _drawing = true; execute(renderBlock, object, matrix, alpha); } finally { _drawing = false; painter.popState(); } } /** Clears the render texture with a certain color and alpha value. Call without any * arguments to restore full transparency. */ public function clear(color:uint=0, alpha:Number=0.0):void { _activeTexture.root.clear(color, alpha); _bufferReady = true; } // properties /** Indicates if the render texture is using double buffering. This might be necessary for * persistent textures, depending on the runtime version and the value of * 'forceDoubleBuffering'. */ private function get isDoubleBuffered():Boolean { return _bufferTexture != null; } /** Indicates if the texture is persistent over multiple draw calls. */ public function get isPersistent():Boolean { return _isPersistent; } /** @inheritDoc */ public override function get base():TextureBase { return _activeTexture.base; } /** @inheritDoc */ public override function get root():ConcreteTexture { return _activeTexture.root; } /** Indicates if new persistent textures should use double buffering. Single buffering * is faster and requires less memory, but is not supported on all hardware. * *
By default, applications running with the profile "baseline" or "baselineConstrained" * will use double buffering; all others use just a single buffer. You can override this * behavior, though, by assigning a different value at runtime.
* * @default true for "baseline" and "baselineConstrained", false otherwise */ public static function get useDoubleBuffering():Boolean { if (Starling.current) { var painter:Painter = Starling.painter; var sharedData:Dictionary = painter.sharedData; if (USE_DOUBLE_BUFFERING_DATA_NAME in sharedData) { return sharedData[USE_DOUBLE_BUFFERING_DATA_NAME]; } else { var profile:String = painter.profile ? painter.profile : "baseline"; var value:Boolean = profile == "baseline" || profile == "baselineConstrained"; sharedData[USE_DOUBLE_BUFFERING_DATA_NAME] = value; return value; } } else return false; } public static function set useDoubleBuffering(value:Boolean):void { if (Starling.current == null) throw new IllegalOperationError("Starling not yet initialized"); else Starling.painter.sharedData[USE_DOUBLE_BUFFERING_DATA_NAME] = value; } } }