Introduction

This tutorial is a loose follow-up to an article I wrote detailing how to maximise render performance for the iPhone Packager. It outlined, with some simple examples, the use of bitmap sheets to load and render bitmap images as opposed to using Flash’s vector rendering engine.

I had also written an ActionScript 3 library to handle the loading and management of bitmap sheets, but due to the limited scope of the original article it wasn’t actually covered.

With Apple lifting their restrictions on the Packager for iPhone, and Adobe releasing both Flash Player 10.1 and AIR for Android, the use of bitmaps within Flash projects is once again a hot topic. I therefore felt the time was right to release my bitmap sheet library and detail how to use it.

I’ll lead you through the steps required to load bitmap sheets and detail how to obtain the data used to represent each of the bitmaps within the sheets.

If you understand the concepts involved and/or are a competent developer then you may actually want to skip the detailed steps and simply jump to the installation instructions and then onto the code examples at the end of this tutorial. There are also some more complete code examples bundled along with the library.

Requirements

To make the most of this tutorial, you’ll need the following software and files:

Installing the Code Library

To publish and run the code examples within this tutorial you will need to add the code library to your FLA’s publish settings.

  1. Unzip bitmap_sheets_library.zip to your local drive.
  2. From your current FLA file, select File | Publish Settings...
  3. From the Publish Settings pane, select the Flash tab.
  4. Ensure that the Script version is set to ActionScript 3.0.
  5. Click on the Settings... button next to the Script version.
  6. From within the Advanced ActionScript 3.0 Settings pane, click on the Library path tab.
  7. Click on the Browse to SWC file, browse to, and select the bitmap_sheets.swc file.
  8. Click OK to exit from the Advanced ActionScript 3.0 Settings pane.
  9. Click OK to exit from the Publish Settings pane.

Working with Bitmap Sheets

So you’ve created a sheet of bitmaps that you’d like to cut-out and store in memory. Here’s how a typical bitmap sheet might look.

It’s a simple 2 x 2 grid-shaped layout with each image covering 268 x 268 pixels. For the purpose of this tutorial let’s assume that the bitmap sheet is saved relative to your FLA at bitmap_sheets/monkey_sheet.png.

The following code will load the bitmap sheet and specify a rectangular region for each of its images. The code will also assigned a name to each image that you can use at a later date to quickly retreive a particular image’s bitmap data.

import com.ccaleb.utils.gfx.BitmapSheet;
import com.ccaleb.utils.gfx.Rectangles;
import com.ccaleb.utils.gfx.Names;
import flash.events.Event;
import flash.net.URLRequest;
 
var bs :BitmapSheet = new BitmapSheet();
bs.setRegions(
    new <Rectangle>[
        new Rectangle( 0, 0, 268, 268 ),  new Rectangle( 268, 0, 268, 268 ),
        new Rectangle( 0, 268, 268, 268 ), new Rectangle( 268, 268, 268, 268 ) ],
    new <String>[ "monkey1", "monkey2", "monkey3", "monkey4" ]
);
bs.addEventListener( Event.COMPLETE, loaded );
bs.load( new URLRequest( "bitmap_sheets/monkey_sheet.png" ) );
 
function loaded( e :Event ) :void
{
    trace( "The bitmap sheet has been loaded." );
}

The steps required to set-up and load a bitmap sheet are fairly straight forward and are listed below:

  1. Create a BitmapSheet instance.
  2. Specify the regions to cut-out from the sheet and give each region a unique name.
  3. Add event listeners to the BitmapSheet instance.
  4. Load the bitmap sheet.

Let’s look at the code in more detail.

Creating a BitmapSheet Instance

For every bitmap sheet you wish to load, create a separate instance of the BitmapSheet class. This tutorial’s example code creates an instance and assigns it to a local variable name bs:

var bs :BitmapSheet = new BitmapSheet();

Remember to import the BitmapSheet class before using it. Its fully qualified class name is com.ccaleb.utils.gfx.BitmapSheet.

Specify the Regions

As stated, the bitmap sheet contains 4 bitmaps, with each consuming a 40 x 40 rectangular region. The bitmaps are contained within a 2 x 2 grid-like layout. The rectangular regions representing each of these bitmaps should be passed to your BitmapSheet instance. Each bitmap should also be assigned a unique string identifier. The code below illustrates this:

bs.setRegions(
    new <Rectangle>[
        new Rectangle( 0, 0, 268, 268 ),  new Rectangle( 268, 0, 268, 268 ),
        new Rectangle( 0, 268, 268, 268 ), new Rectangle( 268, 268, 268, 268 ) ],
    new <String>[ "monkey1", "monkey2", "monkey3", "monkey4" ]
);

The code above declares to your BitmapSheet instance that your bitmap sheet is cut into the following named rectangular regions:

monkey1: (   0,   0 ) to ( 268, 268 )
monkey2: ( 268,   0 ) to ( 536, 268 )
monkey3: (   0, 268 ) to ( 268, 536 )
monkey4: ( 268, 268 ) to ( 536, 536 )

Don’t get confused by Flash’s Rectangle class. The first two parameters specify the x and y position of the top-left corner of the rectangle. The last two parameters specify the width and height of the rectangle.

Therefore new Rectangle( 268, 268, 268, 268) represents a rectangle with a top-left corner of( 268, 268 ) and a bottom right-hand corner of ( 536, 536 ).

Adding Event Listeners

You need to be notified when the bitmap sheet has been loaded, so remember to listen for the COMPLETE event that gets dispatched:

bs.addEventListener( Event.COMPLETE, loaded );

The line of code above makes a call to a method named loaded() once the bitmap sheet has been loaded. The loaded() method is shown below:

function loaded( e :Event ) :void
{
    trace( "The bitmap sheet has been loaded." );
}

Load the Bitmap Sheet

The final thing to do is make a call to the load() method:

bs.load( new URLRequest( "bitmap_sheets/monkey_sheet.png" ) );

The bitmap will be loaded and the regions will be stored with the sheet.

It’s also possible, and is probably more desirable, to load a bitmap sheet that is sitting within the library of the Flash IDE.

Using Flash Professional CS5, import a bitmap into your library and right-click on it. From the context menu select Properties... From the Bitmap Properties panel that appears, click on the ActionScript tab. Click on Export for ActionScript and enter a unique class name you’d like to assign to the bitmap.

For example, if you assigned the class name MonkeySheet to the bitmap then the following line of code would load it into your bitmap sheet instance:

bs.load( new MonkeySheet() );

One important thing to note. The bitmap sheet has not yet been cut into separate regions yet. The code that has been written so far only loads the sheet and stores the information required to cut-up the bitmap sheet. We’ll cover cutting-up the bitmap sheet soon enough, but first let’s back track a little.

Generating Regions

Take a look at the code required to specify the regions again:

bs.setRegions(
    new <Rectangle>[
        new Rectangle( 0, 0, 268, 268 ),  new Rectangle( 268, 0, 268, 268 ),
        new Rectangle( 0, 268, 268, 268 ), new Rectangle( 268, 268, 268, 268 ) ],
    new <String>[ "monkey1", "monkey2", "monkey3", "monkey4" ]
);

It certainly isn’t a lot of code but it could be if the bitmap sheet contained many more regions. For example a 10 x 10 grid would require 100 Rectangle objects to be specified and would be prone to error when typing out the data.

There are helper classes to help generate the vector array of Rectangle objects and name strings.

Let’s start with the vector array of Rectangle objects. A helper class called Rectangles is available that can be used to generate your vector array.

The Rectangles class provides a static method named create() that generates and returns the vector array. The method expects the following four parameters:

  • The width of each rectangle.
  • The height of each rectangle.
  • The horizontal rectangle count.
  • The vertical rectangle count.

Therefore the vector array of Rectangle objects from the code example above could be replaced with the following:

Rectangles.create( 268, 268, 2, 2 )

Remember to import the Rectangles class before using it. Its fully qualified class name is com.ccaleb.utils.gfx.Rectangles.

Generating Names

Generating a unique name for each bitmap is also fairly easy and again saves time and reduces the chance of errors being introduced.

This time use the Names class, which has its own static create() method. It expects the following 3 parameters:

  • The number of names to generate.
  • The base string to use for each name.
  • The start index to use when appending a unique number to each name.

