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.
Related
I am trying to do something I have never done before.
My goal is to be able to manipulate the opacity of a BufferedImage. First off, I do not use Graphics. I am developing a simple Game Engine and I only use pixel data from BufferedImages.
What have I tried?
I made my own "Image" class that takes in a BufferedImage.
BufferedImage image = null;
try {
image = ImageIO.read(Image.class.getResourceAsStream(resourcePath));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
width = image.getWidth();
height = image.getHeight();
hasAlpha = image.getColorModel().hasAlpha();
pixels = image.getRGB(0, 0, width, height,null,0,width);
After that I proceeded to my rendering code. I have a function called "drawImage()" that takes the image and it's data and then puts it out on a specific spot on the screen depending on the users wishes. I however want the user to choose it's opacity as well.
I started out by taking the pixel data for that pixel and I instead made a Color.
Color c = new Color(image.getPixels()[value2],true);
The "true" statement, what I know of, says that it should "care" about Alpha.
After that I place the pixel by using this code:
pixels[x + y * pWidth] = new Color(c.getRed(),c.getGreen(),c.getBlue(),alphaValue).getRGB();
This doesn't work. It doesnt output any errors and I can change the alphaValue to a number between 0 and infinity. However one issue I found is that it doesnt affect the image at all when I change the alpha from 255 to something like 50. It stays the same. The affect comes at 0-5. Another thing that I noticed is that when I set alphaValue to something like 2 I get an animation instead of the image with lower opacity. Sounds weird, that's why I provided a .GIF of the issue:
I guarantee you that there is no code that changes the alphaValue. During that "animation" it is set to 2 and nothing else.
So something is weird here and I wonder if you guys know the issue?
It might have something to do with the Image class and that I don't set it to BufferedImage.RGBA but I don't know how I can do so when I load an image?
If you need any more information, please leave a comment and I will provide more info!
Thanks in advance!
EDIT
I clear the screen every 1/60 of a second by manipulating the backgrounds pixels (also a bufferedImage):
public void clear() {
for(int i = 0; i < pixels.length; i++) {
pixels[i] = clearColor.getRGB();
}
}
Possible issue
I did some troubleshooting and I found out that it actually somehow increased in opacity each frame as #Joni said. Somehow the opacity or alpha seems to increase each frame but I am clearing the screen between drawings. I will troubleshoot further.
UPDATE
I made so I could move the image around and found this weird behaviour:
Link to Imgur
It seems that the "alpha" isn't reset. I know that the RGB is being reset but something is really off when it comes to the Alpha.
I changed the clear function to this:
public void clear() {
for(int i = 0; i < pixels.length; i++) {
pixels[i] = new Color(clearColor.getRed(),clearColor.getGreen(),clearColor.getBlue(),255).getRGB();
}
}
And I defined clearColor as:
Color clearColor = new Color(Color.black.getRGB(),true);
That did however not work either. Does anyone have a solution?
Your game engine is drawing the image on top of itself in a loop. After drawing it enough many times the effect is the same as not having used transparency at all. The lower the alpha, the longer it takes though: with 50% alpha you need 7 frames to get 99% opacity, with 5% alpha you need about 90 frames.
For example, suppose you are drawing a pixel value of 100 on a screen that's intially 0 (black) with 50% opacity. After the first frame, the output pixel value is .5*100 + .5*0 = 50. The second frame is drawn on top of the first frame, so the output pixel value is .5*100 + .5*50 = 75. The third frame, drawn on top of the second frame, will show .5*100 + .5*75 = 87.5.
To avoid this, you need to fill a solid background color under the image in every frame.
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;
}
I am trying to get the value of the White Colored pixel from a GrayScale image and replace it with another Color but when I run my code, the whole GrayScale image is transfered to another Color. Can anyone please tell me where is fault in the code or how can I get my desired results??
This is the code...
public class gray {
public static void main (String args[])throws IOException{
int width;
int height;
BufferedImage myImage = null;
File f = new File("E:\\eclipse\\workspace\\Graphs\\src\\ColorToGray\\1.png");
myImage = ImageIO.read(f);
width = myImage.getWidth();
height = myImage.getHeight();
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
int pixels[];
pixels = new int[width * height];
myImage.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
if (pixels[i] == 0xFFFFFF) {
pixels[i] = 0x000000FF;
}
}
File f2 = new File("E:\\eclipse\\workspace\\Graphs\\src\\ColorToGray\\out 1.png");
image.setRGB(0, 0, width, height, pixels, 0, width);
ImageIO.write( image, "jpg", f2);
}
}
Image Before:
Image Before Output
Image After:
Image After Output
I looked into it, and found a bunch of problems.
First of all, when specifying the filename to save, you supply a ".png" extension, but when you call the ImageIO.write() function, you specify file type "jpg". That tends not to work very well. If you try to open up the resulting file, most programs will give you a "this is not a valid .PNG file" error. Windows explorer tries to be smart, and re-interprets the .PNG as a .JPG, but this spared you from the chance of discovering your mistake.
This takes care of the strange redness problem.
However, if you specify "png" in ImageIO.write(), you still don't get the right image. One would expect an image that looks mostly like the original, with just a few patches of blue there where bright white used to be, but instead what we get is an overall brighter version of the original image.
I do not have enough time to look into your original image to find out what is really wrong with it, but I suspect that it is actually a bright image with an alpha mask that makes it look less bright, AND there is something wrong with the way the image gets saved that strips away alpha information, thus the apparent added brightness.
So, I tried your code with another image that I know has no tricks in it, and still your code did not appear to do anything. It turns out that the ARGB format of the int values you get from myImage.getRGB(); returns 255 for "A", which means that you need to be checking for 0xFFFFFFFF, not 0x00FFFFFF.
And of course when you replace a value, you must replace it with 0xFF0000FF, specifying a full alpha value. Replacing a pixel with 0x000000FF has no visible effect, because regardless of the high blue value, alpha is zero, so the pixel would be rendered transparent.
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
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.