PDA

View Full Version : Updating BitmapData objects



Rezmason
July 21st, 2007, 01:48 AM
The language reference mentions that you can pass the unlock() method of the BitmapData class a Rectangle, which is called the "changeRect". Apparently, this tells Flash that the pixels outside the boundaries of that Rectangle in the BitmapData object have not changed since before the BitmapData object was first locked with the lock() method.

This suggests that you can tell Flash to update only parts of a BitmapData object using the changeRect argument. I haven't gotten this to work, and I was hoping to use it for a speed boost in a project.

Does anyone else know how this changeRect is supposed to work? I'll post code if I have to.(-:

CarlLooper
July 21st, 2007, 02:26 AM
I haven't used the changeRect parameter, but I have used lock() and unlock().

This is how I interpret the docs and is consistent with what I've experienced in the past (but see notes at end).

During lock, the bitmapdata is not rendered to the display buffer, ie. you can change the data without the display buffer reflecting such changes. Which means your not fighting with the display buffer during changing of the data.

So, for example, if you were to lock a bitmapdata object, change it, and forget to unlock it then the display version of the bitmapdata would remain the same as it was prior to locking. This is something I tried and my interpretation remained consistent with what visibly transpired, ie. an old version of the bitmapdata remained displayed. The changes I made were not visible. Once unlocked the chnages made their way to the display buffer.

Now presumably, if you lock the bitmapdata, and just change a small portion of the bitmap data - you can use an unlock with a changeRect so that the display buffer updates itself (from the bitmapdata) from only that area specified.

At least that is the way I interpret the docs.

Is that the way you're interpreting it?

Carl

ps. in other words the model I'm using is that there is a second buffer (what I've called the display buffer) in addition to the bitmapdata object. The display buffer updates itself every time you make a change to the bitmapdata object - but you can stop the display buffer from updating by using bitmapdata.lock() and control the subsequent display buffer update with bitmapdata.unlock(). The bitmapdata object remains in whatever state you've put it in - irregardless of lock() and unlock().

BUT ... for some reason I can't reproduce my interpretation at the moment. Whether I issue unlock() or not the changes made to bitmapdata are being rendered. And tests using unlock(changeRect) don't appear to have any visible meaning.

...

Well I did a performance test and there is a still a well functioning difference in performance between using lock/unlock and not doing so. On my machine a locked bitmapdata object was rewritten at twice the speed as one that wasn't locked. So that's okay ...

What I was also able to do was to reproduce what I understood a missing unlock() should do - but I haven't yet rationalised why - why one peice of code I've written would produce the effect and another piece of code doesn't ... the only difference being in the quantity of data being rewritten and the higher probability (forced) that this might occur on a display buffer refresh ... and

Ok - I think I've worked out. Still need to test proposition. In one piece of code, lock() is called just once - without a corresponding unlock() - and in the second piece of code - also without a corresponding unlock(), lock() is being called a second time (and third time etc.) - so it might be a more logical proposition to suggest that the first lock() is locking out subsequent attempts to write the data - rather than locking out reads (writes to the display buffer).

So I guess the next test is to see what unlock(changeRect) is doing.

...

No joy. I can't get unlock(changeRect), as distinct from just unlock(), to indicate any clue as to what it is supposed to do, or is otherwise actually doing.

...

One possible idea is to manufacture the conditons under which two threads are fighting for parallel access to the bitmapdata object. The first thread obtains a lock and the second thread is locked out - other than where specified by the changeRect. Two timer events, fired milliseconds apart, might do it. The handler for the first event would lock the data, and the handler for the second thread would be hopefully, selectively thwarted by the unlock(changeRect) ...

