Search code examples
apache-flexflashactionscriptspritefilter

Get bounds of filters applied to Flash Sprite within Sprite


I have a Flash library with Sprite symbols composed of other sprites with design-time applied filters. I'm embedding those symbols into a Flex application like so:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            [Bindable]
            [Embed(source="Resources.swf", symbol="SquareContainer")]
            private var squareContainer_class:Class;

            private function log(msg:String):void {
                    output.text = output.text + "\n" + msg;
            }
        ]]>
    </mx:Script>

    <mx:VBox horizontalAlign="center" width="100%" height="100%" >
        <mx:Image id="squareContainer" source="{squareContainer_class}"/>
        <mx:Button click="log(squareContainer.width + ', ' + squareContainer.height);"/>
        <mx:TextArea id="output" width="100%" height="100%" />
    </mx:VBox>

</mx:Application>

In this example, the SquareContainer symbol is 100px wide by 100px height; however it contains a child sprite with a glow and blur filter, that cause the sprite to appear to be significantly larger than 100x100. Since I cannot know for certain the composition of the container, I cannot use BitmapData.generateFilterRect() to get at the filters applied to nested sprites.

How can I get the size of the sprite plus its filters?


Solution

  • Oh sweet success! (and thanks for the tips) A friend helped solve the problem with a nice recursive function to handle the filters which may exist on nested sprites:

    private function getDisplayObjectRectangle(container:DisplayObjectContainer, processFilters:Boolean):Rectangle {
        var final_rectangle:Rectangle = processDisplayObjectContainer(container, processFilters);
    
        // translate to local
        var local_point:Point = container.globalToLocal(new Point(final_rectangle.x, final_rectangle.y));
        final_rectangle = new Rectangle(local_point.x, local_point.y, final_rectangle.width, final_rectangle.height);       
    
        return final_rectangle;
    }
    
    private function processDisplayObjectContainer(container:DisplayObjectContainer, processFilters:Boolean):Rectangle {
        var result_rectangle:Rectangle = null;
    
        // Process if container exists
        if (container != null) {
            var index:int = 0;
            var displayObject:DisplayObject;
    
            // Process each child DisplayObject
            for(var childIndex:int = 0; childIndex < container.numChildren; childIndex++){
                displayObject = container.getChildAt(childIndex);
    
                //If we are recursing all children, we also get the rectangle of children within these children.
                if (displayObject is DisplayObjectContainer) {
    
                    // Let's drill into the structure till we find the deepest DisplayObject
                    var displayObject_rectangle:Rectangle = processDisplayObjectContainer(displayObject as DisplayObjectContainer, processFilters);
    
                    // Now, stepping out, uniting the result creates a rectangle that surrounds siblings
                    if (result_rectangle == null) { 
                        result_rectangle = displayObject_rectangle.clone(); 
                    } else {
                        result_rectangle = result_rectangle.union(displayObject_rectangle);
                    }                       
                }                       
            }
    
            // Get bounds of current container, at this point we're stepping out of the nested DisplayObjects
            var container_rectangle:Rectangle = container.getBounds(container.stage);
    
            if (result_rectangle == null) { 
                result_rectangle = container_rectangle.clone(); 
            } else {
                result_rectangle = result_rectangle.union(container_rectangle);
            }
    
    
            // Include all filters if requested and they exist
            if ((processFilters == true) && (container.filters.length > 0)) {
                var filterGenerater_rectangle:Rectangle = new Rectangle(0,0,result_rectangle.width, result_rectangle.height);
                var bmd:BitmapData = new BitmapData(result_rectangle.width, result_rectangle.height, true, 0x00000000);
    
                var filter_minimumX:Number = 0;
                var filter_minimumY:Number = 0;
    
                var filtersLength:int = container.filters.length;
                for (var filtersIndex:int = 0; filtersIndex < filtersLength; filtersIndex++) {                      
                    var filter:BitmapFilter = container.filters[filtersIndex];
    
                    var filter_rectangle:Rectangle = bmd.generateFilterRect(filterGenerater_rectangle, filter);
    
                    filter_minimumX = filter_minimumX + filter_rectangle.x;
                    filter_minimumY = filter_minimumY + filter_rectangle.y;
    
                    filterGenerater_rectangle = filter_rectangle.clone();
                    filterGenerater_rectangle.x = 0;
                    filterGenerater_rectangle.y = 0;
    
                    bmd = new BitmapData(filterGenerater_rectangle.width, filterGenerater_rectangle.height, true, 0x00000000);                      
                }
    
                // Reposition filter_rectangle back to global coordinates
                filter_rectangle.x = result_rectangle.x + filter_minimumX;
                filter_rectangle.y = result_rectangle.y + filter_minimumY;
    
                result_rectangle = filter_rectangle.clone();
            }               
        } else {
            throw new Error("No displayobject was passed as an argument");
        }
    
        return result_rectangle;
    }