implementing mask functionality using BitmapData - actionscript-3

I have two BitmapData objects with transparency enabled. One is a large red square, the other is a small blue circle.
If, for example, I position the blue circle over the red square. I would like to create an area of transparency in the red square's BitmapData where the blur circle is. Similar to how a mask works.
I have tried using getPixel32() operations but it is very slow (see below). Is there another way I can do this? Thanks
for(var x:int = 0; x < circleBitmapData.width; x++){
for(var y:int = 0; y < circleBitmapData.width; y++){
if(circleBitmapData.getPixel32(x,y) != 0x00000000){
squareBitmapData.setPixel(x,y,0x00000000);
}
}
}
EDIT - I have one possible solution, but it's not ideal. I can merge the two bitmaps, then use the threshold method to turn pixels above a certain value to transparent. So I could set all blue pixels to transparent. However, I get a thin ring of blue around the transparent area

Check out bitmapData's threshold method. It should return you a bitmapData with the intersected area. With that, you don't have to get and set pixel anymore. Also, getPixel32 should have setPixel32 :P

Related

LibGDX - Efficient way to use tiles & Z-Order of Sprites

I have a problem. The sprites I'm rendering aren't in the Z position I want them to be.
The game I'm making is a 2D sandbox game - similar to terraria.
I make a 2-dimensional array and then iterate through it with a for-loop.
Basic code outline:
for(int x = 0; x < worldwidth; x ++){
for(int y = 0; y < worldheight; y ++){
//complicated calculations to check which texture it should use
// draws background blocks
Sprite s = new Sprite(texture);
s.setSize(12, 12);
s.setOriginCenter();
s.rotate(rotation);
s.setPosition(x* world.blocksize, y* world.blocksize);
s.draw(batch);
//draws foreground blocks
//same thing as with background blocks, only with a different texture
}
}
I want the player to be between the background and foreground sprites. But if I draw it at the start or the end of the loop, it ends up at the very back or very front.
I tried drawing it after the background tiles, but that didn't work. What do I do? Do I use something instead of a spritebatch for rendering the player?
Okay, second question: Is drawing tiles this way efficient? Am I doing it right, or should I use something like a tilemap?

Scale, Position & Rotate Parent object to make child object take up entire stage

Using the first photo below, let's say:
The red outline is the stage bounds
The gray box is a Sprite on the stage.
The green box is a child of the gray box and has a rotation set.
both display object are anchored at the top-left corner (0,0).
I'd like to rotate, scale, and position the gray box, so the green box fills the stage bounds (the green box and stage have the same aspect ratio).
I can negate the rotation easily enough
parent.rotation = -child.rotation
But the scale and position are proving tricky (because of the rotation). I could use some assistance with the Math involved to calculate the scale and position.
This is what I had tried but didn't produce the results I expected:
gray.scaleX = stage.stageWidth / green.width;
gray.scaleY = gray.scaleX;
gray.x = -green.x;
gray.y = -green.y;
gray.rotation = -green.rotation;
I'm not terribly experienced with Transformation matrices but assume I will need to go that route.
Here is an .fla sample what I'm working with:
SampleFile
You can use this answer: https://stackoverflow.com/a/15789937/1627055 to get some basics. First, you are in need to rotate around the top left corner of the green rectangle, so you use green.x and green.y as center point coordinates. But in between you also need to scale the gray rectangle so that the green rectangle's dimensions get equal to stage. With uniform scaling you don't have to worry about distortion, because if a gray rectangle is scaled uniformly, then a green rectangle will remain a rectangle. If the green rectangle's aspect ratio will be different than what you want it to be, you'd better scale the green rectangle prior to performing this trick. So, you need to first transpose the matrix to offset the center point, then you need to add rotation and scale, then you need to transpose it away. Try this set of code:
var green:Sprite; // your green rect. The code is executed within gray rect
var gr:Number=green.rotation*Math.PI/180; // radians
var gs:Number=stage.stageWidth/green.width; // get scale ratio
var alreadyTurned:Boolean; // if we have already applied the rotation+scale
function turn():void {
if (alreadyTurned) return;
var mat:flash.geom.Matrix=this.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs*green.x,-gs*green.y);
mat.rotate(-1*gr);
this.transform.matrix=mat;
alreadyTurned=true;
}
Sorry, didn't have time to test, so errors might exist. If yes, try swapping scale, translate and rotate, you pretty much need this set of operations to make it work.
For posterity, here is what I ended up using. I create a sprite/movieClip inside the child (green) box and gave it an instance name of "innerObj" (making it the actually content).
var tmpRectangle:Rectangle = new Rectangle(greenChild.x, greenChild.y, greenChild.innerObj.width * greenChild.scaleX, greenChild.innerObj.height * greenChild.scaleY);
//temporary reset
grayParent.transform.matrix = new Matrix();
var gs:Number=stage.stageHeight/(tmpRectangle.height); // get scale ratio
var mat:Matrix=grayParent.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs * tmpRectangle.x, -gs * tmpRectangle.y);
mat.rotate( -greenChild.rotation * Math.PI / 180);
grayParent.transform.matrix = mat;
If the registration point of the green box is at one of it's corners (let's say top left), and in order to be displayed this way it has a rotation increased, then the solution is very simple: apply this rotation with negative sign to the parent (if it's 56, add -56 to parent's). This way the child will be with rotation 0 and parent -> -56;
But if there is no rotation applied to the green box, there is almost no solution to your problem, because of wrong registration point. There is no true way to actually determine if the box is somehow rotated or not. And this is why - imagine you have rotated the green box at 90 degrees, but changed it's registration point and thus it has no property for rotation. How could the script understand that this is not it's normal position, but it's flipped? Even if you get the bounds, you will see that it's a regular rectangle, but nobody know which side is it's regular positioned one.
So the short answer is - make the registration point properly, and use rotation in order to display it like in the first image. Then add negative rotation to the parent, and its all good :)
Edit:
I'm uploading an image so I can explain my idea better:
 