Still no joy. If each handler is operating in a separate thread (which I don't know if they are anyway) the first thread didn't selectively lock out the second thread.

In other words there doesn't appear to be any conditions (and interpretations) under which one can demonstrate any difference between unlock() and unlock(changedRect).

Carl

From the docs:

lock()
Locks an image so that any objects that reference the BitmapData object, such as Bitmap objects, are not updated when this BitmapData object changes.

unlock()
Unlocks an image so that any objects that reference the BitmapData object, such as Bitmap objects, are updated when this BitmapData object changes.

Rezmason
July 21st, 2007, 08:28 AM
I just thought of something. Say you've got some ActionScript 3 code that makes a BitmapData instance and a Bitmap instance, locks the data, draws something, and unlocks it using a changeRect. After Flash runs through the script, isn't it just going to keep updating the bitmapData instance? Maybe we simply need to lock the data right after unlocking it.

EDIT: Or not. It'd help if Adobe had some example somewhere of the changeRect in use.

CarlLooper
July 21st, 2007, 08:35 AM
Maybe - but my brain's kaput. I'm going to bed. :)

Carl

I couldn't go to bed. I had to try your idea - but still no joy - the lock() following an unlock(changeRect) just locked out any subsequent writes altogether (or display buffer updates)

Now I'm off to bed for real.

senocular
July 21st, 2007, 10:07 AM
var changeRect:Rectangle = new Rectangle(20, 20, 60, 60);
var clickCount:int = 0;

var bitmapData:BitmapData = new BitmapData(100, 100, false, 0xFF);
var bitmap:Bitmap = new Bitmap(bitmapData);
addChild(bitmap);

stage.addEventListener(MouseEvent.CLICK, click);
function click(event:MouseEvent):void {
clickCount++;
switch(clickCount) {
case 1:{
bitmapData.lock();
trace("bitmapData.lock();");
break;
}
case 2:{
bitmapData.fillRect(bitmapData.rect, 0xFF0000);
trace("bitmapData.fillRect(bitmapData.rect, 0xFF0000);");
break;
}
case 3:{
bitmapData.unlock(changeRect);
trace("bitmapData.unlock(changeRect);");
break;
}
case 4:{
clickCount = 0;
bitmapData.fillRect(bitmapData.rect, 0xFF);
trace("[Reset]");
break;
}
}
}

Rezmason
July 21st, 2007, 03:13 PM
That's some nice, clear code, Sen, but am I supposed to see the entire bitmapData object change to red, or only part, when it's unlocked? I'm seeing it change entirely.

CarlLooper
July 21st, 2007, 05:38 PM
Yep.

Sen's code establishes yet another condition - in addition to all the others - where a difference between unlock() and unlock(changeRect) remains as ellusive as ever.

I suspect it really is a bug - ie. rather than some interpretation we've failed to consider.

In Sen's code:

click1: issues a lock.
click2: the data is written to bitmapdata (because in next click ...)
click3: unlock - result is that bitmapdata is displayed
click4: reset

Which is consistent with a face value reading of the docs.

With the exception that in click3, there is no difference between using unlock() and using unlock(changeRect).

And the following is inconsistent with docs as well (but forgiveable)




var bitmapData:BitmapData = new BitmapData(100, 100, true, 0xFF0000FF); // BLUE
var bitmap:Bitmap = new Bitmap(bitmapData);
addChild(bitmap);

trace("Initialised to BLUE");


bitmapData.lock();
bitmapData.fillRect(bitmapData.rect, 0xFFFF0000); // RED

trace("See RED? I do");

Carl

senocular
July 21st, 2007, 05:58 PM
So you're seeing solid red? Really?
Come to think of it I think I am working in higher version than release; let me check on something...

CarlLooper
July 21st, 2007, 06:04 PM
No problem. We might be on the home run here.
:)

edit - just to confirm that yes, in the version of flashplayer I'm using one always gets a solid block of colour rather than some subset (ie. as otherwise expected of an unlock(changeRect) call)

senocular
July 21st, 2007, 06:13 PM
Ok, I've traced it down and it appears someone caught it and fixed it, but not in time for this latest release (which was just under two weeks ago). It should be in the next release of the player, though, which is the same release currently available on labs.adobe.com. I can't remember the number of that build, but I suspect the fix didn't make it to that posting, so as of this point in time, you can go about life as though that feature didn't exist :ne:
And no, I can't tell you when the next version of the player will be released... though I can look into maybe having the documentation team possibly putting a version note for that parameter

