mirror of
https://github.com/TerryCavanagh/VVVVVV.git
synced 2024-06-26 14:38:30 +02:00
634 lines
26 KiB
ActionScript
634 lines
26 KiB
ActionScript
|
// =================================================================================================
|
||
|
//
|
||
|
// 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.filters
|
||
|
{
|
||
|
import flash.display3D.Context3DTextureFormat;
|
||
|
import flash.errors.IllegalOperationError;
|
||
|
import flash.geom.Matrix3D;
|
||
|
import flash.geom.Rectangle;
|
||
|
|
||
|
import starling.core.Starling;
|
||
|
import starling.core.starling_internal;
|
||
|
import starling.display.DisplayObject;
|
||
|
import starling.display.Stage;
|
||
|
import starling.events.Event;
|
||
|
import starling.events.EventDispatcher;
|
||
|
import starling.rendering.FilterEffect;
|
||
|
import starling.rendering.IndexData;
|
||
|
import starling.rendering.Painter;
|
||
|
import starling.rendering.VertexData;
|
||
|
import starling.textures.Texture;
|
||
|
import starling.textures.TextureSmoothing;
|
||
|
import starling.utils.MatrixUtil;
|
||
|
import starling.utils.Padding;
|
||
|
import starling.utils.Pool;
|
||
|
import starling.utils.RectangleUtil;
|
||
|
|
||
|
/** Dispatched when the settings change in a way that requires a redraw. */
|
||
|
[Event(name="change", type="starling.events.Event")]
|
||
|
|
||
|
/** Dispatched every frame on filters assigned to display objects connected to the stage. */
|
||
|
[Event(name="enterFrame", type="starling.events.EnterFrameEvent")]
|
||
|
|
||
|
/** The FragmentFilter class is the base class for all filter effects in Starling.
|
||
|
* All filters must extend this class. You can attach them to any display object through the
|
||
|
* <code>filter</code> property.
|
||
|
*
|
||
|
* <p>A fragment filter works in the following way:</p>
|
||
|
* <ol>
|
||
|
* <li>The object to be filtered is rendered into a texture.</li>
|
||
|
* <li>That texture is passed to the <code>process</code> method.</li>
|
||
|
* <li>This method processes the texture using a <code>FilterEffect</code> subclass
|
||
|
* that processes the input via fragment and vertex shaders to achieve a certain
|
||
|
* effect.</li>
|
||
|
* <li>If the filter requires several passes, the process method may execute the
|
||
|
* effect several times, or even make use of other filters in the process.</li>
|
||
|
* <li>In the end, a quad with the output texture is added to the batch renderer.
|
||
|
* In the next frame, if the object hasn't changed, the filter is drawn directly
|
||
|
* from the render cache.</li>
|
||
|
* <li>Alternatively, the last pass may be drawn directly to the back buffer. That saves
|
||
|
* one draw call, but means that the object may not be drawn from the render cache in
|
||
|
* the next frame. Starling makes an educated guess if that makes sense, but you can
|
||
|
* also force it to do so via the <code>alwaysDrawToBackBuffer</code> property.</li>
|
||
|
* </ol>
|
||
|
*
|
||
|
* <p>All of this is set up by the basic FragmentFilter class. Concrete subclasses
|
||
|
* just need to override the protected method <code>createEffect</code> and (optionally)
|
||
|
* <code>process</code>. Multi-pass filters must also override <code>numPasses</code>.</p>
|
||
|
*
|
||
|
* <p>Typically, any properties on the filter are just forwarded to an effect instance,
|
||
|
* which is then used automatically by <code>process</code> to render the filter pass.
|
||
|
* For a simple example on how to write a single-pass filter, look at the implementation of
|
||
|
* the <code>ColorMatrixFilter</code>; for a composite filter (i.e. a filter that combines
|
||
|
* several others), look at the <code>GlowFilter</code>.
|
||
|
* </p>
|
||
|
*
|
||
|
* <p>Beware that a filter instance may only be used on one object at a time!</p>
|
||
|
*
|
||
|
* <p><strong>Animated filters</strong></p>
|
||
|
*
|
||
|
* <p>The <code>process</code> method of a filter is only called when it's necessary, i.e.
|
||
|
* when the filter properties or the target display object changes. This means that you cannot
|
||
|
* rely on the method to be called on a regular basis, as needed when creating an animated
|
||
|
* filter class. Instead, you can do so by listening for an <code>ENTER_FRAME</code>-event.
|
||
|
* It is dispatched on the filter once every frame, as long as the filter is assigned to
|
||
|
* a display object that is connected to the stage.</p>
|
||
|
*
|
||
|
* <p><strong>Caching</strong></p>
|
||
|
*
|
||
|
* <p>Per default, whenever the target display object is changed in any way (i.e. the render
|
||
|
* cache fails), the filter is reprocessed. However, you can manually cache the filter output
|
||
|
* via the method of the same name: this will let the filter redraw the current output texture,
|
||
|
* even if the target object changes later on. That's especially useful if you add a filter
|
||
|
* to an object that changes only rarely, e.g. a TextField or an Image. Keep in mind, though,
|
||
|
* that you have to call <code>cache()</code> again in order for any changes to show up.</p>
|
||
|
*
|
||
|
* @see starling.rendering.FilterEffect
|
||
|
*/
|
||
|
public class FragmentFilter extends EventDispatcher
|
||
|
{
|
||
|
private var _quad:FilterQuad;
|
||
|
private var _target:DisplayObject;
|
||
|
private var _effect:FilterEffect;
|
||
|
private var _vertexData:VertexData;
|
||
|
private var _indexData:IndexData;
|
||
|
private var _padding:Padding;
|
||
|
private var _helper:FilterHelper;
|
||
|
private var _resolution:Number;
|
||
|
private var _textureFormat:String;
|
||
|
private var _textureSmoothing:String;
|
||
|
private var _alwaysDrawToBackBuffer:Boolean;
|
||
|
private var _cacheRequested:Boolean;
|
||
|
private var _cached:Boolean;
|
||
|
|
||
|
// helpers
|
||
|
private static var sMatrix3D:Matrix3D;
|
||
|
|
||
|
/** Creates a new instance. The base class' implementation just draws the unmodified
|
||
|
* input texture. */
|
||
|
public function FragmentFilter()
|
||
|
{
|
||
|
_resolution = 1.0;
|
||
|
_textureFormat = Context3DTextureFormat.BGRA;
|
||
|
_textureSmoothing = TextureSmoothing.BILINEAR;
|
||
|
|
||
|
// Handle lost context (using conventional Flash event for weak listener support)
|
||
|
Starling.current.stage3D.addEventListener(Event.CONTEXT3D_CREATE,
|
||
|
onContextCreated, false, 0, true);
|
||
|
}
|
||
|
|
||
|
/** Disposes all resources that have been created by the filter. */
|
||
|
public function dispose():void
|
||
|
{
|
||
|
Starling.current.stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreated);
|
||
|
|
||
|
if (_helper) _helper.dispose();
|
||
|
if (_effect) _effect.dispose();
|
||
|
if (_quad) _quad.dispose();
|
||
|
|
||
|
_effect = null;
|
||
|
_quad = null;
|
||
|
}
|
||
|
|
||
|
private function onContextCreated(event:Object):void
|
||
|
{
|
||
|
setRequiresRedraw();
|
||
|
}
|
||
|
|
||
|
/** Renders the filtered target object. Most users will never have to call this manually;
|
||
|
* it's executed automatically in the rendering process of the filtered display object.
|
||
|
*/
|
||
|
public function render(painter:Painter):void
|
||
|
{
|
||
|
if (_target == null)
|
||
|
throw new IllegalOperationError("Cannot render filter without target");
|
||
|
|
||
|
if (_target.is3D)
|
||
|
_cached = _cacheRequested = false;
|
||
|
|
||
|
if (!_cached || _cacheRequested)
|
||
|
{
|
||
|
renderPasses(painter, _cacheRequested);
|
||
|
_cacheRequested = false;
|
||
|
}
|
||
|
else if (_quad.visible)
|
||
|
{
|
||
|
_quad.render(painter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private function renderPasses(painter:Painter, forCache:Boolean):void
|
||
|
{
|
||
|
if (_helper == null) _helper = new FilterHelper(_textureFormat);
|
||
|
if (_quad == null) _quad = new FilterQuad(_textureSmoothing);
|
||
|
else { _helper.putTexture(_quad.texture); _quad.texture = null; }
|
||
|
|
||
|
var bounds:Rectangle = Pool.getRectangle(); // might be recursive -> no static var
|
||
|
var drawLastPassToBackBuffer:Boolean = false;
|
||
|
var origResolution:Number = _resolution;
|
||
|
var renderSpace:DisplayObject = _target.stage || _target.parent;
|
||
|
var isOnStage:Boolean = renderSpace is Stage;
|
||
|
var stage:Stage = Starling.current.stage;
|
||
|
var stageBounds:Rectangle;
|
||
|
|
||
|
if (!forCache && (_alwaysDrawToBackBuffer || _target.requiresRedraw))
|
||
|
{
|
||
|
// If 'requiresRedraw' is true, the object is non-static, and we guess that this
|
||
|
// will be the same in the next frame. So we render directly to the back buffer.
|
||
|
//
|
||
|
// -- That, however, is only possible for full alpha values, because
|
||
|
// (1) 'FilterEffect' can't handle alpha (and that will do the rendering)
|
||
|
// (2) we don't want lower layers (CompositeFilter!) to shine through.
|
||
|
|
||
|
drawLastPassToBackBuffer = painter.state.alpha == 1.0;
|
||
|
painter.excludeFromCache(_target);
|
||
|
}
|
||
|
|
||
|
if (_target == Starling.current.root)
|
||
|
{
|
||
|
// full-screen filters use exactly the stage bounds
|
||
|
stage.getStageBounds(_target, bounds);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Unfortunately, the following bounds calculation yields the wrong result when
|
||
|
// drawing a filter to a RenderTexture using a custom matrix. The 'modelviewMatrix'
|
||
|
// should be used for the bounds calculation, but the API doesn't support this.
|
||
|
// A future version should change this to: "getBounds(modelviewMatrix, bounds)"
|
||
|
|
||
|
_target.getBounds(renderSpace, bounds);
|
||
|
|
||
|
if (!forCache && isOnStage) // normally, we don't need anything outside
|
||
|
{
|
||
|
stageBounds = stage.getStageBounds(null, Pool.getRectangle());
|
||
|
RectangleUtil.intersect(bounds, stageBounds, bounds);
|
||
|
Pool.putRectangle(stageBounds);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_quad.visible = !bounds.isEmpty();
|
||
|
if (!_quad.visible) { Pool.putRectangle(bounds); return; }
|
||
|
|
||
|
if (_padding) RectangleUtil.extend(bounds,
|
||
|
_padding.left, _padding.right, _padding.top, _padding.bottom);
|
||
|
|
||
|
// integer bounds for maximum sharpness + to avoid jiggling
|
||
|
bounds.setTo(Math.floor(bounds.x), Math.floor(bounds.y),
|
||
|
Math.ceil(bounds.width), Math.ceil(bounds.height));
|
||
|
|
||
|
_helper.textureScale = Starling.contentScaleFactor * _resolution;
|
||
|
_helper.projectionMatrix3D = painter.state.projectionMatrix3D;
|
||
|
_helper.renderTarget = painter.state.renderTarget;
|
||
|
_helper.targetBounds = bounds;
|
||
|
_helper.target = _target;
|
||
|
_helper.start(numPasses, drawLastPassToBackBuffer);
|
||
|
|
||
|
_quad.setBounds(bounds);
|
||
|
_resolution = 1.0; // applied via '_helper.textureScale' already;
|
||
|
// only 'child'-filters use resolution directly (in 'process')
|
||
|
|
||
|
var wasCacheEnabled:Boolean = painter.cacheEnabled;
|
||
|
var input:Texture = _helper.getTexture();
|
||
|
var output:Texture;
|
||
|
|
||
|
painter.cacheEnabled = false; // -> what follows should not be cached
|
||
|
painter.pushState();
|
||
|
painter.state.alpha = 1.0;
|
||
|
painter.state.renderTarget = input;
|
||
|
painter.state.setProjectionMatrix(bounds.x, bounds.y,
|
||
|
input.root.width, input.root.height,
|
||
|
stage.stageWidth, stage.stageHeight, stage.cameraPosition);
|
||
|
|
||
|
_target.render(painter); // -> draw target object into 'input'
|
||
|
|
||
|
painter.finishMeshBatch();
|
||
|
painter.state.setModelviewMatricesToIdentity();
|
||
|
painter.state.clipRect = null;
|
||
|
|
||
|
output = process(painter, _helper, input); // -> feed 'input' to actual filter code
|
||
|
|
||
|
painter.popState();
|
||
|
painter.cacheEnabled = wasCacheEnabled; // -> cache again
|
||
|
|
||
|
if (output) // indirect rendering
|
||
|
{
|
||
|
painter.pushState();
|
||
|
|
||
|
if (_target.is3D) painter.state.setModelviewMatricesToIdentity(); // -> stage coords
|
||
|
else _quad.moveVertices(renderSpace, _target); // -> local coords
|
||
|
|
||
|
_quad.texture = output;
|
||
|
_quad.render(painter);
|
||
|
|
||
|
painter.finishMeshBatch();
|
||
|
painter.popState();
|
||
|
}
|
||
|
|
||
|
_helper.target = null;
|
||
|
_helper.putTexture(input);
|
||
|
_resolution = origResolution;
|
||
|
Pool.putRectangle(bounds);
|
||
|
}
|
||
|
|
||
|
/** Does the actual filter processing. This method will be called with up to four input
|
||
|
* textures and must return a new texture (acquired from the <code>helper</code>) that
|
||
|
* contains the filtered output. To to do this, it configures the FilterEffect
|
||
|
* (provided via <code>createEffect</code>) and calls its <code>render</code> method.
|
||
|
*
|
||
|
* <p>In a standard filter, only <code>input0</code> will contain a texture; that's the
|
||
|
* object the filter was applied to, rendered into an appropriately sized texture.
|
||
|
* However, filters may also accept multiple textures; that's useful when you need to
|
||
|
* combine the output of several filters into one. For example, the DropShadowFilter
|
||
|
* uses a BlurFilter to create the shadow and then feeds both input and shadow texture
|
||
|
* into a CompositeFilter.</p>
|
||
|
*
|
||
|
* <p>Never create or dispose any textures manually within this method; instead, get
|
||
|
* new textures from the provided helper object, and pass them to the helper when you do
|
||
|
* not need them any longer. Ownership of both input textures and returned texture
|
||
|
* lies at the caller; only temporary textures should be put into the helper.</p>
|
||
|
*/
|
||
|
public function process(painter:Painter, helper:IFilterHelper,
|
||
|
input0:Texture=null, input1:Texture=null,
|
||
|
input2:Texture=null, input3:Texture=null):Texture
|
||
|
{
|
||
|
var effect:FilterEffect = this.effect;
|
||
|
var output:Texture = helper.getTexture(_resolution);
|
||
|
var projectionMatrix:Matrix3D;
|
||
|
var bounds:Rectangle = null;
|
||
|
var renderTarget:Texture;
|
||
|
|
||
|
if (output) // render to texture
|
||
|
{
|
||
|
renderTarget = output;
|
||
|
projectionMatrix = MatrixUtil.createPerspectiveProjectionMatrix(0, 0,
|
||
|
output.root.width / _resolution, output.root.height / _resolution,
|
||
|
0, 0, null, sMatrix3D);
|
||
|
}
|
||
|
else // render to back buffer
|
||
|
{
|
||
|
bounds = helper.targetBounds;
|
||
|
renderTarget = (helper as FilterHelper).renderTarget;
|
||
|
projectionMatrix = (helper as FilterHelper).projectionMatrix3D;
|
||
|
effect.textureSmoothing = _textureSmoothing;
|
||
|
}
|
||
|
|
||
|
painter.state.renderTarget = renderTarget;
|
||
|
painter.prepareToDraw();
|
||
|
painter.drawCount += 1;
|
||
|
|
||
|
input0.setupVertexPositions(vertexData, 0, "position", bounds);
|
||
|
input0.setupTextureCoordinates(vertexData);
|
||
|
|
||
|
effect.texture = input0;
|
||
|
effect.mvpMatrix3D = projectionMatrix;
|
||
|
effect.uploadVertexData(vertexData);
|
||
|
effect.uploadIndexData(indexData);
|
||
|
effect.render(0, indexData.numTriangles);
|
||
|
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
/** Creates the effect that does the actual, low-level rendering.
|
||
|
* Must be overridden by all subclasses that do any rendering on their own (instead
|
||
|
* of just forwarding processing to other filters).
|
||
|
*/
|
||
|
protected function createEffect():FilterEffect
|
||
|
{
|
||
|
return new FilterEffect();
|
||
|
}
|
||
|
|
||
|
/** Caches the filter output into a texture.
|
||
|
*
|
||
|
* <p>An uncached filter is rendered every frame (except if it can be rendered from the
|
||
|
* global render cache, which happens if the target object does not change its appearance
|
||
|
* or location relative to the stage). A cached filter is only rendered once; the output
|
||
|
* stays unchanged until you call <code>cache</code> again or change the filter settings.
|
||
|
* </p>
|
||
|
*
|
||
|
* <p>Beware: you cannot cache filters on 3D objects; if the object the filter is attached
|
||
|
* to is a Sprite3D or has a Sprite3D as (grand-) parent, the request will be silently
|
||
|
* ignored. However, you <em>can</em> cache a 2D object that has 3D children!</p>
|
||
|
*/
|
||
|
public function cache():void
|
||
|
{
|
||
|
_cached = _cacheRequested = true;
|
||
|
setRequiresRedraw();
|
||
|
}
|
||
|
|
||
|
/** Clears the cached output of the filter. After calling this method, the filter will be
|
||
|
* processed once per frame again. */
|
||
|
public function clearCache():void
|
||
|
{
|
||
|
_cached = _cacheRequested = false;
|
||
|
setRequiresRedraw();
|
||
|
}
|
||
|
|
||
|
// enter frame event
|
||
|
|
||
|
/** @private */
|
||
|
override public function addEventListener(type:String, listener:Function):void
|
||
|
{
|
||
|
if (type == Event.ENTER_FRAME && _target)
|
||
|
_target.addEventListener(Event.ENTER_FRAME, onEnterFrame);
|
||
|
|
||
|
super.addEventListener(type, listener);
|
||
|
}
|
||
|
|
||
|
/** @private */
|
||
|
override public function removeEventListener(type:String, listener:Function):void
|
||
|
{
|
||
|
if (type == Event.ENTER_FRAME && _target)
|
||
|
_target.removeEventListener(type, onEnterFrame);
|
||
|
|
||
|
super.removeEventListener(type, listener);
|
||
|
}
|
||
|
|
||
|
private function onEnterFrame(event:Event):void
|
||
|
{
|
||
|
dispatchEvent(event);
|
||
|
}
|
||
|
|
||
|
// properties
|
||
|
|
||
|
/** The effect instance returning the FilterEffect created via <code>createEffect</code>. */
|
||
|
protected function get effect():FilterEffect
|
||
|
{
|
||
|
if (_effect == null) _effect = createEffect();
|
||
|
return _effect;
|
||
|
}
|
||
|
|
||
|
/** The VertexData used to process the effect. Per default, uses the format provided
|
||
|
* by the effect, and contains four vertices enclosing the target object. */
|
||
|
protected function get vertexData():VertexData
|
||
|
{
|
||
|
if (_vertexData == null) _vertexData = new VertexData(effect.vertexFormat, 4);
|
||
|
return _vertexData;
|
||
|
}
|
||
|
|
||
|
/** The IndexData used to process the effect. Per default, references a quad (two triangles)
|
||
|
* of four vertices. */
|
||
|
protected function get indexData():IndexData
|
||
|
{
|
||
|
if (_indexData == null)
|
||
|
{
|
||
|
_indexData = new IndexData(6);
|
||
|
_indexData.addQuad(0, 1, 2, 3);
|
||
|
}
|
||
|
|
||
|
return _indexData;
|
||
|
}
|
||
|
|
||
|
/** Call this method when any of the filter's properties changes.
|
||
|
* This will make sure the filter is redrawn in the next frame. */
|
||
|
protected function setRequiresRedraw():void
|
||
|
{
|
||
|
dispatchEventWith(Event.CHANGE);
|
||
|
if (_target) _target.setRequiresRedraw();
|
||
|
if (_cached) _cacheRequested = true;
|
||
|
}
|
||
|
|
||
|
/** Indicates the number of rendering passes required for this filter.
|
||
|
* Subclasses must override this method if the number of passes is not <code>1</code>. */
|
||
|
public function get numPasses():int
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/** Called when assigning a target display object.
|
||
|
* Override to plug in class-specific logic. */
|
||
|
protected function onTargetAssigned(target:DisplayObject):void
|
||
|
{ }
|
||
|
|
||
|
/** Padding can extend the size of the filter texture in all directions.
|
||
|
* That's useful when the filter "grows" the bounds of the object in any direction. */
|
||
|
public function get padding():Padding
|
||
|
{
|
||
|
if (_padding == null)
|
||
|
{
|
||
|
_padding = new Padding();
|
||
|
_padding.addEventListener(Event.CHANGE, setRequiresRedraw);
|
||
|
}
|
||
|
|
||
|
return _padding;
|
||
|
}
|
||
|
|
||
|
public function set padding(value:Padding):void
|
||
|
{
|
||
|
padding.copyFrom(value);
|
||
|
}
|
||
|
|
||
|
/** Indicates if the filter is cached (via the <code>cache</code> method). */
|
||
|
public function get isCached():Boolean { return _cached; }
|
||
|
|
||
|
/** The resolution of the filter texture. "1" means stage resolution, "0.5" half the stage
|
||
|
* resolution. A lower resolution saves memory and execution time, but results in a lower
|
||
|
* output quality. Values greater than 1 are allowed; such values might make sense for a
|
||
|
* cached filter when it is scaled up. @default 1
|
||
|
*/
|
||
|
public function get resolution():Number { return _resolution; }
|
||
|
public function set resolution(value:Number):void
|
||
|
{
|
||
|
if (value != _resolution)
|
||
|
{
|
||
|
if (value > 0) _resolution = value;
|
||
|
else throw new ArgumentError("resolution must be > 0");
|
||
|
setRequiresRedraw();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** The smoothing mode of the filter texture. @default bilinear */
|
||
|
public function get textureSmoothing():String { return _textureSmoothing; }
|
||
|
public function set textureSmoothing(value:String):void
|
||
|
{
|
||
|
if (value != _textureSmoothing)
|
||
|
{
|
||
|
_textureSmoothing = value;
|
||
|
if (_quad) _quad.textureSmoothing = value;
|
||
|
setRequiresRedraw();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** The format of the filter texture. @default BGRA */
|
||
|
public function get textureFormat():String { return _textureFormat; }
|
||
|
public function set textureFormat(value:String):void
|
||
|
{
|
||
|
if (value != _textureFormat)
|
||
|
{
|
||
|
_textureFormat = value;
|
||
|
if (_helper) _helper.textureFormat = value;
|
||
|
setRequiresRedraw();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Indicates if the last filter pass is always drawn directly to the back buffer.
|
||
|
*
|
||
|
* <p>Per default, the filter tries to automatically render in a smart way: objects that
|
||
|
* are currently moving are rendered to the back buffer, objects that are static are
|
||
|
* rendered into a texture first, which allows the filter to be drawn directly from the
|
||
|
* render cache in the next frame (in case the object remains static).</p>
|
||
|
*
|
||
|
* <p>However, this fails when filters are added to an object that does not support the
|
||
|
* render cache, or to a container with such a child (e.g. a Sprite3D object or a masked
|
||
|
* display object). In such a case, enable this property for maximum performance.</p>
|
||
|
*
|
||
|
* @default false
|
||
|
*/
|
||
|
public function get alwaysDrawToBackBuffer():Boolean { return _alwaysDrawToBackBuffer; }
|
||
|
public function set alwaysDrawToBackBuffer(value:Boolean):void
|
||
|
{
|
||
|
_alwaysDrawToBackBuffer = value;
|
||
|
}
|
||
|
|
||
|
// internal methods
|
||
|
|
||
|
/** @private */
|
||
|
starling_internal function setTarget(target:DisplayObject):void
|
||
|
{
|
||
|
if (target != _target)
|
||
|
{
|
||
|
var prevTarget:DisplayObject = _target;
|
||
|
_target = target;
|
||
|
|
||
|
if (target == null)
|
||
|
{
|
||
|
if (_helper) _helper.purge();
|
||
|
if (_effect) _effect.purgeBuffers();
|
||
|
if (_quad) _quad.disposeTexture();
|
||
|
}
|
||
|
|
||
|
if (prevTarget)
|
||
|
{
|
||
|
prevTarget.filter = null;
|
||
|
prevTarget.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
|
||
|
}
|
||
|
|
||
|
if (target)
|
||
|
{
|
||
|
if (hasEventListener(Event.ENTER_FRAME))
|
||
|
target.addEventListener(Event.ENTER_FRAME, onEnterFrame);
|
||
|
|
||
|
onTargetAssigned(target);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
import flash.geom.Matrix;
|
||
|
import flash.geom.Rectangle;
|
||
|
|
||
|
import starling.display.DisplayObject;
|
||
|
import starling.display.Mesh;
|
||
|
import starling.rendering.IndexData;
|
||
|
import starling.rendering.VertexData;
|
||
|
import starling.textures.Texture;
|
||
|
|
||
|
class FilterQuad extends Mesh
|
||
|
{
|
||
|
private static var sMatrix:Matrix = new Matrix();
|
||
|
|
||
|
public function FilterQuad(smoothing:String)
|
||
|
{
|
||
|
var vertexData:VertexData = new VertexData(null, 4);
|
||
|
vertexData.numVertices = 4;
|
||
|
|
||
|
var indexData:IndexData = new IndexData(6);
|
||
|
indexData.addQuad(0, 1, 2, 3);
|
||
|
|
||
|
super(vertexData, indexData);
|
||
|
|
||
|
textureSmoothing = smoothing;
|
||
|
pixelSnapping = false;
|
||
|
}
|
||
|
|
||
|
override public function dispose():void
|
||
|
{
|
||
|
disposeTexture();
|
||
|
super.dispose();
|
||
|
}
|
||
|
|
||
|
public function disposeTexture():void
|
||
|
{
|
||
|
if (texture)
|
||
|
{
|
||
|
texture.dispose();
|
||
|
texture = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function moveVertices(sourceSpace:DisplayObject, targetSpace:DisplayObject):void
|
||
|
{
|
||
|
if (targetSpace.is3D)
|
||
|
throw new Error("cannot move vertices into 3D space");
|
||
|
else if (sourceSpace != targetSpace)
|
||
|
{
|
||
|
targetSpace.getTransformationMatrix(sourceSpace, sMatrix).invert(); // ss could be null!
|
||
|
vertexData.transformPoints("position", sMatrix);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function setBounds(bounds:Rectangle):void
|
||
|
{
|
||
|
var vertexData:VertexData = this.vertexData;
|
||
|
var attrName:String = "position";
|
||
|
|
||
|
vertexData.setPoint(0, attrName, bounds.x, bounds.y);
|
||
|
vertexData.setPoint(1, attrName, bounds.right, bounds.y);
|
||
|
vertexData.setPoint(2, attrName, bounds.x, bounds.bottom);
|
||
|
vertexData.setPoint(3, attrName, bounds.right, bounds.bottom);
|
||
|
}
|
||
|
|
||
|
override public function set texture(value:Texture):void
|
||
|
{
|
||
|
super.texture = value;
|
||
|
if (value) value.setupTextureCoordinates(vertexData);
|
||
|
}
|
||
|
}
|