Question

Im using a very big BitmapData as a pathing map for my platformer game, however I only use pixels for 4 particular values, instead of, well 4294967295.

Would converting this Bitmapdata as 2 2D Vectors of Boolean save me some memory ? And if it does, what about performance, would it be faster or slower to do something like:

MapGetPixel(x:int, y:int):int
{
    return MapBoolFirst[x][y] + MapBoolSecond[x][y]*2;
}

instead of the bitmapdata class getPixel32(x:int, y:int):uint ?

In short im looking for a way to reduce the size and/or optimize my 4 colors bitmapdata.

Edit : Using my boolean method apparently consumes 2 times more memory than the bitmapdata one. I guess a boolean takes more than one bit in memory, else that would be too easy. So im thinking about bitshifting ints and thus have an int store the value for several pixels, but im not sure about this…

Edit 2 : Using int bitshifts I can manage the data of 16 pixels into a single int, this trick should work to save some memory, even if it'll probably hit performance a bit.

Was it helpful?

Solution

Bitshifting will be the most memory-optimized way of handling it. Performance wise, that shouldn't be too big of an issue unless you need to poll a lot of asks each frame. The issue with AS is that booleans are 4bits :(

As I see it you can handle it in different cases:

1) Create a lower res texture for the hit detections, usually it is okay to shrink it 4 times (256x256 --> 64x64)

2) Use some kind of technique of saving that data into some kind of storage (bool is easiest, but if that is too big, then you need to find another solution for it)

3) Do the integer-solution (I haven't worked with bit-shifting before, so I thought it would be a fun challenge, here's the result of that)

And that solution is way smaller than the one used for boolean, and also way harder to understand :/

public class Foobar extends MovieClip {
    const MAX_X:int = 32;
    const MAX_Y:int = 16;

    var _itemPixels:Vector.<int> = new Vector.<int>(Math.ceil(MAX_X * MAX_Y / 32));

    public function Foobar() {
        var pre:Number = System.totalMemory;
        init();
        trace("size=" + _itemPixels.length);
        for (var i = 0; i < MAX_Y; ++i) {
            for (var j = 0; j < MAX_X; ++j) {
                trace("item=" + (i*MAX_X+j) + "=" +  isWalkablePixel(j, i));
            }
        }
        trace("memory preInit=" + pre);
        trace("memory postInit=" + System.totalMemory);
    }

    public function init() {
        var MAX_SIZE:int = MAX_X * MAX_Y;
        var id:int = 0;
        var val:int = 0;
        var b:Number = 0;
        for(var y=0; y < MAX_Y; ++y) {
            for (var x = 0; x < MAX_X; ++x) {
                b = Math.round(Math.random()); //lookup the pixel from some kind of texture or however you expose the items
                if (b == 1) {
                    id = Math.floor((y * MAX_X + x) / 32);
                    val = _itemPixels[id];
                    var it:uint = (y * MAX_X + x) % 32;
                    b = b << it;
                    val |=  b;
                    _itemPixels[id] = val;
                }
            }
        }
    }

    public function isWalkablePixel(x, y):Boolean {
        var val:int = _itemPixels[Math.floor((y * MAX_X + x) / 32)];
        var it:uint = 1 << (y * MAX_X + x) % 32;
        return (val & it) != 0;
    }
}

OTHER TIPS

One simple improvement is to use a ByteArray instead of BitmapData. That means each "pixel" only takes up 1 byte instead of 4. This is still a bit wasteful since you're only needing 2 bits per pixel and not 8, but it's a lot less than using BitmapData. It also gives you some "room to grow" without having to change anything significant later if you need to store more than 4 values per pixel.

ByteArray.readByte()/ByteArray.writeByte() works with integers, so it's really convenient to use. Of course, only the low 8 bits of the integer is written when calling writeByte().

You set ByteArray.position to the point (0-based index) where you want the next read or write to start from.

To sum up: Think of the ByteArray as a one dimensional Array of integers valued 0-255.

Here are the results, I was using an imported 8 bit colored .png by the way, not sure if it changes anything when he gets converted into a BitmapData.

Memory usage :

  • BitmapData : 100%
  • Double Boolean vectors : 200%
  • Int Bitshifting : 12%

So int bitshifting win hands down, it works pretty much the same way as hexadecimal color components, however in that case I store 16 components (pixel values in 2 bits) not the 4 ARGB:

var pixels:int = -1;// in binary full of 1
for (var i:int = 0; i < 16; i++)
    trace("pixel " + (i + 1) +" value : " + (pixels >> i * 2 & 3));

outputs as expected : "pixel i value : 3"

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top