Concepts / Loading Image Resources from Files

Loading Image Resources from Files

A Java Cursor combines an associated image with a hot spot, a Point that names the pixel matching the exact pointing position.

  • Programming

A Small Picture with a Big Job

Every time you move a mouse, something on the screen moves with it. That small picture is the visible part of the pointer, and it does more work than it appears to. When you click, the computer has to decide which exact spot on the screen you meant. A picture alone cannot answer that question, because a picture has many pixels and only one of them is the real target.

This article follows that question through three connected ideas. The first is how a pointer is represented in Java. The second is how a small picture becomes an icon a program can display. The third is how a program can save pictures to files and load them back again. The last part of the article traces one concrete loading sequence step by step, because that is where most real bugs appear.

Where a Click Lands: The Hot Spot

In Java, the pointer is represented by an object of type java.awt.Cursor. Think of a Cursor as a bundle with two parts. The first part is an associated image, which is the small picture that appears on screen. The second part is a hot spot. The hot spot is a Point, and it names one particular pixel inside that image. That pixel is the one that lines up with the exact location on the screen where the mouse is pointing.

The hot spot exists because an image is just a rectangle of colored pixels. The computer needs a rule for which pixel counts as the pointing position, and the hot spot is that rule. The same picture can be anchored differently depending on what the cursor is meant to communicate, which is why the hot spot is stored as part of the cursor rather than assumed from the picture.

The position of the hot spot depends on the shape of the pointer. An arrow's hot spot is at its tip, while a crosshair's hot spot is at its center. The diagram below lists those two shapes and where their hot spots sit.

Arrow cursortip of the arrowCrosshair cursorcenter of the crosshair
Which pixel of the cursor image is the click position for each shape?

What do you think happens?

For a crosshair cursor, which pixel of the image is the hot spot?

  • The top-left corner

  • The center

  • The bottom-right corner

Reveal answer
Answer:

The center

The hot spot depends on the shape. For a crosshair it is the center, and for an arrow it is the tip. The hot spot is a Point that names this one pixel, so a click registers at that pixel's position on screen.

Icons, ImageIcon, and Image

A pointer is one kind of small picture. Programs also show small pictures inside their windows and buttons, and Java calls these icons. An icon is represented by an object of type Icon. Icon is an interface rather than a class, which means it describes what an icon must be able to do without providing the full implementation itself.

The class that does the concrete work is ImageIcon. It implements the Icon interface and is used to create icons from Image objects. If you already have an Image, and that image is fairly small, you can wrap it in an ImageIcon. The constructor call builds an ImageIcon whose picture is the image you passed in.

Iconinterface: what an iconmust doImageIconclass that implements IconImagethe picture passed inimplementswrapped by
How do the Icon interface, the ImageIcon class, and Image relate, and which one contains the other?

Moving Images Between Memory and Files

A program's picture exists while the program runs, but it disappears when the program stops unless it is written somewhere. The javax.imageio.ImageIO class handles that movement in both directions. It makes it easy to save an image from a program into a file, and to read an image from a file back into a program.

Consider a painting program. Such a program could let users save their drawings, then reopen an existing picture later and keep editing it. Saving and reading are the two halves of that cycle. The first turns in-memory pixels into a stored file, and the second turns a stored file into pixels the program can edit again.

Tracing a Resource from Name to Image

A program that displays pictures, such as a card game, needs image files that ship with the application. Rather than hard-coding where those files live on one particular computer, Java code can ask for them by name. Loading then happens in two steps. First a resource is located, and then the located resource is turned into an image the program can use. The three lines below show that chain. Before reading the explanation, notice which variable holds the resource address and which holds the finished image.

Code walkthrough
ClassLoader cl = getClass().getClassLoader();
URL imageURL = cl.getResource("cards.png");
Image cardImages = Toolkit.getDefaultToolkit().createImage(imageURL);
  1. 1
    getClass().getClassLoader() asks the current object's class for the ClassLoader that loaded it, and the result is stored in cl. Every resource lookup in this example goes through cl.
  2. 2
    cl.getResource takes the name of the resource. The name can be a bare file name such as cards.png or include folders such as resources/images/cards.png. It returns the resource's location as a URL, stored in imageURL. The variable holds a location, not the picture itself.
  3. 3
    Toolkit.getDefaultToolkit() obtains the default Toolkit, and createImage builds an image from the URL it receives. The result is assigned to cardImages. This is the point where a location becomes an image object the program can work with.
  • Line 1 obtains the ClassLoader that loaded the current object's class
  • Line 2 asks the ClassLoader to locate the resource by name and returns its location as a URL
  • Line 3 passes that URL to Toolkit, which builds the Image object
