Merge two images and retain its transparency - actionscript-3

It was a while since I programmed AS3. Now I have a problem where I need to merge the two images where the upper image is a png that must retain its transparency. The upper image is an area that must pass through the lower image. A bit like a masked layer.
The result of this merge should result in a a display object. This object will later be sent to a method with the following signature:
public function addImage (
display_object:DisplayObject,
x:Number = 0,
y:Number = 0,
width:Number = 0,
height:Number = 0,
image_format:String = "PNG",
quality:Number = 100,
alpha:Number = 1,
resizeMode:String = "None",
blendMode:String = "Normal",
keep_transformation:Boolean = true,
link:String = ''
):void
Any advice is of the utmost interest. Thanks!
UPDATE;
After some struggling I've come up with this:
var bitmapDataBuffer:BitmapData = new BitmapData ( front.loader.width, front.loader.height, true );
bitmapDataBuffer.draw ( front.loader );
var bitmapOverlay:BitmapData = new BitmapData ( front.loader.width, front.loader.height, true );
bitmapOverlay.draw ( frontBanner.loader );
var rect:Rectangle = new Rectangle(0, 0, front.loader.width, front.loader.height);
var pt:Point = new Point(0, 0);
var mult:uint = 0x00;
bitmapOverlay.merge(bitmapDataBuffer, rect, pt, mult, mult, mult, mult);
var bmp:Bitmap = new Bitmap(bitmapOverlay);
pdf.addImage(bmp,0,0,0,0,ImageFormat.PNG,100,1,ResizeMode.FIT_TO_PAGE);
The problem is that my background image (represented by bitmapDataBuffer) will be totally overwritten by my upper image (the one I call overlay).
The overlay image is a png image. This image has a part of it that is transparent. Through this transparency I want to see my background image.
Any more suggestions?

You should be more specific about what kind of merge you want. You have a few options:
BitmapData.copyPixels - Provides a fast routine to perform pixel manipulation between images with no stretching, rotation, or color effects. This method copies a rectangular area of a source image to a rectangular area of the same size at the destination point of the destination BitmapData object.
BitmapData.merge - Performs per-channel blending from a source image to a destination image. For each channel and each pixel, a new value is computed based on the channel values of the source and destination pixels.
BitmapData.draw - Draws the source display object onto the bitmap image, using the Flash runtime vector renderer. You can specify matrix, colorTransform, blendMode, and a destination clipRect parameter to control how the rendering performs.
Each will work out for a different thing - the first will just copy some image over another (can keep/merge alphas). The second will merge channels data and modify them. The third one is the easiest and can draw one bitmap over another, as well as use blend modes.
Just chose one! :)

In order to make overlay image over the buffer image in your case, you are to use copyPixels() with mergeAlpha set to true.
bitmapDataBuffer.copyPixels(bitmapOverlay, rect, new Point(), null, null, true);
This will place data from bitmapOverlay to those parts of bitmapDataBuffer where overlay's alpha is above 0, blending semitransparent regions with the background.

Related

What is the gradient mask equivalent in Flash CC for HTML5 Canvas?

