Sign in to follow this  
rgk

Only update portion of bitmapdata.

Recommended Posts

So one major optimization issue I have is that I update a bitmap and I have huge bitmaps, originally 4800 wide by 2100 high. ( Currently shrinking them to optimize because of update() )
Bullets remove chunks of ground, so I can't really call it less.

The update function completely recreates all of that data. https://github.com/photonstorm/phaser/blob/v2.6.2/src/gameobjects/BitmapData.js#L596

Using the x, y, width and height forgets all the other data besides what is inside that.

Does anyone know of any way of only updating a small portion of it while still keeping the rest of the array intact?

I've been looking into it for quite some time with little luck.

Share this post


Link to post
Share on other sites

 

1 hour ago, rgk said:

How would I do that? I'm having a hard time figuring out where to update this data in the array.

The pixels array is one-dimensional, so to get to pixel [x, y] you need the element at: y * width + x

Have not tested it, but this should work:

function copyBitmapData(source, dest, x, y) {
  var sourcePixels = new Uint32Array(source.data.buffer),
    destPixels = new Uint32Array(dest.data.buffer);
    
  for (var i = 0; i < source.height; i++) {
    var destRowStart = (y + i) * dest.width + x;
    for (var j = 0; j < source.width; j++) {
        var pixel = sourcePixels[i * source.width + j];
        destPixels[destRowStart + j] = pixel;
    }
  }    
}

 

Share this post


Link to post
Share on other sites

You can get smarter than direct array access to make things a little more intuitive for you as developer. Take a look at ndarray, specifically array shifting to cut out part of the structure, this is one of the exact use-cases for the module.

It's been a while since I mucked around with this stuff, but BitmapData is a load of ClampedUint8's right? Infact, I don't think it matters, you just create an ndarray based on the entire bitmap data, then use .lo and .hi to cut out a portion to create a shifted view of that array you can then manipulate:

// Using pixels as its easier to manipulate, .data might suit better
var image = ndarray(bitmapData.pixels, [4800, 2100])
var chunk = image.lo(400,400).hi(200,200)

for (let i = 0; i < chunk.length; i += 4) {
  // Specify red (this might be how you do it, can't remember)
  // just saw this is wrong! haha, you'll get some colour though!
  let value = (255 << 24) |
    (255 << 16) |
    (0 << 8) |
    0 

  // Set it to red
  chunk.set(x, value)
}

Chunk uses the same data structure as image and bitmapData so you're not duplicating arrays (memory), this does require you to keep track of it all and make sure you're not mutating the wrong underlying structure, but, well, yay javascript.

edit:

3 hours ago, rgk said:

I'm having a hard time figuring out where to update this data in the array.

Just saw that, have you seen this? I checked the Phaser docs and BitmapData helpfully gives you both the Uint8Clamped view into the image data and the Uint32 view, the clamped view groups up pixels into 4 components, rgba, whereas the Uint32 grouped those into one 32-bit component (rather than 4 8-bit components, hence why they are known as views on to the same piece of memory). If you're having trouble updating the data in the array it could be you're using BitmapData.data which gives you the clamped Uint8, you might be expecting each array entry to represent a colour (e.g. 0xff0033ff) but it does not, the Uint32 version however, works exactly like this.

Again, if you use ndarray because of the way it represents the data internally it is quite easy to use either structure and provide different ways of entering the data, e.g. if you change the stride you effectively change how it iterates over the structure, so you could use the Uint8Clamped views and change the stride to only target the alpha, then when you iterate through using `ndarray.get(0)`, `ndarray.get(1)`, etc you'd only get the 8 bits referring to alpha. All quite fun if thats your bag.

Share this post


Link to post
Share on other sites

@Fatalist Thank you, that got me moving in the right direction. Problem is if I set dest.pixels to destPixels, nothing happens. Do I also have to update other values like the buffer?

            this.bmd.copy(this.level, 1200, 1200, 100, 100, 0, 0); // This grabs an obvious square for testing.
            this.bmd.update();
            this.level.copy(this.bmd, 0, 0, 100, 100, bullet.x - 50, bullet.y - 50);
            this.copyBitmapData(this.bmd, this.level, bullet.x - 50, bullet.y - 50);

The copy back visually updates it but getPixel doesn't pickup on the changes, obviously doing a this.level.update does the trick after the copy.

@mattstyles That is a very cool library (I'm going to be using it to handle huge arrays in the future), I'm looking to do it as lightweight as possible for the sake of putting it back in Phaser. I am sure someone can use an optimized way to update large bitmaps.

Share this post


Link to post
Share on other sites
1 hour ago, rgk said:

Problem is if I set dest.pixels to destPixels, nothing happens.

You don't need to set it, they point to the same data anyway. In fact, you don't need destPixels/sourcePixels at all, just use .pixels property instead.

Put this after copyBitmapData to see where do the pixels actually change: this.level.context.putImageData(this.level.imageData, 0, 0);

 

Share this post


Link to post
Share on other sites

I forgot to Math.floor() the bullet.x and bullet.y values, and it works!

Obvious issues are you can't use getPixel, only getPixel32 because of what they access, I am going to be trying to add this to phaser and I will make sure to give you credit @Fatalist

Share this post


Link to post
Share on other sites

@Fatalist I had to change all the use I had of getPixel to getPixel32, because I oddly enough used both in two different situations and only getPixel32 worked properly. I don't alter the buffer, I switched to the .pixels property directly. Also here is the initial pull request, it needs some work though. https://github.com/photonstorm/phaser-ce/pull/12

Edit: Which I just sent over a PR to fix those doc issues and changed the method scope.

Share this post


Link to post
Share on other sites

Nice. Another thing that would be useful(and more straightforward in your case too), is to have an optional boolean parameter in BitmapData.update that tells it to not discard old data and just update the specified rectangle.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.