The previous code example had the following string names explicitly listed:

  • monkey1, monkey2, monkey3, monkey4

We want to generate four names, therefore the first argument to pass to create() is 4. Each name contains the string monkey, which is what will be passed as the second argument. The first name is appended with the number one, meaning 1 should be passed as the third argument.

Therefore we could replace the vector array of string objects from the code above with the following:

Names.create( 4, "monkey", 1 );

Again, remember to import the class before using it. Its fully qualified name is com.ccaleb.utils.gfx.Names.

The final version of the setRegions() call is shown below:

bs.setRegions(
    Rectangles.create( 268, 268, 2, 2 ),
    Names.create( 4, "monkey", 1 )
)

Using the Rectangle and Names helper classes helps produce, not only compact, but reliable code.

Working with the Bitmap Library

Okay, so we have a sprite sheet but it’s still to be chopped up and stored in memory. The BitmapLibrary class will help here.

Put simply, you pass bitmap sheets to the bitmap library and it breaks each sheet into a collection of bitmaps that you can access via the library. You can access a bitmap by using the unique name you assigned to it or by using its index position within the library.

The following code creates a bitmap library and feeds it the bitmap sheet created previously:

import com.ccaleb.utils.gfx.BitmapLibrary;
 
var bl :BitmapLibrary = new BitmapLibrary();
bl.addBitmapSheet( bs );
 
bs.destroy();
bs = null;

You can add as many bitmap sheets as you like to the library. Each will be cut into bitmaps and stored internally within the library. Notice that the bitmap sheet is destroyed after it has been passed to the bitmap library. After the library has cut-out the bitmaps the sheet is no longer required and can be freed from memory.

Accessing the bitmap data

There are various ways you can access the bitmap data from a bitmap manager:

  • Using the unique name assigned to the bitmap data.
  • Using the bitmap data’s index position within the bitmap manager.

Using an index position to access the bitmap data is faster but a bitmap’s index position isn’t always easy to know. Let’s look at using the bitmap’s unique name first:

var bmData :BitmapData = bl.getBitmapData( "monkey1" );

The line above retrieves a reference to the bitmap data associated with monkey1.

The monkey1 bitmap was the first region in this tutorial’s bitmap sheet. Therefore we know its index position within the bitmap library will be 0 and its bitmap data can be accessed like this:

var bmData :BitmapData = bl.getBitmapData( 0 );

Of course in a more complex application it may not be wise to assume the position of a bitmap within the bitmap library. You could of course query the bitmap library for certain index positions and then use those values at a convenient time. The getIndex() method provides this functionality and its use is shown below where a looping monkey animation is created:

// Store the index positions for the first and last frames.
var firstFrameIndex :int = bl.getIndex( "monkey1" );
var lastFrameIndex  :int = bl.getIndex( "monkey4" );
var currFrameIndex  :int = firstFrameIndex;
 
// Create a monkey sprite.
var monkey :Bitmap = new Bitmap();
monkey.x = 100;
monkey.y = 100;
monkey.bitmapData = bl.getBitmapData( currFrameIndex );
monkey.addEventListener( Event.ENTER_FRAME, updateMonkey );
 
addChild( monkey );
 
// Update the monkey animation.
function updateMonkey( e :Event ) :void
{
    if( ++currFrameIndex > lastFrameIndex )
    {
        currFrameIndex = firstFrameIndex;
    }
 
    monkey.bitmapData = bl.getBitmapData( currFrameIndex );
}

Directly Accessing Bitmap Data

As a final option, you can directly access the vector array that is used to store the BitmapData objects. This provides the fastest access and is done via the public bitmaps property:

var bmData :BitmapData = bl.bitmaps[ 0 ];

The line of code above accesses the very first item stored in the library.

It’s also possible to determine the total number of items in the bitmap library and access the last one:

var count :uint = bl.bitmaps.length;
var bmData :BitmapData = bl.bitmaps[ count - 1 ]; // Remember: zero-indexing.

Where to Go From Here

You should now be ready to use the bitmap management API within your own ActionScript 3 projects. You may find this API particularly useful for iPhone and Android mobile development where bitmap intensive tasks can be passed to the device’s GPU for improved performance over complex vector shapes.