I used to create a gradient mask in Flash using these three lines of AS3 while having two movieclips on the stage, one of which had a gradient block in it:
gradientMask_mc.cacheAsBitmap = true;
something_mc.cacheAsBitmap = true;
something_mc.mask = gradientMask_mc;
Now that I'm experimenting with Flash CC's HTML5 Canvas, I can't seem to find the equivalent. Is it an entirely different setup or what? My searches yield only the AS3 solutions. Please and thanks!
In pure HTML5 canvas and javascript to blend two images with a gradient.
// img1, img2 are the two images
// ctx is the canvas.
// setup. Only do this once or when you change the masking
// create a mask canvas the size of the first image
var gradImg1Masked = document.createElement("canvas");
gradImg1Masked.width = img1.width; // set the width and height
gradImg1Masked.height = img1.height;
var ctx1 = gradImg1Masked.getContext("2d",{ alpha: true }); // get the context
var gradient = ctx1.createLinearGradient(0, 0, 0, img1.height); // create a gradient
// assuming its from
// top to bottom.
// add colour stops black full opaque at top to full transparent at bottom
gradient.addColorStop( 0, "rgba(0,0,0,1)" );
gradient.addColorStop( 1, "rgba(0,0,0,0)" );
ctx1.globalAlpha = 1; // ensure alpha is on full
ctx1.globalCompositeOperation = "source-over"; // ensure correct filter
ctx1.drawImage(img1, 0, 0); // draw the image on the mask canvas
ctx1.fillStyle = gradient; // set the fill style
ctx1.globalCompositeOperation = "destination-in"; // set filter to mask letting
// only opaque pixels in
ctx1.fillRect(0, 0, img1.width, img1.height); // create the masked image
// by rendering the gradiant ontop
// gradImg1Masked is now the masked copy of img1
That was the setup. Below is the rendering.
// Rendering the images with the mask
// simply draw the second image and the the first on top
// ctx is the render surface you are displaying
// x and y are where you want the top left of the images to be.
ctx.globalAlpha = 1; // ensure alpha is on full
ctx.globalCompositeOperation = "source-over"; // ensure correct filter
// draw the second image at x,y screen pos
ctx.drawImage(img2,0,0,img2.width,img2.height,x,y,img2.width,img2.height);
// draw the masked image ontop using the second image's size
ctx.drawImage( gradImg1Masked,
0, 0, gradImg1Masked.width, gradImg1Masked.height,
x, y, img2.width, img2.height);
Hope this helps. There may be an easier way Flash CC's HTML5 Canvas but I find frameworks provide little benefit over the very functional HTML5/Javascript while obfuscating what is happening and also slowing everything down. Their use is of course a personal decision.

as3 - setting textures alpha-value

