setVector vs copyPixels

Actionscript:
  1. var brushNum:int = 10000;
  2. var canvas:BitmapData = new BitmapData(800,600,false, 0x000000);
  3. addChild(new Bitmap(canvas));
  4.  
  5. var shape:Shape = new Shape();
  6. with(shape.graphics) beginFill(0xFF0000), drawCircle(10,10,5);
  7. shape.filters = [new BlurFilter(6, 6, 4)];
  8.  
  9. var brush:BitmapData = new BitmapData(20, 20, false, 0x000000);
  10. brush.draw(shape, shape.transform.matrix);
  11.  
  12.  
  13.  
  14. // make sure brushVect is fixed length
  15. var brushVect:Vector.<uint> = new Vector.<uint>(brush.width * brush.height, true);
  16. var tempVect:Vector.<uint> =  brush.getVector(brush.rect);
  17. var i:int = 0;
  18. // quick hack to retain fixed length
  19. for (i = 0; i<brushVect.length; i++){
  20.     brushVect[i] = tempVect[i];
  21. }
  22.  
  23. canvas.setVector(brush.rect, brushVect);
  24.  
  25. var pnt:Point = new Point();
  26. var brushRect:Rectangle = brush.rect;
  27. var destRect:Rectangle = brushRect.clone();
  28. var locX:Vector.<Number> = new Vector.<Number>(brushNum, true);
  29. var locY:Vector.<Number> = new Vector.<Number>(brushNum, true);
  30. for (i= 0; i<brushNum; i++){
  31.     locX[i] = Math.random() * stage.stageWidth - 10;
  32.     locY[i] = Math.random() * stage.stageHeight - 10;
  33. }
  34.  
  35. var iterations:int = 50;
  36. var timer:Number;
  37. var avg:Vector.<Number> = new Vector.<Number>();
  38. trace("copyPixels");
  39. addEventListener(Event.ENTER_FRAME, onLoop);
  40. function onLoop(evt:Event):void {
  41.        canvas.fillRect(canvas.rect, 0x000000);
  42.        var i:int = brushNum;
  43.        if (drawMode == "copyPixels"){
  44.            timer = getTimer();
  45.            canvas.lock();
  46.            while(--i> -1){
  47.                 pnt.x = locX[i];
  48.                 pnt.y = locY[i];
  49.                 // copyPixels can easily do alpha manipulation, setVector cannot (see comment below);
  50.                 canvas.copyPixels(brush, brushRect, pnt);//, null, null, true);
  51.            }
  52.            canvas.unlock();
  53.            avg.push(getTimer() - timer);
  54.        }else{
  55.            timer = getTimer();
  56.            canvas.lock();
  57.            while(--i> -1){
  58.                destRect.x = locX[i];
  59.                destRect.y = locY[i];
  60.                canvas.setVector(destRect, brushVect);
  61.            }
  62.            canvas.unlock();
  63.            avg.push(getTimer() - timer);
  64.        }
  65.        // take an average of iterations iterations
  66.        if (avg.length == iterations){
  67.            var average:Number = 0;
  68.            for (i = 0; i<iterations; i++){
  69.                  average += avg[i];
  70.            }
  71.            trace(average / iterations);
  72.            avg = new Vector.<Number>();
  73.        }
  74. }
  75.  
  76. var drawMode:String = "copyPixels";
  77. stage.addEventListener(KeyboardEvent.KEY_UP, onKeyReleased);
  78. function onKeyReleased(evt:Event):void{
  79.     avg = new Vector.<Number>();
  80.     drawMode = (drawMode == "copyPixels") ? "setVector" : "copyPixels"
  81.     trace(drawMode);
  82. }

In the comments of a post from a few days back, Piergiorgio Niero mentioned that setVector may be faster than copyPixels. To see if this was true, Piergiorgio and I each tried a few things and while Piergiorgio seemed to reach some conclusions... I wasn't sure we had properly tested to see which one was actually faster.

I created the above snippet to help test to see which is indeed faster. This snippet draws 10,000 small 20x20 pixel graphics to the stage. There is no animation because I wanted to try and isolate the speed of the setVector and copyPixels calls. These are the results on my macbook pro 2.4 ghz duo:

// average speed of 50 iterations of drawing
copyPixels
15.1
15.68
15.44
15.46
15.62
15.74
15.68
setVector
32.6
32.6
31.1
32.1
32.82
32.54
copyPixels
15.48
15.62
15.74
15.46
15.42
15.44
15.64
setVector
32.62
32.8
33.08
32.48
32.74
32.32

If you interested in this, post your results in the comments along with the type of computer you're using. I have a feeling there will be a wide variety of results... just make sure you're not using the flash debug player, as that can act significantly different than the release version of the player.

setVector() and copyPixels() Usage