Edit: Actually, I think the labs player does include the fix. I think that version is r60.120 which should work with my example (and those you've attempted and failed).

http://labs.adobe.com/downloads/flashplayer9.html

CarlLooper
July 21st, 2007, 06:21 PM
Thanks Sen. Problem solved.

Carl

CarlLooper
July 21st, 2007, 07:19 PM
On a side note.

I notice that if you use FireFox (Mozilla etc) to download the release version of FlashPlayer you'll be directed to the plugin version of such whereas if you use Windows Explorer you'll be directed to the ActiveX version of such.

Do you know if both versions (plugin and ActiveX) can be installed and happily co-operate on the same machine- ie. operate at the same time - eg. if one had both Windows Explorer and Firefox open at the same time?

I can always try it but I'm too scared :)

And if they can co-operate - I guess the reciprical question is whether the uninstaller (be it the most recent one or earlier) will uninstall both versions for you (plugin version and ActiveX version)

Carl

CarlLooper
July 21st, 2007, 07:38 PM
Edit: Actually, I think the labs player does include the fix. I think that version is r60.120 which should work with my example (and those you've attempted and failed).
Not yet. The available labs beta version, named flashplayer9_install_plugin_061107 doesn't have the fix.

But anyway, the problem is known and a fix is in the pipeline. So all is well.

Carl

edit: I've submitted a comment to LiveDocs for the unwary.

senocular
July 21st, 2007, 08:11 PM
The ActiveX version is for Win IE only; the plugin version is for all other browsers. Each creates instances of the player that can work fine on their own and won't interfere with the others, this including AX vs plugin - you know, that's the idea at least ;).

Uninstallers exist individually for both versions of the player and there is also an uninstaller that takes care of both at once. The individual uninstallers you should be able to find in (Win):
C:\WINDOWS\system32\Macromed\Flash
(though I'm not entirely sure that all installers install these uninstallers either...)

The installer that uninstalls both I think is only available on some obscure Flash support page on adobe.com. I don't have the link handy, but it shouldn't be hard to find in a search.

senocular
July 21st, 2007, 08:34 PM
Not yet. The available labs beta version, named flashplayer9_install_plugin_061107 doesn't have the fix.
You should be able to see it. Go to
http://www.adobe.com/products/flash/about/

And make sure your version is 9,0,60,120. You should be able to check the Product Version in flashplayer9_install_plugin_061107 to see that it is 9,0,60,120 (also noted in the release notes http://labs.adobe.com/technologies/flashplayer9/releasenotes.html ). The current release is 9,0,47,0 (9,0,45,0 was the release before that if you hadn't updated in the past 2 weeks).

I just installed the labs version and got my example to work :)

CarlLooper
July 21st, 2007, 09:04 PM
Yep - the version I downloaded an hour ago is Version 9,0,60,120.

Ok, I haven't actually run all my test code - or yours - so I'd better do that first.

The only test code I ran was the single call one - ie. with a single lock() and no corresponding unlock(). In that test the writes to bitmapdata, following lock(), were immediately reflected in the display ...

... but you obtained a different result - didn't you?

Carl

CarlLooper
July 21st, 2007, 09:13 PM
Ok. Just ran your test code with 9,0,60,120

Results:

Doing a "Test Movie" in CS3 - no joy.
If the swf is opened on it's own (from the file directory) - no joy.
But if opened in a browser (ie. via published html) - it works (nice one).

----
For the single lock() call test code (which is admittedly untypical usage)


var bmd:BitmapData = new BitmapData(100,100,true,0xFF0000FF);
var bitmap:Bitmap = new Bitmap(bmd);
addChild(bitmap);

var rect:Rectangle = new Rectangle(0,0,100,100);

bmd.lock();
bmd.fillRect(rect,0xFFFF0000); // this is immediately reflected in display
whether through CS3, file system, or browser - no joy.

Carl

Rezmason
July 21st, 2007, 09:34 PM
Quick note, Carl– when updating the Player to an Adobe Labs beta, you can be sure that you won't be updating the standalone and debug players. That'll have to wait, no matter what, for a major release.

I avoided this update, since I had trouble with the last one and still have a bad taste in my mouth. But in this case I'll bite the bullet; I've always wanted to see this work.

senocular
July 21st, 2007, 09:58 PM
Ah, I wasn't even thinking about that. Yeah, the Flash Authoring environment uses the external player.

There are essentially 4 kinds of players: ActiveX, Plugin, Standalone (.exe) and external - the player used in other applications. For Flash this is authplay.dll and that would have to be updated separately from the plugins. For public releases, they are usually available in a tech note via zip which includes both the browser plugin installers as well. But for labs, I don't think they offer that. Tests for this feature would have to be made in the browser with the labs plugin installed.

CarlLooper
July 21st, 2007, 10:00 PM
Yeah - that's what occured to me - that the standalone player wasn't updated - which is why I tried it via the browser ...

... and yep - at last - a demo of unlock(changeRect) - woo hoo

the update also includes hardware acceleration which is pretty funky. I want to try out some new algorithms I'm working on - which leverage bitmapdata functions to do otherwise mundane tasks such as sqrt functions ... except that the only thing missing from the bitmapdata functions is one that would do bitshifting on the value of pixels ... so will need to use colorTransform instead - but with hardware acceleration perhaps that will be do fine

:)

Carl

Rezmason
July 21st, 2007, 10:04 PM
I'm about to start a post about that, if someone hasn't beaten to the punch. Hardware acceleration is a new frontier for the platform, but I'm confused about whether it has limitations.

Back on topic, though, I've gotten your code to work, Sen, but not mine. I'm about to look at it again.

senocular
July 21st, 2007, 10:06 PM
Note that the use of hardware is for full screen only. There is now a new stage property that allows you to scale a portion of the flash movie to full screen and hardware is used to upscale that. Basically what you're doing is allowing the player to player at a normal size but in show it in full screen via hardware for better playback performance (rather than making the player draw the entire screen).

Rezmason
July 21st, 2007, 11:13 PM
:( Aww.

On a bright note, I've managed to get my own code to work. It's kind of strange, though- if changes are made to the BitmapData object too soon after the function call to lock it, the Bitmap objects subscribed to the BitmapData object seem to update anyhow. I've only been able to get positive results by separating the locking and updating logic by a significant period of time.

Here, try compiling and testing this in your browser:

stop();

var bd:BitmapData = new BitmapData(500, 500, false, 0);
var bmp:Bitmap = new Bitmap(bd);
var rect:Rectangle = new Rectangle(125, 125, 250, 250);
var ike:int;
var timer:Timer = new Timer(10, 1);
var color:uint;

addChild(bmp);
timer.addEventListener(TimerEvent.TIMER, clickResponse);
bd.lock();
color = 0xFF0000; // red
clickResponse();
color = 0xFFFF00; // yellow
timer.start();

function clickResponse(te:TimerEvent=null):void {
for (ike = 0; ike < 5000; ike++) {
bd.setPixel(Math.random()*bd.width, Math.random()*bd.height, color);
}
bd.unlock(rect);
bd.lock();
}

Or, you can just try the link here: http://homepage.mac.com/rezmason/changeRectTest.swf.

Note that the red dots show up everywhere, and the yellow dots show up only in the changeRect's bounds. This shows how updating too soon after locking will cause the updates to trickle down (so to speak) to the Bitmaps.

EDIT: I forgot to mention the implications of this. This means, if you're refreshing a BitmapData object in a function that responds to a TimerEvent, you'll need to somehow delay the locking and updating.

CarlLooper
July 21st, 2007, 11:34 PM
I see just as many yellow dots everywhere as there are red ones.

Carl

Rezmason
July 22nd, 2007, 12:03 AM
I see just as many yellow dots everywhere as there are red ones.

Carl

:sigh: Try it again in a minute.

CarlLooper
July 22nd, 2007, 12:41 AM
I saw it briefly then it was gone. Yellow dots constrained to a square in the middle.

Carl