views:

938

answers:

3

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"&gt;
    <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?

A: 

I'm not sure this is possible using the normal methods of getBounds or getRect as these methods will just return the normal 100x100 square.

I have a few of suggestions for you.

Firstly you could apply the filters dynamically. That way you will have the numbers for the filters and you could do the maths programatically to work out what the actual size is.

You could add a second layer to the movieclip in the fla, which has the dimensions of your original square plus all the filters. Then set the alpha for this square to zero

Thirdly you could just have a png in the fla that contained the square plus, say, the glow within it.

Personally i'd go with the middle option as it would require the least amount of maintenance if the filters or the original square were to change.

James Hay
Thanks for the ideas. I agree about the second suggestion; however, I don't have control over the content/sprite compositions. Surely, I'm overlooking something. It's seems it would be quite common to want to identify the sprite dimensions AND the sprite plus filter dimensions.
Michael Prescott
A: 

Well, there is good news and bad news. The bad news is that there really isn't an effective way to do this "the right way". The good news is that there is an adequate way to approximate it.

The general rule is that the size of the width is that it is approximately ( filter.blurX * 1.5 ) + sprite.width where "filter" is the Filter in the sprite.filters array. The same is true regarding blurY and height. The other general rule is that the minimum x becomes sprite.x - ( filter.blurX * 1.5 ) / 2;

None of these numbers are "Adobe official", but the work within a reasonable enough margin of error to allow you to create a BitmapData based on that.

Christopher W. Allen-Poole
What I presented is the simplest case. I don't know the internal structure of the SquareContainer instance. In some cases, it could be a composition of many sprites all with different filters, and some sprites within sprites within filters. I'm trying to accurately measure just the parent sprite.
Michael Prescott
+1  A: 

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;
}
Michael Prescott