Flex/Actionscript White to Transparent - apache-flex

I am trying to write something in my Flex 3 application with actionscript that will take an image and when a user clicks a button, it will strip out all the white(ish) pixels and convert them to transparent, I say white(ish) because I have tried exactly white, but I get a lot of artifacts around the edges. I have gotten somewhat close using the following code:
targetBitmapData.threshold(sourceBitmapData, sourceBitmapData.rect, new Point(0,0), ">=", 0xFFf7f0f2, 0x00FFFFFF, 0xFFFFFFFF, true);
However, it also makes red or yellows disappear. Why is it doing this? I'm not exactly sure how to make this work. Is there another function that is better suited for my needs?

A friend and I were trying to do this a while back for a project, and found writing an inline method that does this in ActionScript to be incredibly slow. You have to scan each pixel and do a computation against it, but doing it with PixelBender proved to be lightning fast (if you can use Flash 10, otherwise your stuck with slow AS).
The pixel bender code looks like:
input image4 src;
output float4 dst;
// How close of a match you want
parameter float threshold
<
minValue: 0.0;
maxValue: 1.0;
defaultValue: 0.4;
>;
// Color you are matching against.
parameter float3 color
<
defaultValue: float3(1.0, 1.0, 1.0);
>;
void evaluatePixel()
{
float4 current = sampleNearest(src, outCoord());
dst = float4((distance(current.rgb, color) < threshold) ? 0.0 : current);
}
If you need to do it in AS you can use something like:
function threshold(source:BitmapData, dest:BitmapData, color:uint, threshold:Number) {
dest.lock();
var x:uint, y:uint;
for (y = 0; y < source.height; y++) {
for (x = 0; x < source.width; x++) {
var c1:uint = source.getPixel(x, y);
var c2:uint = color;
var rx:uint = Math.abs(((c1 & 0xff0000) >> 16) - ((c2 & 0xff0000) >> 16));
var gx:uint = Math.abs(((c1 & 0xff00) >> 8) - ((c2 & 0xff00) >> 8));
var bx:uint = Math.abs((c1 & 0xff) - (c2 & 0xff));
var dist = Math.sqrt(rx*rx + gx*gx + bx*bx);
if (dist <= threshold)
dest.setPixel(x, y, 0x00ffffff);
else
dest.setPixel(x, y, c1);
}
}
dest.unlock();
}

You can actually do it without pixelbender and real-time thanks to the inbuilt threshold function :
// Creates a new transparent BitmapData (in case the source is opaque)
var dest:BitmapData = new BitmapData(source.width,source.height,true,0x00000000);
// Copies the source pixels onto it
dest.draw(source);
// Replaces all the pixels greater than 0xf1f1f1 by transparent pixels
dest.threshold(source, source.rect, new Point(), ">", 0xfff1f1f1,0x00000000);
// And here you go ...
addChild(new Bitmap(dest));

it looks like the above code would make a range of colors transparent.
pseudo-code:
for each pixel in targetBitmapData
if pixel's color is >= #FFF7F0F2
change color to #00FFFFFF
something like this will never be perfect, because you will lose any light colors
i would find an online color picker that you can use to see exactly what colors will be altered

The answer in 1 of pixel bender code:
dst = float4((distance(current.rgb, color) < threshold) ? 0.0 : current);
should be:
dst = (distance(current.rgb, color) < threshold) ? float4(0.0) : current;
or
if (distance(current.rgb, color) < threshold)
dst = float4(0.0);
else
dst = float4(current);

Related

Drawing the "Seed of Life" without redrawing anything

