You are on page 1of 50

2D Graphics: Output Primitives

CMSC 161 – Interactive Computer Graphics


Overview

• Point plotting
• Line-drawing Algorithms
• Circle-generating Algorithms
• Filled-area Primitives
Points and Lines

• Point plotting
– Random Scan: deflection voltages position the electron beam at
the screen locations to be plotted during each refresh cycle
– Raster Scan: set the bit value corresponding to a specified screen
position within the frame buffer to 1
• Line drawing
– Calculating intermediate positions along the line path between two
specified endpoint positions
Scan Converting Lines

• Drawing lines on a raster grid implicitly involves


approximation
• The general process is called rasterization or scan-
conversion
Optimal Line Drawing

• What is the best way to draw a line from pixel (x 1,y1) to


(x2,y2)?
• Such a line should ideally have the following properties:
– Straight
– Pass through endpoints
– Smooth
– Uniform brightness
– Efficient
Straightforward Implementation
/* assume x1 < x2 */
void DrawLine (int x1, int y1, int x2, int y2)
{
float y;
int x;
for (x = x1; x <= x2; x++) {
y = y1 + (x – x1)*(y2 – y1)/(x2 – x1);
setPixel (x, Round(y));
}
}
A Better Implementation
void DrawLine (int x1, int y1, int x2, int y2)
{
float m, y;
int dx, dy, x;

dx = x2 - x1;
dy = y2 - y1;
m = dy / dx;
y = y1 + 0.5;

for (x = x1; x <= x2; x++) {


SetPixel (x, Floor(y));
y = y + m;
}
}
Midpoint Algorithm

• Bresenham’s Line Algorithm


• Better because it uses only integer calculations
Midpoint Algorithm
/* assume x1 < x2 and slope <= 1 */
void DrawLine (int x1,int y1,int x2,int y2)
{
int dx, dy, d, incE, incNE, x, y;

dx = x2 - x1;
dy = y2 - y1;
d = 2 * dy - dx;
incE = 2 * dy;
incNE = 2 * (dy - dx);
y = y1;

for (x = x1; x <= x2; x++) {


setPixel (x, y, color);
if (d > 0) {
d = d + incNE;
y = y + 1;
} else {
d = d + incE;
}
}
}
Example

• Determine the pixel positions along the line path with


endpoints (20, 10) and (30, 18) using Bresenham’s Line
drawing algorithm.
Scan Converting Circles

• Since the equation for a circle with radius r centered at (0,0)


is

• An obvious choice is to plot


Algorithm
public void circleSimple (int xCenter, int yCenter, int radius, Color
c)
{
int pix = c.getRGB();
int x, y, r2;
r2 = radius * radius;
for (x = -radius; x <= radius; x++)
{
y = (int) (Math.sqrt(r2 - x*x) + 0.5);
raster.setPixel(pix, xCenter + x, yCenter + y);
raster.setPixel(pix, xCenter + x, yCenter - y);
}
}
Results

• This works, but is inefficient because of the multiplications


and square root operations
• It also creates large gaps in the circle for values of x close
to r
Symmetry

• A circle exhibits a great deal of symmetry


• We’ve already exploited this somewhat by plotting two
pixels for each function evaluation ...
• This symmetry was about the x-axis
• Net result: 2-way symmetry about the x-axis
4-Way Symmetry

• Just as every point above


an x-axis drawn through
the circle’s center has a
symmetric point an equal
distance from, but on the
other side of the x-axis,
each point also has a
symmetric point on the
opposite side of a y-axis
drawn through the center’s
circle
Algorithm: 4-Way Symmetry
public void circleSym4(int xCenter, int yCenter, int radius, Color c)
{
int pix = c.getRGB();
int x, y, r2;
r2 = radius * radius;
raster.setPixel(pix, xCenter, yCenter + radius);
raster.setPixel(pix, xCenter, yCenter - radius);
for (x = 1; x <= radius; x++) {
y = (int) (Math.sqrt(r2 - x*x) + 0.5);
raster.setPixel(pix, xCenter + x, yCenter + y);
raster.setPixel(pix, xCenter + x, yCenter - y);
raster.setPixel(pix, xCenter - x, yCenter + y);
raster.setPixel(pix, xCenter - x, yCenter - y);
}
}
Results

• This algorithm has all


the problems of our
previous algorithm ...
• But it gives the same
results with half as
many function
evaluations
• Optimizing
8-Way Symmetry

