Concepts / Shapes and Outlines with drawRect and drawOval

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.

  • Programming

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?

  • The drawing call has a bug and draws the wrong coordinates

  • The drawing call is fine, but the pixel grid cannot match a smooth edge exactly

  • The computer is too slow to finish the drawing

Reveal answer
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.

Row 1, Col 1onRow 2, Col 1offRow 3, Col 1offRow 1, Col 2onRow 2, Col 2onRow 3, Col 2offRow 1, Col 3offRow 2, Col 3onRow 3, Col 3onRow 1, Col 4offRow 2, Col 4offRow 3, Col 4on
In a small grid, a diagonal edge is represented by whole pixels that are either on or off. The on pixels form a staircase rather than a straight slope.

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.

Key point

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.

drawRect outlineearlier widthsetStroke BasicStrokenew width setdrawOval outlinenew widththenthen
The first outline is drawn while the earlier stroke width is still in effect. After setStroke changes the width, only the later outline takes on the new thickness.
Worked example

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?

  1. 1
    List the calls in execution order

    The order is: drawRect A, setStroke, drawOval B, drawRect C. The position of setStroke in this list is the key fact.

  2. 2
    Check outline A

    Outline A is drawn before setStroke runs, so it keeps the width that was in effect when it was drawn. It does not take the new width.

  3. 3
    Check outline B

    Outline B is drawn after setStroke runs, so it uses the new width. Its border is thicker than the earlier outline.

  4. 4
    Check 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.

Result

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.

  1. 1
    Write down every call that changes the graphics context, such as the antialiasing rendering hint and setStroke, in the order the program runs them.
  2. 2
    Write down every drawing call, such as drawRect and drawOval, in the same ordered list.
  3. 3
    For each drawing call, note which antialiasing and stroke settings were in effect at that point.
  4. 4
    Compare 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 like

    A program calls setStroke near the bottom of the code, but the outlines drawn above it look unchanged.

    Why it fails

    The 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 instead

    Move 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 like

    A developer changes the stroke width of an oval and expects its interior to change too.

    Why it fails

    The thickness affects the outline drawn by drawOval, not the fill of the shape.

    Do this instead

    Treat 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 fails

    A 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 fails

    Antialiasing smooths the boundary pixels. It does not change the geometry the program asked for.

Practice: Tracing a Drawing Sequence

MEDIUM

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 setStroke call.
  • A setStroke applies to lines drawn after it, including the outlines from drawRect and drawOval.
  • Outlines P and Q are drawn after the first setStroke but before the second one.
EASY

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 from drawRect and drawOval.
  • 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.