I have a mildly interesting problem which I can't quite figure out (although in fairness, I am pretty drunk)
The "Seed of Life" is a pattern created from drawing circles of equal radius, centred on the intersection of the previous circle.
Language doesn't really matter, the theory is more important here. Anything which can draw a circle will do it. For example, HTML5 + JS canvas can do it. It's a lovely example of how recursion can help solve problems.
The problem is that a naive approach will end up redrawing many, many circles. With 7 layers, you'll end up with over 300,000 circle draws.
A simple approach is to maintain a list of previous circle centre points, and only draw circles which are not in that list.
My question is whether there's a "better" way to approach this? Something which doesn't require checking that list.
A fun problem to ponder.
I think I have this solved thanks to a friend. I'll post here what I'm doing now in case someone ever is curious.
In short, starting from the center and working out, calculate the vertices of a hexagon, and subdivide each edge of the hexagon into i number of places, where i is the layer number.
I drew it in C# using SkiaSharp, but the code is nothing special to the language, there's no reason this couldn't be written in any language. Here's the significant bits:
const float seedAngle = (float)(Math.PI / 3.0);
static void SeedOfLifeDemo(int x, int y) {
//setting up Skia stuff, this will be different depending what language you're using.
var info = new SKImageInfo(x, y);
using var bitmap = FlatImage(info, SKColors.White);
SKCanvas canvas = new SKCanvas(bitmap);
float radius = Math.Min(x, y) / 15;
SKPoint center = new SKPoint(x / 2f, y / 2f);
SKPaint strokePaint = new SKPaint {
Color = SKColors.Black,
Style = SKPaintStyle.Stroke,
StrokeWidth = 1,
IsAntialias = true,
};
int layers = 4;
//Draw the very central circle. This is just a little easier than adding that edge case to SubdividedHexagonAboutPoint
canvas.DrawCircle(center, radius, strokePaint);
for (int i = 1; i <= layers; i++) {
foreach (SKPoint p in SubdividedHexagonAboutPoint(center, radius * i, i)) {
canvas.DrawCircle(p, radius, strokePaint);
}
}
SaveImage(bitmap, "SeedOfLifeFastDemo.Jpg");//More Skia specific stuff
}
//The magic!
static List<SKPoint> SubdividedHexagonAboutPoint(SKPoint centre, float radius, int subdivisions) {
List<SKPoint> points = new List<SKPoint>(6 * subdivisions);
SKPoint? prevPoint = null;
for (int i = 0; i < 7; i++) {//Step around the circle. The 7th step is to close the last edge
float x = (float)(Math.Sin(seedAngle * i) * radius + centre.X);
float y = (float)(Math.Cos(seedAngle * i) * radius + centre.Y);
SKPoint point = new SKPoint(x, y);
if (prevPoint != null) {
points.Add(point);//include the "primary" 6 points
if (subdivisions > 0) {
float xDist = (point.X - prevPoint.Value.X) / subdivisions;
float yDist = (point.Y - prevPoint.Value.Y) / subdivisions;
for (int sub = 1; sub < subdivisions; sub++) {
SKPoint subPoint = new SKPoint(point.X - xDist * sub, point.Y - yDist * sub);
points.Add(subPoint);//include the edge subdivisions
}
}
}
prevPoint = point;
}
return points;
}
This is quite an interesting exercise really, and another example of where recursion can really bite you when used badly.

Manual loop unrolling with known maximum size

