Java's Graphics.drawImage produces an inaccurate image - java

I'm trying to draw one image onto another using graphics.drawImage() but it's only working accurately for some images, others it messes up. My code's below, I've made sure texture is the right image when it enters the method so that's definitely not it. Any ideas?
private BufferedImage currentSheet;
public void swapRegionWithTexture(Rectangle region, Image texture) {
Graphics sheetGraphics = currentSheet.createGraphics();
for (int ix = region.x; ix < region.x + region.width; ix++) {
for (int iy = region.y; iy < region.y + region.height; iy++) {
currentSheet.setRGB(ix, iy, 0x000000);
}
}
sheetGraphics.drawImage(texture, region.x, region.y, null);
sheetGraphics.dispose();
}
The general idea is:
Grab the graphics to draw to.
Clear out the section of the graphics that will be drawn on.
Draw the image at the given location on the graphics.
Dispose of the graphics.

Your current atomic steps are thus:
create a new buffered image
write to that buffered image some crazy manipulation of the original texture (looks like you're reversing it?)
write the original image to the current sheet.
So what if you change this
sheetGraphics.drawImage(texture, region.x, region.y, null);
to this
sheetGraphics.drawImage(bufferedTexture, region.x, region.y, null);
Otherwise you're spending time reversing the image and putting into buffered texture and then never doing anything with that buffer... so chances are you intended to use that buffered texture somewhere.

Related

How do I fade a BufferedImage when manipulating it's pixel data?

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.

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

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;
}

How to move everything in Graphics2D by x,y coordinates.

I need to move already made BufferedImage by x,y coordinates and then draw another things on it with Graphics2D object. I tried to use this code to do that :
Graphics2D g = img.createGraphics();
g.translate(x, y);
but it doesn't work. Is there any way to move everything in Graphics2D object and then draw on it or I have to use this code to do that:
BufferedImage temp = new BufferedImage(img.getWidth(),img.getHeight(),BufferedImage.TYPE_INT_ARGB);
Graphics2D g = temp.createGraphics();
g.drawImage(img,x, y,null);
Using this code and then drawing only few elements rather than making whole image from scratch isn't big leap in performance so I think making new BufferedImage then drawing image on it isn't best way. I would rather just create Graphics2D object from already made image and then just move it by a few pixels diagonally, but I couldn't find the way to do that.
From the Graphics2d docs when you use translate:
All coordinates used in subsequent rendering operations on this graphics context are relative to this new origin.
You are defining a transformation that affects future operations. After calling translate if you were to call a method on graphics like draw3DRect(0, 0, ... snipped ... ) the starting coordinates 0,0 would be translated by x,y.
I think your best bet might be to use the methods of BufferedImage to move all the pixels before you get the graphics object. You have getRgb and setRgb
A naive example of moving the pixels:
BufferedImage buffImg = ImageIO.read(img);
int width = buffImg.getWidth();
int horizontalOffset = 10;
int verticalOffset = 10;
int widthToMove = width - horizontalOffset;
int heightToMove = buffImg.getHeight() - verticalOffset;
int[] rgb = buffImg.getRGB(0, 0, widthToMove, heightToMove, null, 0, widthToMove);
buffImg.setRGB(horizontalOffset,verticalOffset,widthToMove, heightToMove,rgb, 0, widthToMove);
This still leaves you with some work to do because there is a strip at the top and to the left that you need to fill with background colour.
If it's going to be used on big images you might want to use a buffer int[] and pass it to getRGB in a loop, getting and setting in chunks.

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.

Why does VolatileImage have no set/getPixel() method

