Bitmap conversion - Creating a transparent + black image from a B&W source - actionscript-3

I have a whole bunch of jpg files that I need to use in a project, that for one reason or another cannot be altered. Each file is similar (handwriting), black pen on white BG. However I need to use these assets against a non-white background in my flash project, so I'm trying to do some client-side processing to get rid of the backgrounds using getPixel and setPixel32.
The code I am currently using currently uses a linear comparison, and while it works, the results are less than expected, as the shades of grey are getting lost in the mix. Moreso than just tweaking my parameters to get things looking proper, I get the feeling that my method for computing the RGBa value is weak.
Can anyone recommend a better solution than what I'm using below? Much appreciated!
private function transparify(data:BitmapData) : Bitmap {
// Create a new BitmapData with transparency to return
var newData:BitmapData = new BitmapData(data.width, data.height, true);
var orig_color:uint;
var alpha:Number;
var percent:Number;
// Iterate through each pixel using nested for loop
for(var x:int = 0; x < data.width; x++){
for (var y:int = 0; y < data.height; y++){
orig_color = data.getPixel(x,y);
// percent is the opacity percentage, white should be 0,
// black would be 1, greys somewhere in the middle
percent = (0xFFFFFF - orig_color)/0xFFFFFF;
// To get the alpha value, I multiply 256 possible values by
// my percentage, which gets multiplied by 0xFFFFFF to fit in the right
// value for the alpha channel
alpha = Math.round(( percent )*256)*0xFFFFFF;
// Adding the alpha value to the original color should give me the same
// color with an alpha channel added
var newCol = orig_color+alpha;
newData.setPixel32(x,y,newCol);
}
}
var newImg:Bitmap = new Bitmap(newData);
return newImg;
}

Since it's a white background, blendMode may give you a better result.

Related

How to detect that the AlphaMaskFilter is completely gone in easeljs/createjs

