AS3 - How to remove all black and grey from bitmap, but retain color? - actionscript-3

I'd like to be able to alter an image (specifically a bitmap) in order to replace all dark grey and black pixels with white in ActionScript 3, but maintain all other colors in the image. I am familiar with ColorMatrixFilter and bitmapdata.threshold, but I don't know how to use them to either target the colors I want to remove or check within a specific range of colors. Is there any (efficient) way to go about doing this?
Thanks for any help you can offer.

The AS3 API gives pretty good documentation on how to use the treshhold value. You can find it here. Their example actually checks for a certain range of colors. I've modified their example to work for your problem. I haven't tested it, so it might need some tweaking.
var bmd2:BitmapData = new BitmapData(200, 200, true, 0xFFCCCCCC);
var pt:Point = new Point(0, 0);
var rect:Rectangle = new Rectangle(0, 0, 200, 200);
var threshold:uint = 0x00A9A9A9; //Dark Grey
var color:uint = 0x00000000; //Replacement color (white)
var maskColor:uint = 0xFFFFFFFF; //What channels to affect (this is the default).
bmd2.threshold(bmd1, rect, pt, ">", threshold, color, maskColor, true);
Another option would be to use a double for loop that iterates over all pixels and takes a certain action based on the value of the pixel.
for(var y:int = 0; y < height; y++){
for(var x:int = 0; x < width; x++){
var currentPixel:uint = image.getPixel( x, y );
if(currentPixel != color){
image.setPixel( destPoint.x + j, destPoint.y + i, currentPixel );
}
}
}

Related

Dynamically generated display object as a gradient mask

