Displaying Images in an Applet

Applets can load an image file and paint it onto their drawing surface. Loading is handled by a method inherited from Applet - getImage() - and drawing is handled by a method on the Graphics object you already know - drawImage(). The tricky part isn't the API, it's understanding that loading happens in the background.

Note: This page's code is a preview, not something you can run right on this website - this server runs PHP, not a Java runtime. See Environment Setup for how to actually compile and run it.

Loading an Image: getImage()

Call getImage() inside init(), passing a base URL and a filename. It returns immediately with an Image object, even though the actual file may not have finished downloading yet:

Image img = getImage(getDocumentBase(), "canvas-bg.png");

getImage() is asynchronous: it kicks off loading on a background thread and returns straight away. You don't get an error if the file is missing yet, and you don't get a fully decoded image right away either - the Image object simply fills in as data arrives.

getDocumentBase() vs getCodeBase()

getImage() needs a base URL to resolve the filename against. Applet gives you two choices, and picking the right one matters once your HTML page and your compiled classes live in different places:

MethodResolves relative toUse it when...
getDocumentBase()The URL of the HTML page embedding the appletThe resource (image/audio) sits next to the HTML file
getCodeBase()The URL of the applet's own .class/JAR fileThe resource is bundled alongside the compiled applet code

For PaintApplet, we'll assume canvas-bg.png sits in the same folder as the HTML page that embeds it, so getDocumentBase() is the right choice.

Drawing an Image: drawImage()

Once you have an Image, draw it inside paint() with Graphics.drawImage(). The stretched-to-fit overload takes a target width and height:

if (backgroundImage != null) {
    g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
}

The last argument, this, is an ImageObserver. Applet (through Component) already implements that interface, so passing this is enough - AWT uses it to automatically trigger another repaint() once more image data has arrived, which is exactly why you don't have to manually wait for the image in simple cases like this one. The null check matters too: right after init() runs, backgroundImage may not have any pixels loaded yet, but it's never actually null once getImage() has returned an object - the check here is just good defensive habit for the field before assignment.

When you actually need to wait: if your applet must not proceed until an image is fully loaded (for example, before measuring its exact pixel dimensions), use the classic java.awt.MediaTracker class. You register the image with a tracker and call a blocking method like waitForID(), which pauses until loading completes or fails. Most simple drawing code, like the example below, doesn't need this - drawImage()'s own observer callback is enough.

Extending PaintApplet: A Background Image

Add one field, load it in init(), and draw it first in paint() - before the title, the border, and the shapes - so it acts as a backdrop for everything else already on the canvas. Only the changed parts of the class are shown; everything else (mouse dragging, keyboard color switching) stays exactly as it was at the end of Applet Parameters.

// New field
private Image backgroundImage;

public void init() {
    // ...existing title/bgcolor/parameter handling stays here...

    // New: kick off loading the background image
    backgroundImage = getImage(getDocumentBase(), "canvas-bg.png");

    // ...existing addMouseMotionListener / addKeyListener stay here...
}

public void paint(Graphics g) {
    // New: draw the background image first, stretched to fill the canvas
    if (backgroundImage != null) {
        g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
    }

    // ...everything below is unchanged: title text, border, drawShapes(), drawn points...
    g.setFont(titleFont);
    g.setColor(Color.BLACK);
    g.drawString(title, 20, 30);
    // ... rest of paint() as before ...
}

Because the image is drawn before anything else in paint(), the title text, the border, the fixed shapes, and the freehand strokes all render on top of it, exactly as you'd expect for a background layer.

Next steps: Images cover the visual side of loading external resources. Head to Playing Audio in an Applet to give PaintApplet a sound effect using the same background-loading idea, applied to AudioClip instead of Image.