Drawing Lines and Shapes in Graphics2D
Antialiasing reduces the jagged look of shapes by blending the drawing color with the original pixel color, in proportion to how much of the pixel the shape covers.
Why Diagonal Lines Look Jagged
Open almost any drawing program or game and look closely at a diagonal line or a curved outline. On a screen the line is built from small square pixels, and when no special handling is applied the edge of the line can look like a staircase. Programmers call this a jagged or aliased appearance. This article explains how Graphics2D, the Java class used for two-dimensional drawing, can smooth those edges, and how you control that smoothing with a single call.
The central question is simple: if two drawings contain the same shape, why can one look smooth and the other look rough? The answer lies in one setting on the Graphics2D context, and in the order in which your code changes that setting. By the end of this article you should be able to predict which drawing operations a setting affects, and to find the line of code responsible when a drawing does not match what you expected.
Pixels Cannot Be Partly One Color
A diagonal or curved line is drawn onto a grid of square pixels. Each pixel is a single unit with a single color. Without any special handling, each pixel is either colored by the shape or left alone, so the boundary of the shape ends up as a set of hard, all-or-nothing steps. A pixel that the edge only grazes gets the full shape color just as a pixel the edge fills completely does. That is what produces the staircase.
The source material illustrates this with a picture of several rows of panels. The top row is drawn without antialiasing, and its lines have a visibly jagged edge. The second and third rows are drawn with antialiasing, and their edges look noticeably smoother. The difference between those rows is the subject of the rest of this article.
How Antialiasing Blends Colors
Antialiasing reduces the jagged look of shapes by blending the drawing color with the original pixel color, in proportion to how much of the pixel the shape covers.
Read that definition carefully, because each part matters. The drawing color is the color of the shape. The original pixel color is whatever was already on the pixel before the shape was drawn there, such as the background. The blend is a mixture of those two colors. The proportion of the mixture depends on how much of the pixel the shape covers. A pixel that the shape fully covers takes on the shape color. A pixel the shape only touches at a corner takes on mostly the background color. A pixel that the edge splits roughly in half ends up with an intermediate color.
Reading a setRenderingHint Call
Antialiasing is supported in Graphics2D, but it is turned off by default. If g2 is a Graphics2D context, you turn it on by passing a rendering hint to its setRenderingHint method. A hint has two parts: a key that names the setting being controlled, and a value that says what you want that setting to be. Read the call from left to right. The object g2 is the context whose settings are changing. setRenderingHint is the method that changes one setting. RenderingHints.KEY_ANTIALIASING names the setting that concerns antialiasing. RenderingHints.VALUE_ANTIALIAS_ON gives that setting its enabling value.
Turning Antialiasing On and Off in Code
- Turns antialiasing on for this context
- Draws a diagonal line that benefits from smoothing
To turn antialiasing off, the method name stays the same and the key stays the same. Only the second argument changes to VALUE_ANTIALIAS_OFF. This is the important pattern to remember: because the key and the value are separate arguments, the only difference between turning antialiasing on and turning it off is that second argument. If you find yourself writing a different method name to control antialiasing, you are probably looking at the wrong call.
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawLine(10, 10, 200, 120);Antialiasing on: the diagonal edge is blended with the background
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
g2.drawLine(10, 10, 200, 120);Antialiasing off: the diagonal edge is aliased with a staircase look
How a Hint Persists Across Draw Calls
The effect of the hint is not limited to the call that contains it. Once you set the hint on a Graphics2D context, subsequent drawing operations on that context are likely to be antialiased, and the setting remains in place until you change it again. Earlier operations, performed before the hint was set, are not described as changing. The order of your calls therefore matters: the position of the setRenderingHint call decides which drawings it can influence.
Imagine a method that draws a background grid with antialiasing turned on, then draws a set of sharp rectangle borders that must stay pixel-exact. If the hint is still on when the rectangles are drawn, their edges will be blended too. Placing a setRenderingHint call with VALUE_ANTIALIAS_OFF before the rectangle drawing, and a matching ON call before any later curved shapes, makes the state explicit at each step rather than relying on whatever was set earlier.
When the Hint Is Not Honored
A rendering hint is exactly that: a hint. The call expresses a request, and the system may choose to ignore it. This means that writing setRenderingHint with VALUE_ANTIALIAS_ON does not guarantee that every shape you draw afterward will be smoothed. If your output does not match what the call seems to promise, do not assume the call itself is wrong. The renderer or platform may have chosen not to apply the request.
Mistakes When Debugging Drawings
Common mistake Assuming the hint applies to drawings that happened before it
What it looks likeCalling drawLine, then setRenderingHint with VALUE_ANTIALIAS_ON, and expecting that first line to be smoothed.
Why it failsThe hint affects subsequent drawing operations on the context; earlier operations are not described as changing.
Do this insteadMove the setRenderingHint call above the drawing calls that need it.
Common mistake Using a different method to control antialiasing
What it looks likeSearching for a separate antialiasing method instead of looking at the key and value of the hint call.
Why it failsAntialiasing is controlled through the same
setRenderingHintmethod, with the key kept the same and only the value changed.Do this insteadFind the setRenderingHint call that uses RenderingHints.KEY_ANTIALIASING and check its second argument.
Common mistake Expecting perfect geometric accuracy from antialiased output
Why it failsAntialiased pixels are still discrete, so the output is a better approximation, not an exact picture of the shape.
Do this insteadTreat antialiasing as a visual improvement and not as a guarantee of exact geometry.
Common mistake Forgetting that the hint is a request that may be ignored
Tracing an Unexpected Result
When a drawing looks wrong, the most useful question is not whether antialiasing exists but where in the code the result stopped matching your expectation. Trace the drawing calls in order. For each one, ask whether the antialiasing hint had been set at that point, and whether the value passed was ON or OFF. The first call where your expectation and the outcome disagree is the place to look.
Finding the Line Behind a Jagged Edge
A paint method sets the hint to ON, draws a curved outline, then sets the hint to OFF, and finally draws a diagonal line. The diagonal line looks jagged, but the author expected it to be smooth. Which line produced the unexpected output?
- 1List the calls in order
Hint ON, curved outline, hint OFF, diagonal line.
- 2Track the state at each draw
The curved outline is drawn while the hint is on. Before the diagonal line, the hint was switched off.
- 3Find the first mismatch
The author expected smoothing on the diagonal line, but the most recent hint call before it passed VALUE_ANTIALIAS_OFF.
- 4Identify the originating line
The
setRenderingHintcall with VALUE_ANTIALIAS_OFF is the line that produced the jagged result, not the diagonal drawing call itself.
The line g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF) is where the unexpected result originates.
A method draws a circle, then calls setRenderingHint with VALUE_ANTIALIAS_ON, then draws a rectangle, then calls setRenderingHint with VALUE_ANTIALIAS_OFF, then draws a diagonal line. Before you look at any output, predict which of the three shapes will have antialiased edges, and name the single line you would check first if the diagonal line looked wrong.
Hints
- Only calls made after a hint is set are described as affected by it.
- Check the second argument of the most recent
setRenderingHintcall before each draw.
Key Takeaways
- Antialiasing reduces the jagged look of shapes by blending the drawing color with the original pixel color, in proportion to how much of the pixel the shape covers.
- Antialiasing is off by default in Graphics2D and is turned on with
setRenderingHintusing RenderingHints.KEY_ANTIALIASING and RenderingHints.VALUE_ANTIALIAS_ON; the off version uses the same key with VALUE_ANTIALIAS_OFF. - A rendering hint persists for subsequent drawing operations on that context until it is changed, so the position of the call in your code determines what it affects.
- A hint is a request and may be ignored, so when a result is unexpected, trace the calls in order and find the first point where expectation and outcome disagree.