• A circle also exhibits symmetry about the pair of lines with


slopes of one and minus one
8-Way Symmetry

• We can find any point’s symmetric complement about these


lines by permuting the indices
• For example, the point (x,y) has a complementary point
(y,x) about the line x=y
• The total set of complements for point (x,y) are:
Algorithm: 8-Way Symmetry
public void circleSym8(int xCenter, int yCenter, int radius, Color c)
{
int pix = c.getRGB();
int x, y, r2; r2 = radius * radius;
raster.setPixel(pix, xCenter, yCenter + radius);
raster.setPixel(pix, xCenter, yCenter radius);
raster.setPixel(pix, xCenter + radius, yCenter);
raster.setPixel(pix, xCenter - radius, yCenter);
y = radius; x = 1;
y = (int) (Math.sqrt(r2 - 1) + 0.5);
Algorithm: 8-Way Symmetry
while (x < y)
{
raster.setPixel(pix, xCenter + x, yCenter + y);
raster.setPixel(pix, xCenter + x, yCenter - y);
raster.setPixel(pix, xCenter - x, yCenter + y);
raster.setPixel(pix, xCenter - x, yCenter - y);
raster.setPixel(pix, xCenter + y, yCenter + x);
raster.setPixel(pix, xCenter + y, yCenter - x);
raster.setPixel(pix, xCenter - y, yCenter + x);
raster.setPixel(pix, xCenter - y, yCenter - x);
x += 1;
y = (int) (Math.sqrt(r2 - x*x) + 0.5);
}
if (x == y) {
raster.setPixel(pix, xCenter + x, yCenter + y);
raster.setPixel(pix, xCenter + x, yCenter - y);
raster.setPixel(pix, xCenter - x, yCenter + y);
raster.setPixel(pix, xCenter - x, yCenter - y);
}
}
Results

• The algorithm loops over one-eighth of the circle.


Specifically it starts at the top of the circle and goes to the
right by 45º where the slope of the radial line is 1.
• Stopping state is crossing the x = y line
• So now we get 8 points for every function evaluation, and
this routine should be approximately 4 times faster than our
initial circle-drawing algorithm
Filling Rectangles

• Can be broken down into two parts:


– Decision of which pixels to fill
– Decision of what value to fill them
• In general, determining which pixels to fill consists of taking
successive scan lines that intersect the primitive and filling
in spans of adjacent pixels that lie inside the primitive from
left to right
Filling Rectangles

• The following code concentrates on defining spans and


ignores the issue of writing memory efficiently:

for (y from ymin to ymax of the rectangle)


{
for (x from xmin to xmax) {
WritePixel (x, y, value);
}
}
Filling Rectangles

• A simple rule is to say that a boundary pixel is not


considered part of the primitive if the half-plane defined by
that edge and containing the primitive lies below or to the
left of the edge.

4,5
Hence, left and bottom
edges will be drawn,
but pixels on top and
right edges will not be
drawn
0,0
Filling Rectangles

• A number of points must be made about this rule:


– It applies to arbitrary polygons as well as to rectangles
– The bottom-left vertex of a rectangle would be drawn twice
– We may apply the rule also to unfilled rectangles and polygons
– It causes each span to be missing its rightmost pixel, and each
rectangle to be missing its topmost row
Filling Rectangles

• These problems illustrate that there is no perfect solution to


the problem of not writing pixels on (potentially) shared
edges twice
• But implementors generally consider that it is better
(visually less distracting) to have missing pixels at the right
and top edge than it is to have pixels that disappear or are
set to unexpected colors
Some Concepts ...

• Polygon Types:
– Convex
• Horizontally convex
– Concave - A type of polygon that has one or more interior angles
greater than 180 degrees
Why Polygons?

• You can approximate practically any surface if you have


enough polygons
• Graphics hardware is optimized for polygons (especially
triangles)
Filled-Area Primitives

• Scan-Line Polygon Fill Algorithm


• Fill methods / techniques
– Boundary-Fill Algorithm
– Flood-Fill Algorithm
Scan-Line Polygon-Fill

• Polygon scan conversion algorithm operates by computing


spans that lie between the left and right edges of the
polygon
• Straightforward implementation: use midpoint line scan
conversion algorithm on each edge
Scan-Line Polygon-Fill

• The figures show one problem: if we use the midpoint line


scan-conversion algorithm to find the end of spans, we will
select pixels outside the polygon
• Not good for polygons that share edges
Scan-Line Polygon-Fill