As you can see, I've created a green object inside the grey one, and the graphics INSIDE are rotated. The green object itself, has rotation of 0, and origin point - top left.
#Vesper - I don't think that the matrix will fix anything in this situation (remember that the green object has rotation of 0).
Otherwise I agree, that the matrix will do a pretty job, but there are many ways to do it :)

Speeding up a canvas image crop

I'm working on a simple image crop where the user draws a line with the mouse around an area that they want to keep. When they confirm, the rest of the image will be cropped out. Here's how I'm currently handling said cropping:
var data = c.getImageData(0,0,canvas.width,canvas.height);
for (var x = 0; x < data.width; x++) {
for (var y = 0; y < data.height; y++) {
if (!c.isPointInPath(x,y)) {
var n = x + (data.width * y);
var index = n*4;
data.data[index+3] = 0;
}
}
}
However, this can bog down really quickly. The less of the image you try to retain, the faster it goes, but even saving 30% of the image (canvas is 800x800) causes it to hang for several seconds. Is there a faster way to go about this?
I don't really understand why you are diving into pixel details to manipulate your cropping image functionality. It's understandable as bigger the image is get as more time is needed for cropping out the rest of the image, because practically with iterating over a two dimensional array of pixels the processing time needed for the operation is exponentially increasing with the increasing in size of the pixels map.
So my suggestion would be to try to remake the function without to even touch the getImageData and putImageData function. It's useless. I would make in the following way:
Obtain the pixel coordinates at the mouse down.
Create an event listener for the mouse move.
Create a semi-transparent image over the original image and use the fillRect function to draw into the created canvas.
Create an event listener for mouse up.
Obtain the pixel coordinates at the mouse up.
Calculate the coordinates of the resulting square.
Draw the resulting image into the canvas using as parameters the square coordinates.
As a final step draw the content of the canvas to an image.
This way you will save a lot of overhead on image cropping processing.
Here is a script for your reference: https://github.com/codepo8/canvascropper/blob/master/canvascrop.js
There is no real way to speed it up when you have to use a user defined shape, but the bogging down can be handled with a worker.
Some ideas:
Restrict getImageData to the bounding box of the polygon the user draws.
Put data.height, data.width etc. used inside the loop in a variable.
Maybe you can split up inside/outside tests and setting the imagedata alpha value.
Maybe even draw the polygon to a black and white imagedata object, then get the difference between the two?
Can you share the isPointInPath(x,y) function?