Something to note here is that setVector and copyPixels aren't normally suitable for the same thing. copyPixels is used to move a rectangle of pixels from one BitmapData to another - you can easily do advanced alpha channel manipulation and scaling with it and you don't have to do pixel by pixel logic. setVector is used to do pixel by pixel manipulation - it is a sort of mature/advanced setPixel function that allows you to do logic on a Vector of uints and then set the pixels of a rectangular region of a BitmapData object equal to the data within that Vector. So if you need to do alpha manipulation or image scaling with setVector you'll find yourself running into some more advanced programming than copyPixels requires... and if you tried to do pixel by pixel manipulation with copyPixels... well, that just isn't what it was meant to be used for...

I'm always wary of these kind of speed tests... if anyone has suggestions about how to improve this test, please feel free to comment.

UPDATE katopz pointed out I should use fixed length Vectors... so I changed the brushVect instantiation code slightly. I didn't do it for the avg vector because it only has 50 values and it doesn't help improve the speed of setVector and copyPixels in any way and complicates the code slightly... it's hard to decide which optimization techniques you should choose to make habbit and which ones you should only whip out when you need speed...

This entry was posted in BitmapData, Vector. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

6 Comments

  1. Posted July 16, 2009 at 11:13 am | Permalink

    Hey Zevan

    Can you try test again with fixed length Vector for avg?
    i know is not relate to overall result…but push is kinda slow i hate to see it there…btw nvm that just my bad habit ;)

  2. Posted July 16, 2009 at 12:12 pm | Permalink

    I made the call not to bother because it will increase the complexity of the snippet .. and since it doesn’t relate to the overall result I choose simplicity for the avg vector. But it does make sense to add it for the brushVect Vector… so I’ll update that in a minute. As it turns out, if you have a fixed length Vector and then do something like:

    brushVect = canvas.getVector(brush.rect);

    your setting it to a new vector that is not fixed length…

  3. Posted July 16, 2009 at 12:20 pm | Permalink

    updated the post…

  4. Posted July 18, 2009 at 9:45 am | Permalink

    a new vector that is not fixed length… hmm, i think i can set

    brushVect .fixed = true;

    after that isn’t it?

    btw i found this interesting ;D

    http://webr3.org/blog/haxe/bitmapdata-vectors-bytearrays-and-optimization/comment-page-1/

  5. Posted July 18, 2009 at 7:30 pm | Permalink

    I think your right… you can test to see if it worked by trying to push another value to the vector and seeing if you get an error… if you do, its fixed…

    I’ll check out that link tomorrow… after skimming it, looks pretty interesting…

  6. Posted October 27, 2009 at 11:55 pm | Permalink

    yup setVector is slower, until you do about 1 million iterations
    10000 iterations
    setVector took: 66
    copyPixels took: 56

    100000 iterations
    setVector took: 568
    copyPixels took: 557

    1000000 iterations
    setVector took: 5604
    copyPixels took: 5591

    Heres my source, its abit purer to the actual method call.

    package {
    import flash.display.Sprite;
    import flash.display.Shape;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.events.Event;
    import flash.geom.Point;
    import flash.utils.getTimer;
    // jarradhope.com
    [SWF(width="800", height="600", backgroundColor="#ffffff", frameRate="30")]
    public class Main extends Sprite {
    private var canvas:BitmapData = new BitmapData(800, 600, false, 0×000000);
    private var brush:BitmapData = new BitmapData(32, 32, true, 0×00000000);
    private var iterations:int = 1000000;
    //
    private function doSetVector():void {
    var i:int = iterations;
    var brushVect:Vector. = brush.getVector(brush.rect);
    while (–i > -1) {
    canvas.lock();
    canvas.setVector(brush.rect, brushVect);
    canvas.unlock();
    }
    }
    //
    private function doCopyPixels():void {
    var i:int = iterations;
    var p:Point = new Point();
    while (–i > -1) {
    canvas.lock();
    canvas.copyPixels(brush, brush.rect, p);
    canvas.unlock();
    }
    }
    //
    private function init($e:Event=null):void {
    this.removeEventListener(Event.ADDED_TO_STAGE, init);
    // Start
    var shape:Shape = new Shape();
    with (shape.graphics) beginFill(0xFF0000), drawCircle(16, 16, 16);
    brush.draw(shape, shape.transform.matrix);
    addChild(new Bitmap(canvas));
    // Start setVector
    var timeStart:int = getTimer();
    doSetVector();
    trace(”setVector took: ” + (getTimer() - timeStart));
    // Start copyPixels
    timeStart = getTimer();
    doCopyPixels();
    trace(”copyPixels took: ” + (getTimer() - timeStart));
    }
    //
    public function Main():void {
    super();
    if (stage) init();
    else this.addEventListener(Event.ADDED_TO_STAGE, init);
    }
    }
    }

One Trackback

  1. [...] their respective readers, it appears that BitmapData.setVector may approach comparable performance (example) if you are doing many [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*