4

I've been spending the last hours trying to solve the stack trace below. With major research on here SO and also through Google, I understand the exception can mean several things:

  • the program can't find the requested images with the provided path;

  • the images are being rendered after the width and the height are generated, reason why it equals 0...

Am I missing something? I can't figure out how to solve this...

Stack

Exception in thread "main" java.lang.IllegalArgumentException: Width (-1) and height (-1) cannot be <= 0 at java.awt.image.DirectColorModel.createCompatibleWritableRaster(DirectColorModel.java:1016) at java.awt.image.BufferedImage.(BufferedImage.java:331) at tp6.Interface.toBufferedImage(Interface.java:157) at tp6.Interface.(Interface.java:36) at tp6.Interface.main(Interface.java:171)

tp6.Interface.toBufferedImage(Interface.java:157):

public BufferedImage toBufferedImage(Image image) {

        if( image instanceof BufferedImage ) {

                return( (BufferedImage)image );
        } else {

                image = new ImageIcon(image).getImage();

                BufferedImage bufferedImage = new BufferedImage(
                                                      image.getWidth(null),
                                                      image.getHeight(null),
                                                      BufferedImage.TYPE_INT_RGB );
                Graphics g = bufferedImage.createGraphics();
                g.drawImage(image,0,0,null);
                g.dispose();
                return( bufferedImage );
        }
}

tp6.Interface.(Interface.java:36)

//IMAGE JPANEL
Image map=new ImageIcon("images/main.gif").getImage();
    Image digi=new ImageIcon("images/digits.gif").getImage();
    BufferedImage mapmodifiable= toBufferedImage(map);
    BufferedImage digits= toBufferedImage(digi);

tp6.Interface.main(Interface.java:171)

public static void main(String[] args)
    {
        Window windowintro = new Window( 440, 400, 1);
        //INTERFACE GRAPHIC
        Interface graphic=new Interface();

        graphic.addWindowListener(new WindowAdapter() {
                        @Override
            public void windowClosing(WindowEvent evt) {
                System.exit(0);
            }
        });
    }
davmac
  • 20,150
  • 1
  • 40
  • 68
