java.awt.Image getWidth and getHeight methods not working well? - java

I'm trying to display images which happen to be instances of the Image class (http://docs.oracle.com/javase/7/docs/api/java/awt/Image.html).
I need to have it a BufferedImage and I found a piece of code that works well for that purpose (using Graphics).
However to create the BufferedImage I need to know the width and height of the image. The Image class does have getWidth and getHeight methods but they don't seem to work properly all the time. Using debug I found out that sometimes, the width and height were set to -1. I also found out that actually calling getWidth and getHeight methods "updates" the width and height value, so the height and width have correct values after then. So I added a fake "image.getWidth(null)" before calling getWidth and getHeight "for real" and got the right values.
That works fine when I do it in debug mode and step by step, BUT it doesn't work well anymore when I run it normally. I added a "Thread.sleep(5);" after the "fake" getWidth call and it made it work.
This behaviour is quite weird and maybe someone would have an explanation and/or a way to make it work without that sleep trick ?
Edit:
this is how the image is loaded :
URL url = "path/to/img";
Image img = Toolkit.getDefaultToolkit().getImage(url);
I have let's say read-only access to this code, this is why I'd like to start from there to get my BufferedImage.
What I added to this is
image.getWidth(null); // "Fake" call to force the image to update his width&height
try {
Thread.sleep(5);
} catch...
if(image.getWidth(null) != -1 && image.getHeight(null) != -1) {
BufferedImage bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics bg = bi.getGraphics();
bg.drawImage(image, 0, 0 null);
bg.dispose();
}
If I remove the sleep it seems that getWidth "doesn't have time" to complete and I still have width/height = -1 for some images.
Some methods used to compute the dimensions is probably asynchrone.
I understand that the ImageIO.read() is a simpler and more effective method but I'd still like to find a workaround to get this work :)
If it's not possible I'll find another way it's no big deal actually.

Maybe the image isn't fully loaded yet. Try adding a MediaTracker, add the image to the tracker and wait for the image to be fully loaded (using waitForAll or waitForID).

A crude but effective way to wait for an image to fully load is to construct an ImageIcon with it. The constructor doesn't return until the image is fully loaded. I.e.,
new javax.swing.ImageIcon(image); // force image to load

Related

Nullpointer exception when drawing ImageIcons