I am a relative newbie in game programming. I know how to draw pixels to a BufferedImage using setPixel(). It is horribly slow on larger formats so I moved on and found VolatileImage (took me a week or so). It is fairly easy to draw lines, strings, rects, etc but I can't draw individual pixels. I already tried using drawLine(x,y,x,y) but I get 3-4 FPS on an 800x600 image.
The fact that java didn't include setPixel() or setRGB() in the VolatileImage makes me pretty angry and confused.
I have 4 questions:
Is there a way to draw individual pixels on a VolatileImage? (on 1440x900 formats with FPS > 40)
Can I draw pixels in a BufferedImage with a faster method? (same 1440x900, FPS > 40)
Is there any other way to draw pixels fast enough for 3D games?
Can I make my BufferedImage hardware accelerated( tried using setAccelerationPriority(1F) but it doesn't work)
Please if you have any idea tell me. I can't continue making my game wihout this information. I already made 3D rendering algorithms but i need to be able to draw fast pixels. I have got a good feeling about this game.
Here's the code if it can help you help me:
public static void drawImageRendered (int x, int y, int w, int h) { // This is just a method to test the performance
int a[] = new int[3]; // The array containing R, G and B value for each pixel
bImg = Launcher.contObj.getGraphicsConfiguration().createCompatibleImage(800, 600); // Creates a compatible image for the JPanel object i am working with (800x600)
bImg.setAccelerationPriority(1F); // I am trying to get this image accelerated
WritableRaster wr = bImg.getRaster(); // The image's writable raster
for (int i = 0; i < bImg.getWidth(); i++) {
for (int j = 0; j < bImg.getHeight(); j++) {
a[0] = i % 256;
a[2] = j % 256;
a[1] = (j * i) % 256;
wr.setPixel(i, j, a); // Sets the pixels (You get a nice pattern)
}
}
g.drawImage(bImg, x, y, w, h, null);
}
I would much prefer not using OpenGL or any other external libraries, just plain Java.
Well you're basically drawing one pixel after the other using the CPU. There's no way that this can be accelerated, thus such a method does simply not make any sense for a VolatileImage. The low FPS you get suggest that this even causes a significant overhead, as each pixel drawing operation is sent to the graphics card (with information such as location & colour), which takes longer than to modify 3 or 4 bytes of RAM.
I suggest to either stop drawing each pixel separately or to figure out a way to make your drawing algorithm run directly on the graphics card (which most likely requires another language than Java).
It's been over 4 years since this post got an answer. I was looking for an answer to this question as well and stumbled on this post. After some more searching, I got it to work. Below I'll post the source to rendering pixels with a VolatileImage.
It seems Java hides our ability to plot pixels directly to a VolatileImage, but we can draw buffered images to it. For good reason. Using the software to plot a pixel doesn't really help with acceleration(in Java it seems). If you can plot pixels to a BufferedImage, and then render it on a VolatileImage, you may get a speed bonus since it's hardware accelerated from that point.
The source down below is a self-contained example. You can copy-pasta practically all of it to your project and run it.
https://github.com/Miekpeeps/JavaSnippets-repo/blob/master/src/graphics_rendering/pixels_03/PlottingVolatile.java
In the constructor I save the Graphics environment of the app/game.
private GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
private GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
Then, when I call a method to enable hardware we create a buffer. I set the transparency to Opaque. In my little engine, I deal with transparency/alpha blending on another thread in the pipeline.
public void setHardwareAcceleration(Boolean hw)
{
useHW = hw;
if (hw)
{
vbuffer = gc.createCompatibleVolatileImage(width, height, Transparency.OPAQUE);
System.setProperty("sun.java2d.opengl", hw.toString()); // may not be needed.
}
}
For each frame I update, I get the Graphics from the VolatileImage and render my buffer there. Nothing gets rendered if I dont flush().
#Override
public void paintComponent(Graphics g)
{
if(useHW)
{
g = vbuffer.getGraphics();
g.drawImage(buffer, 0, 0, null);
vbuffer.flush();
}
else
{
g.drawImage(buffer, 0, 0, null);
buffer.flush();
}
}
There is still a little bit of overhead when calling to plot a pixel on the BufferedImage writable raster. But when we update the screen, we get a speed boost when using the Volatile image instead of using the Buffered image.
Hope this helps some folks out. Cheers.

Categories