开发者

BitmapData.rect creates a new rectangle?

I was working on some blitting code today and after profiling it found that I was creating 1000s of new rectangles. I was shocked because I only had 1 or 2 different new Rectangle() calls or I was using the .rect property of BitmapData.

I started commenting out huge swathes of code until I was left with this code in my sprite class: canvas.bitmapData.copyPixels(_bitmapData, _bitmapData.rect, destination, null, null, true);

I cached the result of _bitmapData.rect at object creation and my thousand of new rectangle calls were suddenly gone from the profil开发者_C百科er.

Why the heck would BitmapData.rect create a new rectangle? Is there a way to check the core libraries or something and confirm this is true? It seems baffling.


Bethanny Anne said:

Why the heck would BitmapData.rect create a new rectangle? Is there a way to check the core libraries or something and confirm this is true? It seems baffling.

Imagine the following hypothetical situation where BitmapData.rect always returns the same Rectangle instance:

public function BitmapDataRectTest()
{
    var bmp:BitmapData = new BitmapData(100, 100, true, 0);

    var rect1:Rectangle = bmp.rect;
    rect1.width = 200;

    var rect2:Rectangle = bmp.rect;
    trace(rect2.width); // 200 <--- this would be wrong, 
                        //          the BitmapData is still only 100 pixels wide.
}

BitmapData.rect returns a new instance every time to avoid that situation and to make sure you are getting the correct data in the returned Rectangle instance.

Final note: this relates to 'pass-by-value' (primitive types) and 'pass-by-reference' (complex type) variables. For more info check out google or other posts on stackoverflow: Pass by reference or pass by value?


Interesting observation.

You can use the === operator to check if two instances of any Object are the same.

It's likely that BitmapData internally uses different data structures to maintain its visual state. The rect property must be implemented as a getter function. However, one would expect that because the dimensions of BitmapData are immutable, that the Rectangle object would never need to be recreated, or even re-computed at all.

Edit: The rect property on BitmapData is read-only (no setter), however the properties on a Rectangle are not. Creating a new instance of Rectangle ensures that external objects cannot make mutations.


Yes, it sounds discouraging, but if you will take a look at BitmapData class source code you will discover this:

public class BitmapData extends Object implements IBitmapDrawable {
...
    public function get rect() : Rectangle {
        return new Rectangle(0, 0, this.width, this.height);
    }
...
}

So the answer is Yes, AVM creates new instance of Rectangle in the heap every time you retrieve it through accessor-function.


Yes, BitmapData.rect is apparently a getter that creates new rectangle on access.

You can prove it by comparing references or by examining memory addresses:

Example:

package
{
    import flash.display.Sprite;
    import flash.display.BitmapData;
    import flash.geom.Rectangle;

    public class BitmapDataRectTest extends Sprite
    {
        public function BitmapDataRectTest()
        {
            var bmp:BitmapData = new BitmapData(100, 100, true, 0);
            trace(bmp.rect == bmp.rect); // false
            var rect1:Rectangle = bmp.rect;
            var rect2:Rectangle = bmp.rect;

            trace("Place breakpoint here and look at rect1 and rect2 memory addresses");
            // rect1 address on my pc: @6900f71
            // rect2 address on my pc: @6900f41
        }
    }
}

EDIT Similar behaviour in native classes:

  • DisplayObject.transform: returns new Transform instance on each access
  • Transform.colorTransform: returns new ColorTransform instance on each access
  • DisplayObject.filters: returns new Array instance on each access
  • ...you can find a lot of other cases when you get a copy of an internal object.

In all of these cases classes protect themselves from being broken. Just think of it this way: when a class uses aggregation it can not expose it's aggregated instances as is, because otherwise it would be necessary to notify main class of changes in internal objects when they occur. That's a lot of logic to update and validate changes in each field of each aggregated instance.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