1

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 profiler.

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.

  • Are you absolutely, 100% certain it is the rect data that is causing the issue? I would be completely baffled as well. Any chance of pastebin'ing the full source for inspection? – Nate Jul 15 '11 at 04:43

4 Answers4

1

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?

Community
  • 1
  • 1
Sly_cardinal
  • 12,270
  • 5
  • 49
  • 50
  • Excellent point, I figured that might be the reason too. While `Rectangle` is not a primitive type, `BitmapData` returns a new instance giving it a similar pass by value effect -- so now similar to the `x` property of a `DisplayObject`, you cannot update the value by storing a reference in another variable. Anyway, in the case of `Rectangle` why oh why would anyone change the value like that ... perhaps to do some matrix math. ;) – Peter Jul 15 '11 at 08:12
  • Also, the `rect` property is essentially read only (although properties on the Rectangle instance are not). Creating and returning a new Rectangle instance is the only way to enforce this. You are absolutely correct! – Peter Jul 15 '11 at 08:20
0

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.

Michael Antipin
  • 3,522
  • 17
  • 32
0

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.

Peter
  • 3,998
  • 24
  • 33
  • There is no need in strict comparison actually. It's only usefull when you are comparing values of different (or unknown) types and want to avoid type conversion. And here we compare a Rectangle to a Rectangle, `==` and `===` yield identical results. – Michael Antipin Jul 15 '11 at 07:36
  • That's true, in this case the types are the same. However why not use `===` just for the sake of completeness? – Peter Jul 15 '11 at 07:54
0

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.

surlac
  • 2,961
  • 2
  • 22
  • 31
  • There is only one way to get it — to reverse-engineer `playerglobal.swc` :). – surlac Jul 15 '11 at 20:03
  • Some of the classes like `Rectangle` I can view the entire source. Although for many others like `BitmapData`, most of what I can see are stubs -- which I assume have C++ implementations. Cool stuff! – Peter Jul 15 '11 at 20:21
  • Have a look at Tamarin project, there is open source implementation of AVM. It's really worth seeing. – surlac Jul 15 '11 at 22:35
  • Thanks, so I take it you're a pretty technical Flash enthusiast! I've been meaning to take a look at Tamarin for a while, maybe I will now. :) – Peter Jul 17 '11 at 07:36