Resource namecards.pngClassLoadercl.getResourceToolkitcreateImageImagecardImagesname is passed inURL is handed forwardimage is created
What happens to a resource name, in what order, as it becomes an Image object, and at which handoff could the result differ from what I expected?
Worked example

Following the Card Image Through Both Steps

Starting from the name cards.png, determine what each variable holds after the three lines run.

  1. 1
    Get the loader

    cl holds the ClassLoader obtained from the current object's class. It is the component responsible for finding the resource.

  2. 2
    Locate the resource

    cl.getResource("cards.png") finds the resource and returns its location. imageURL now holds a URL, which is a location, not pixels.

  3. 3
    Create the image

    Toolkit takes imageURL and builds an Image. cardImages now holds the picture the program can draw or edit.

Result

cl holds a ClassLoader, imageURL holds a location, and cardImages holds the finished Image object.

Where the Two Steps Hand Off

The whole process is a chain. ClassLoader is responsible for finding the resource and producing a URL. Toolkit is responsible for taking that URL and creating an image. The URL is the only thing passed between the two stages, so it is the handoff point. If the handoff value is wrong, the image step receives the wrong input, even though its own line of code is correct.

This is why a tracing habit helps. Read the chain as a relay in which each arrow names what is handed forward: the name of the resource goes in, a URL comes out of the lookup, and that URL goes into the image step. When a bug appears, ask at which arrow the value stopped being what you expected. Tracing a failure means finding the first line where the value stops being what you expected.

Mistakes When Loading Images

Most failures in this process come from a mismatch between the name given to getResource and the actual location of the file, or from passing the wrong variable to createImage. The items below show where those mismatches usually appear.

  • Common mistake

    Treating the cursor as only its picture

    What it looks like

    Assuming a click lands at the center of a cursor image because the picture looks centered.

    Why it fails

    A Cursor is an image plus a hot spot Point, and the hot spot is a separate piece of data that names one pixel.

    Do this instead

    Read the hot spot to find the click position. For an arrow it is the tip, and for a crosshair it is the center.

  • Common mistake

    Confusing Icon, ImageIcon, and Image

    What it looks like

    Writing code as if an Image could be used directly wherever an icon is expected, without creating an ImageIcon.

    Why it fails

    Icon is an interface, ImageIcon is the class that implements it, and Image is the picture data passed into ImageIcon.

    Do this instead

    Wrap an Image in an ImageIcon when you need an icon object.

  • Common mistake

    Thinking getResource returns the picture

    What it looks like

    Passing the variable imageURL to a drawing call as if it were the finished picture.

    Why it fails

    getResource returns a location as a URL. The picture is only created when Toolkit's createImage receives that URL.

    Do this instead

    Pass the URL to Toolkit.getDefaultToolkit().createImage and use the Image it returns.

  • Common mistake

    Using a name that does not match the file's location

    What it looks like

    Asking for resources/images/cards.png when the file sits at the top level and is named cards.png.

    Why it fails

    The resource name must match where the file actually is, so the lookup step hands forward the wrong location.

    Do this instead

    Compare the name passed to getResource with the actual location of the file before debugging the image line.

  • Common mistake

    Assuming ImageIO and the resource lookup are the same step, when ImageIO is for saving and reading image files in both directions

Practice: Find the Broken Handoff

MEDIUM

A card game ships its file cards.png at the top level of the classpath, but the code calls cl.getResource with resources/images/cards.png. Trace the chain from the resource name to cardImages. At which variable does the value first stop being what you expect, and which line would you change first?

Hints
  • Start with the name string passed to getResource and ask where that name points.
  • The URL in imageURL is the handoff. Ask whether it describes the file you actually have.
  • Do not change the createImage line first, because it is correct and receives whatever imageURL holds.

Key Takeaways

  • A Java Cursor combines an associated image with a hot spot, a Point naming the pixel that matches the exact pointing position, and the hot spot depends on the shape, such as the tip of an arrow or the center of a crosshair.
  • Icon is an interface, ImageIcon is the class that implements it to create icons from Image objects, and Image holds the picture itself.
  • ImageIO moves images in both directions, saving images from a program into files and reading them back in.
  • Loading a resource is a two-step chain: ClassLoader's getResource finds the resource by name and returns a URL, and Toolkit's createImage turns that URL into an Image.
  • The URL is the handoff point, so when a result is wrong, trace the chain to find the first value that stopped being what you expected.