For an Actionscript 3 "drawing application", I want to be able to chose a Texture and set it's transparency.
Therefore I try to set the alpha-transparency of a texture.
But it's not working.
What I do:
At first I use graphics.linestyle() to set the lines thickness and ALPHA-value.
After that I (a) load a png, (b) read it's bitmapData and (c) then use it with lineBitmapStyle.
Result:
When drawing lines (with moveTo, lineTo, etc) the lines use the texture, but IGNORE THE "Alpha" which was set with lineStyle.
What am I doing wrong?
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, setTexture);
setTexture(e:Event):void
{
e.currentTarget.removeEventListener(e.type, arguments.callee);
//Try 1: Trying to set the Alpha-Trasparency with "lineStyle"-Command:
myDrawContainer.graphics.lineBitmapStyle(5, 0xFF0000, 0,6);
//Try 2: Trying to set the Alpha-Transparency by changing the Alpha-Value of the loaded content:
myLoader.content.alpha = 0.6;
//Getting the BitmapData of the Image:
BitmapDataOfMyTexture = Bitmap(LoaderInfo(e.target).content).bitmapData
//"Using" the TBitmapData as "Color/Texture" for my Drawing:
myDrawContainer.graphics.lineBitmapStyle( BitmapDataOfMyTexture );
//Test-Drawing:
myDrawContainer.graphics.moveTo( 0, 0 );
myDrawContainer.graphics.moveTo( 500, 500 ); //-> RESULT: Textured Line WITHOUT Transparency!
}
RESULT: I get a line which uses the texture but WITHOUT Transparency.
(Update) THE SOLUTION: (Thanks to DodgerThud)
For setting/changing the Alpha-Channel of a loaded image, you don't use "lineStyle" but...
Create a NEW colorTransform-object
Then set it's "alphaMultiplier"-attribute to the specific alphaChannel
And then apply this newly created colorTransform-Object to the loaded BitmapData, by using the "colorTransform"-Method of the loaded BitmapData.
BUT :
This does NOT work with images that don't have an alpha-Channel or don't have their alpha-channel activated. Those images only get DARKER when lowering the alpha-Channel. In those cases you have to do this:
At FIRST I create NEW BitmapData-Object with "new", set its width and height to the width and height of the loaded Image and set it 3rd Argument to TRUE (= transparency: ON). So you got a "Container" which has transparency ACTIVATED.
Then you use "copyPixels" on this "Container"-Object to fill it with the pixels of the LOADED BitmapData-Object.
And right after this the above approach with the "colorTransform"-Object brings the expected result.
So HERE'S THE WORKING CODE:
myLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, setTexture);
setTexture(e:Event):void
{
e.currentTarget.removeEventListener(e.type, arguments.callee);
//Getting the BitmapData of the Image:
BitmapDataOfMyTexture = Bitmap(LoaderInfo(e.target).content).bitmapData
//Create an ADDITIONAL BitmapData-Object with 3rd
//argument set to TRUE and with same width and height
//as the LOADED image:
var BMDContainerWithAlphaActivated:BitmapData;
BMDContainerWithAlphaActivated = new BitmapData(BitmapDataOfMyTexture.width, BitmapDataOfMyTexture.height, true, 0xFFFFFF);
//Copy the pixels of the loaded image into the newly created
//"BitmapData-Container with activated AlphaChannel":
BMDContainerWithAlphaActivated.copyPixels(BitmapDataOfMyTexture, new Rectangle(0, 0, BitmapDataOfMyTexture.width, BitmapDataOfMyTexture.height), new Point(0,0))
//Modify the Alpha-Value (of the NEW BitmapData-Object):
var colorChanges:ColorTransform = new ColorTransform();
colorChanges.alphaMultiplier = 0.3;
BMDContainerWithAlphaActivated.colorTransform(new Rectangle(0, 0, BitmapDataOfMyTexture.width, BitmapDataOfMyTexture.height), colorChanges);
//"Using" the (NEW) BitmapData as "Color/Texture" for my Drawing:
myDrawContainer.graphics.lineBitmapStyle( BMDContainerWithAlphaActivated );
//Test-Drawing:
myDrawContainer.graphics.moveTo( 0, 0 );
myDrawContainer.graphics.moveTo( 500, 500 ); //-> RESULT: Textured Line WITH Transparency 0.3!
}
Ah I see, this is a bit more complex than I initially thought.
Alright, looking at the documentation for lineBitmapStyle shows me that the function expects the following parameters: lineBitmapStyle(bitmap:BitmapData, matrix:Matrix = null, repeat:Boolean = true, smooth:Boolean = false)
Now, matrix, repeat and smooth will not help us here (matrix is used here for transformation, i.e. positioning, rotation etc.), but bitmap:BitmapData might. What we need to do is manipulate the BitmapData of the loaded PNG file before passing it to lineBitmapStyle. Sadly we cannot set the alpha value directly on the BMD, so we can try to colorTransform it.
This is untested code, but I think it is the right approach:
..
//store the bitmapdata in a seperate local variable
var bmd:BitmapData = LoaderInfo(e.target).content;
//create a ColorTransform Object to change the values of the BMD
var cTransform:ColorTransform = new ColorTransform();
//now here I am unsure, manipulating the alpha value of the BMD
cTransform.alphaMultiplier = 0.6;
//defining the rectangle dimensions of the bmd, we want it to be over the entire texture
var rect:Rectangle = new Rectangle(0,0,bmd.width,bmd.height);
//apply the colorTransformation on the BMD
bmd.colorTransform(rect,cTransform);
...
//the now manipulated BMD gets set as lineBitmapStyle
myDrawContainer.graphics.lineBitmapStyle(bmd);
And now that I think about it, maybe we can workaround setting the alpha value on the BMD, by creating a Bitmap first, set the alpha value there and use the bitmapdata of the Bitmap instead. Like this:
var bmd:BitmapData = LoaderInfo(e.target).content;
var bm:Bitmap = new Bitmap(bmd);
bm.alpha = 0.6;
myDrawContainer.graphics.lineBitmapStyle(bm.bitmapData);
Alright, the first snippet from above seems to be the way to do it, but the transparent value of the BitmapData needs to be true. Given that you do not directly create the BitmapData yourself and the value is false, we have quite a tricky situation here.
Another approach would be to create an additional bitmapdata that allows for transparancy and draw() the bitmapdata of the loaded image on it:
var bmdSource:BitmapData = LoaderInfo(e.target).content;
var bmd:BitmapData = new BitmapData(bmdSource.width, bmdSource.height,true,0xffffffff);
var cTransform:ColorTransform = new ColorTransform();
cTransform.alphaMultiplier = 0.6;
var rect:Rectangle = new Rectangle(0,0,bmd.width,bmd.height);
bmd.colorTransform(rect,cTransform);
//now we have a completely white bitmapdata bmd, with an alpha value of 0.6
//we draw the contents of the bmdSource onto bmd, the alpha value effect should carry over
bmd.draw(bmdSource);

