Professional Documents
Culture Documents
places the origin of a continuous coordinate system for (x, y) at the lower left corner of the pixel
grid in the image space [see Fig. 3-1(a)]. All points that satisfy x x < x + 1 and y y < y + 1
are mapped to pixel (x, y). For example, point P1(1.7, 0.8) is represented by pixel (1, 0). Points
P2(2.2, 1.3) and P3(2.8, 1.9) are both represented by pixel (2, 1).
Another approach is to align the integer values in the coordinate system for (x, y) with the
pixel coordinates [see Fig. 3-1(b)]. Here we can convert (x, y) by making x = Floor(x + 0.5) and
y = Floor(y + 0.5). This essentially places the origin of the coordinate system for (x, y) at the
center of pixel (0, 0). All points that satisfy x 0.5 x < x + 0.5 and y 0.5 y < y + 0.5 are
mapped to pixel (x, y). This means that points P1 and P2 are now both represented by pixel(2, 1),
whereas point P3 is represented by pixel (3, 2).
We will assume, in the following sections, that this second approach to coordinate system
alignment is used. This all pixel are centered at the integer values of a continuous coordinate
system where abstract graphical primitives are defined.
A note of caution: this slope-intercept equation* is not suitable for vertical lines. Horizontal,
vertical, and diagonal (|m| = 1) lines can, and often should, be handled as special cases without
going through the following scan-conversion algorithms. These commonly used lines can be
mapped to the image space in a straightforward fashion for high execution efficiency.
corresponding value of y using the equation and scan-convert (x, y). If |m| > 1, then for every
integer value of y between and excluding y1 and y2, calculate the corresponding value of x using
the equation and scan-convert (x, y).
While this approach is mathematically sound*, it involves floating-point computation*
(multiplication and addition) in every step that uses the line equation since m and b are generally
real numbers. The challenge is to find a way to achieve the same goal as quickly as possible.
An example: Draw a line from point (0, 0) to point (4, 4.8).
1 (0, 0) (0, 0)
Solution: (1)
(8, 4.8) (8, 5)
2
m = (5-0) / (8-0) = 0.625 b = 0
3 m<1 xi+1 = xi+1 = 1
xi
yi
0.625
1.25
1.875
2.5
3.125
3.75
4.375
[yi]
(xi, yi)
(1, 1)
(2, 1)
(3, 2)
(4, 3)
(5, 3)
(6, 4)
(7, 4)
(8, 5)
DDA Algorithm
The digital differential analyzer (DDA) algorithm is an incremental* scan-conversion
method. Such an approach is characterized by performing calculations at each step using results
from the preceding step.
-3-
Suppose at step i we have calculated (xi, yi) to be a point on the line. Since the next point
(xi+1, yi+1) should satisfy y / x = m where y = yi+1 - yi and x = xi+1 - xi, we have
yi+1 = yi + mx
or
xi+1 = xi +y / m
These formulas are used in the DDA algorithm as follows. When |m| 1, we start with x =
x 1, (assuming that x1 < x2) and y = y1, and set x = 1 (i.e., unit increment in the x direction).
The y coordinate of each successive point on the line is calculated using yi+1 = yi + m. When |m|
> 1, we start with x = x1 and y = y1 (assuming that y1 < y2), and set y = 1 (i.e., unit increment
in the y direction). The x coordinate of each successive point on the line is calculated using xi+1 =
xi +1 / m. This process continues until x reaches x2 (for the |m| < 1 case) or y reaches y2 (for the
|m| > 1 case) and all points found are scan-converted to pixel coordinates.
The DDA algorithm is faster than the direct use of the line equation since it calculates
points on the line without any floating-point multiplication. However, a floating-point addition is
still needed in determining each successive point. Furthermore, cumulative error due to limited
precision in the floating-point representation may cause calculated points to drift away from
their true position when the line is relatively long.
-4-
An example: Draw a line from point (0, 0) to point (4, 7) with the DDA algorithm.
Solution: (1) Write the DDA algorithm. (omitted)
(2) Operation and data table
(0, 0) (0, 0)
(4, 7) (4, 7)
abs(4-0) = 4
putpixel
(x, y)
abs(7-0) = 7
length = 7
x=4/7
y =7/7=1.0
(0, 0)
(0, 1)
(1, 2)
(2, 3)
(2, 4)
(3, 5)
(3, 6)
(4, 7)
4/7
8/7
12/7
16/7
20/7
24/7
32/7
Outside
while loop
Outside
while loop
lower bound for the line) or the one to its right and up (which constitutes an upper bound for the
line) due to the limit on m. The line is best approximated by those pixels that fall the least
distance from its true path between P1 and P2.
P
Using the notation of Fig. 3-3, the coordinates of the last chosen pixel upon entering step i
are (xi, yi). Our task is to choose the next one between the bottom pixel S and the top pixel T. If S
is chosen, we have xi+1 = xi +1 and yi+1 = yi. If T is chosen, we have xi+1 = xi +1 and yi+1 = yi +1.
The actual y coordinate of the line at x = xi+1 is y = mxi+1 + b = m(xi + 1) + b. The distance from S
to the actual line in the y direction is s = y - yi. The distance from T to the actual line in the y
direction is t = (yi + 1) - y.
Now consider the difference between these two distance values: s - t. When s - t is less
than zero, we have s < t and the closest pixel is S. Conversely, when s - t is greater than zero, we
have s > t and the closest pixel is T. We also choose T when s - t is equal to zero. This difference
is
s t = (y - yi) [(yi +1) - y]
= 2y - 2yi 1 = 2m(xi + 1) + 2b - 2 yi - 1
Substituting m by y / x and introducing a decision variable di = x(st), which has the same
sign as (s-t) since x is positive in our case, we have
di = 2y * xi - 2x * yi + C where C = 2y +x(2b1)
Similarly, we can write the decision variable di+1 for the next step as
di+1 = 2y * xi+1 - 2x * yi+1 + C
Then
-6-
if d i 0
if d i < 0
Finally, we calculate d1, the base case value for this recursive* formula, from the original
definition of the decision variable di:
d1 = x[2m(x1 + 1) + 2b 2 y1 1] = x[2(mx1 + b y1) + 2m 1]
Since mx1 + b y1 = 0, we have
d1 = 2y -x
In summaryBresenhams algorithm for scan-converting a line from P1(x1, y1), to P2(x2,
P
Here we first initialize* decision variable d and set pixel P1. During each iteration of the
while loop, we increment x to the next horizontal position, then use the current value of d to
select the bottom or top (increment y) pixel and update d, and at the end set the chosen pixel.
As for lines that have other m values we can make use of the fact that they can be mirrored
either horizontally, vertically, or diagonally* into this 0 to 45 angle range. For example, a line
from (x1, y1) to (x2, y2) with -1 < m < 0 has a horizontally mirrored counterpart* from (x1, -y1)
to (x2, -y2) with 0 < m < 1. We can simply use the algorithm to scan-convert this counterpart,
but negate the y coordinate at the end of each iteration to set the right pixel for the line. For a
line whose slope is in the 45 to 90 range, we can obtain its mirrored counterpart by exchanging
the x and y coordinates of its endpoints. We can then scan-convert this counterpart but we must
exchange x and y in the call to setPixel.
P
An example: Draw a line from the Point P1(0, 0) to Point P2(10, 6) with the Bresenham
Algorithm.
Solution: (1) Write the Bresenham algorithm. (omitted)
(2) Data Table.
x=0, y=0, dx=10-0=10, dy=6-0=6, dT=2*(6-10)=-8, dS=2*6=12, d=2*6-10=2, setPixel(0,0)
while
(xx2)
-6
-2
(x, y)
(1, 2)
(2, 1)
(3, 2)
10
-6
-2
10
(4, 2)
(5, 3)
(6, 4)
(7, 4)
(8, 5)
(9, 5)
(10, 6)
-8-
10
6
Defining a Circle
There are two standard methods of mathematically defining a circle centered at the origin.
The first method defines a circle with the second-order polynomial* equation (see Fig. 3-5)
y2 = r2 x2
where x = the x coordinate
y = the y coordinate
r = the circle radius
-9-
With this method, each x coordinate in the sector, from 90 to 45, is found by stepping x
from 0 to r / 2 , and each y coordinate is found by evaluating r 2 x 2 for each step of x. This is
a very inefficient method, however, because for each point both x and r must be squared and
subtracted from each other; then the square root of the result must be found.
The second method of defining a circle makes use of trigonometric* functions (see Fig. 3-6):
x = r cos
y = r sin
- 10 -
- 11 -
The best approximation of the true circle will be described by those pixels in the raster that
fall the least distance from the true circle. Examine Fig. 3-8. Notice that, if points are generated
from 90 and 45, each new point closest to the true circle can be found by taking either of two
actions: (1) move in the x direction one unit or (2) move in the x direction one unit and move in
the negative y direction one unit. Therefore, a method of selecting between these two choices is
all that is necessary to find the points closest to the true circle.
Assume that (xi, yi) are the coordinates of the last scan-converted pixel upon entering step
i (see Fig.3-8). Let the distance from the origin to pixel T squared minus* the distance to the true
circle squared = D(T). Then let the distance from the origin to pixel S squared minus the distance
to the true circle squared = D(S). As the coordinates of T are (xi + 1, yi) and those of S are (xi + 1,
yi - 1), the following expressions can be developed:
D(T) = (x i + 1) 2 + y i2 - r 2
D(S) = (x i + 1) 2 + (y i - 1) 2 - r 2
This function D provides a relative measurement of the distance from the center of a pixel
to the true circle. Since D(T) will always be positive (T is outside the true circle) and D(S) will
always be negative (S is inside the true circle), a decision variable di may be defined as follows:
di = D(T) + D(S)
Therefore
d i = 2( xi + 1) 2 + yi2 + ( yi 1) 2 2r 2
When di < 0, we have |D(T)| < |D(S)| and pixel T is chosen. When di 0, we have |D(T)|
|D(S)| and pixel S is selected. We can also write the decision variable di+1 for the next step:
d i +1 = 2( xi +1 + 1) 2 + yi2+1 + ( yi +1 1) 2 2r 2
Hence
d i +1 d i = 2( xi +1 + 1) 2 + yi2+1 + ( yi +1 1) 2 2( xi + 1) 2 yi2 ( yi 1) 2
On the other hand, if S is the chosen pixel (meaning that di 0) then yi+1 = yi 1 and so
d i +1 = d i + 4( xi yi ) + 10
Hence we have
- 12 -
d i + 4 xi + 6
d i +1 =
d i + 4( xi yi ) + 10
if d i < 0
if d i 0
Finally, we set (0, r) to be the starting pixel coordinates and compute the base case value d1
for this recursive formula from the original definition of di:
d1 = 2(0 + 1) 2 + r 2 + ( r 1) 2 2r 2 = 3 2r
We can now summarize the algorithm for generating all the pixel coordinates in the 90 to
45 octant* that are needed when scan-converting a circle of radius r:
int x = 0, y = r, d = 3 - 2r;
while (x <= y) {
setPixel(x, y);
if (d < 0)
d = d + 4x + 6;
else {
d = d + 4(x - y) +10;
y--;
}
x++;
}
Note that during each iteration of the while loop we first set a pixel whose position has
already been determined, starting with (0, r). We then test the current value of decision variable
d in order to update d and determine the proper y coordinate of the next pixel. Finally we
increment x.
An example: Indicate which raster locations would be chosen by Bresenhams algorithm when
scan-converting a circle centered at the origin with the radius of 8 pixels (from 90 to 45).
Solution: (1) Write the algorithm. (omitted).
(2) Data Table.
x = 0, y = 8, d = 3-2*8 = -13
while (xy)
while (x>y)
(x, y)
(0, 8)
(1, 8)
(2, 8)
(3, 7)
(4, 7)
(5, 6)
-7
-11
11
y
x
7
1
4
- 13 -
settings. Some packages provide users with a combination of attribute functions and attribute
parameters in the output primitive commands. With the GKS and PHIGS standards, attribute
settings are accomplished with separate functions that update a system attribute list.
Basic attributes of a straight line segment are its type, its width, and its color. In some
graphics packages, lines can also be displayed using selected pen or brush options. In the
following sections, we consider how line-drawing routines can be modified to accommodate
various attribute specifications.
- 16 -
We can further apply the concept of connected pixels to decide if a region is connected to
another region. For example, using the 8-connected approach, we do not have an enclosed region
in Fig. 3-10(c) since "interior" pixel C is connected to "exterior" pixel D. On the other hand, if
we use the 4-connected definition we have a triangular region since no interior pixel is
connected to the outside.
Note that it is not a mere coincidence that the figure in Fig. 3-10(c) is a boundary-defined
- 17 -
region when we use the 8-connected definition for the boundary pixels and the 4-connected
definition for the interior pixels. In fact, using the same definition for both boundary and interior
pixels would simply result in contradiction. For example, if we use the 8-connected approach we
would have pixel A connected to pixel B (continuous boundary) and at the same time pixel C
connected to pixel D (discontinuous boundary). On the other hand, if we use the 4-connectd
definition we would have pixel A disconnected from pixel B (discontinuous boundary) and at the
same time pixel C disconnected from pixel D (continuous boundary).
Figure 3-11
Example color boundaries for a boundary-fill procedure.
Boundary-Fill Algorithm
Another approach to area filling is to start at a point inside a region and paint the interior
outward toward the boundary. If the boundary is specified in a single color, the fill algorithm
proceeds outward pixel by pixel until the boundary color is encountered. This method, called the
boundary-fill algorithm, is particularly useful in interactive painting packages, where interior
points are easily selected. Using a graphics tablet or other interactive device, an artist or designer
can sketch a figure outline, select a fill color or pattern from a color menu, and pick an interior
point. The system then paints the figure interior. To display a solid color region (with no border),
the designer can choose the fill color to be the same as the boundary color.
A boundary-fill procedure accepts as input the coordinates of an interior point (x, y), a fill
color, and a boundary color. Starting from (x, y), the procedure tests neighboring positions to
determine whether they are of the boundary color. If not, they are painted with the fill color, and
their neighbors are tested. This process continues until all pixels up to the boundary color for the
area have been tested. Both inner and outer boundaries can be set up to specify an area, and
some examples of defining regions for boundary fill are shown in Fig. 3-11.
Figure 3-12 shows two methods for proceeding to neighboring pixels from the current test
position. In Fig. 3-12(a), four neighboring points are tested. These are the pixel positions that are
right, left, above, and below the current pixel. Areas filled by this method are called 4-connected.
The second method, shown in Fig. 3-12(b), is used to fill more complex figures. Here the set of
- 18 -
neighboring positions to be tested includes the four diagonal pixels. Fill methods using this
approach are called 8-connected. An 8-connected boundary-fill algorithm would correctly fill the
interior of the area defined in Fig. 3-13, but a 4-connected boundary-fill algorithm produces the
partial fill shown.
Figure 3-12
Fill methods applied to a
4-connected area (a) and to
an 8-connected area (b).
Open
circles
represent
pixels to be tested from the
current test position, shown
as a solid color.
The following procedure illustrates a recursive method for filling a 4-connected area with
an intensity specified in parameter fill up to a boundary color specified with parameter boundary.
We can extend this procedure to fill an 8-connected region by including four additional
statements to test diagonal positions, such is (x+1, y+1).
Recursive boundary-fill algorithms may not fill regions correctly if some interior pixels are
already displayed in the fill color. This occurs because the algorithm checks next pixels both for
boundary color and for fill color. Encountering a pixel with the fill color can cause a recursive
branch to terminate, leaving other interior pixels unfilled. To avoid this, we can first change the
color of any interior pixels that are initially set to the fill color before applying the boundary-fill
procedure.
Also, since this procedure requires considerable stacking of neighboring points, more
efficient methods are generally employed. These methods fill horizontal pixel spans across scan
lines, instead of proceeding to 4-connected or 8-connected neighboring points. Then we need
only stack a beginning position for each horizontal pixel span, instead of stacking all
unprocessed neighboring positions around the current position. Starting from the initial interior
point with this method, we first fill in the contiguous* span of pixels on this starting scan line.
Then we locate and stack starting positions for spans on the adjacent scan lines, where spans are
defined as the contiguous horizontal string of positions bounded by pixels displayed in the area
border color. At each subsequent step, we unstack the next start position and repeat the process.
Figure 3-13
The area defined within the color boundary (a) is only partially
filled in (b) using a 4-connected boundary-fill algorithm.
An example of how pixel spans could be filled using this approach is illustrated for the
4-connected fill region in Fig. 3-14. In this example, we first process scan lines successively
from the start line to the top boundary. After all upper scan lines are processed, we fill in the
pixel spans on the remaining scan lines in order down to the bottom boundary. The leftmost
pixel position for each horizontal span is located and stacked, in left to right order across
successive scan lines, as shown in Fig. 3-14. In (a) of this figure, the initial span has been filled,
and starting positions 1 and 2 for spans on the next scan lines (below and above) are stacked. In
Fig. 3-14(b), position 2 has been unstacked and processed to produce the filled span shown, and
the starting pixel (position 3) for the single span on the next scan line has been stacked. After
position 3 is processed, the filled spans and stacked positions are as shown in Fig. 3-14(c). And
Fig. 3-14(d) shows the filled pixels after processing all spans in the upper right of the specified
- 20 -
area. Position 5 is next processed, and spans are filled in the upper left of the region; then
position 4 is picked up to continue the processing for the lower scan lines.
Figure 3-14
Boundary fill across pixel spans for a 4-connected area. (a) The filled initial pixel
span, showing the position of the initial point (open circle) and the stacked positions
for pixel spans on adjacent scan lines. (b) Filled pixel span on the first scan line
above the initial scan Line and the current contents of the stack. (c) Filled pixel spans
on the first two scan lines above the initial scan line and the current contents of the
stack. (d) Completed pixel spans for the upper-right portion of the defined region and
the remaining stacked positions to be processed.
An example: Write 4_connected boundary algorithm and the order in which pixel are filled in
the following Fig. The number 1 represents a seed.
Solution: (1) Write the algorithm. (omitted).
(2) Plot the result.
- 21 -
Flood-Fill Algorithm
Sometimes we want to fill in (or recolor) an area that is not defined within a single color
boundary. Figure 3-15 shows an area bordered by several different color regions. We can paint
such areas by replacing a specified interior color instead of searching for a boundary color value.
This approach is called a flood-fill algorithm.
Figure 3-15
An area defined within
multiple color boundaries.
We start from a specified interior point (x, y) and reassign all pixel values that are currently
set to a given interior color with the desired fill color. If the area we want to paint has more than
one interior color, we can first reassign pixel values so that all interior points have the same
color. Using either a 4-connected or 8-connected approach, we then step through pixel positions
until all interior points have been repainted. The following procedure flood fills a 4-connected
region recursively, starting from the input position.
stack only the beginning positions for those pixel spans having the value oldColor. The steps in
this modified flood-fill algorithm are similar to those illustrated in Fig. 3-45 for a boundary fill.
Starting at the first position of each span, the pixel values are replaced until a value other than
oldColor is encountered.
dc.MoveTo(300,275);//nose
dc.LineTo(280,325);
dc.LineTo(320,325);
dc.LineTo(300,275);
dc.MoveTo(250,340);//mouth
dc.LineTo(300,375);
dc.LineTo(350,340);
dc.LineTo(300,350);
dc.LineTo(250,340);
seed(300,300,RGB(228,192,118),bv);
seed(255,270,RGB(70,70,70),bv);
seed(345,270,RGB(70,70,70),bv);
seed(300,360,RGB(247,120,98),bv);
- 24 -