The following resources may also be of interest to you and help with the concepts discussed in this tutorial:

For slightly more complete examples take a look at the section below.

Examples

Example 1

The following code loads a bitmap sheet that represents a 2 x 4 grid of 40 x 20 pixel regions. The regions will be named croc/snap2 to croc/snap9.

import com.ccaleb.crocdare.gfx.BitmapSheet;
import com.ccaleb.utils.gfx.Rectangles;
import com.ccaleb.utils.gfx.Names;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.IOErrorEvent;
 
var bs :BitmapSheet = new BitmapSheet();
bs.setRegions(
    Rectangles.create( 40, 20, 2, 4 ), // 2 x 4 grid of 268 x 268 pixel regions.
    Names.create( 8, "croc/snap", 2 )  // Creates "croc/snap2" to "croc/snap9".
);
bs.addEventListener( Event.COMPLETE, loaded );
bs.addEventListener( IOErrorEvent.IO_ERROR, loadError );
 
try
{
    bs.load( new URLRequest( "sprites/croc_sheet.png" );
}
catch( e :ArgumentError )
{
    trace( e.message );
}
 
function loaded( p_event :Event ) :void
{
    trace( "bitmap data: " + p_event.target.bitmapData );
}
 
function loadError( p_event :Event ) :void
{
    trace( "failed to load." );
}

Example 2

Adds a bitmap sheet to the bitmap library.

import com.ccaleb.crocdare.gfx.BitmapSheet;
import com.ccaleb.utils.gfx.BitmapLibrary;
import com.ccaleb.utils.gfx.errors.InvalidBitmapSheetError;
import com.ccaleb.utils.gfx.errors.DuplicateBitmapNameError;
 
var bl :BitmapLibrary = new BitmapLibrary();
 
try
{
    bl.addBitmapSheet( bitmapSheet );
}
catch( e : DuplicateBitmapNameError )
{
    trace( e.message );
    return;
}
catch( e :InvalidBitmapSheetError )
{
    trace( e.message );
    return;
}
 
trace( "Number of bitmaps in library: " + bl.bitmaps.length );
trace( "Index of bitmap 'croc4' is: " + bl.getIndex( "croc4" ) );

Example 3

Various ways of accessing the same bitmap data.

// Assuming croc3 is the 3rd item in the library.
var bmData :BitmapData = bl.getBitmapData( "croc3" );
bmData = bl.getBitmapData( 2 );
bmData = bl.bitmaps[ bl.getIndex( "croc3" ) ];
bmData = bl.bitmaps[ 2 ];

Acknowledgements

A big thanks to Steve Koran for his great concept drawings of Dare the monkey.

  1. Hi Christopher, great blog post!

    Over at the Adobe Iphone forums we are having a similar discussion, and i brought up your article, but we are having some questions with regards to what happens when you do this:

    var bmData :BitmapData = bl.bitmaps[ 0 ];

    how is the task divided between the CPU/GPU on the Iphone ? IE do we know if the CPU is still utilised to get the cached GPU data into the target sprite, therefore in essence doing things twice and slowing things down? or is it all done via OpenGL from that point?

    With traditional blitting you have 1 spritesheet + a lookup table, and each frame doing a copyPixels() that grabs the right spritedata and copies it into a single bitmapdata on stage. However the problem is that copyPixels() doesnt use GPU acceleration on the Iphone ( is that correct ????) and that is why you came up with this method(?)

    So, what you are doing is creating a pseudo-blitting effect with your method, by blowing apart those Spritesheets, caching each ‘frame’ and then linking them to display objects. And therefore taking advantage of the GPU, as GPU only helps display objects and not copyPixels().

    Have you done any control group testing against copypixels()? if your method indeed improves performance?

    Also another problem is see with your method is storing bitmapdatas in a ‘loose’ array structure tends to use a lot more memory, IE an 1024×1024 spritesheet that holds 65×65 frames, may increase 200% in memory usage, if you ‘cut them out’.

    (PS: i could never find out the GPU RAM available on the Iphone, which would be important as I would assume with your memory intensive method we would need to be really careful)

    Martin

    Martin
  2. Hi Martin,

    I don’t know the exact ins and outs of the AIR rendering engine for iOS but from my observations it would seem that the following:

    var bmData :BitmapData = bl.bitmaps[ 0 ];

    takes place on the GPU rather than a copy being done from the CPU to the GPU (I’m using a reference to the bitmap data rather than directly creating a BitmapData object for each display object and copying to it). When using GPU render mode with the Packager for iPhone, all scene composition takes place on the GPU.

    Since the bitmap data within bl.bitmaps is already being stored in pixel buffers on the GPU there will be no need to copy any data from the CPU to the GPU each time your display list is rendered.

    >> However the problem is that copyPixels() doesnt use GPU acceleration on
    >> the Iphone ( is that correct ????) and that is why you came up with this
    >> method(?)
    Yeah the copyPixels() approach seems to be slow, plus you need to explicitly create a BitmapData() object for each of your display objects, which will consume memory. With the approach I took, I simply have references from each of my Bitmap objects to my pool of BitmapData objects within the bitmap library.

    In other words if I had a bitmap image of a monkey within the bitmap library and I had 10 sprites that I wanted to use the monkey bitmap for, then I would not need to create 10 BitmapData objects (one for each sprite). Instead I would point the bitmapData property of each sprite to the single monkey instance in my library. When it comes to the rendering of those 10 sprites, the GPU will take care of the composition and the results should be faster than individually copying the exact same bitmap data to each of the sprites before hand.

    I ran some tests on a 1st generation iPod touch. Using the code outlined in my tutorial I was able to render 2,306,532 pixels per frame (GPU mode). Using copyPixels() to perform the same task my test managed 768,844 (CPU mode) pixels per frame. The copyPixels() approach seems to work best when using CPU mode, while my approach works best using GPU mode.

    Of course the GPU will have a limit to the amount of bitmap data it can store so there is always the risk that you can max the GPU out, forcing Flash to shuffle bitmap data between the GPU and CPU to alleviate memory issues. It tends to be the case that the more you try to make an application perform, the more memory you will consume doing so. It’s something you just need to watch for and try and strike a nice balance between.

    I’m hoping to add a new blog post soon going over my findings in a little more detail. Hopefully I’ll try and get that done next week at some point.

    You might also want to check-out some of the session videos from this years MAX. I think there were a few sessions detailing the rendering pipeline for mobile and how to get the best frame rates.

    Hope all that helps and if you happen to find any holes in my understanding of rendering on iPhone or a faster way of doing things then I’d love to know – there isn’t a huge amount of documentation on the subject out there.

    Christopher (Author)
  3. Hi Chris

    >> takes place on the GPU rather than a copy being done from the CPU to the GPU

    – This is good news indeed, I wish Adobe would have some hard facts published just to confirm!

    >> Yeah the copyPixels() approach seems to be slow, you need to explicitly create a BitmapData() object for each of your display objects,

    – this is incorrect though, with Blitting you have a single target ‘Canvas’ bitmap, and you copyPixels() into that single bitmap each frame. So you shouldn’t have multiple display objects.

    have a look here:
    http://www.8bitrocket.com/2008/7/2/Tutorial-AS3-The-basics-of-tile-sheet-animation-or-blitting/

    – This is in fact even more memory efficient than yours, as you break up a 1024×1024 Spritesheet into individual frames, and put them into an array of individual bitmapdadas, which will usually be non powers of 2, therefore Might increase memory usage by 200%

    in Blitting you just use a single source BitmapData(spritesheet) and copy from there each gameloop tick with copypixels() into a single ‘Canvas’ Bitmap object. You use a Lookup Table to find the location of the Frames in the source Spritesheet (similar to the monkey example you posted)

    I dont know how the above would affect your test results as regards to copyPixels() vs Display objects, as you might have done the copypixels control group unfairly then.
    Would be nice to see a final comparison between the two approaches.

    About the GPU RAM: i dig up this: http://forum.unity3d.com/threads/50095-iPad-gfx-mem
    – they say that basically all RAM is shared, so in theory you dont need to worry about overloading the GPU, as the GPU uses the same source memory as the CPU, so in theory you can use all RAM available on the Iphone.

    Martin

    Martin
  4. Hi Martin,

    >> this is incorrect though, with Blitting you have a single target โ€˜Canvasโ€™
    >> bitmap, and you copyPixels()
    Thanks for highlighting that mistake in my tests.

    I re-ran the test and performed only one copy to a bitmap buffer per frame. Here are the results on a 1st generation iPod touch:

    1 copyPixels() per frame:
    CPU: 961,055 pixels per frame
    GPU: 1,922,110 pixels per frame

    I also performed a few other tests with more writes per frame to see the impact. Here are the results:

    4 copyPixels() per frame:
    CPU: 961,055 pixels per frame
    GPU: 1,729,899 pixels per frame

    8 copyPixels() per frame:
    CPU: 961,055 pixels per frame
    GPU: 1,345,477 pixels per frame

    25 copyPixels() per frame:
    CPU: 576,633 pixels per frame
    GPU: 961,055 pixels per frame

    As you can see, the number of copy operations doesn’t heavily impact the performance for trivial cases when running in CPU mode. GPU mode gave better results but started to tail off quickly when the number of copyPixels() operations per frame was increased.

    The more complex your app/game, the more copyPixel() operations you will need to perform per frame to handle all the animating content, which might limit what you can do with the copyPixels() approach. However, I still thought the results from the tests were quite good.

    Using the blitting technique outlined in my tutorial I was able to render 1,845,225 pixels per frame in GPU mode with 25 display objects animating independently on screen.

    >> This is in fact even more memory efficient than yours….
    >> …which will usually be non powers of 2, therefore Might
    >> increase memory usage by 200%
    I’m not aware of how Arrays are implemented for mobile, but I’d be surprised if the iPhone implementation is as wasteful as the desktop player. If it is then you could simply tell Flash upfront the size you’d like your array to be, which will create the exact memory (well more or less) required. Personally I don’t see a problem in using the Array or Vector class for storage – it’s what they’re there for and as developers we use both for almost all applications we may write – even mobile ones.

    Either way, it’s all very interesting and always good to try some other things out with the Packager for iPhone.

    Oh and thanks for the link regarding the GPU RAM!

    Christopher (Author)
  5. Chris!

    Now I am getting confused, I just realised now you are saying that even copyPixels() is GPU accelerated, its just the gains drop off quicker if you use it a lot?
    ( also you probably did this right, but what I meant was that you could use hundreds of copyPixels() calls, but they ALL get copied into a single target Canvas BitmapData and not into separate displayObjects )

    >> “Using the blitting technique outlined in my tutorial I was able to render 1,845,225 pixels per frame in GPU ”

    – do you mean the ‘pseudo-blitting’ method where you use the Bitmap.bitmapData = cachedBitmapdataArray[0] right ?
    how did you get that figure? Can you shed some more light on the test parameters?

    Also the first generation Iphone has a very very poor GPU, the biggest upgrade of the current/last gen Iphones was the improved GPU, as that is of a different class altogether, you can run the latest Unreal engine on them, so it might make more sense if the final comparison was performed on 3GS/4 devices.

    >> Iโ€™m not aware of how Arrays are implemented for mobile,

    Its not the arrays mate, its the actual graphics! Consider this example:

    You have a 1024×1024 Spritesheet Bitmapdata, that holds an 513×513 and a 257×257 graphic.

    If you Blit from the original source Spritesheet you only used 1024×1024 Memory
    If you cut them out of the 1024×1024 and store them as separate BitmapDatas, then the 513×513 graphic will turn into 1024×1024 in memory, and the 257×257 will turn into 512×512, as Memory is allocated based on powers of two.
    Which means you just wasted 512×512 worth of memory.

    I hope that makes sense!

    Of course if you could make your ‘inner graphics’ be powers of 2 also, then you wouldn’t have this problem, however this is almost always impossible.

    Martin

    Martin
  6. Also when you blit, make sure you use Bitmapdata.lock() and Bitmapdata.unLock() before and after you start copying stuff!

    Martin
  7. >> Now I am getting confused, I just realised now you are saying that even
    >> copyPixels() is GPU accelerated, its just the gains drop off quicker if you
    >> use it a lot?
    Yeah the results where a bit crazy. Of course my tests could simply be wrong/broken – I’m going to try and get some time to double check them – writing code at 2am is never a good idea ๐Ÿ™‚

    >> Also the first generation Iphone has a very very poor GPU,
    >> …so it might make more sense if the final comparison was performed on
    >> 3GS/4 devices.
    Yes totally agree with you Martin. I was originally trying to squeeze as much as possible out of the older iPod touch and iPhones. And yes anything I’ve said in my posts may not be true for more recent devices. I might try and get my hands on an iPhone 4 and run the tests again – would be interesting.

    >> I hope that makes sense!
    Completely misread your original post, and yeah it makes perfect sense. I wasn’t aware of this so thanks for pointing it out, and as you said, it’s a very good reason for not cutting-up the bitmap sheets up-front.

    >> Bitmapdata.lock() and Bitmapdata.unLock() before and after you start
    >> copying stuff
    Yeah I was locking the pixels for those tests, although again the tests may not have been fair – really depends on my understanding of the blitting technique you outlined. And again I did quickly bash those tests together when I really should have been sleeping instead ๐Ÿ™‚

    >> how did you get that figure? Can you shed some more light on the test
    >> parameters?
    I’ll try and find some spare time to tidy-up my original test code and send it over to you.

    Christopher (Author)
  8. My tests for the https://github.com/timkerchmar/AS3-Tile-Ripper-Utility show the same thing, that copyPixels is affected by the GPU setting and that using Bitmaps instead of copyPixels is much faster. My test also showed that less bitmaps even with no overdraw is faster than more bitmaps, which makes sense because the underlying 3D apis always have had limitations regarding the number of unique commands that can be issued to the graphics card, often overshadowing other issues like overdraw or vertex count and texture size.

  9. Thanks for sharing pTymN!

    Christopher (Author)
  10. Hi guys, I’m just a noob so sorry for this noob question.
    The power of flash pro was the ability to quickly create or prototyping animation. We have great vector drawing tools, nested animation, sound stream sync and so forth.
    Looking at your great tutorial make me think: “If I need to get dirty with all the sprite sheet creation and manual blitting to make good performance on iOS devices, why would I want to use flash pro anymore? (or any flash platform) just doesn’t make any sense. Better to use straight mobile sdk (such as Corona or Cocos2D or GameSalad etc,) is it? cheaper, less complicated, faster? dont you think?”

    widhi
  11. Hi Widhi, yeah many people have found the initial release of the packager for iPhone difficult to work with. It does seem like rendering performance in a future version of the PFI is going to be significantly improved and the need to work at the sprite sheet level will disappear. Check out Renaun Erickson’s response to one of my other posts here: http://www.yeahbutisitflash.com/?p=2447#comments

    But yes, if you can find the time to learn the iOS SDK then it’s always a great advantage. Flash will be suitable for many apps but not all.

    Christopher (Author)
  12. Hi Christopher, I just read about adobe air 2.6 speed improvement. this is maybe the prove of your latest statement and a very very good news -for me at least-.
    I love flash and I hate spending time on learning other SDK (life is too short you know).
    One thing that I learn while trying to optimize flash on iOS (prior to adobe air 2.6) is that importing your animation as video/flv speed up the performance considerably. Maybe its due to hardware acceleration of video content. I created a walking cycle in flash (complex nested movieclips), exported as mov files. convert to flv and imported it back as a movieclip. move it across the screen with greensock’s tween engine. And voila!! it runs smoother on iOS devices. Its less hassle for non coder like me :). Just to share my experience, perhaps you know it already though.

  13. Hi Widhi! It’s something I hadn’t considered so thanks for sharing!

    Christopher (Author)
  14. Hi Christopher. You made really good work, wish I found it a bit sooner.
    I have a question. Is it possible to set bitmap smoothing to true with your classes somehow please? If I’m trying to move sprites across the stage it looks crappy with smoothing set to false, you know.
    Thanks for sharing!

  15. Hi Brano. You should be able to set the smoothing property to true for each of your bitmap instances. Also, check that your stage’s quality property is set to an appropriate value. Hope that helps.

    Christopher (Author)