
Original image by Edwin Tofslie
I was having a few gradient alpha mask disabilities today and thought I should share my misery and consequential solution with anyone who might run into the same problems!
Update: TF pointed out a very simple solution – cacheAsBitmap WILL work, providing you cache the Bitmap as a Bitmap, as you would if you were working with two vector drawings. Why a Bitmap wouldn’t be cached as a Bitmap I don’t fully understand.
Still use the copyPixels method if you want to create a single object and discard the mask, but otherwise don’t listen to me overcomplicating things and do it old school!
As you know, since Flash Player 8 we’ve been able to use an alpha channel when masking display objects. A common implementation is to first draw a gradient that fades from white with opacity 1 to white with opacity 0 and then tell a display object to use this as its mask.
Apparently, however – it’s not always that simple…
In this instance, I was using an embedded image (though the same would be true for a loaded one) which was in PNG format and contained an alpha channel. The alpha channel was crucial as the image was an icon with a transparent background and faded edges. I needed to dynamically mask and fade out this image to create a reflection via code, rather than doing this all in Photoshop. The resulting Bitmap would be sitting on top of a colour changing background, and so transparency was needed on both the original icon shape and the fading reflection – a common enough requirement.
The “cacheAsBitmap” Approach
The simplest way to do this and the technique that I immediately reached for, is to create the mask – being a Shape object with a gradient drawn using its instance of the Graphic class – set it’s cacheAsBitmap property to true and then set the embedded Bitmap’s mask property to use this gradient.
The result:

