May 25, 2009

get visible width/height of a display object

When it comes to the width and height variables of ActionScript's DisplayObject, what you see is not always what you get. For example, consider the following code, which draws a 50x75 rectangle at position (10, 10) in a Sprite, and then masks it with a 40-pixel-diameter circle:

// The mask
var maskShape:Sprite = new Sprite();
maskShape.graphics.beginFill(0);
maskShape.graphics.drawCircle(20, 20, 20);
addChild(maskShape);
// The rectangle
var rect:Sprite = new Sprite();
rect.graphics.beginFill(0xFF0000);
rect.graphics.drawRect(10, 10, 50, 75);
rect.mask = maskShape;
// Give the rectangle a parent
addChild(rect);

If we check, the "width" and "height" variables of "rect", we get 50 and 75, even though "rect" is masked.

trace(rect.width);   // 50
trace(rect.height);  // 75

But the "rect" object's width and height variables contradict what you see on screen:

On screen, the preceding graphic measures only 40 by 40 (the diameter of the circular mask).

The DisplayObject class's width and height variables can also seem misleading when a DisplayObjectContainer holds non-visible children. For example, the following code creates a container with two children, one of which is non-visible.

// The circle
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0);
circle.graphics.drawCircle(10, 10, 10);
// The non-visible rectangle
var rect:Sprite = new Sprite();
rect.graphics.beginFill(0xFF0000);
rect.graphics.drawRect(10, 10, 50, 75);
rect.visible = false;
// The container
var container:Sprite = new Sprite();
container.addChild(circle);
container.addChild(rect);
// Give the container a parent
addChild(container);

If we check, the "width" and "height" variables of "container", we get 60 and 85, even though "rect" is non-visible:

trace(container.width);   // 60
trace(container.height);  // 85

Again, the "container" object's width and height variables contradict what you see on screen:

On screen, the preceding graphic measures only 20 by 20 (the diameter of the circle).

As a final example of misleading width and height values, consider the following code, which creates a simple text field:

var t:TextField = new TextField();
t.text = "hi there"
addChild(t);

If we check, the "width" and "height" variables of "t", we get 100 and 100--the dimensions of the TextField object's bounding box, even though that bounding box is not visible:

trace(t.width);   // 100
trace(t.height);  // 100

The TextField object's width and height variables again contradict what you see on screen:

On screen, the preceding graphic measures only 37 by 12 (the dimensions of the visible text, not the bounding box).

So what do you do if you need to know the dimensions of what's actually visible on screen? Unfortunately, in ActionScript, there's no convenient way to find that information. I've logged a bug with Adobe requesting that they add one. But until then, the only option is to copy your desired display object to a bitmap and measure the occupied pixels. I did just that on a recent MegaPhone project; you can grab the code below. Note, however, that the bitmap approach is slower than a native implementation would be, so if you use the code, all I ask is that you please vote for the preceding bug. Thanks!

/**
 * Returns the distance from the registration point of the specified 
 * object to the bottom-most visible pixel, ignoring any region
 * that is not visible due to masking. For example, if a display
 * object contains a 100-pixel-high shape and a 50-pixel-high mask,
 * getVisibleHeight() will return 50, whereas DisplayObject's
 * "height" variable would yield 100. 
 * 
 * The maximum measureable dimensions of the supplied object is
 * 2000x2000.
 */
function getVisibleHeight (o:DisplayObject):Number {
  var bitmapDataSize:int = 2000;
  var bounds:Rectangle;
  var bitmapData:BitmapData = new BitmapData(bitmapDataSize, 
                                             bitmapDataSize,
                                             true,
                                             0);
  bitmapData.draw(o);
  bounds = bitmapData.getColorBoundsRect( 0xFF000000, 0x00000000, false );
  bitmapData.dispose(); 
  return bounds.y + bounds.height;
}
/**
 * Returns the distance from the registration point of the specified 
 * object to the right-most visible pixel, ignoring any region
 * that is not visible due to masking. For example, if a display
 * object contains a 100-pixel-wide shape and a 50-pixel-wide mask,
 * getVisibleWidth() will return 50, whereas DisplayObject's
 * "width" variable would yield 100. 
 * 
 * The maximum measureable dimensions of the supplied object is
 * 2000x2000.
 * 
 * @since MegaPhone App Framework 1.0
 */
function getVisibleWidth (o:DisplayObject):Number {
  var bitmapDataSize:int = 2000;
  var bounds:Rectangle;
  var bitmapData:BitmapData = new BitmapData(bitmapDataSize, 
                                             bitmapDataSize, 
                                             true, 
                                             0);
  bitmapData.draw(o);
  bounds = bitmapData.getColorBoundsRect( 0xFF000000, 0x00000000, false );
  bitmapData.dispose(); 
  return bounds.x + bounds.width;
}

[Updates May 26, 2009: 1) Made the code faster based on an observation by Mario Klingemann. 2) Added the TextField example.]

Posted by moock at 10:58 PM | Comments (10)

May 21, 2009

can't enable gpu acceleration in flex builder/mxmlc

while working with a colleague at megaphone, it recently came to my attention that there is currently no way to enable hardware acceleration at the .swf level with flex builder 3 or the flex framework's standalone compiler, mxmlc. hence, a .swf file compiled via the flex framework or mxmlc currently cannot use hardware acceleration when running in the standalone version of flash player.

i logged a bug as soon as i confirmed the compiler-option omission with flex builder product manager, matt chotin. please vote for it if you'd like to see this issue addressed. matt's comments on the bug seem encouraging; he indicates that the option could be added to the flex framework's 3.4 and 4.0 versions within the 4.0 development cycle. until then, if you're desperate, you'll have to write a post-compiler tool to set the appropriate swf tag options yourself. such a tool could be written as an air app, based on thibault imbert's SWFExplorer.

mr. chotin said that flex builder 4 (now "flash builder 4") likely won't have a gui option to enable hardware acceleration, but that it might provide support via the project properties dialog's "additional compiler arguments" field.

note that in flex builder 3, hardware acceleration *can* be enabled for .swf files running in a browser by adding the appropriate wmode attribute setting in your project's html template's .swf-object/embed tags.

Posted by moock at 04:18 PM

May 01, 2009

Announcing USER1 and Union

three days ago at fitc, derek clayton and i announced the launch of USER1 Subsystems Corporation and the Union Platform, a platform for creating multiuser applications. Union Alpha 1 is now available for download.

USER1 is working in an exclusive partnership with Play MegaPhone Inc to create a network of big-screen, multiuser applications you control with a phone call. like shoot 'em up in Times Square or trivia at an NBA Allstar game.

Posted by moock at 01:17 AM