AS3: BitmapData linked to certain sprite in library?

I have a problem. I have a sprite added to library and I linked and named its class "CarriedHead_Normal". Now I want to display this sprite and make every black(000000) pixel invisible (just convert background to alpha).
I know I can use this
var bmpd:BitmapData = new BitmapData(width, height, true, 0x000000)
But how am I supposed to merge this with my function, so I call the class from the library and make it's bg transparent?
My current code:
var imageSprite = new Sprite();
addChild(imageSprite);
var libraryImage:Bitmap = new Bitmap(new CarriedHead_Normal(0, 0))
imageSprite.addChild(libraryImage);
Thanks in advance.
A number of ways you could do it, some working better than others:
On a side node, your question title has nothing to do with your actual question
1) Use blend modes
Depending on your graphics and background, you might be able to get away with a simple blendMode - check out BlendMode.SCREEN in particular. Note that some blend modes require that your parent container have a blendMode of BlendMode.LAYER
2) copyPixels() using an alpha BMD
copyPixels() lets you specify an alpha BitmapData to use when deciding which pixels to copy over (actually the alpha value of the pixels to copy over). If you have an alpha BitmapData that matches your black areas, you can remove it like that:
var b:BitmapData = new BitmapData( myImageBMD.width, myImageBMD.height, true, 0x00000000 );
b.copyPixels( myImageBMD, myImageBMD.rect, new Point, alphaBMD, new Point, false ); // copy our image, using alphaBMD to remove the black
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#copyPixels()
3) floodFill() the black areas
If your black area is continuous and you know a pixel that it starts on (e.g. it's a black frame around an image), then you can use floodFill() to remove the black areas
var b:BitmapData = new BitmapData( myImageBMD.width, myImageBMD.height, true, 0x00000000 );
b.copyPixels( myImageBMD, myImageBMD.rect, new Point ); // copy our image so we keep the original
b.floodFill( 0, 0, 0x00000000 ); // assuming the first pixel is black, floodFill() from here, replacing with transparence
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#floodFill()
This technique will work best on pixel art, where the edges are well defined.
4) threshold() to remove the black
threshold() takes pixel values and replaces them with the value you set if they pass a certain test (<, <=, ==, etc). You can use this to replace black with transparence.
var b:BitmapData = new BitmapData( myImageBMD.width, myImageBMD.height, true, 0x00000000 );
b.copyPixels( myImageBMD, myImageBMD.rect, new Point ); // copy our image so we keep the original
b.threshold( b, b.rect, new Point, "==", 0xff000000, 0x00000000 ); // test the pixels; if they're black, replace with transparence
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#threshold()
Again, this works best when the edges are well defined.
5) Use filters
You can use applyFilter() to create a new image, using a BitmapFilter to remove the black. Possibly only of the other filters will work, but ShaderFilter should definitely do the job (you write your own shader, so you can essentially do what you want). I don't have any real experience with filters, so I can't tell you how they work, but some googling should give you the necessary code
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#applyFilter()
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/filters/ShaderFilter.html
6) Use photoshop (or equivalent)
The easiest method; just edit your image to remove the black; much less hassle :)
Try something like:
var libraryImage:Bitmap = spriteToBitmap(new CarriedHead_Normal());
imageSprite.addChild(libraryImage);
function spriteToBitmap(sprite:Sprite, smoothing:Boolean = false):Bitmap
{
var bitmapData:BitmapData = new BitmapData(sprite.width, sprite.height, true, 0x00000000);
bitmapData.draw(sprite);
bitmapData.threshold(bitmapData, bitmapData.rect, new Point(), '==', 0xff000000, 0x00000000);
return new Bitmap(bitmapData, "auto", smoothing);
}

To check whether Movie Clip got filled at one place