I am trying to create graphics for a very simple 2D game. I have arrayLists of imageIcons that will get drawn in succession when the player moves. One for up, one for down, etc. As of right now, I do not have the finished sprites so I replaced each separate one with a temporary image.
//this is inside the constructor
playerUp.add(new ImageIcon("Player1.png"); //will be a different image, but this image still exists
playerUp.add(new ImageIcon("Player1.png"); //will be a different image, but this image still exists
playerUp.add(new ImageIcon("PLayer1.png"); //will be a different image, but this image still exists
Then I draw them to an offscreen image that I later draw to the screen to prevent flickering.
//offg is the graphics object of the offscreen image and g is for the panel
offg.clearRect(0, 0, panel.getWidth(), panel.getHeight());
playerUp.get(0).paintIcon(panel, offg, x, y);
g.drawImage(offscreen, 0, 0, panel); //this is where the error occurs!
Anyways There is some kind of error in the code I tried using:
playerUp.add(new ImageIcon(getClass().getResource("Player1.png")));
but I do not know if I needed to, or how to use it properly (when researching I saw it a lot), however, That did not fix the problem. Also, I made sure that the image is in the same folder as the .class and .java files.
Also, I created a separate class that just drew an ImageIcon straight to the panel, and it used a completely different image.
ImageIcon ii = new ImageIcon("downlaodedimage.png"); //downlaodedimage.png is the different image
ii.paintIcon(panel, g, 0, 0);
And that gives a null pointer exception. I used:
System.out.println(ii);
And it printed out downloadedimage.png, so I do not know what the issue is?
the issue is that something on that line hasn't been initialized yet. Try debugging it to find which one is null, and making sure it is initialized prior to the call. Alternatively, if this occurs during a game loop, and the variable is initialized later in the loop, you can surround it with a try - catch to wait until the variable has been initialized.

Alternatives to Canvas.snapshot() on JavaFX

I'm developing a little graphic engine using Canvas in JavaFX. In some point I had to render an off screen image, and then print it on my primary canvas using its GraphicContext.
I'm using this code right now:
private Canvas offScreenCanvas;
private GraphicsContext offScreenGraphic;
private SnapshotParameters parameters;
private WritableImage offScreenImage;
[...]
offScreenCanvas = new Canvas(WIDTH, HEIGHT);
offScreenGraphic = offScreenCanvas.getGraphicsContext2D();
parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
[...]
offScreenImage = offScreenCanvas.snapshot(parameters, offScreenImage);
graphic.setGlobalBlendMode(BlendMode.HARD_LIGHT);
graphic.drawImage(offScreenImage, 0, 0);
graphic.setGlobalBlendMode(BlendMode.SRC_OVER);
My problem is the method snaphot() takes too much time, ~14ms, in each execution. I need to update the canvas at least at 60fps, so this consumes practically all the time I have to draw.
Is there another way to get an Image or WritableImage from a canvas? Maybe another different process?
This is another method to obtain a visual equivalent result, without reduce performance.
I have used java.awt clases, instead of JavaFX clases. The creation of a java.awt.image.BufferedImage offers the possibility to get a java.awt.Graphics2D where you can draw using other methods. The main problem here is that draw big images consumes a lot of time using this libraries, but you can create a scaled image. In my case, I have created a quarter-size BufferedImage, and I have drawn all the objects using that scale factor.
At the end of the draw process, just convert the BufferedImage, to a javafx.scene.image.Image, using:
SwingFXUtils.WritableImage toFXImage(BufferedImage bimg, WritableImage wimg);
Then print it on the main canvas using:
graphic.drawImage(Image image, 0, 0, WIDTH, HEIGHT);
To fill all the canvas with the image.
Finally, the scale factor is configurable, so if you need a detailed image, just use a higher value. For me, a 25-percent-size image is enough because I am drawing gradients. Now, it takes 1-3ms to draw the image, this is much better than before.
Hope it helps someone.

Setting the size of a customized cursor in java

I have customized a cursor on a label component called ageLabel using the following code
ageLabel.setCursor(Toolkit.getDefaultToolkit().createCustomCursor(new javax.swing.ImageIcon(getClass().getResource("/images/image1.jpg")).getImage(),new Point(5,5),"custom cursor"));
It works fine anyway but what I want to do is increase the size of the customized cursor. I have tried changing the point to (10,10) but the cursor size wouldn't change. I also tried changing the dimensions of the image that I used ,it still wouldn't change. I have searched through the internet but to no avail. Is It possible to resize the cursor in anyway? If it is how do I do that?
Thanks in advance for all helps.
Java's Image class has a built in function for scaling.
Documentation: https://docs.oracle.com/javase/7/docs/api/java/awt/Image.html#getScaledInstance(int,%20int,%20int)
My approach to the problem:
Split the line up into many to make it look a bit nicer (not necessary).
Add an extra method call to scale the image up.
Potential Solution:
URL imageResource = getClass().getResource("/images/image1.jpg");
ImageIcon imageIcon = new ImageIcon(imageResource);
Image image = imageIcon.getImage();
// This is the important line that scales the image up!
Image scaledImage = image.getScaledInstance(newWidth, newHeight, Image.SCALE_DEFAULT);
Toolkit toolkit = Toolkit.getDefaultToolkit();
Cursor cursor = toolkit.createCustomCursor(scaledImage, new Point(5, 5), "Custom Cursor");
ageLabel.setCursor(cursor);
Afterthoughts:
Remember to replace newWidth and newHeight with the values you want.
After you get it working, I'd even go further and extract some of this code into methods.
You might need to tweak your imports a bit if it isn't compiling.
I haven't tried running this code so I don't know if it works.

LibGDX Stage - How to Stretch ImageButton?

Recently we got a issue regarding stretching an ImageButton in LibGDX's Stage API.
We have a graphics-heavy game that can pick a textureatlas based on your screen (either HDPI, MDPI or LDPI etc)
We build our game with 480x854 as virtual resolution and scale nicely using viewports on devices that differ from that.
We explicitly set each sprite size to make sure it scales well regardless what size the source texture is.
However, sometimes it could happen that a texture is slightly larger or smaller than the actual size it is displayed (for instance, a resolution of 320x480 will pick ldpi but the image is slightly smaller than the sprite it's displayed on)
For some reason the ImageButton can't work very well with that.
Here is an example of what I tried (in an imagebutton subclass):
#Override
public void setSize(float w, float h) {
super.setSize(w, h);
if (this.getImage() != null) {
//this.getImageCell().expand().fill();
this.getImage().setSize(w, h);
this.getImage().setScaling(Scaling.stretch);
//this.getImage().invalidate();
this.invalidate();
}
}
What I'm trying to achieve is that when I call button.setSize(300, 320) it will actually stretch the image to become 300x320. But instead, it either shows the button using the original size of the source image (without workaround above) or shows it completely washed / stretched out (also out of proportions) using the workaround above.
I hope someone might have a good fix for this, I'm banging my head on this one for 2 days now :(
Thanks a lot! Have a nice weekend!
Update
I managed to display the image button correctly now but outside a panel I used to have it in. Inside the panel it goes showing up weird again. I'll keep this up-to-date and when I find it myself I'll post a valid answer.

Make a dynamically re-sized, BufferedImage, scroll, that renders actively

I have stumbled upon a bottle neck that I would love to fix.
I need to make a BufferedImage grow with time, but the Buffered image should support scrolling as my application requires that the Bufferedimage grow a significant size over time. However the draw calls are not done by the event dispatch thread.
I have a while loop that performs the render calls. What I attempted was to create an instance of a Canvas and add it to a JScrollPane however when I take this approach, the JScrollPane performs its own draw calls and I am not sure how the JScrollPane will detect that the canvas has resized at runtime.
The other issue is that since within the canvas I use a BufferedImage to draw onto, I cannot get it to resize with a temporary BufferedImage.
Here is how I attempted to create a new, larger BufferedImage
if(needsToBeResized)
{
BufferedImage temporaryBufferedImag =
new BufferedImage(originalImage + extraSpace, height, originalImage.getType());
Graphics g2d = temporaryBufferedImag.createGraphics();
g2d.drawImage(originalImage, 0,0,null);
//I presume that this should copy the graphics object with the original img
originalImage = temporaryBufferedImag;
g2d.dispose();
}
If it helps I am developing an oscilloscope type application that needs to be able to keep plotting values real-time but I also need to be able to show the history of values.
Render each interval in its own "mini" BufferedImage. So, for a gross example, say you render every second, and you want to show 20 seconds of history.
So, you render your current second in to a "1 second sized" image. Then you take your cache of the images from the previous 19 seconds, and you stamp them all together, one after the other in to your new total image, and display that.
Next second, you drop off the oldest image, create a new one, stick it on the end, rinse and repeat.
If your overall frame size changes, when you get to render all of the pieces all over again…c'est la vie.
Obviously you will be sampling more than once a second, but you get the gist of it.
All this assumes this is cheaper than simply re-rendering the entire thing from scratch each time.

Categories