How do I properly draw one vector object onto a specific position of another in ActionScript, accounting for positioning, transparency, etc?
My idea (as suggested e.g. here) was to use beginBitmapFill
() and drawRect
() to draw a bitmap copy (made using BitmapData.draw
()) of one MovieClip onto the .graphics
property of the other MovieClip, but this proved more difficult than I thought, mainly for these reasons:
Transparency is not preserved: where the source MovieClip is transparent, the target Graphics becomes white. I've tried using aIt seems the problem was that the documentation for theBlendMode
, but it doesn't help. It seems white pixels are actually copied into the BitmapData. I must be missing something, but I don't know what.BitmapData
constructor is wrong: transparency does not default to true, but to false. So if you create anew BitmapData(x, y, true)
transparency is preserved, otherwise not.Positioning. If the source MovieClip is centered (with the center at (0, 0), you have to apply a Matrix to move it when copying it to a BitmapData, or the bitmap will only contain the bottom right of the source MovieClip. Figuring out how much to move it is tricky, and I assume it involved getting the boundaries somehow. (If the source object is perfectly centered within its canvas, you can simply offset it by half its width and height, of course.)
beginBitmapFill
tiling. It seems if you don't start thedrawRect
() at (0, 0), the bitmap fill doesn't start from (0, 0) within the bitmap either. This means you have to shift the source bitmap once again, based on where you want to start drawing.
(Then there's other stuff like having to lineStyle(0,0,0) so the drawRect() doesn't draw a border etc.)
All this tricky trouble leads me to believe I'm going about this the wrong way. Is there an easier way to do this? Is there a library somewhere that can help me? Has anyone successfully done this? Perhaps my drawing canvas shouldn't be a Sprite but a Bitmap? But then I don't know how to draw to it using lineTo
() etc.
Here's the situation: I have a Sprite to which I draw using its .graphics, using lineTo() etc. Now I want to use a MovieClip as a brush when drawing (or just place a copy of another MovieClip on the drawing surface), making the other MovieClip part of the graphics of the first one. I can't addChild
() because then the vector graphics wouldn't interact with the drawn graphics.
EDIT: Theo has provided a working solution that works perfectly except for the fact that it does not apply the rotation and scaling applied to the DisplayObject being drawn onto the Graphics surface. This is Theo's solution:
private function drawOntoGraphics(source : IBitmapDrawable, target : Graphics, position : Point = null) : void
{
position = position == null ? new Point() : position;
var bounds : Rectangle = DisplayObject(source).getBounds(DisplayObject(source));
var bitmapData : BitmapData = new BitmapData(bounds.width, bounds.height, true, 0x00000000);
bitmapData.draw(source, new Matrix(1, 0, 0, 1, -bounds.x, -bounds.y), null, null, null, true);
target.beginBitmapFill(bitmapData, new Matrix(1, 0, 0, 1, bounds.x + position.x, bounds.y + position.y));
target.drawRect(bounds.x + position.x, bounds.y + position.y, bounds.width, bounds.height);
}
To support rotation and scaling, I assume the following has to be modified:
- The
Matrix
used indraw()
should copy the scaling and rotation from the sourceDisplayObject
? - The
Matrix
used inbeginBitmapFill()
should copy this scaling and rotation as well? Or maybe that would create "double" scaling and rotation? - The
drawRect()
coordinates should be modified to match the size of the scaled and rotatedDisplayObject
? Which meansgetBounds()
will not be accurate?
EDIT: Here is a working implementation which supports scaling and rotation, made from all the feedback:
static function DrawOntoGraphics(source:MovieClip, target:Graphics,
position:Point = null):void {
var sourceClass:Class = getDefinitionByName(getQualifiedClassName(source))
as Class;
var sourceCopy:MovieClip = new sourceClass();
sourceCopy.rotation = source.rotation;
sourceCopy.scaleX = source.scaleX;
sourceCopy.scaleY = source.scaleY;
sourceCopy.graphics.clear();
var placeholder:MovieClip = new MovieClip();
placeholder.addChild(sourceCopy);
if (!position)
position = new Point();
var bounds:Rectangle = placeholder.getBounds(placeholder);
var bitmapData:BitmapData = new BitmapData(bounds.width, bounds.height,
true, 0x00000000);
var drawMatrix:Matrix = new Matrix(1, 0, 0, 1, -bounds.x, -bounds.y);
bitmapData.draw(placeholder, drawMatrix);
placeholder.removeChild(sourceCopy);
var fillMatrix:Matrix = new Matrix(1, 0, 0, 1, bounds.x + position.x,
bounds.y + position.y);
target.beginBitmapFill(bitmapData, fillMatrix);
target.lineStyle(0, 0x00000000, 0);
target.drawRect(bounds.x + position.x, bounds.y + position.y,
bounds.width, bounds.height);
target.endFill();
}
Somewhat related but not so hot question: How to put an image (say, PNG) on a graphics in Flex 3?