I have created a empty movie clip .
And i,m brushing on that .(i.e)Filling(Begin Fill) it with ellipse for every click .
How do I check whether it is filled at particular place .(say 400 x 400 from x=0,y=0)?
First you would need to convert it to a bitmapdata format.
Then you would use the function getPixel to query it.
import flash.display.Bitmap;
import flash.display.BitmapData;
// Lets say drawingpad is a MovieClip that is getting ellipses drawn on it.
var bitmap:Bitmap = new Bitmap(new BitmapData(stage.stageWidth,stage.stageHeight));
bitmap.draw(drawingpad);
var value:uint = bitmap.bitmapData.getPixel(400,400);
Hope this helps
I assume with "400 x 400 from x=0,y=0" you mean a the area between 0,0 and 400,400.
You need to copy the content of the rectangle of your MovieClip to a Bitmap, to be able to get information about its pixels.
// coordinates as in the question's example, could be method parameters
var x1:int=0;
var y1:int=0;
var x2:int=400;
var y2:int=400;
// bitmap with transparency, with a size of the rectangle to check
var bitmap:Bitmap = new Bitmap(new BitmapData(x2-x1, y2-y1, true, 0x00000000));
// draw the painted mc to the bitmap, displaced to upper left x and y
bitmap.draw(mc, new Matrix(1,0,0,1,-x1,-y1)));
Using getColorBoundsRect, you can check if a bitmap contains a colour, or if it contains any other colours. Now check if it contains any other colour than 'transparent' (= any other colours with an alpha value>0):
var maskColor:uint = 0xFF000000; // ignore rgb, only check alpha values
var color:uint = 0x00000000; // 'empty', fully transparent color
// get a rect containing all pixels with not fully transparent colors
var colorBoundsRect:Rectangle = bitmap.getColorBoundsRect(maskColor, color, false);
if (colorBoundsRect.width == 0 && colorBoundsRect.height == 0){
trace("empty"); // rect contains no visible pixels
}
Assuming the places where it's not filled are transparent, a simple hitTestPoint should do the trick:
myObj.hitTestPoint( 400.0, 400.0, true ); // true means we use the pixels of the shape and not the bounding box
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObject.html#hitTestPoint()

operations with BitmapData

we have a square movie clip with some clild movie clips on it. Consider a diagonal of a squere. I want to make the following transformation with bimap data of a movie clip: the triangle located upper then diagonal should remain unchanged, the lower triangle should be replaced with simetric reflection of upper triangle.
Is there a simple way to do that?
Exactly, the way is to take a snapshot of the movie, then mirror it, take another snapshot and combine these two snapshots.
Note, that the content you will get will be "static", so this way you can't copy the movieclip's animated parts.
//store the movie's graphical content in a bitmapdata
var bmd:BitmapData = new BitmapData(movie.width, movie.height);
bmd.draw(movie);
//then create a temporary movie in which you will do the mirroring
var temp:MovieClip = new MovieClip();
temp.addChild(new Bitmap(bmd));
//create the diagonal mask
var _mask:MovieClip = new MovieClip();
with(_mask.graphics) beginFill(0xff0000), lineTo(movie.width, 0), lineTo(movie.width, movie.height), lineTo(0, 0), endFill();
temp.addChild(_mask);
temp.mask = _mask;
//this is the mirroring part
temp.scaleX = temp.scaleY = -1;
addChild(temp);
//then create another bitmapdata for storing the so called "upper-triangle"
//important, that this bitmapdata should be transparent, "true" sets this
var bmd1:BitmapData = new BitmapData(temp.width, temp.height, true, 0x00);
//then do another mirroring transformation
var matrix:Matrix = new Matrix(-1, 0, 0, -1);
matrix.tx = temp.width;
matrix.ty = temp.height;
//draw the visual content on the bitmapdata
bmd1.draw(temp, matrix);
//and finally, on the original bitmapdata, draw the mirrored part
bmd.draw(bmd1);
//and add it to the top layer of the original movie, or whatever you want to do with it
movie.addChild(new Bitmap(bmd));
The simplest way I can think of is rotating the movieclip 45º and draw the upper rectangle into the lower rectangle inversed, and then rotate the bitmapdata back 45º. You can use a Matrix (2nd argument in the BitmapData.draw method) to draw it rotated and not actually rotate anything.