Mark Doe
  • 125
  • 2
  • 2
  • 9
  • 1
    `image.getWidth(null)` returns -1, but -1 is not a valid argument to the BufferedImage constructor. – President James K. Polk Jan 19 '16 at 02:53
  • @JamesKPolk: ok what are you suggesting? `ImageObserver` can't be converted into `int`... My brain is mash potatoes at this point. – Mark Doe Jan 19 '16 at 02:55
  • [read the javadoc on `getWidth` and `getHeight`](https://docs.oracle.com/javase/7/docs/api/java/awt/Image.html#getHeight(java.awt.image.ImageObserver)) - Determines the height of the image. If the height is not yet known, this method returns -1 and the specified ImageObserver object is notified later – Krease Jan 19 '16 at 03:00
  • I think you should use the [tag:java]-tag. Syntax highlighting would work then, too. – JojOatXGME Jan 19 '16 at 15:10
  • The exception message is clear enough. What do you not understand about it? – Raedwald Jan 19 '16 at 15:35
  • 2
    why do you read images through imageicons?? are these icons to be used anywhere? not. so use proper/normal ways of reading the image – gpasch Jan 19 '16 at 15:59

3 Answers3

3

The reason for the exception has already been explained, Image methods getWidth(null) and getHeight(null) both return -1 when the image dimensions are not (yet) known. This is implemented so, because the old Image API in Java is asynchronous and loads image resources off the current thread. As you write, it could also happen because the image is not found.

However, as you want to use your images as BufferedImages (presumably because you want to modify them at some stage), it's better and easier to just load them using the more recent synchronous ImageIO API. In most cases, the code will be clearer and easier to understand, and more importantly; you'll get error messages right away if the image can't be found/loaded.

So, instead of:

Image map = new ImageIcon("images/main.gif").getImage();
BufferedImage mapmodifiable = toBufferedImage(map);

You can simply do:

BufferedImage mapmodifiable = ImageIO.read(new File("images/main.gif"));

PS: It is possible to convert an Image to a BufferedImage like you do in your toBufferedImage method, and using ImageIcon.getImage(..) should ensure the image was preloaded (ImageIcon internally uses a MediaTracker for preloading). However, as I say above, the old Image API is not very good at error feedback, so most likely the problem is that your image isn't found.

Harald K
  • 26,314
  • 7
  • 65
  • 111
  • 1
    Very clean sir. After updating my code with yours, I received a `javax.imageio.IIOException: Can't read input file!` I found the cure here http://stackoverflow.com/questions/13130982/imageio-cant-read-input-file – Mark Doe Jan 20 '16 at 22:17
  • I had very similar error message "Width (0) and height (0) cannot be <= 0". I was already using ImageIO.read. My problem was caused by the width and height of my component being zero. The image was fine. – Gordon Tytler Jul 07 '21 at 10:57
1

I was having this problem too. I solved it by adding one line of code. In your in the first line of your toBufferedImage() method you can put

while(image.getWidth() == -1);

This line will just keep looping until there is a value in getWidth() besides -1.

  • Note that this is a horribly inefficient spin-lock, pegging the CPU while waiting for an asynchronous process to complete. **Put a sleep in the loop!** Waiting like this (with a sleep) would be reasonable if there was other work your program could do after initiating it and before using it. Otherwise, use the synchronous method as per the accepted answer. That's what it's for. – Jeff Learman May 30 '18 at 22:27
0

I found this question concerning your problem. Maybe you are using an asynchronous way to load the images. This mean the image may not be loaded yet, when you are calling getWidth(null) or getHeight(null). Since the image may not be loaded at this time, the width and height may not be known yet. This is the reason why -1 is returned.

Maybe you will get the right result if you add some delay with Thread.sleep(1000). I did not investigate it but it is definitively not a good solution. You may sleep not long enough on some systems. On other systems you may sleep unnecessary long. And since I do not know the specification very well, it may even be a valid implementation of Java if Thread.sleep blocks the process from reading the image (Does someone know it?).

I would see two ways which could be used to solve the problem:

First solution

One solution would be to load the image with blocking IO. Just like descripted in the answers of the linked question.

Second solution

Another solution would be to use an ImageObserver:

int width = getWidth(new ImageObserver() {
    @Override
    public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
        // TODO use the width when the image is loaded and the size is known
    }
});
if (width != -1) {
    // TODO use the width, it is already known
}
Community
  • 1
  • 1
JojOatXGME
  • 3,023
  • 2
  • 25
  • 41
  • 2
    Sleeping for an arbitrary amount of time is never the right solution. In this case there is a mechanism to be informed when the image dimensions become known (the `ImageObserver` passed to `getWidth()`/`getHeight()`) - this is what should be used. – davmac Jan 19 '16 at 15:28
  • @davmac Yes, I know that and I wrote it down. My question would be if there is a warranty that Java will try to read the image if you are sleeping long enough. If this is the case, you could sleep until the image was read using `wait()` and `notify()`. If I would know it, I would try to show such implementation. – JojOatXGME Jan 19 '16 at 15:42
  • Thanks for this. Are you suggesting to rewrite the whole `public BufferedImage toBufferedImage(Image image) {` method? Just want to make sure I get it. – Mark Doe Jan 19 '16 at 17:29
  • @MarkDoe It depends on your project. If you ensure that all images are ready befor you are using this method, it should be fine. Depending on your project, another possibility could be to implement an alternative handling if you cannot create that object yet and update it when this information is available. Maybe it is also possible that the method wait until the image is ready, but I am not sure with that. First, you could test if it work when you are using `Thread.sleep(1000)`, although you should not use it as a solution at the end. – JojOatXGME Jan 19 '16 at 23:24