• In this figure we can see the basic polygon


scan-conversion process:
– Note: for line 8 a and d are at integer values, whereas b and c are
not
– We must determine which pixels on each scan line are within the
polygon, and we must set the corresponding pixels (x = 2 through
4, and 9 through 13) to their appropriate value
– We then repeat this process for each scan line that intersects the
polygon
Algorithm

• The spans can be filled in by a three-step process:


– Find the intersections of the scan line with all edges of the polygon
– Sort the intersections by increasing x-coordinates
– Fill in all pixels between pairs of intersections that lie interior to the
polygon using the odd-parity rule to determine that a point is
inside a region
Span Filling

• Step 3 requires four elaborations:


– Given an intersection with an arbitrary, fractional x value, how do
we determine which pixel on either side of that intersection is
interior?
– How do we deal with the special case of intersections at integer
pixel coordinates?
– How do we deal with the special case in the second elaboration for
shared vertices?
– How do we deal with the special case in the second elaboration in
which the vertices define a horizontal edge?
Span Filling

• To handle the first elaboration, we say that:


– If we are approaching a fractional intersection to the right and
inside the polygon, we round down the x-coordinate of the
intersection to define the interior pixel
– If we are outside the polygon, we round up to be inside
Span Filling

• We handle the second elaboration by applying the criterion


we used to avoid conflicts at shared edges of rectangles:
– If the leftmost pixel in a span has integer x-coordinate, we define it
to be interior
– If the rightmost pixel in a span has integer x-coordinate, we define
it to be exterior
– See scan line 8
Span Filling

• For the third case, we count the ymin vertex of an edge in the
parity calculation, but not the ymax vertex
– Therefore a ymax vertex is drawn only if it is the ymin vertex for the
adjacent edge (consider vertex A)
– Thus edges are treated as intervals (closed at ymin and open at
ymax)
Span Filling

• For the fourth case (horizontal edges), the desired effect is


that, as with rectangles, bottom edges are drawn but top
edges are not
• We deal properly with horizontal edges by not counting
their vertices
Horizontal Edges

• Rules:
– Don’t consider the vertex contribution for the horizontal edge
– Invert the parity if the vertex is a ymin of some edge
– Do not invert if the vertex is a ymax of some edge
Example

• Scan line 3 – A counts


• Scan line 1 – B is drawn
• Scan line 9 – nothing is drawn at F
Slivers

• Polygons with edges that lie sufficiently close together


create a sliver – a polygonal area so thin that its interior
does not contain a distinct span for each scan line
Filling Techniques

• Another approach to polygon fill is using a filling technique,


rather than scan conversion
• Pick a point inside the polygon, then fill neighboring pixels
until the polygon boundary is reached
Propagating to Neighbors

• Most frequently used approaches:


– 4 – connected area
– 8 – connected area
Fill Problems

• Fill algorithms have potential problems


• For example, a 4-connected area fill
Fill Problems

• Similarly, 8-connected can “leak” over to another polygon


Boundary-Fill Algorithm

• Draw polygon boundary in the frame buffer


• Determine an interior point
• Starting at the given point, do
– If the point is not the boundary color or the fill color
• Set this pixel to the fill color
• Propagate to the pixel’s neighbor and continue
Boundary-Fill Algorithm
void boundaryFill4 (int x, int y, int fill, int boundary)
{
int current;
current = getPixel (x, y);
if ((current != boundary) && (current != fill)) {
setColor (fill);
setPixel (x, y);
boundaryFill4 (x+1, y, fill, boundary);
boundaryFill4 (x-1, y, fill, boundary);
boundaryFill4 (x, y+1, fill, boundary);
boundaryFill4 (x, y-1, fill, boundary);
}
}
Flood-Fill Algorithm

• Set all interior pixels to a certain color


• The boundary can be any other color
• Pick an interior point and set it to the polygon color
• Propagate to neighbors, as long as the neighbor is the
interior color

• Can be used for regions with multi-color boundaries


Flood-Fill Algorithm
void floodFill4 (int x, int y, int fillColor, int oldColor)
{
if (getPixel (x, y) == oldColor) {
setColor (fillColor);
setPixel (x, y);
floodFill4 (x+1, y, fillColor, oldColor);
floodFill4 (x-1, y, fillColor, oldColor);
floodFill4 (x, y+1, fillColor, oldColor);
floodFill4 (x, y-1, fillColor, oldColor);
}
}

You might also like