Concepts / Loading Image Resources in Java

Loading Image Resources in Java

A Java cursor is a java.awt.Cursor object made of an associated image and a hot spot, which is a Point naming the pixel that corresponds to the exact pointer position.

  • Programming

Why a Cursor Needs to Be an Object

The small picture that follows your mouse across the screen is called a cursor. Most of the time you never think about it, but a Java program that wants to control that picture has to be able to name it, choose it, and replace it. Java handles this by representing the cursor as an ordinary object rather than as some hidden system setting. Once the cursor is an object, a program can hold a reference to it, pick a different one, and attach it to a part of the interface.

This article covers two linked skills. The first is understanding what a cursor object is made of, and which pixel of its picture counts as the position of the mouse. The second is getting an image from the application's own files so that it can be used as a custom cursor. Both skills depend on a chain of small steps, and the article traces that chain one step at a time.

A Cursor Is an Image Plus a Hot Spot

Definition

In Java, a cursor is an object of type java.awt.Cursor. Every Cursor carries two pieces of information. The first is an associated image, which is the picture drawn on the screen. The second is a hot spot, which is a Point. A Point in this context names one pixel inside the image, and that pixel corresponds to the exact screen location where the mouse is pointing.

The hot spot is the reason a cursor image can be any shape at all. A picture of a hand, a magnifying glass, or a crosshair can be used as a cursor as long as the program records which pixel inside it is the active point. The image tells the screen what to draw, while the hot spot tells the system which pixel of that drawing counts as the position of the mouse. Two cursors can share the same picture and still behave differently if their hot spots differ.

Where the Click Lands

Before looking at the parts in detail, consider the question that matters most when a cursor is drawn. When the picture on the screen has a particular shape, which point inside that picture is the one the mouse is actually pointing at? The picture is what you see, but the position it reports is a single spot. The answer depends on the cursor shape, and the diagram below shows the two shapes the source uses as examples.

Arrow cursortypical arrow pictureArrow tiphot spot pixelCrosshair cursorcrosshair pictureCrosshair centerhot spot pixelactive pixelactive pixel
Which pixel of the cursor image is the click position, and how does it differ across common cursor shapes?

For a typical arrow cursor, the active pixel is the tip of the arrow. For a crosshair cursor, it is the center of the crosshairs. Notice that the hot spot is not always the top-left corner of the image. The position of the active pixel depends on the picture, so a program that creates its own cursor must record the hot spot explicitly rather than assuming it sits at a fixed corner.

What do you think happens?

For a crosshair cursor, which pixel of the image is the one that corresponds to the mouse position?

  • The top-left corner of the image

  • The tip of the crosshair arms

  • The center of the crosshairs

Reveal answer
Answer:

The center of the crosshairs

The hot spot is the pixel that corresponds to the exact pointer position. For a crosshair cursor, the source identifies that pixel as the center of the crosshairs, not a corner.

Requesting a Standard Cursor

You do not have to build every cursor yourself. The Cursor class defines several standard cursors, and each one is identified by a constant such as Cursor.CROSSHAIR_CURSOR or Cursor.DEFAULT_CURSOR. To obtain one, you call the static method Cursor.getPredefinedCursor(code), where code is one of those constants. The method turns the constant, which is only a selection, into a real Cursor object.

Code walkthrough
Cursor waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
panel.setCursor(waitCursor);
  1. 1
    Line 1 reads from the inside out: the constant selects a standard cursor, and getPredefinedCursor returns it as a Cursor stored in waitCursor.
  2. 2
    Line 2 attaches that object to the panel, so the panel displays it whenever the mouse is over the panel.
  • Cursor.WAIT_CURSOR is the constant that selects which standard cursor is wanted.
  • getPredefinedCursor turns that selection into a Cursor object.
  • setCursor hands the object to the panel, which then uses it as the pointer while the mouse is over it.

The source uses a wait cursor as its example, which is the kind of cursor a program might show while it is busy. The same pattern works with any of the standard constants: choose the constant that matches the situation, request the cursor, and attach the result.

Building a Custom Cursor From an Image

When none of the standard cursors fits your program, you can create a custom cursor from an Image. The image might come from an image resource, or it could be a BufferedImage that your program creates itself. The cursor is produced by the static method createCustomCursor() in the Toolkit class, which you reach through Toolkit.getDefaultToolkit(). The call takes the image, the hot spot as a Point, and a name for the cursor.

java
Notes on the code
  • The image supplies the picture that is drawn on screen.
  • The Point names the pixel of that picture that corresponds to the pointer position.
  • The name identifies the cursor; the arguments are passed in the order image, hot spot, name.

Finding an Image Resource by Name

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. The first stage of the process is a lookup. A ClassLoader is the component used to load image resources, and it is the thing that finds a resource from the name you supply.

Code walkthrough
ClassLoader cl = getClass().getClassLoader();
URL imageURL = cl.getResource("resources/images/cards.png");
  1. 1
    Line 1 obtains the ClassLoader and stores it in cl, so every lookup in this example goes through cl.
  2. 2
    Line 2 passes a name to getResource. The source shows two forms: a bare file name such as cards.png, or a name that includes folders such as resources/images/cards.png. getResource uses the name to find the resource and returns its location as a URL, stored in imageURL.
  • getClass().getClassLoader() asks the current object's class for the ClassLoader that loaded it.
  • getResource takes the resource's name as a string and returns its location as a URL.
  • The variable imageURL holds a location, not the picture itself.