Please take a look at this code in an OpenCL kernel:
uint point_color = 4278190080;
float point_percent = 1.0f;
float near_pixel_size = (...);
float far_pixel_size = (...);
float delta_pixel_size = far_pixel_size - near_pixel_size;
float3 near = (...);
float3 far = (...);
float3 direction = normalize(far - near);
point_position = (...) + 10;
for (size_t p = 0; p < point_count; p++, position += 4)
{
float3 point = (float3)(point_list[point_position], point_list[point_position + 1], point_list[point_position + 2]);
float projection = dot(point - near, direction);
float3 projected = near + direction * projection;
float rejection_length = distance(point, projected);
float percent = projection / segment_length;
float pixel_size = near_pixel_size + percent * delta_pixel_size;
bool is_candidate = (pixel_size > rejection_length && point_percent > percent);
point_color = (is_candidate ? (uint)point_list[point_position + 3] | 4278190080 : point_color);
point_percent = (is_candidate ? percent : point_percent);
}
This code attempts to find the point in a list that is nearest to the line segment between far and near, and assigning its color to point_color and its "percentual distance" into point_percent. (Incidentally, the code seems to be OK).
The number of elements specified by point_count is variable, so I cannot assume too much about it, save for one thing: point_count will always be equal or less than 8. That's a fixed fact in my code and data.
I would like to unroll this loop manually, and I'm afraid I will need to use lots of
value = (point_count < constant ? new_value : value)
for all lines in it. In your experience, will such a strategy increase performance in my kernel?
And yes, I know, I should be performing some benchmarking by myself; I just wanted to ask someone with lots of experience in OpenCL before actually attempting this on my own.
Most OpenCL drivers (that I'm familiar with, at least) support the use of #pragma unroll to unroll loops at compile time. Simply use it like so:
#pragma unroll
for (int i = 0; i < 4; i++) {
/* ... */
}
It's effectively the same as unrolling it manually, with none of the effort. In your case, this would probably look more like:
if (pointCount == 1) {
/* ... */
} else if (pointCount == 2) {
#pragma unroll
for (int i = 0; i < 2; i++) { /* ... */ }
} else if (pointCount == 3) {
#pragma unroll
for (int i = 0; i < 3; i++) { /* ... */ }
}
I can't say for certain whether there will be an improvement, but there's one way to find out. If pointCount is constant for the local work group for example, it might improve performance, but if it's completely variable, this might actually make things worse.
You can read more about it here.

Metaballs in Processing as vector

i am trying to create a kind of metaball, nice curves between two circles.
Something like the image, the lines are drawn straight but can also be more curved. I need them as a vector in Processing. Does anyone can help me?
thanks in advance!
Example in paperjs:
http://paperjs.org/examples/meta-balls/
image:
http://www.smeulders.biz/tmp/metaballs.png
void setup() {
size(500,500);
ellipse(100, 250, 100, 100);
ellipse(350, 250, 200, 200);
}
void draw() {}
With a bit of math (to workout distance between circles) and a bit of pixel manipulation to set pixel colours based on these calculated distances, you can render 2D metaballs and there plenty of examples
For fun however I decided to take a stab at making a very hacky version of the example you shared by simply rendering ellipses into an image, then filtering the image at the end:
PGraphics pg;//a separate layer to render into
int dilateAmt = 3;
PImage grid;//pixels of the grid alone, minus the 'cursor'
void setup(){
size(400,400);
//create a new layer
pg = createGraphics(width,height);
pg.beginDraw();
//draw a di-grid inside
pg.background(255);
pg.noStroke();pg.fill(0);
for(int y = 0 ; y < 5; y++)
for(int x = 0 ; x < 5; x++)
pg.ellipse((y%2==0?40:0)+(x * 80),40+(y * 80), 40, 40);
pg.endDraw();
//grab a snapshot for later re-use
grid = pg.get();
}
void draw(){
pg.beginDraw();
//draw the cached grid (no need to loop and re-render circles)
pg.image(grid,0,0);
//and the cursor into the layer
pg.ellipse(mouseX,mouseY,60,60);
pg.endDraw();
//since PGraphics extends PImage, you can filter, so we dilate
for(int i = 0; i < dilateAmt; i++) pg.filter(DILATE);
//finally render the result
image(pg,0,0);
}
void keyPressed(){
if(keyCode == UP) dilateAmt++;
if(keyCode == DOWN) dilateAmt--;
if(dilateAmt < 1) dilateAmt = 1;
println(dilateAmt);
}
Note that the end result is raster, not vector.
If you want to achieve the exact effect you will need to port your example from JavaScript to Java. The source code is available.
If you like Processing the above example you could use plain javascript using p5.js. You'll find most of the familiar functions from Processing, but also directly use the paper.js library.

How to animate through a rainbow of colors on a bitmap image

I have a bitmap (bitmapData) that I'd like it to change colors over time. I've seen examples of applying a color transform and I couldn't get it to work. Also, with those examples they only apply one color. I would like to animate through the ROYGBIV range. Note: I am dynamically loading images. I don't want to tint the image I want the color in the image to change if that makes sense.
To manipulate/tint colors you would use the colorTransform function
To illustrate:
In the example below I embed the image and reference it, if you are using adobe flash you would 'name' your asset and reference it in you code.
import flash.geom.ColorTransform;
import flash.events.Event;
import flash.display.Bitmap;
[Embed(source="img/logo.png")] private var logoCls:Class;
private var bitmapLogo:Bitmap = new logoCls();
public function Test()
{
addEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function init(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
addChild(bitmapLogo);
}
// r, g, b colors
var r:int = 256; // start at Red
var g:int = 0;
var b:int = 0;
var increment:int = 16; // how quickly to change the color
var colorCycle:int = 0;
var mult:Number = 0.25; // How heavily TINTED you want the image
private function onEnterFrame(e:Event):void
{
var ct:ColorTransform = new ColorTransform (1-mult,1-mult,1-mult,1,r*mult,g*mult,b*mult,0);
bitmapLogo.transform.colorTransform = ct;
incrementRainbowColors();
}
private function incrementRainbowColors():void
{
if (colorCycle == 0) // -> yellow
if ((g+=increment) >= 256) colorCycle=1;
else if (colorCycle == 1) // -> green
if ((r-=increment) <= 0) colorCycle=2;
else if (colorCycle == 2) // -> cyan
if ((b+=increment) >=256) colorCycle=3;
else if (colorCycle == 3) // -> blue
if ((g-=increment) <= 0) colorCycle = 4;
else if (colorCycle == 4) // -> magenta
if ((r+=increment) >=256) colorCycle = 5;
else if (colorCycle == 5) // -> red
if ((b-=increment)<=0) colorCycle = 0;
}
}
You said you were working with bitmapData, so this answer is assuming you can / want to directly manipulate it.
You can use bitmapData.setPixel(row, column, color); to change a pixel's color. row and column are 0-based and represent exactly which pixel to modify - think of it like grid paper if you have never dealt with this before. The color parameter is actually a uint - unsigned integer. You can use a hex value like 0x000000 to set the pixel to black and 0xFFFFFF to white.
To alternate colors, you can just loop through different values for uint. There are 16777215 different possible values for colors and I honestly don't know much about hex colors - I just google the color I want.
But, here's a little tip if you're mathematically inclined:
Each spot in a hex number corresponds to 16 to the power of that spot * the actual number in that spot, in decimal.
I probably worded that wrong, so here is a visual example:
456789 in hex is (4 * 16^5) + (5 * 16^4) + (6 * 16^3) + (7 * 16^2) + (8 * 16^1) + (9 * 16^0) in decimal which equals 4548489.
The first 2 spots (from the left) correspond to Red, then Blue, then Green. so FF0000 is red, 00FF00 is green and 0000FF is blue. You can (I think) supply the decimal value for color in the setPixelfunction, so you can do (or find online) some conversions to get the color you want / loop through colors in a rainbow style pattern!
I hope this random crash course on Hex was helpful :P

How to get RGB values of a pixel in JavaFX

I am getting started with JavaFX and basically what I am trying to implement is a Color Picker.
At first, I thought of having a rectangle with a LinearGradient that goes through all primary/secondary colors.
Looks like what I want, but the problem is that I can not get the RGB values at a given coordinate(x,y) in this Node.
I know you can do it through the fill property of any Shape IF it is a Color.
But Is there anyway to get the RGB values of anything inside a LinearGradient/Paint ?
Does this ColorPicker JavaFX example help?
[...]
function colorAtLocation(x:Integer, y:Integer) : Color {
var bimg = iv.image.bufferedImage;
if (x < 0 or x >= bimg.getWidth() or y < 0 or y >= bimg.getHeight()) {
return null;
}
var rgb = bimg.getRGB(x, y);
var r = Bits.bitAnd(Bits.shiftRight(rgb, 16), 0xff);
var g = Bits.bitAnd(Bits.shiftRight(rgb, 8), 0xff);
var b = Bits.bitAnd(Bits.shiftRight(rgb, 0), 0xff);
Color.rgb(r, g, b)
}
function updateSelectedColor(e:MouseEvent) {
var rgb = colorAtLocation(e.x, e.y);
if (rgb != null) {
picker.selectedColor = rgb;
}
}
[...]
The ColorPicker JavaFX example starts with a png image that is loaded to an image that then populates the ImageView.
The question starts with a JavaFX Rectangle containing LinearGradient.
To get the rectangle contents into a buffered image, one can use the java.awt.Robot:
var rectangle = new java.awt.Rectangle(x,y,width,height);
var robot = new java.awt.Robot();
var bufferedImage = robot.createScreenCapture(rectangle);
where rectangle would be describe the coordinates of the JavaFX Rectangle containing the bits of interest.
The robot.createScreenCapture call has the gotcha that to do the screen capture, the screen has to be visible. There should be a better of way to populate the buffered image but I've not yet encountered it.

Resources