Shapes and Outlines with drawRect and drawOval
Aliasing is the jagged appearance of shapes drawn on a pixel grid, and antialiasing is the technique that reduces that jaggedness, especially for diagonal lines, oval outlines, and text.
A Rough Diagonal Line
Suppose you draw a diagonal line or the outline of an oval in a Java graphics program and the result looks rough. Instead of a smooth slope or curve, the edge appears as a set of small steps. It is natural to suspect the drawing call itself, but the call is often not the problem. The difficulty comes from the surface you are drawing on, which is a grid of pixels, and a smooth geometric edge rarely lines up exactly with that grid.
What do you think happens?
When a diagonal outline looks jagged, what is the most likely cause?
Reveal answer
The drawing call is fine, but the pixel grid cannot match a smooth edge exactly.
Nothing in the drawing call is necessarily wrong. A smooth edge rarely lines up with whole pixels, so the stepped look is a property of the drawing surface. This article covers two settings that control appearance: antialiasing, which softens stepped edges, and stroke width, which controls how thick outlines are.
Why Pixel Grids Create Stair-Steps
The name for the jagged look is aliasing. It appears when shapes are drawn using pixels. A pixel is either colored or not colored at a given position. There is no partly colored pixel in this basic model, so a slanted edge must be represented by a staircase of whole pixels. The eye reads that staircase as roughness.
Read the grid row by row. Each row has to commit to whole pixels, so the on region steps down and to the right instead of forming one continuous slanted line. Each individual step is small, but the eye notices the pattern. That is the stair-step effect you see on rough diagonals and oval outlines.
What Antialiasing Changes
Antialiasing is the technique that tries to reduce this roughness. Instead of treating an edge as a hard boundary between colored and uncolored pixels, the rendering process blends the boundary pixels so that the transition looks smoother. The practical results are that diagonal lines and oval outlines look much cleaner, and text can also look better.
Turning On Antialiasing
Antialiasing is enabled through a rendering hint set on the graphics context. A rendering hint is a request that tells the drawing system how to handle a certain aspect of rendering. The hint for antialiasing uses a key, which names the aspect being controlled, and a value, which says whether it is on or off. To turn antialiasing on, you set the antialiasing key to the on value on the graphics context, which this article calls g2.
Read the statement from left to right and it names exactly what it does: it sets a rendering option on g2, the option is antialiasing, and the chosen setting is on. The setting belongs to the context. It is a property of how g2 draws, not something attached to one particular shape. That is why the position of the call in your program matters.
Stroke Width and BasicStroke
Stroke width is the thickness of the lines used to draw shapes. The thickness is set by creating a BasicStroke object and handing it to the graphics context through setStroke. The value passed to the BasicStroke constructor can be an integer or a floating-point number, and it becomes the width that subsequent lines will have.
The setting applies to lines drawn after the setStroke command runs. Lines drawn before it keep whatever width was in effect when they were drawn. This timing rule is the most important fact about this command.
The rule also covers outlined shapes. The thickness affects the outlines of shapes drawn with methods such as drawRect and drawOval, so a width change alters the border of those shapes, not their fill. This is the reason an outline can look thicker in one place in your program and thinner in another, even though the shape call looks identical.
Tracing Which Outlines Change
Because the stroke setting is part of the graphics context, the easiest way to predict its effect is to trace the state as the calls run in order. Think of the context as holding a current stroke width. Each setStroke call replaces it, and each outline drawn afterward uses whatever value is current at that moment.
Tracing a stroke change across three outlines
In execution order, a program first draws outline A with drawRect, then calls setStroke with a new BasicStroke, then draws outline B with drawOval, and finally draws outline C with drawRect. Which outlines use the new width?
- 1List the calls in execution order
The order is:
drawRectA,setStroke,drawOvalB,drawRectC. The position ofsetStrokein this list is the key fact. - 2Check outline A
Outline A is drawn before
setStrokeruns, so it keeps the width that was in effect when it was drawn. It does not take the new width. - 3Check outline B
Outline B is drawn after
setStrokeruns, so it uses the new width. Its border is thicker than the earlier outline. - 4Check outline C
Outline C is also drawn after
setStroke, and no later command changes the width in this list, so it also uses the new width.
Only outline A keeps the earlier width. Outlines B and C use the new width. Fill colors are not affected by this stroke change.
Finding the Call Where Output Diverges
When a drawing does not match what you expected, do not start by rewriting shape code. Instead, list the calls that touch the graphics context in the order they execute. For each outline or edge, ask which settings were in effect at the moment it was drawn. The cause of the difference usually shows up at one specific call, and that call is where the investigation should end.
- 1Write down every call that changes the graphics context, such as the antialiasing rendering hint and
setStroke, in the order the program runs them. - 2Write down every drawing call, such as
drawRectanddrawOval, in the same ordered list. - 3For each drawing call, note which antialiasing and stroke settings were in effect at that point.
- 4Compare each drawing's actual settings with what you assumed. The first call where they differ is the point of divergence.
A useful way to confirm the cause is to draw the same outline twice in one program, changing only the presence or position of one setting, and then compare the results. This keeps the investigation grounded in what the program actually renders rather than in an assumption about how the settings interact.
Mistakes Beginners Make with Stroke Timing
Common mistake Expecting a width change to affect every outline in the program
What it looks likeA program calls setStroke near the bottom of the code, but the outlines drawn above it look unchanged.
Why it failsThe setting applies only to lines drawn after the command runs. Earlier outlines keep the width that was in effect when they were drawn.
Do this insteadMove the setStroke call before the drawing calls that should use the new width, and confirm the order by listing the calls.
Common mistake Assuming a stroke width change also fills the shape with a different color
What it looks likeA developer changes the stroke width of an oval and expects its interior to change too.
Why it failsThe thickness affects the outline drawn by
drawOval, not the fill of the shape.Do this insteadTreat stroke width and fill as separate properties of the drawing, and check each one independently.
Common mistake Rewriting the shape code when the output looks rough
Why it failsA rough diagonal or oval can come from aliasing, which is a property of the pixel grid, not from a wrong shape call.
Common mistake Assuming antialiasing moves the shape
Why it failsAntialiasing smooths the boundary pixels. It does not change the geometry the program asked for.
Practice: Tracing a Drawing Sequence
A program runs these calls in order: (1) setStroke with a thin BasicStroke, (2) drawRect for outline P, (3) drawOval for outline Q, (4) setStroke with a thick BasicStroke, (5) drawRect for outline R, (6) drawOval for outline S. Which outlines are drawn thin, and which are drawn thick? Also, which call is the point where the output first changes?
Hints
- Make a list in execution order and mark each
setStrokecall. - A
setStrokeapplies to lines drawn after it, including the outlines fromdrawRectanddrawOval. - Outlines P and Q are drawn after the first
setStrokebut before the second one.
Explain in one or two sentences why a diagonal outline can look jagged even when the drawing call is correct. Then state what antialiasing does and what it does not do to the shape's position.
Hints
- Think about what a pixel can and cannot represent.
- Antialiasing changes the boundary appearance, not the coordinates.
Key Takeaways
- Aliasing is the jagged look that appears when smooth shapes are drawn on a grid of whole pixels, most visible on diagonal lines and oval outlines.
- Antialiasing is enabled with a rendering hint on the graphics context, using the antialiasing key and the on value. It smooths boundary pixels without changing the shape's geometry.
- BasicStroke sets the line width through
setStroke, and the width applies only to lines drawn after the command, including outlines fromdrawRectanddrawOval. - When output differs from expectation, list the calls in execution order and find the first call where the settings in effect differ from your assumption.