Java - Get all pixels inside of a circle on a Buffered Image - java

I wasn't able to find the solution to this problem online, sorry if this question is already asked before, also I think my question's title wasn't specific enough, so, I will explain in more details.
So, what I did is created a BufferedImage with a specific size and type, let's say for example:
BufferedImage img1 = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
And what I am trying to do, is to use a "for" loop and loop thru every single image's pixel inside of an imaginary circle, lets say that circle starts on (X, Y)(0, 0), and ends on (X, Y)(500, 500).
Now, what I am trying to do, is to loop thru every single pixel inside of that imaginary circle, and then later, do something with that pixel (changing it's color for example).
Can anyone please help me do it? Thanks!

I came up with an idea that could solve this problem not only for circles, but also for any other shapes.
So, my idea is to create another Buffered Image, make it the same size as the one i want to edit (work on), make sure that all of it's pixels are blank (transparent), and draw a shape that you want on that new image (a circle for example) also, we keep the track of the shape's color, and the shape needs to be filled.
After that, we loop thru each pixel on the image that we want to edit using the for loops and the x, y integer variables, each time the loop repeats, we check on the shape's image to see if the shape is drawn there, and to do that we check if the pixel color is same as the shape's color on the shape's image. And if it is, the loop has detected the coordinates to the pixel inside of the shape, and then we can do something with it. (The for loops use the X and Y coordinate integers)
Here is an example code:
public static BufferedImage yourMethodName(BufferedImage inputImage, Point circlePosition, Dimension circleSize)
{
BufferedImage outputImage = inputImage;
BufferedImage circleImage = new BufferedImage(inputImage.getWidth(), inputImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics drawOnCircleImg = circleImage.getGraphics();
Color circleColor = new Color(0, 255, 0);
drawOnCircleImg.setColor(circleColor);
drawOnCircleImg.fillOval(circlePosition.x, circlePosition.y, circleSize.width, circleSize.height);
for(int y = 0; y < outputImage.getHeight(); y++)
{
for(int x = 0; x < outputImage.getWidth(); x++)
{
if(circleImage.getRGB(x, y) == circleColor.getRGB())
{
//PIXEL IS INSIDE OF THE SHAPE, IT IS DETECTED, DO SOMETHING NOW
//VARIABLES FOR THE PIXEL POSITION ARE: X, Y
}
}
}
return outputImage;
}

Related

How to scale a pixel-drawing function in Java

In Java, I have a function like this:
public void setPixel(int x, int y, boolean on);
It sets a virtual black and white pixel, given whether it is on or not.
How can I call this function so the resulting display will be four times larger?
I tried this:
int x = 3;
int y = 3;
setPixel(x, y, true);
setPixel(x+1, y+1, true);
setPixel(x+2, y+2, true);
setPixel(x+3, y+3, true);
But naturally, it overlapped when I tried to draw something. How should I call the method?
While I'm tagging this Java, the concept could apply to any language.
Answering on these assumptions: setPixel sets a single pixel to white or black (if on is true, to black, else to white). You want to use this function to get a B&W image and make it four times larger. The code you provided is wrong and just makes a diagonal instad of a 4x4 block. Is this correct? If so:
A way to draw a 4 times larger image would then be, for example, to have a "getPixel(x,y)" which gets you whether the pixel at (x,y) is on in the original image and then start painting somewhere else in 4x4 blocks. Whenever you move by one pixel in either X or Y direction when getting the values of your original image, you move by 4 in your new image to scale. So then what you intended to do maybe was something like this?
void setBlock(int x, int y, boolean on, int scale)
for(int i=0; i < scale; i++){
for(int j=0; j < scale; j++){
setPixel(scale*x + i, scale*y + j, on);
And then iterate over your original image's coordinates doing something like this?
setBlock(x, y, getPixel(x, y), 4);

checking to see if the image is the same

So i have a lot of questions to this but first here is my assignment.
Write a method public Picture authenticate(Picture p) which checks to
see if ”this” picture and the picture p are identical. It returns a
new picture that it the same size as ”this” picture. For each pixel
position in the original picture ”this”, compare the color of that
pixel with the color of the pixel in the same position from second
picture. If the two pixels have the same color (i.e. the original is
not modified in the copy), set the color of this position in the
resulting picture to white. If the two pixels do not have the same
color, set the color of this position in the resulting picture to the
color of the pixel from the original picture. Return the resulting
picture. If the two pictures are not the same size, the resulting
picture should contain all black pixels
This is my code as of right now
public Picture Authenticate(Picture p)
{
Pixel sourcePixel = null;
Pixel targetPixel = null;
Picture result = new Picture(this.getWidth(), this.getHeight());
for(int sourceX = 0, targetX= getWidth()-1; sourceX < getWidth(); sourceX++,targetX++)
{
for(int sourceY = 0, targetY= getHeight(); sourceY< getHeight(); sourceY++,targetY++)
{
sourcePixel = this.getPixel(sourceX,sourceY);
targetPixel = result.getPixel(targetX, targetY);
}
if (sourcePixel.getColor() == targetPixel.getColor())
targetPixel.setColor(Color.WHITE);
else
targetPixel.setColor(Color.BLACK);
}
return result;
}
Here are my questions:
How do i make p in the authenticate parameter fit into this?
Am i going about this the right way within the loop?
With my if and else statement, i feel as if that is not even close
to changing the pixel i need tooa
How do i make p in the authenticate parameter fit into this?
this is your source image; p is the target image; result is where you place the output pixels resulting from comparing the source and target.
Am i going about this the right way within the loop?
Yes, but the if/else statement belongs inside the inner loop
With my if and else statement, i feel as if that is not even close to changing the pixel i need to
You are close. Keep working on it and step through the code in your debugger to see what is actually happening.
Don't get discouraged, this is closer than you think.

2D Dynamic Lighting in Java

I am making a game that has campfire objects. What I want to do is to brighten all pixels in a circle around each campfire. However, looping through every pixel and changing those within the radius is not all that efficient and makes the game run at ~7 fps. Ideas on how to either make this process efficient or simulate light differently?
I haven't written the code for the fires but this is the basic loop to check each pixel/change its brightness based on a number:
public static BufferedImage updateLightLevels(BufferedImage img, float light)
{
BufferedImage brightnessBuffer = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
brightnessBuffer.getGraphics().drawImage(img, 0, 0, null);
for(int i = 0; i < brightnessBuffer.getWidth(); i++)
{
for(int a = 0; a < brightnessBuffer.getHeight(); a++)
{
//get the color at the pixel
int rgb = brightnessBuffer.getRGB(i, a);
//check to see if it is transparent
int alpha = (rgb >> 24) & 0x000000FF;
if(alpha != 0)
{
//make a new color
Color rgbColor = new Color(rgb);
//turn it into an hsb color
float[] hsbCol = Color.RGBtoHSB(rgbColor.getRed(), rgbColor.getGreen(), rgbColor.getBlue(), null);
//lower it by the certain amount
//if the pixel is already darker then push it all the way to black
if(hsbCol[2] <= light)
hsbCol[2] -= (hsbCol[2]) - .01f;
else
hsbCol[2] -= light;
//turn the hsb color into a rgb color
int rgbNew = Color.HSBtoRGB(hsbCol[0], hsbCol[1], hsbCol[2]);
//set the pixel to the new color
brightnessBuffer.setRGB(i, a, rgbNew);
}
}
}
return brightnessBuffer;
}
I apologize if my code is not clean, I'm self taught.
I can give you lots of approaches.
You're currently rendering on the CPU, and you're checking every single pixel. That's hardcore brute force, and brute force isn't what the CPU is best at. It works, but as you've seen, the performance is abysmal.
I'd point you in two directions that would massively improve your performance:
Method 1 - Culling. Does every single pixel really need to have its lighting calculated? If you could instead calculate a general "ambient light", then you could paint most of the pixels in that ambient light, and then only calculate the really proper lighting for pixels closest to lights; so lights throw a "spot" effect which fades into the ambient. That way you're only ever performing checks on a few of the pixels of the screen at a time (the circle area around each light). The code you posted just looks like it paints every pixel, I'm not seeing where the "circle" dropoff is even applied.
Edit:
Instead, sweep through the lights, and just loop through local offsets of the light position.
for(Light l : Lights){
for(int x = l.getX() -LIGHT_DISTANCE, x< l.getX() + LIGHT_DISTANCE, y++){
for(int y = l.getY() - LIGHT_DISTANCE, y < l.getY() + LIGHT_DISTANCE, y++){
//calculate light
int rgb = brightnessBuffer.getRGB(x, y);
//do stuff
}
}
You may want to add a check with that method so overlapping lights don't cause a bunch of rechecks, unless you DO want that behavior (ideally those pixels would be twice as bright)
Method 2 - Offhand calculation to the GPU. There's a reason we have graphics cards; they're specifically built to be able to number crunch those situations where you really need brute force. If you can offload this process to the GPU as a shader, then it'll run licketysplit, even if you run it on every pixel several times over. This will require you to learn graphics APIs however, but if you're working in java, LibGDX makes it very painless to render using the GPU and pass off a couple shaders to the GPU.
I am uncertain about the way in which you are going about calculating light values, but I do know that using the BufferedImage.getRGB() and BufferedImage.setRGB() methods is very slow.
I would suggest accessing the pixels of the BufferedImage directly from an array (much faster IMO)
to do this:
BufferedImage lightImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
Raster r = lightImage.getRaster();
int[] lightPixels = ((DataBufferInt)r.getDataBuffer()).getData();
Now, changing any pixel in this array will show on your image. Note that the values used in this array are color values in the format of whatever format you defined your image with.
In this case it is TYPE_INT_ARGB meaning you will have to include the alpha value in the number when setting the coloar (RRGGBB*AA*)
Since this array is a 1D array, it is more difficult to access pixels using x and y co-ordinates. The following method is an implementation of accessing pixels from the lightPixels array more easily.
public void setLight(int x, int y,int[] array,int width, int value){
array[width*y+x] = value;
}
*note: width is the width of your level, or the width of the 2D array your level might exist as, if it was a 2D array.
You can also get pixels from the lightPixels array with a similar method, just excluding the value and returning the array[width*y+x].
It is up to you how you use the setLight() and getLight() methods but in the cases that I have encountered, using this method is much faster than using getRGB and setRGB.
Hope this helps

Drawing objects behind circle except the ones behind 'background'

Situation: I have a canvas on an Android game, I have some objects (I will keep it as simple as possible):World(where are storaged all Laser and Block objects), Block and Laser. I can draw all this objects in the canvas.
I would like to 'hide' them behind a black 'background', and then draw a blurry 'transparent' circle, so all objects are hidden behind the black background, except the objects behing the circle.
I have thought about it, but I can't think of an approach to do this.
Images:
This is my actual situation:
This is the expected:
Do something like this:
public void drawBitmapsInCanvas(Canvas c){
c.drawBitmap(block, new Rect(/*coordinates here*/), new Rect(/*More coordinates*/),null);
c.drawBitmap(block2, new Rect(/*coordinates here*/), new Rect(/*More coordinates*/),null);
c.drawBitmap(laser, new Rect(/*coordinates here*/), new Rect(/*More coordinates*/),null);
c.drawColor(Color.BLACK);//this hides everything under your black background.
c.drawBitmap(circle, new Rect(/*coordinates here*/), new Rect(/*More coordinates*/),null);
}
If you want transparency:
Paint paint =new Paint();
paint.setARGB(120,0,0,0); //for the "120" parameter, 0 is completely transparent, 255 is completely opaque.
paint.setAntiAlias(true);
c.drawBitmap(bmp,Rect r,Rect rr, paint);
or if you are trying to change the opacity of individual pixels, the approach is a bit more complicated (I have not tested the code, but you get the gist of it):
public static final Bitmap getNewBitmap(Bitmap bmp, int circleCenterX,
int circleCenterY,int circleRadius){
//CIRCLE COORDINATES ARE THE DISTANCE IN RESPECT OF (0,0) of the bitmap
//, not (0,0) of the canvas itself. The circleRadius is the circle's radius.
Bitmap temp=bmp.copy(Bitmap.Config.ARGB_8888, true);
int[]pixels = new int[temp.getWidth()*temp.getHeight()];
temp.getPixels(pixels,0 ,temp.getWidth(),0,0,temp.getWidth(), temp.getHeight());
int counter=0;
for(int i=0;i<pixels.length;i++){
int alpha=Color.alpha(pixels[i]);
if(alpha!=0&&!((Math.pow(counter/temp.getWidth()-circleCenterY,2.0)+
Math.pow(counter%temp.getWidth()-circleCenterX,2.0))<Math.pow(circleRadius,2.0))){
//if the pixel itself is not completely transparent and the pixel is NOT within range of the circle,
//set the Alpha value of the pixel to 0.
pixels[i]=Color.argb(0,Color.red(pixels[i]),Color.green(pixels[i]),Color.blue(pixels[i]));
}
counter++;
}
temp.setPixels(pixels,0, temp.getWidth(),0,0,temp.getWidth(),temp.getHeight());
return temp;
}
and then draw temp.
I'm not completely sure what you are trying to ask, so you may have to modify as necessary.
If you try the second answer of qwertyuiop5040, you will get a ver low - perfomance when you try to apply it to a large image. Let's say a 1000*800 pixels image. Then you will have a loop:
for (int i = 0 ; i < 1000*800; i++)
You could create an image that's a black rectangle with a transparent hole in it. The hole would be the circle that you can see through, and the image would be rendered over the spot you want to be visible. Then, you can draw four black rectangles around the image to cover the rest of the screen.

Java2D: Clipping a Graphics object with a Line

Is there a way to use a Graphics object's 'setClip()' method to clip using a Line-ish shape? Right now I'm trying to use a Polygon shape but I'm having problems simulating the "width" of the line. I basically draw the line, and when I reach the end, I redraw it but this time subtract the line width from y-coordinate:
Polygon poly = new Polygon();
for(int i = 0; i < points.length; i++)
poly.addPoint(points.[i].x, points.[i].y);
// Retrace line to add 'width'
for(int i = points.length - 1; i >=0; i--)
poly.addPoint(points[i].x, points[i].y - lineHeight);
It almost works but the width of the line varies based upon its slope.
I can't use the BrushStroke and drawLine() methods because the line can change color once it passes some arbitrary reference line. Is there some implementation of Shape that I overlooked, or an easy one I can create, that will let me do this more easily?
If there is a better way, I've never run across it. The best I can think of is to use some trigonometry to make the line width more consistent.
OK, I managed to come up with a pretty nice solution without using the setClip() method. It involves drawing my background to an intermediate Graphics2D object, using setComposite() to specify how I want to mask the pixels, THEN drawing my line using drawLine() on top. Once I have this line, I draw it back on top of my original Graphics object via drawImage. Here's an example:
BufferedImage mask = g2d.getDeviceConfiguration().createCompatibleImage(width, height, BufferedImage.TRANSLUCENT);
Graphics2D maskGraphics = (Graphics2D) mask.getGraphics();
maskGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
maskGraphics.setStroke(new BasicStroke(lineWidth));
maskGraphics.setPaint(Color.BLACK);
// Draw line onto mask surface first.
Point prev = line.get(0);
for(int i = 1; i < line.size(); i++)
{
Point current = line.get(i);
maskGraphics.drawLine(prev.x, prev.y, current.x, current.y);
prev = current;
}
// AlphaComposite.SrcIn: "If pixels in the source and the destination overlap, only the source pixels
// in the overlapping area are rendered."
maskGraphics.setComposite(AlphaComposite.SrcIn);
maskGraphics.setPaint(top);
maskGraphics.fillRect(0, 0, width, referenceY);
maskGraphics.setPaint(bottom);
maskGraphics.fillRect(0, referenceY, width, height);
g2d.drawImage(mask, null, 0, 0);
maskGraphics.dispose();
Maybe you could use a Stroke.createClippedShape to do this? (May need to use an Area to add subtract the stroked shape from/to your original shape depending on what exactly you are trying to do.

Categories