I am doing a little scratch/reveal game based on the AlphaMaskFilter example:
http://createjs.com/demos/easeljs/AlphaMaskReveal.html
I want to detect that the the mask is completely gone, or use a threshold (90% scratched for example).
I read the doc on AlphaMaskFilter, shape and graphics objects and im not really sure how to achieve this.
Im not even sure i Have acess to the pixel information and check the alpha channel to detect it, but even so, I wonder if I will performance issue.
any help is welcome, thanks.
**** EDIT **** ADD TO THE ACCEPTED ANSWER ****
So, I was able to have the pct of transparency using the AlphaMapFilter (thanks Lanny).
The AlphaMapFilter offer you a mapping to the alpha channel of all the pixels.
Here is a little sample code that worked for me:
// mShapeToScratch is a createjs Shape. like in the http://createjs.com/demos/easeljs/AlphaMaskReveal.html example
var alphaMaskFilter = new createjs.AlphaMapFilter(mShapeToScratch.cacheCanvas);
var canvas = alphaMaskFilter.alphaMap;
var ctx = canvas.getContext("2d");
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var alphaData = imgData.data;
var pixelTotal = rect.h*rect.w;
var transparentPixel = 0;
// rect.h is the height of the image and rect.w is the width not shown in the example
for (var y = 0; y < rect.h; ++y)
{
for (var x=0; x < rect.w; ++x)
{
var pixelIdx = (y*rect.w + x);
if(alphaData[pixelIdx] > 128) // transparent will be 255.
{
transparentPixel++;
}
}
console.log("transparent % = " + transparentPixel/pixelTotal);
This example checks all the pixels, but it's pretty easy to check one every X pixels to speeds up checks as Lanny suggested.
The alpha mask uses canvas composite operation, and not pixel access, so without some completely custom approach, there isn't a great way to do this.
Iterating pixels (check out AlphaMapFilter as an example) would work - but could be fairly slow. Maybe checking every 4th, 10th, or 25th pixel would speed it up.
Cheers.

How to detect if the area was 100% painted in as3

I´m making a game that simulates an industry of pan, and one of the process is Painting.
What I want to do is to let the player paint the pan, but i don´t want it to be easy using FILL, i want that the player paint the pan with an brush and then the game detects if all the area was painted and let the player advance.
For the painting i intend to use that library: http://www.nocircleno.com/graffiti/
But i have no idea how to detect if all the area was painted. Can someone show me some way of doing that?
One of the ways would be - you make a shielding BitmapData that has transparency and is opaque in place which you need your player to paint. (Color it as needed, but make sure the color is fully opaque). Then gather histogram() then query alpha vector for 255th value, this will be the initial value for zero percent filled. These range from 0-255, so you can't use 100 or any other fixed value. Then, while the player is painting, you draw the brush over that BitmapData with blendMode parameter set to BlendMode.ERASE, this will net your BitmapData to gain transparency where the brush was drawn. After your player finishes drawing by any means (say, the paint is used up), you run another histogram() over the BitmapData, and query the 255th value of alpha channel vector. 0 means the bitmap is fully transparent (or at least, only a small amount of pixels is left opaque), thus you can count a zero as 100% fill, for anything greater use the proportion.
var bd:BitmapData=new BitmapData(w,h,true,0x0); // fully transparent initial bitmap
bd.draw(yourPaintBase); // a shape that designates area to be painted. Must be fully opaque
var bm:Bitmap=new Bitmap(bd);
// position it as needed, so the area which should be painted is aligned to wherever you need
addChild(bm);
addEventListener(Event.ENTER_FRAME,doPaint);
var baseValue:int=bd.histogram()[3][255]; // Vector #3 contains alpha, #255 contains
// percentage of those pixels that have alpha of 255 = fully opaque
function doPaint(e:Event):void {
if (!areWePainting) return;
var sh:Shape=getBrush(); // shuold return an existing Shape object reference for performance
sh.x=e.localX;
sh.y=e.localY; // we are drawing where the mouse is
bd.draw(sh,null,null,BlendMode.ERASE);
decreasePaint(); // we have used some paint
if (noMorePaint()) {
e.target.removeEventListener(Event.ENTER_FRAME,doPaint);
var endValue:int=Math.floor(100*(1-bd.histogram()[3][255]/baseValue));
// aligning to percentage. This is the value you seek
reportFilledPercentage(endValue);
}
}
You can iterate over the pixels on your BitmapData and use getPixel() to check if the color of all those pixels is not white. If a white one is found, the image is not fully painted.
Something like this:
function containsWhite(bitmapData:BitmapData):Boolean
{
for(var c:int = 0; c < bitmapData.width; c++)
{
for(var r:int = 0; r < bitmapData.height; r++)
{
// Check if pixel is white.
if(bitmapData.getPixel(c, r) >= 0xFFFFFF)
{
return true;
}
}
}
return false;
}
Your essentially dealing with a collision detection problem. From looking at their API you could try something like a for loop with getColorAtPoint and try to determine they have drawn at each pixel.
If all else fails look into collision between the objects the library generates using the .hitTestObject method of an object.
See this: http://sierakowski.eu/list-of-tips/39-collision-detection-methods-hittest-and-hittestobject-alternatives.html
And this to see how someone handles collision with pixels: http://www.emanueleferonato.com/2010/08/05/worms-like-destructible-terrain-in-flash-part-2/

Glow Effect on HTML Canvas, Potentially Using Convolute Kernel / Matrix

I am drawing a PNG image to an HTML canvas and I have implemented a filter system to allow convolute filters to be executed against the image data before it is blitted to the canvas.
Does anyone have an idea how to create a glow effect using either a convolute kernel / matrix (I'm not sure what the terminology is but I'm talking about these: http://www.html5rocks.com/en/tutorials/canvas/imagefilters/) or by other means such as using the globalCompositeOperation (https://developer.mozilla.org/samples/canvas-tutorial/6_1_canvas_composite.html)?
I am aware that you can start with a low opacity and a scaled image then increase opacity while scaling the image down a bit. This works to create a sort-of glow effect but only around the edge of an image.
In an ideal world it would be great to be able to designate areas of the image that have glow using a secondary glow texture. Any ideas on either scenario? :)
Hope the following is along the lines of what you were looking to do, I think it turned out pretty well. So I used the filter library code from the article, and just created a new glow filter for the library, since his code was done pretty well to begin with. Here is a Live Demo showing the glow effect in action.
This is the filter code that you need to add to the library
Filters.glow = function(pixels, passes, image, glowPasses){
for(var i=0; i < passes; i++){
pixels = Filters.convolute(pixels,
[1/9, 1/9, 1/9,
1/9, 1/9, 1/9,
1/9, 1/9, 1/9 ]);
}
var tempCanvas = document.createElement("canvas"),
glowCanvas = document.createElement("canvas"),
tCtx = tempCanvas.getContext("2d"),
gCtx = glowCanvas.getContext("2d");
tempCanvas.width = glowCanvas.width = pixels.width;
tempCanvas.height = tempCanvas.height = pixels.height;
tCtx.putImageData(pixels, 0, 0);
gCtx.drawImage(image, 0, 0);
gCtx.globalCompositeOperation = "lighter";
for(i = 0; i < glowPasses; i++){
gCtx.drawImage(tempCanvas,0,0);
}
return Filters.getPixels(glowCanvas);
}
And this is how you would use the above filter.
var glowImage = document.images[1],
glowMask = document.images[0],
c = document.getElementById("canvas"),
ctx = c.getContext("2d");
window.onload = function() {
var pData = Filters.filterImage(Filters.glow, glowImage, 5, glowMask, 2);
c.width = pData.width;
c.height = pData.height;
ctx.putImageData(pData, 0, 0);
}
You need to provide it with 2 images. The first is the image you want the glow to appear on, and the second is the actual glow mask that is applied to the image. You can then specify how many blur passes to perform, which makes the glow more prominent, and how many glow passes to perform, which add the glow to the image. I use the lighter global composition for the canvas which alpha blends it.
This article is a pretty great resource on creating a glow effect, its also where I got the graphics in order to test my results against theirs.

Actionscript object colour change / tint

i am new to Actionscript and I need help changing the colour of an object which is called with add child, i need the colour to change once the user has selected the desired colour they want from a combo box:
//MY CODE
if (e.target.value == "blue")
{
//need to change to this (0x0000FF)
//enter code here
}
Any help much appreciated!
Try using the color transform object.
var redAmount:Number = 0;
var greenAmount:Number = 0;
var blueAmount:Number = 1;
var alphaAmount:Number = 1;
var redOffset:Number = 0;
var greenOffset:Number = 0;
var blueOffset:Number = 0;
var alphaOffset:Number = 0;
yourDisplayObject.transform.colorTransform = new ColorTransform(redAmount, greenAmount, blueAmount, alphaAmount, redOffset, greeenOffset, blueOffset, alphaOffset);
The first four variables are multipliers - they will take the existing color value and adjust it as if multiplying by that number. So to reduce a color by 1/2 use 0.5. To make black set all to 0. To change nothing make all 1's.
The second four variables will increase or decrease the color amount of all pixels by that amount. So to make a color hit a specific hex value, say 0xFFCC33, you would do this:
yourDisplayObject.transform.colorTransform = new ColorTransform(0, 0, 0, 1, 0xFF, 0xCC, 0x33, 0x00);
Here is a link to the adobe documentation if you need more help:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/geom/ColorTransform.html
If you decide you want to change the color gradually instead of instantly take a look at TweenMax from greensock. The greensock tween classes are a widely used workhorse of actionscript programming.
To tint a display object with TweenMax the code would be:
TweenMax.to(yourDisplayObject, 1, {tint:0x0000FF});
This class applies a color transform to the object using that code, but it changes the values gradually over time
Here is a link to where you can get TweenMax.
http://www.greensock.com/tweenmax/
Here is the easiest to understand (as I see it anyway) way to accomplish this:
if (e.target.value == "blue")
{
var colorTransform:ColorTransform = yourObject.transform.colorTransform; //store the current color data for the object
colorTransform.color = 0x0000FF; //make it totally blue
yourObject.transform.colorTransform = colorTransform; //now assign it back to the object
}

How to Snap linestyle in Actionscript 3.0?

Hai, i am a newbie to actionscript.
i am trying to make a brush tool in actionscript 3, where kids can paint colors inside a circle using the brush. i have achieved this using linestyle. Now i want the brush to snap the color when the kid is about to complete(say when they are 95% to complete the painting). How do i know how much the kid has painted on the circle?
Thanks
How do i know how much the kid has
painted on the circle?
You can:
make your circles and other shapes Sprites to get separate container
render them into bitmap and count number of non-transparent pixels in it (you should know what number corresponds to 100%)
since counting pixels is heavy operation (can take hundreds of milliseconds, depending of shape size), you don't want to run it on every frame. I suggest to do it on MOUSE_UP, right after kid finishes next stroke.
How to count pixels:
function countPixels(shape:DisplayObject):int
{
var bd:BitmapData = new BitmapData(shape.width, shape.height);
bd.draw(shape);
//here you get sequence of ARGB-packed colors in vector
var pixels:Vector.<uint> = bd.getVector(bd.rect);
//AFAIK, fastest method to traverse vector is 'for' loop
var pixel:uint;
var filledCount:int = 0;
for (var i:int = pixels.length - 1; i >= 0; i--)
{
pixel = pixels[i];
//pixel is 32-bit ARGB color value, test upper 8 bits (alpha):
if ((pixel >> 24) > 0) filledCount++;
}
return filledCount;
}
Run this on filled shape to get total pixel count to compare with.
After pixelCount reaches 95%, you can clear kid's drawing and show filled shape.