     }
 
     public function SwfTexture (symbol :String, disp :DisplayObject, scale :Number, quality :String) {
-        this.symbol = symbol;
-        this.scale = scale;
-        this.quality = quality;
-        _disp = disp;
+        this._symbol = symbol;
+        this._quality = quality;
 
-        origin = getOrigin(_disp, scale);
+        // wrap object twice for convenience
+        var wrapper:Sprite = new Sprite();
+        wrapper.addChild(disp);
+        this._disp = new Sprite();
+        this._disp.addChild(wrapper);
 
-        const size :Point = getSize(_disp, scale);
-        w = size.x;
-        h = size.y;
-        a = w * h;
+        // set the scale and size info
+        this._treatAsFiltered = this.hasPotentiallySizeAlteringFilters(disp);
+        this.setScale(scale);
+    }
+    
+    // scale can be changed after creation, if desired
+    public function setScale(value:Number):void
+    {
+        // To compensate for the fact that filters don't "scale", do the following
+        // if filtered:
+        //    render at scale 1
+        //    then scale bitmapData to target size
+        // if not filtered:
+        //    set the scale in the wrapper
+        //    render directly at target size with vector renderer
+        if (this._treatAsFiltered) {
+            // cache the scale to use later
+            this._scale = value;
+            // only need to calculate size once, since this._disp is not changing
+            if (this._visualBounds == null) {
+                recalculateSizeInfo();
+            }
+        } else {
+            // embed the scale in this._disp
+            var wrapper:DisplayObject = this._disp.getChildAt(0);
+            wrapper.scaleX = wrapper.scaleY = value;
+            this._scale = 1;
+            // recalculate size since this._disp has changed
+            recalculateSizeInfo();
+        }
     }
 
     public function toBitmapData (borderPadding :int = 0) :BitmapData {
-        const bmd :BitmapData = Util.renderToBitmapData(_disp, w, h, quality, scale);
+        // render with vector renderer
+        var bmd :BitmapData = new BitmapData(Math.ceil(this._w), Math.ceil(this._h), true, 0x00);
+        var m :Matrix = new Matrix();
+        m.translate(this._origin.x, this._origin.y);
+        bmd.drawWithQuality(this._disp, m, null, null, null, true, this._quality);
+
+        // scale bitmap to target size if necessary (only used if _disp contains filters)
+        if (this._scale != 1.0) {
+            bmd = Util.renderToBitmapData(bmd, this.w, this.h, this._quality, this._scale);
+        }
+
+        // add padding if necessary
         return (borderPadding > 0 ? Util.padBitmapBorder(bmd, borderPadding) : bmd);
     }
 
-    public function toString () :String { return "a " + a + " w " + w + " h " + h; }
+    public function toString () :String { return "a " + this.a + " w " + this.w + " h " + this.h; }
 
-    protected static function getSize (disp :DisplayObject, scale :Number) :Point {
-        const bounds :Rectangle = getBounds(disp, scale);
-        return new Point(Math.ceil(bounds.width), Math.ceil(bounds.height));
+    private function recalculateSizeInfo() :void {
+        // get normal bounds
+        this._strictBounds = this._disp.getChildAt(0).getBounds(this._disp);
+        this._visualBounds = this._strictBounds;
+        
+        // possibly increase the visual bounds (due to filter action)
+        if (this._treatAsFiltered) {
+            // render to bmd
+            var topLeft:Point = new Point(_filteredBmd.width / 2 - this._strictBounds.width / 2 - this._strictBounds.x, _filteredBmd.height / 2 - this._strictBounds.height / 2 - this._strictBounds.y);
+            var m :Matrix = new Matrix(1,0,0,1, topLeft.x, topLeft.y);
+            _filteredBmd.drawWithQuality(this._disp, m, null, null, null, true, this._quality);
+            
+            // calculate visual bounds
+            this._visualBounds = _filteredBmd.getColorBoundsRect(0xff000000, 0x00000000, false);
+            _filteredBmd.fillRect(this._visualBounds, 0x0);
+
+            // adjust registration point
+            this._visualBounds.x = -(topLeft.x - this._visualBounds.x);
+            this._visualBounds.y = -(topLeft.y - this._visualBounds.y);
+        }
+        
+        // calculate derivative info
+        this._origin = new Point(-this._visualBounds.x, -this._visualBounds.y);
+        this._w = this._visualBounds.width;
+        this._h = this._visualBounds.height;
     }
-
-    protected static function getOrigin (disp :DisplayObject, scale :Number) :Point {
-        const bounds :Rectangle = getBounds(disp, scale);
-        return new Point(-bounds.x, -bounds.y);
+    
+    private function hasPotentiallySizeAlteringFilters(dObj:DisplayObject) :Boolean {
+        // check dObj's filter list
+        var filters:Array = dObj.filters;
+        for (var ff:int = 0, nf:int = filters.length; ff < nf; ++ff) {
+            // all standard filters except ColorMatrixFilter can change the visual bounds
+            if (!(filters[ff] is ColorMatrixFilter)) {
+                return true;
+            }
+        }
+        // recursively check children
+        var dObjContainer:flash.display.DisplayObjectContainer = dObj as flash.display.DisplayObjectContainer;
+        if (dObjContainer) {
+            for (var cc:int = 0, nc:int = dObjContainer.numChildren; cc < nc; ++cc) {
+                var child:DisplayObject = dObjContainer.getChildAt(cc);
+                if (hasPotentiallySizeAlteringFilters(child)) {
+                    return true;
+                }
+            }
+        }
+        // all clear
+        return false;        
     }
-
-    protected static function getBounds (disp :DisplayObject, scale :Number) :Rectangle {
-        const oldScale :Number = disp.scaleX;
-        disp.scaleX = disp.scaleY = scale;
-        const holder :Sprite = new Sprite();
-        holder.addChild(disp);
-        const bounds :Rectangle = disp.getBounds(holder);
-        disp.scaleX = disp.scaleY = oldScale;
-        return bounds;
-    }
-
-    protected var _disp :DisplayObject;
+    
+    private var _symbol :String;
+    private var _disp :DisplayObjectContainer;
+    private var _w :int, _h :int;
+    private var _origin :Point;
+    private var _strictBounds :Rectangle;
+    private var _visualBounds :Rectangle;
+    private var _scale :Number;
+    private var _quality :String;
+    private var _treatAsFiltered:Boolean;
+    
+    static private var _filteredBmd:BitmapData = new BitmapData(2048, 2048, true, 0x0);
+    { _filteredBmd.lock(); }
 }
 }


public class SwfTexture
{
    public var symbol :String;
    public var origin :Point;
    public var w :int, h :int, a :int;
    public var scale :Number;
    public var quality :String;

    public static function fromFlipbook (lib :XflLibrary, movie :MovieMold, frame :int,
        quality :String = StageQuality.BEST, scale :Number = 1) :SwfTexture {

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageQuality;
import flash.filters.ColorMatrixFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flump.xfl.XflMovie;

import flump.executor.load.LoadedSwf;
import flump.mold.MovieMold;
