AS3 – Cutting a shape out of a bitmapData

Not sure what to call this (or what others will search for):

  • Cutting a shape out of a bitmapData without using a mask
  • Masking a bitmap with a shape

But here is the solution ūüôā

If you have a shape (for example a circle, or maybe a complex shape), you can cut out a bitmap in this same shape using the steps below. ¬†My example uses a ‘Shape’ instance as the ‘mask’, but you can easily use a Sprite or MovieClip instead, or remove a step and use another BitmapData as the mask.

The basic principle is:

  • Convert the mask to a bitmapData (if it isn’t already) using bitmapData.draw
  • Copy only the ALPHA channel from the mask to the source bitmap

Note that if the source image also has alpha, the alpha channel will be replaced by that of the mask, so the image may not look right. ¬†If you need to mask a source image with alpha (for example a transparent PNG) then you can’t use this method. ¬†See below.

In my example below I do it slightly differently.  The above steps will not preserve the source (the source alpha channel is modified).  Instead I do it the following way.  It uses more processing as it needs to copy 3 channels instead of 2, but it preserves the source:

  • Convert the mask to a bitmapData (if it isn’t already) using bitmapData.draw
  • Copy the RED, GREEN and BLUE channels from the source to the mask bitmapData, leaving the ALPHA intact
  • Use the mask bitmapData as your new bitmap

Here is the code (remember to import all the classes used below):

/**
 * Cuts the bitmap out in the shape provided. Please ensure that shape.x and shape.y are
 * relative to bitmap (for example 0,0 is top left of bitmap). This way you can position the
 * shape within the bitmap. It can also be rotated and scaled etc. Important: The original bitmap
 * is unchanged and a new bitmap is created.
 * @param bitmapData The source image
 * @param shape The shape to cut out
 */
public function cutShapeFromBitmapData( bitmapData : BitmapData, shape : Shape ):BitmapData {
    // Copy the shape to a bitmap
    var shapeBitmapData : BitmapData = new BitmapData( bitmapData.width, bitmapData.height, true, 0x00000000 );
    shapeBitmapData.draw( shape, shape.transform.matrix, null, null, null, true );
    // Now keep the alpha channel, but copy all other channels from the source
    var p : Point = new Point(0, 0);
    shapeBitmapData.copyChannel( bitmapData, bitmapData.rect, p, BitmapDataChannel.RED, BitmapDataChannel.RED );
    shapeBitmapData.copyChannel( bitmapData, bitmapData.rect, p, BitmapDataChannel.GREEN, BitmapDataChannel.GREEN );
    shapeBitmapData.copyChannel( bitmapData, bitmapData.rect, p, BitmapDataChannel.BLUE, BitmapDataChannel.BLUE );
    // Tada!
    return shapeBitmapData;
}

As mentioned above, if your source image has an alpha channel that you wish to preserve (like a transparent PNG) then the steps are as follows:

  • Create a new bitmapData the same size as source (we’ll call ours ‘dest’)
  • Fill it with a solid color (e.g. 0xffffffff)
  • Draw the mask on to dest using BlendMode.ERASE. This will cut out a hole the same shape as your mask
  • Now draw dest on to source ¬†also using BlendMode.ERASE. Done

Edit: The ‘copyPixels’ method lets you copy color data from one bitmap and alpha data from another bitmap, so another solution to this problem is:

  • Convert the mask to a bitmapData (if it isn’t already) using bitmapData.draw
  • Create a new bitmapData the same size as source (we’ll call ours ‘dest’)
  • Use copyPixels to copy the source image pixels to dest, while copying the alpha data from mask