I bet you can’t fill the canvas with paint using the bad painting algorithm below (keep an eye on the frame rate counter). Now check out your paint skills using the good paint algorithm. What’s the difference? Check out the answer after the examples.
Bad Paint:
(pretend your cursor is a paint brush, click and drag around)
Good Paint:
(notice the fps counter in the top left)
I would like to start by pointing out that every Flash ‘paint’ example I found online uses lineTo. That’s fine if your just playing around but what if you want to use a more advanced painting algorithm? It looks MUCH better. Drawing a circle using the ‘lineTo’ method has noticeable rigid corners. Using a good painting algorithm allows for much smoother edges and more control: Safe from the Losing Fight – How to implement a basic bitmap brush.
Well my first implementation of a simple painting algorithm turned out really bad (bad paint above). Why? Two reasons. I was creating a new sprite and BitmapAsset for every circle added (thousands of circles eating up memory). In addition to that I was using my own painting algorithm which was just barely a step up from lineTo.
The fix? First I converted a nice painting algorithm from C (CProgramming.com – Line Drawing Algorithm) into ActionScript. Second I took another look at the circle creation method.
My bad code:
override public function paint(point:Point):void { _wrapper = new Sprite(); _stroke = new Sprite(); _stroke.addChild(_particle); _stroke.width = _stroke.height = 20; _wrapper.addChild(_stroke); while(Math.abs(_point.y - point.y) > _travel || Math.abs(_point.x - point.x) > _travel){ var bitmapData:BitmapData = new BitmapData(_wrapper.width, _wrapper.height, true, 0x00000000); bitmapData.draw(_wrapper); // Creating a new BitmapAsset every time a particle is added: BAD!!! var bitmap:BitmapAsset = new BitmapAsset(bitmapData); bitmap.x = _point.x - (bitmap.width / 2); bitmap.y = _point.y - (bitmap.height / 2); this.addChild(bitmap); // Average painting algorithm if(_point.x < point.x){ _point.x = _point.x + _travel; }else if(_point.x > point.x){ _point.x = _point.x - _travel; }else{ _point.x = point.x; } if(_point.y < point.y){ _point.y = _point.y + _travel; }else if(_point.y > point.y){ _point.y = _point.y - _travel; }else{ _point.y = point.y; } } }
My good code:
public function PaintPngStroke(color:uint, start:Point) { super(color, start); _stroke = new Sprite(); _stroke.addChild(_particle); _stroke.width = _stroke.height = 20; var colorTransform:ColorTransform = _stroke.transform.colorTransform; colorTransform.color = _color; _stroke.transform.colorTransform = colorTransform; // Only create ONE particle _wrapper = new Sprite(); _wrapper.addChild(_stroke); _bitmapData = new BitmapData(SimplePaint.CANVAS_WIDTH, SimplePaint.CANVAS_HEIGHT, true, 0x00FF3300); // Only create ONE BitmapAsset _bitmap = new BitmapAsset(_bitmapData); this.addChild(_bitmap); } override protected function wp(startx:int, starty:int):void { // Move particle _wrapper.x = startx - (_wrapper.width / 2); _wrapper.y = starty - (_wrapper.height / 2); var matrix:Matrix = new Matrix(); matrix.tx = _wrapper.x; matrix.ty = _wrapper.y; // GOOD paint: reuse the BitmapData _bitmapData.draw(_wrapper, matrix); }
The good code runs much faster, is more efficient, looks better, and so on. Check out the source files to get a copy of the GOOD paint algorithm.
Enjoy!
Resources:
CProgramming.com – Line Drawing Algorithm
Safe from the Losing Fight – How to implement a basic bitmap brush
Great job! But I would advise posting some benchmarks. And post the code for your benchmarks so us users can also try it and see results on our own comp.
Benchmarks might be nice but in the end this is more of a memory issue than a performance one. The reason for the slowdown is the massive amount of Sprites / BitmapAssets on the canvas. By only using one BitmapAsset and one Sprite memory is no longer an issue for high volumes of paint. Slowdown occurs in the bad paint because of the massive volume of circles; not the power of the users computer.
The line drawing algorithm is just an appearance thing and does not have a significant effect on performance. Try drawing a circle on the bad paint (before the fps goes bad) than on the good paint. You’ll notice that the edges are much better on the good paint.
Thanks for the feedback!
Interesting Chris. I’ve been searching the web for details of how to make a painting app in flash – this is a good bit of code to help and I’ll add it to my list… I’m not a coder really and struggle with programming. Would you be interested in helping me with a project?
really a good job, I’m starting a drawing app in AS3 and I’ll take your aproach as starting point
thanks!