The code:
[Embed(source = '../lib/image.png')]
private var image:Class;
var myImage:Bitmap = new image as Bitmap;
addChild( myImage );
var matrix:Matrix = new Matrix();
matrix.createGradientBox( myImage.width, myImage.height, Math.PI / 2 );
var gradient:Shape = new Shape();
gradient.graphics.beginGradientFill( 'linear', [0xFFFFFF, 0xFFFFFF], [1, 0], [0, 255], matrix );
gradient.graphics.drawRect(0, 0, myImage.width, myImage.height);
gradient.graphics.endFill();
gradient.cacheAsBitmap = true;
myImage.mask = gradient;
However, presumably due to the fact that the bitmap already contained an alpha channel, the alpha channel of the bitmap-cached gradient shape was ignored, and the result is a mask which is simply the entire area of the gradient box, much like the pre Flash Player 8 mask behaviour of being either on or off and nothing in between. (
The “CopyChannel” Approach
The next approach I tried was to draw the gradient to a new transparent bitmapdata and then use the copyChannel method to copy the alpha channel of the gradient bitmapdata to the bitmapdata of my image.
The result:

The code:
var gradientBitmap:BitmapData = new BitmapData( gradient.width, gradient.height, true, 0 );
gradientBitmap.draw( gradient );
var channel:uint = BitmapDataChannel.ALPHA;
myImage.bitmapData.copyChannel(gradientBitmap, gradientBitmap.rect, new Point(), channel, channel);
Again, an undesirable result – I was left with some kind of graphic monstrosity that was a Frankenstein combination of my wonderfully smooth faded source image and a gradient showing through wherever transparency should have been in the original.
The solution (CopyPixels)
The approach I used in the end was to create a new transparent bitmapdata with the same dimensions of my image, then to use the copyPixels method to copy over the pixels from my source image to the new bitmapdata. The copyPixels method also accepts a parameter for alphaBitmapData, so I passed my gradient bitmapdata to this. Finally, copyPixels also takes a Boolean value for mergeAlpha, so set this to true and the result is a brand new image with the RGB pixel data of the source image and an alpha channel which is a combination of the alpha channels from both the source and the gradient bitmap.
The result:

The code:
var gradientBitmap:BitmapData = new BitmapData( gradient.width, gradient.height, true, 0 );
gradientBitmap.draw( gradient );
var result:BitmapData = new BitmapData( myImage.width, myImage.height, true, 0 );
result.copyPixels(myImage.bitmapData, result.rect, new Point(), gradientBitmap, new Point(), true);
addChild( new Bitmap( result ) );
I’m sure I’ll be mocked by my fellow developers for having been kneecapped by this quite basic problem, but having glanced at a few forum posts it seems common enough. Therefore I felt sharing the solution might be of some benefit.
Full source code:
[Embed(source = '../lib/image.png')]
private var image:Class;
public function Main():void
{
drawCheckerboardPattern();
var myImage:Bitmap = new image as Bitmap;
var matrix:Matrix = new Matrix();
matrix.createGradientBox( myImage.width, myImage.height, Math.PI / 2 );
var linear:String = GradientType.LINEAR;
var colors:Array = [0xFFFFFF, 0xFFFFFF];
var alphas:Array = [1.0, 0.0];
var ratios:Array = [0.0, 255];
var spread:String = SpreadMethod.PAD;
var gradient:Shape = new Shape();
gradient.graphics.beginGradientFill( linear, colors, alphas, ratios, matrix, spread );
gradient.graphics.drawRect(0, 0, myImage.width, myImage.height);
gradient.graphics.endFill();
var gradientBitmap:BitmapData = new BitmapData( gradient.width, gradient.height, true, 0 );
gradientBitmap.draw( gradient );
var result:BitmapData = new BitmapData( myImage.width, myImage.height, true, 0 );
result.copyPixels( myImage.bitmapData, result.rect, new Point(), gradientBitmap, new Point(), true );
addChild( new Bitmap( result ) );
}
private function drawCheckerboardPattern():void
{
var boxSize:int = 8;
var pattern:BitmapData = new BitmapData( boxSize * 2, boxSize * 2, false );
pattern.fillRect( new Rectangle( 0, 0, boxSize, boxSize ), 0xCCCCCC );
pattern.fillRect( new Rectangle( boxSize, boxSize, boxSize, boxSize ), 0xCCCCCC );
graphics.beginBitmapFill( pattern );
graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
graphics.endFill();
}
hi,
in fact i think your first “cacheAsBitmap” approach can work if you :
- 1 cacheAsBitmap the masked image
-2 add your gradientmask to stage
in my first tests it seems to works (an old flash8 memory…)
hope it helps
regards
tf
Hey TF,
Talk about overlooking the obvious. You’re right – though I find it odd that a Bitmap would not be cached as a bitmap!! In the first example I cached the shape object as bitmap as it contained vector drawing, not the bitmap image as it seemed unecessary.
I suppose the main advantage of the copyPixels approach is that you end up with a single object.
Cheers for the pointer, you proved the point that its good to get these questions out there ;)
you’re welcome :)
You’re right, each approach has it’s own pros and cons.
It’s also true that having a Bitmap object that can be not cachedAsBitmap sounds really strange o_0
Unfortunately that’s not the only strange thing in as…
anyway, thanks a lot for sharing your experience
“Unfortunately that’s not the only strange thing in as…”
Haha; Amen to that mate! ;)
How funny, I ran into something similar last week. What I did was probably more hacky,
Since I wasn’t looking for a gradient mask, I drew the vector mask into the green channel of a temporary bitmap and the alpha of the real bitmap into the blue channel, into the other then thresholded to get the areas that overlapped blue and green, then copied that into the alphachannel of the end bitmap.
Bitmaps are rendered as bitmap-filled shapes so that they can be anti-aliased onto sub-pixels – turning on caching snaps them to exact pixels.
Anyhow – very cool technique, thanks for posting!
Cheers Rob! The whole bitmap cached bitmap thing makes much more sense now. Thanks for the note :)
awesome! great help. good techniques..
running debug player on firefox and I’m getting this error.
ArgumentError: Error #2015: Invalid BitmapData.
at flash.display::BitmapData()
at Main()
I thought you should know..
How about masking text “graneintly”?
I was able to do the text mask but when publish to a page internet explorer still shows the mask layer as a layer not a mask! any help is appreciated.
Hello. greatjob. I did not expect this blogs on a sat.