Notice what this stage produces. The output is a URL, a location that points to the image file. No picture has been loaded yet. The name you gave to getResource is the only input this stage receives, so the name must match where the file actually is.

Turning a URL Into an Image

The second stage belongs to Toolkit. The call Toolkit.getDefaultToolkit() obtains the default Toolkit, and its createImage method builds an image from the URL it receives. The result is assigned to a variable. This is the point where a location becomes an image object the program can work with.

Code walkthrough
Image cardImages = Toolkit.getDefaultToolkit().createImage(imageURL);
  1. 1
    The expression on the right is evaluated first: getDefaultToolkit() returns the Toolkit, and createImage(imageURL) builds an image from the URL.
  2. 2
    The result is stored in cardImages, and the program can now use it.
  • getDefaultToolkit() obtains the default Toolkit.
  • createImage receives the URL produced by the lookup step.
  • The finished Image is assigned to cardImages.
Programcalls the lookup and theimage stepClassLoaderfinds the resource by nameToolkitcreates the image1. getResource(name)2. URL imageURL3. createImage(imageURL)4. Image cardImages
In what order do the ClassLoader, the resource URL, and Toolkit hand off to produce the Image?

Read the diagram as a relay. The name goes in, a URL comes out of the lookup, and that URL goes into the image step. The URL is the only value passed between the two stages, so it is the handoff point. Each stage is correct on its own terms, but the image step can only be as correct as the URL it receives.

Tracing a Load to Find the Divergence

Worked example

Tracing the two-step card image load

A program loads cards.png through a ClassLoader and then creates an Image with Toolkit. The picture does not appear. Trace the sequence to find the step where the value stops being what was expected.

  1. 1
    Step 1: obtain the ClassLoader

    getClass().getClassLoader() returns the ClassLoader that loaded the current class, stored in cl. This step is generally reliable and produces the object the lookup needs.

  2. 2
    Step 2: look up the resource

    cl.getResource receives a name and returns a URL. The expected value is a URL pointing to the file. If the name does not match where the file actually is, the URL does not point to the picture. This is the first place the value can diverge from what was expected, even though no error appears on this line.

  3. 3
    Step 3: create the image

    Toolkit.getDefaultToolkit().createImage receives imageURL and builds an Image. The createImage line itself is correct, but it can only work with the URL it was given. If the URL from step 2 is wrong, step 3 receives the wrong input.

  4. 4
    Identify the divergence

    Trace backward from the symptom. The image is wrong at step 3, but the cause is the URL produced at step 2. The first line where the value stopped being what was expected is the getResource call, because its name did not match the file's location.

Result

The divergence is at the URL handoff. The image line is not the bug; the name given to getResource is the first point where the result differs from what was expected.

Mistakes in Resource Names and Handoffs

  • Common mistake

    Assuming the hot spot is always the top-left corner of the cursor image

    What it looks like

    Creating a crosshair cursor with new Point(0, 0) and expecting the click to land at the crosshair center.

    Why it fails

    The hot spot is the pixel that corresponds to the pointer position, and it differs by shape. For a crosshair the active pixel is the center of the crosshairs.

    Do this instead

    Choose the Point that names the active pixel of your specific picture.

  • Common mistake

    Passing a name to getResource that does not match the file's location

    What it looks like

    Calling getResource("cards.png") when the file is stored in resources/images/cards.png.

    Why it fails

    getResource uses the name to find the resource. A mismatch means the URL does not point to the intended file, and the image step then receives the wrong input.

    Do this instead

    Use a name that matches the file's actual location, such as resources/images/cards.png.

  • Common mistake

    Treating the URL as if it were the picture

    What it looks like

    Expecting imageURL to be drawable directly, without calling createImage.

    Why it fails

    The URL is only a location. The Toolkit step is what builds the Image object.

    Do this instead

    Pass the URL to Toolkit.getDefaultToolkit().createImage to obtain the Image.

  • Common mistake

    Passing the wrong variable to createImage, so the image step receives an unrelated value even though its own line is correct

  • Common mistake

    Assuming one custom cursor size works on every platform

    Why it fails

    The source states that usable sizes depend on the platform.

    Do this instead

    Keep the custom cursor image small and test it on the platforms you support.

Practice Tracing a Cursor Load

MEDIUM

A program uses Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR) and attaches the result to a panel. A second program creates a custom cursor from a 16 by 16 image with the hot spot at new Point(15, 15), and the arrow tip of the picture is at the top-left corner. Identify which program's hot spot is most likely to mismatch the intended click position, and explain which pixel each program should use. Then describe the two stages of a resource-based image load and state which value is passed between them.

Hints
  • Think about where the hot spot should sit for an arrow tip compared with a crosshair center.
  • The source says the hot spot names a pixel inside the image, so check whether the Point matches the intended active pixel.
  • The URL is the only value handed from the lookup stage to the image stage.

Key Takeaways

  • A Java cursor is a java.awt.Cursor object made of an image, which is drawn on screen, and a hot spot, a Point that names the pixel corresponding to the exact pointer position.
  • The hot spot differs by cursor shape: the tip of the arrow for a typical arrow cursor, and the center of the crosshairs for a crosshair cursor.
  • Standard cursors come from Cursor.getPredefinedCursor with a constant such as Cursor.WAIT_CURSOR, while custom cursors come from Toolkit.getDefaultToolkit().createCustomCursor(image, hotSpot, name), whose usable sizes depend on the platform.
  • Loading an image resource is a two-step chain: ClassLoader.getResource turns a name into a URL, and Toolkit.createImage turns that URL into an Image, so the URL is the handoff point to check when a result is wrong.