How do I create and distribute diagonal stripes on a rectangle?

I would like to be able to create bar charts with JFreeChart that looks similar to the following picture.
It is a very basic mono-colored bar chart, but with one "fancy" detail: the diagonal stripes. I was thinking that this could be made possible by overlaying another picture on top of the normal bar. This picture would have the same dimensions as the bar, have diagonal white stripes and a transparent background. I am not quite sure how to do this though, as I have very little GUI experience, but I found a very useful article that deals with overlaying images on top of graphics from JFreeChart, so I am quite certain I should be able to pull that of.
But how should I create the diagonal stripes?
I see how I could distribute the lines from the lower left corner to the upper right corner, but not the capped lines in the upper left and lower right corner. Can I somehow paint outside the rectangle (and not have it included in the picture)?
edit: After some searching I cannot see that my suggestion of overlaying an image with a transparent background would work, as I cannot find any examples on how to do this. On the other hand, merely painting the lines on the rectangle is probably easier.
Using a gradient fill to draw lines
On trashgod's tip I tried filling a shape with a gradient that had sharp edges to simulate line drawing. This would prevent a lot of calculations and could potentially be a lot simpler. It worked quite ok for thick lines, but not for thinner lines. Using the following code produces the fill in the first picture:
rect.setSpace(spaceBetweenLines);
Color bg = Color.YELLOW;
Color fg = Color.BLUE;
rect.setPaint(new LinearGradientPaint(
(float) startX, (float) startY, (float) (startX + spaceBetweenLines), (float) (startY + spaceBetweenLines),
new float[] {0,.1f,.1001f}, new Color[] {fg,fg,bg}, MultipleGradientPaint.CycleMethod.REPEAT)
);
Drawing lines using graphic primitives
Although simpler it did not work in my case. The more elaborate, but to me, more natural way of doing it, is simply drawing lines on top of the shape (rectangle, cirle, ...). The following code was used in producing the second image. Observe the use of the clip(Shape s) to restrict the line drawing to the shape underneath. The reason for not simply drawing a rectangle and using clip() to limit the shape is that the clip() operation is not aliased, thus producing jaggies. Therefore I have to draw the shape first to get smooth edges, then set the clip to prevent overflow in the forthcoming line drawing, and finally draw the lines.
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(getBackground());
g2.fill(getShape());
g2.setClip(getShape());
// draw diagonal lines
g2.setPaint(getLineColor());
for (int x = (int) this.x, y = (int) (this.y); y - height < (this.y + height + getSpace()); ) {
g2.drawLine(x, y , x + (int) width , y - (int) width);
y += getSpace();
}
The source code for BarChartDemo1 shows how to apply a GradientPaint, but you may want to experiment with LinearGradientPaint to get the diagonal effect.
I want to paint the bars, not the background.
If you already have a suitable image, TexturePaint may be an alternative.

Actionscript 3: getColorBoundsRect

I have a question. I have a BitmapData with 2 red circles. I want to find the rectangle area or each circle. If i use [B]getColorBoundsRect[/B] I get the smallest area enclosed by the 2 circles.
How can i go about this and get individual area of the circles?
below is a diagram i created to better explain my question:
http://img831.imageshack.us/img831/3360/sampleja.png
previously this question was asked before but don't quite understand
how the provided solution solved the problem.
http://www.kirupa.com/forum/showthread.php?324586-Question-to-getColorBoundsRect
hope someone here can shed some light for me. Thanks a million.
There's a very neat trick to do it. First you need to make sure you get only two colors in your BitmapData (threshold will do the trick). After that, you can use getColorBounds together with floodFill to find all blobs in the image. The pseudo-code would be something like this:
//Do the following until rect.width is zero.
rect = bmp.getColorBoundsRect(red);
//check the first row of pixels until you find the start of the blob
for(y = rect.y; y < rect.height + rect.y; y++) {
if(bmp.getPixel(rect.x,y) == red) {
bmp.floodFill(rect.x,y, green); // paint the blob green
blobs.push(bmp.getColorBoundsRect(green)); // get the green bounds and push a new blob
bmp.floodFill(rect.x,y, white); // clear it
break;
}
}