Image Observer
The Graphics class provides the methods that copy an image onto the screen or an off-screen canvas, most commonly from inside paintComponent() of a JComponent.
Predicting Where an Image Lands
Drawing an image in a Java program is a request, not a self-contained action. The image does not paint itself onto the screen. Instead, the Graphics class provides the methods that copy an image onto a drawing surface, which is usually the screen but can also be an off-screen canvas. Most often this copying happens inside paintComponent() of a JComponent, where a Graphics object arrives as a parameter and you use it to draw.
What do you think happens?
Given the statement g.drawImage(img, x, y, this), what determines where the image lands and how big it appears? Write down your guess before reading on.
Reveal answer
The x and y values determine where the image is placed. The call does not return a new image and does not ask for a size, so the image is placed at its own size.
The source describes this call as a request to draw the image named img with its position at x and y, using the Graphics object g. The call does not return a new image and does not ask for a size. Size is controlled by a different form of the method, which the later sections cover.
This article starts from the simplest drawing statement and treats the more specialized variants as changes to that same call. Scaling and drawing only part of an image are both changes to the argument list. If you can read one drawImage() call carefully, the other forms become much easier to read.
Reading the Basic drawImage() Call
The basic form places an image at a position. Inside a paintComponent() method, the call looks like this. The method receives a Graphics object named g, and the call draws the image named img at the position x and y, with this passed as the final argument.
Each argument has one job. The first argument, g, is the Graphics object that does the drawing. The second, img, is the image being copied. The x and y values give the position where the image is placed. The last argument, this, is the image observer. The source states that the image observer is required when an image is drawn and is passed as the final parameter. In the source's examples, this refers to the JComponent that contains the drawing code.
- g: the Graphics object that performs the copying
- img: the image being drawn
- x and y: the position at which the image is placed
- this: the image observer, which is required as the final argument
Scaling an Image with Width and Height
The drawImage() method has a variation that scales the image as it is drawn, so that it fills a width and height you specify. The form that does this inserts two size values after the position values. The position is still set by x and y, but the drawn size is now set by the width and height you pass rather than the size the image already has.
g.drawImage(img, x, y, this);The image is drawn at x and y at the size it already has.
g.drawImage(img, x, y, 200, 150, this);The image is drawn at x and y and scaled to 200 by 150.
Selecting Part of an Image with Eight Coordinates
The most detailed form of drawImage() takes eight coordinates instead of a position and a size. The arguments are the destination coordinates first, then the source coordinates, then the image observer. The source pair describes two corners of a rectangle inside the original image, and only the pixels inside that rectangle are copied. The destination pair describes the rectangle on the drawing surface that receives those pixels.
The 100 by 80 pixel region starting at the top-left of img is drawn into the rectangle from (10, 20) to (210, 170) on the drawing surface, a 200 by 150 area.- 10, 20 is dx1, dy1: the top-left corner on the drawing surface
- 210, 170 is dx2, dy2: the bottom-right corner on the drawing surface
- 0, 0 is sx1, sy1: the top-left corner of the rectangle in the original image
- 100, 80 is sx2, sy2: the bottom-right corner of the rectangle in the original image
- this is the image observer
The two rectangles are specified separately. The destination rectangle does not have to match the source rectangle in size. In the example above, a 100 by 80 pixel region is placed into a 200 by 150 destination area, so the taken portion is stretched as it is placed. The portion you take and the place you put it are independent choices.
Reading an eight-coordinate call
Describe what this call copies and where it appears: g.drawImage(img, 0, 0, 50, 50, 100, 100, 150, 150, this);
- 1Identify the destination pair
The first four values after img are 0, 0, 50, 50. These are dx1, dy1, dx2, dy2, the rectangle on the drawing surface.
- 2Identify the source pair
The next four values are 100, 100, 150, 150. These are sx1, sy1, sx2, sy2, the rectangle inside the original image.
- 3Compare the sizes
The source rectangle is 50 by 50 pixels and the destination rectangle is also 50 by 50, so no stretching occurs.
- 4Locate the observer
The final argument this is the image observer, as in the basic form.
The 50 by 50 region of img whose top-left corner is at (100, 100) is drawn into the top-left 50 by 50 area of the drawing surface.
Mistakes That Change the Result
When a drawn image is not what you expected, the cause is usually one argument in one call. Read each mistake below as a question to ask about your own code: at which argument did my expectation and the call stop agreeing?
Common mistake Expecting a width and height to move the image
What it looks likeChanging 200, 150 to 300, 225 and expecting the image to shift right
Why it failsWidth and height control the drawn size. The position is still controlled only by x and y.
Do this insteadChange x and y to move the image, and change width and height to resize it.
Common mistake Passing the source rectangle where the destination belongs, or the reverse
What it looks likeWriting the source corners first in the eight-coordinate form
Why it failsThe destination pair comes first, followed by the source pair. Swapping them takes the wrong region and places it in the wrong spot.
Do this insteadCheck that the first four coordinates are dx1, dy1, dx2, dy2 and the next four are sx1, sy1, sx2, sy2.
Common mistake Assuming the source and destination rectangles must match
What it looks likeTaking a 100 by 80 region and expecting it to appear at 100 by 80 on screen
Why it failsThe destination rectangle is not required to match the source rectangle, so the taken region can be placed at a different size.
Do this insteadCompare the source size and the destination size separately to predict the final scale.
Common mistake Dropping the image observer argument
Why it failsThe source states that the image observer is required in the
drawImage()forms it shows, and it is the final parameter.Do this insteadKeep this as the last argument in every form of the call.
Tracing an Unexpected Result
When the drawn image looks wrong, work through the call argument by argument instead of rewriting it. Start with the argument that controls the outcome you are looking at. For a misplaced image, check x and y. For an image that is the wrong size, check width and height, or the destination corners in the eight-coordinate form. For an image that shows the wrong part of the picture, check the source corners.
A drawImage() call uses the eight-coordinate form with source corners 0, 0, 400, 300 and destination corners 0, 0, 200, 150. The image appears too small and shows the whole picture. Which argument group do you check first, and what result do you expect after the fix?
Hints
- Compare the source rectangle size with the destination rectangle size.
- Remember which four values are the destination pair and which are the source pair.
Key Takeaways
- A
drawImage()call is a request that copies an image through a Graphics object, usually insidepaintComponent()of aJComponent. - In the basic form, g is the drawing target, img is the image, x and y set the position, and this is the required image observer.
- Adding width and height scales the image to a specified size while x and y still control where it starts.
- The eight-coordinate form takes destination corners first, then source corners, so the portion you copy and the place you put it are chosen independently.