Update (another): I’m no longer updating the code on here, instead it’s all on my Github account.
Update: there was a slight issue with the “height()” overrride, thanks to the comments I’ve updated the code with the fix.
Most designers and developers will be familiar with Actionscript 3’s “scale9Grid“. Now when you can use “scale9Grid”, it’s awesome, but there are times when you can’t use it or it doesn’t work:
- You can’t use it on Bitmaps or non-vector objects
- If the object has children, they won’t have the grid enforced on them
- And more…
Here’s a nice run down of when it works and when it doesn’t work: http://www.ovidiudiac.ro/blog/2009/05/scale9grid-work-and-fail/
So what do you do if your object has children or is a bitmap? Simple, use my class that I painstakingly wrote this morning!
I’m currently working on a project where I’ve created some assets in the Flash IDE, exported them as classes via a SWC and am using them in the Actionscript 3 code. One of the assets contains multiple children, rather than a straight vector object, meaning that it can’t have a “scale9Grid” thrust upon it.
So I got thinking about it and decided the best way was to do this:
- Convert the object to bitmap data
- Slice the object up into a 9 piece grid
- Align those grid pieces accordingly
- Override the “height” and “width” functions that would expand the centre grid items leaving the outside ones to scale
Simples! So I got on creating the class. One of the biggest problems was getting over the way Actionscript 3’s BitmapData “draw()” function misleads you. You see it says that you can pass a “clipRect” parameter as a “Rectangle()” which leads one to the assumption that it will clip the bitmap data according the rectangle you pass. Wrong, how very wrong. What it does, instead, is leave empty space where it’s been clipped. Not fun.
After much Googling, I came across this post: http://pixelwelders.com/blog/actionscript-3/2008/as3-bitmapdata-foibles/ which explained a little hack around this failed method. I’ve taken his code and just re-factored it so it fits within my code base, here it is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package com.firestartermedia.lib.as3.utils { import flash.display.BitmapData; import flash.display.DisplayObject; import flash.geom.Point; import flash.geom.Rectangle; public class BitmapUtil { public static function grab(source:DisplayObject, rect:Rectangle, smoothing:Boolean=true):BitmapData { var draw:BitmapData = new BitmapData( source.width, source.height, true, 0 ); var copy:BitmapData = new BitmapData( rect.width, rect.height, true, 0 ); draw.draw( source, null, null, null, null, smoothing ); copy.copyPixels( draw, rect, new Point( 0, 0 ) ); draw.dispose(); return copy; } } } |
So by using this static function in the class, I can create a proper grid of any display object, and here’s the class that does it all:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | package com.firestartermedia.lib.as3.display.tools { import com.firestartermedia.lib.as3.utils.BitmapUtil; import flash.display.BitmapData; import flash.display.DisplayObject; import flash.display.Sprite; import flash.geom.Rectangle; public class ScaleObject extends Sprite { private var bitmapTL:Sprite; private var bitmapTC:Sprite; private var bitmapTR:Sprite; private var bitmapML:Sprite; private var bitmapMC:Sprite; private var bitmapMR:Sprite; private var bitmapBL:Sprite; private var bitmapBC:Sprite; private var bitmapBR:Sprite; private var master:DisplayObject; private var scaleGrid:Rectangle; public function ScaleObject(master:DisplayObject, scaleGrid:Rectangle) { this.master = master; this.scaleGrid = scaleGrid; init(); } private function init():void { var tlX:Number = 0; var tlY:Number = 0; var tlWidth:Number = scaleGrid.x; var tlHeight:Number = scaleGrid.y; var tcX:Number = tlWidth; var tcY:Number = tlY; var tcWidth:Number = scaleGrid.width; var tcHeight:Number = tlHeight; var trX:Number = tlWidth + tcWidth; var trY:Number = tlY; var trWidth:Number = master.width - trX; var trHeight:Number = tlHeight; var mlX:Number = 0; var mlY:Number = tlHeight; var mlWidth:Number = tlWidth; var mlHeight:Number = scaleGrid.height; var mcX:Number = tlWidth; var mcY:Number = mlY; var mcWidth:Number = tcWidth; var mcHeight:Number = mlHeight; var mrX:Number = trX; var mrY:Number = mlY; var mrWidth:Number = trWidth; var mrHeight:Number = mlHeight; var blX:Number = 0; var blY:Number = tlHeight + mlHeight; var blWidth:Number = tlWidth; var blHeight:Number = master.height - blY; var bcX:Number = tlWidth; var bcY:Number = blY; var bcWidth:Number = tcWidth; var bcHeight:Number = blHeight; var brX:Number = trX; var brY:Number = blY; var brWidth:Number = trWidth; var brHeight:Number = blHeight; bitmapTL = slice( tlX, tlY, tlWidth, tlHeight ); bitmapTC = slice( tcX, tcY, tcWidth, tcHeight ); bitmapTR = slice( trX, trY, trWidth, trHeight ); bitmapML = slice( mlX, mlY, mlWidth, mlHeight ); bitmapMC = slice( mcX, mcY, mcWidth, mcHeight ); bitmapMR = slice( mrX, mrY, mrWidth, mrHeight ); bitmapBL = slice( blX, blY, blWidth, blHeight ); bitmapBC = slice( bcX, bcY, bcWidth, bcHeight ); bitmapBR = slice( brX, brY, brWidth, brHeight ); } private function slice(x:Number, y:Number, width:Number, height:Number):Sprite { var rect:Rectangle = new Rectangle( x, y, width, height ); var sliceData:BitmapData = BitmapUtil.grab( master, rect ); var slice:Sprite; slice = new Sprite(); slice.graphics.beginBitmapFill( sliceData ); slice.graphics.drawRect( 0, 0, width, height ); slice.graphics.endFill(); slice.x = x; slice.y = y; addChild( slice ); return slice; } override public function set width(value:Number):void { var targetWidth:Number = value - bitmapTL.width - bitmapTR.width; var targetX:Number = value - bitmapTL.width; bitmapTC.width = targetWidth; bitmapMC.width = targetWidth; bitmapBC.width = targetWidth; bitmapTR.x = targetX; bitmapMR.x = targetX; bitmapBR.x = targetX; } override public function set height(value:Number):void { var targetHeight:Number = value - bitmapTL.height - bitmapBL.height; var targetY:Number = value - bitmapBL.height; bitmapML.height = targetHeight; bitmapMC.height = targetHeight; bitmapMR.height = targetHeight; bitmapBL.y = targetY; bitmapBC.y = targetY; bitmapBR.y = targetY; } } } |
The basic usage is like so:
1 2 3 |
And here’s a little example:




Nice post mate
Saved a lot of time for our team
had to make a change to the “override public function set height(value:Number):void”
we changed the
Posted: July 1, 2009 at 7:05 am;var targetY:Number = value – bitmapTL.height;
to
var targetY:Number = bitmapML.y+ targetHeight;
very nicely done!!!
Posted: July 1, 2009 at 8:10 am;Nice thinking there…one thing i noticed: the overridden height property seemed to be off…
I changed:var targetHeight:Number = value – bitmapTL.height – bitmapBL.height;
to:
var targetHeight:Number = value – bitmapTL.height – bitmapTR.height;
and works great!
thx
Glad it’s helped, I’ve updated the code for you.
Posted: July 1, 2009 at 9:00 am;actually:
var targetX:Number = value – bitmapTR.width;
and
var targetY:Number = value – bitmapBL.height;
will do the trick
As Supun pointed out: saved us a lot of hassle.. thanks a lot
Posted: July 1, 2009 at 2:12 pm;I agree actually, makes the setting much nicer. I’ve updated the code, glad it’s of good use!
Posted: July 1, 2009 at 8:08 pm;Thanks it’s exactly what I was looking for!
Posted: July 20, 2009 at 4:51 pm;I’m glad you like it, spread the word!
Posted: July 20, 2009 at 5:28 pm;Thanks for the class… I tweaked it a bit to get this:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
{
/** Scale9grid pieces. **/
private var _tl:Bitmap;
private var _tc:Bitmap;
private var _tr:Bitmap;
private var _ml:Bitmap;
private var _mc:Bitmap;
private var _mr:Bitmap;
private var _bl:Bitmap;
private var _bc:Bitmap;
private var _br:Bitmap;
/** Constructor. **/
public function ScaleObject(original:DisplayObject, scaleGrid:Rectangle)
{
_tl = slice(original, 0, 0, scaleGrid.left, scaleGrid.top);
_tc = slice(original, scaleGrid.left, 0, scaleGrid.width, scaleGrid.top);
_tr = slice(original, scaleGrid.right, 0, original.width - scaleGrid.right, scaleGrid.top);
_ml = slice(original, 0, scaleGrid.top, scaleGrid.left, scaleGrid.height);
_mc = slice(original, scaleGrid.left, scaleGrid.top, scaleGrid.width, scaleGrid.height);
_mr = slice(original, scaleGrid.right, scaleGrid.top, original.width - scaleGrid.right, scaleGrid.height);
_bl = slice(original, 0, scaleGrid.bottom, scaleGrid.left, original.height - scaleGrid.bottom);
_bc = slice(original, scaleGrid.left, scaleGrid.bottom, scaleGrid.width, original.height - scaleGrid.bottom);
_br = slice(original, scaleGrid.right, scaleGrid.bottom, original.width - scaleGrid.right, original.height - scaleGrid.bottom);
}
/** Create a slice. **/
private function slice(original:DisplayObject, x:Number, y:Number, width:Number, height:Number):Bitmap
{
// render a slice of the original
var bd:BitmapData = new BitmapData(width, height, true, 0);
bd.draw(original, new Matrix(1, 0, 0, 1, -x, -y));
// create and position the bitmap
var bitmap:Bitmap = new Bitmap(bd, PixelSnapping.AUTO, true);
bitmap.x = x;
bitmap.y = y;
addChild(bitmap);
return bitmap;
}
/** Width. **/
override public function set width(value:Number):void
{
_tc.width = _mc.width = _bc.width = value - _tl.width - _tr.width;
_tr.x = _mr.x = _br.x = value - _tr.width;
}
/** Height. **/
override public function set height(value:Number):void
{
_ml.height = _mc.height = _mr.height = value - _tl.height - _bl.height;
_bl.y = _bc.y = _br.y = value - _bl.height;
}
}
Forgot to say: it’s a bit more efficient in-memory, on the display list, and in construction time. It’s also completely self-contained (no external bitmap utils class).
Posted: August 21, 2009 at 5:32 pm;Thanks Troy! Sorry about the external classes, I like to keep things split and what not.
Posted: August 21, 2009 at 5:42 pm;[...] Scale Any DisplayObject with My ‘ScaleObject’ Class [...]
Posted: August 26, 2009 at 5:18 pm;