I just want to create a gradient mask –which is generated dynamically through AS3 code– for an object (a pentagon in my following example,) and I simply, cannot! [The SWF file of what I've tried.]
The code works just fine unless it ignores the alpha gradient of the dynamically created Sprite for being used as a gradient mask (it's being treated as a solid mask), while the exact code acknowledges the "on-stage" created object (through the user interface) for being a gradient mask!
I think the runtime just cannot cache the object as bitmap and hence the ignorance! However, I'm stuck at making that happen! So please, shed some lights on this, any help is greatly appreciated in advance :)
var X:Number = 100, Y:Number = 35, W:Number = 350, H:Number = 150;
var mat:Matrix = new Matrix();
mat.createGradientBox(W, H, 0, X, Y);
var gradientMask:Sprite = new Sprite();
gradientMask.graphics.beginGradientFill(GradientType.LINEAR, [0, 0], [1, 0], [0, 255], mat);
gradientMask.graphics.drawRect(X, Y, W, H);
gradientMask.graphics.endFill();
pentagon1.cacheAsBitmap = true;
pentagon2.cacheAsBitmap = true;
onStageGradient.cacheAsBitmap = true;
gradientMask.cacheAsBitmap = true;
pentagon1.mask = gradientMask;
pentagon2.mask = onStageGradient;
stage.addEventListener(Event.ENTER_FRAME, _onEnterFrame);
function _onEnterFrame(e:Event):void {
pentagon1.x += 7;
pentagon2.x += 7;
if (pentagon1.x > 500) {
pentagon1.x = 0;
pentagon2.x = 0;
}
}
You have to add the gradientMask to the display list to have it in effect.
pentagon1.parent.addChild(gradientMask);

putImageData alpha lost

i spend a lot of time to change color of png file with transparent background.
I know how set color in getImageData(ctx,0,0,100,100).data;
but when i want save new PNG image i've got white background...
i've got something like:
var el = document.getElementById('before');
var ctx = el.getContext('2d');
var img=new Image();
img.src='src';
ctx.drawImage(img,0,0,150,150);
var imgData=ctx.getImageData(0, 0, 150, 150);
for (var i=0;i<imgData.data.length;i+=4){
imgData.data[i]=135;
imgData.data[i+1]=222;
imgData.data[i+2]=115;
}
var es = document.getElementById('after');
var cts = es.getContext('2d');
cts.putImageData(imgData,0,0);
EXAMPLE
(i dont know why it was here putImageData)
in other project (ex. https://crosspop.in/croquis )it is posible, but i cant find where is solution.
Do You know how create canvas from modyfited getImageData with transparent background?
regards
Your issue has nothing to do with alpha : while strange (you are changing every pixels of the image, even the transparent one) your algorithm works fine.
But you made the beginner's mistake forgot to watch the console : you'd have seen that the image's data cannot be accessed because of a Cross-Origin issue (CORS).
I modified your fiddle so that it uses a Base64 string for the image and it works fine :
http://jsfiddle.net/gamealchemist/6aZ7L/3/
var data = imgData.data;
for (var i = 0; i < data.length; i += 4) {
if (data[i + 3]) {
data[i] = 135;
data[i + 1] = 222;
data[i + 2] = 115;
}
}
BUT in fact there's a much better way of re-coloring an image, that 1) avoids you to use imageData 2) is faster and 3) does not have CORS issue :
use the globalCompositeOperation of the context, here you can for instance fill your canvas with green, then use a destination-in : each green pixel will be kept only if the copied image's pixel is not transparent.
http://jsfiddle.net/gamealchemist/6aZ7L/4/
function taintIt() {
var es = document.getElementById('after');
var cts = es.getContext('2d');
cts.save();
cts.fillStyle = 'rgb(135,222,115)';
cts.fillRect(0, 0, 150, 150);
cts.globalCompositeOperation = 'destination-in';
cts.drawImage(img, 0, 0, 150, 150);
cts.restore();
}
(By the way -admittedly a bit out of topic- i tried to draw with the pattern, it's quite fun :
http://jsfiddle.net/gamealchemist/6aZ7L/6/embedded/result/ )
i add :
cts.fillStyle = "red";
cts.fillRect(0, 0, 120, 120);
juste before :
cts.putImageData(imgData, 0, 0);
in your exemple : http://jsfiddle.net/gamealchemist/6aZ7L/3/
putImageData ignore the alpha and still print a white backound ...

List unique colors in an HTML Canvas

Is there an efficient way to get a list of colors of what is currently drawn in an HTML Canvas?
I guess the correct term would be a Color Palette of the current composite
Assume you have an html document like the following:
<canvas id="example" width="500" height="300"></canvas>
<ul id="list">
<h3>Click the canvas to extract colors</h3>
</ul>
​I have created a fiddle in which you have a ready made canvas with some shapes and colors that we will be processing when we click on it. I'm not creating a color picker for you, just listing as that was the original answer and should serve as the means to achieve the second part of it.
Here's the fiddle
The function that extracts the colors an lists them is the following, I'm avoiding copying all the canvas drawing stuff since it just serves as the example and isn't generic enough.
var colorList = []; //This will hold all our colors
function getColors(){
var canvas = document.getElementById('example');
var context = canvas.getContext('2d');
var imageWidth = canvas.width;
var imageHeight = canvas.height;
var imageData = context.getImageData(0, 0, imageWidth, imageHeight);
var data = imageData.data;
// quickly iterate over all pixels
for(var i = 0, n = data.length; i < n; i += 4) {
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
//If you need the alpha value it's data[i + 3]
var hex = rgb2hex("rgb("+r+","+g+","+b+")");
if ($.inArray(hex, colorList) == -1){
$('#list').append("<li>"+hex+"</li>");
colorList.push(hex);
}
}
}
This will produce a result containing a seemingly unordered list of hex values that represent the order in which they appear in your image (hence the 'seemingly'), if you want a color picker however you may want to consider the use of colorList.sort(); which will order the array into a black-to-white fashion.
Note that I'm using the rgb2hex function to convert the values returned to the hex format I'm assuming your wanting, if you're ok with rgb feel free to not use it, the function is the following:
function rgb2hex(rgb) {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
function hex(x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
}
return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
}
And the source is this, as I stated as a comment in the code, you could easily extract the alpha channel too if you needed to but I went for the most generic scenario (and simplest) that I could think of.

Html5 Canvas overlay

Is there a good solution to draw a bitmap with an overlay color with canvas ?
What I'm looking to do is drawing a bitmap with a unique color for all not transparent pixel.
I do not find any solution for that, and it could be usefull for me !
Thank's
Live Demo
One way to do it is to loop through each pixel and change the r/g/b values to the value you want it. By skipping over the alpha value it will only change the opaque pixels to the color you want.
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
image = document.getElementById("testImage");
ctx.drawImage(image,0,0);
var imgd = ctx.getImageData(0, 0, 128, 128),
pix = imgd.data,
uniqueColor = [0,0,255]; // Blue for an example, can change this value to be anything.
// Loops through all of the pixels and modifies the components.
for (var i = 0, n = pix.length; i <n; i += 4) {
pix[i] = uniqueColor[0]; // Red component
pix[i+1] = uniqueColor[1]; // Green component
pix[i+2] = uniqueColor[2]; // Blue component
//pix[i+3] is the transparency.
}
ctx.putImageData(imgd, 0, 0);
// Just extra if you wanted to display within an img tag.
var savedImageData = document.getElementById("imageData");
savedImageData.src = canvas.toDataURL("image/png");

As3 and Image ByteArray Data

How can I get the bytearray data from an image that is under certain shape, for example a circule or square?
Let's say I want to modify ONLY the pixel inside this circule, how can I get this Bytearray data?
Any ideas?
Define a rectangle containing the circle, relative to the top left of the image.
var radius:Number = 100;
var centerX:Number = 50;
var centerY:NUmber = 400;
var rect:Rectangle = new Rectangle(centerX-radius, centerY-radius, radius*2, radius*2);
Then use the getPixels() to return a ByteArray of the pixels inside the rectangle.
Now you can loop through each pixel and check if it's contained inside the circle.
var image:BitmapData;
var pixels:ByteArray = image.getPixels(rect);
for(var x:int; x<rect.width; x++){
for(var y:int=0; y<rect.height; y++){
// Read the pixels data ->
var pixel:uint = pixels.readUnsignedInt();
// Check this pixels distance from the center to make sure it is inside the circle.
var dx:Number = x - radius;
var dy:Number = y - radius;
if(dx*dx+dy*dy <= radius*radius){
// This pixel is inside the circle.
...
}
}
}
Then once you've modified it if you want you can write it back to the image using setPixels()
image.setPixels(rect, pixels);
I haven't actually used or tested any of this so it might not work.
It might also be easier to work with the data if you use getVector() and setVector()
instead.