Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
10 views

Basic Raster Algorithms

Uploaded by

mastan
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Basic Raster Algorithms

Uploaded by

mastan
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

Chapter 5

Basic raster algorithms

5.1 Raster grid, connectivity of raster grid, 4-connectivity and


8-connectivity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
5.2 Bresenheim’s line algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
5.3 Bresenheim’s circle algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.4 Triangle filling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
5.5 Flood fill algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

All modern computer-generated graphics are raster-based. Every image is rep-


resented as a two-dimensional array of points (called pixels from picture ele-
ments). Every pixel has coordinates which usually are a pair of integer values
(x, y) and some color (see Figure 5.1).
All pixels are located on the so-called raster grid, which is usually a set of
points with integer coordinates x and y.

FIGURE 5.1: Example of a raster grid.

Usually we work with geometric objects consisting of many pixels such as


lines, arcs, polygons, etc. In order to be rendered they need to be rasterized
(i.e., converted to a set of pixels on a raster grid). Doing so we come from
initial resolution-independent data to a specific raster representation (which
is tied to the raster grid resolution).

81
82 Computer Graphics : From Pixels to Programmable Graphics Hardware

5.1 Raster grid, connectivity of raster grid, 4-connectivity


and 8-connectivity
Rasterization of an ideal mathematical shape (e.g. a line segment) should
pick pixels which are close to the actual line and be considered as a connected
set on the raster grid.
There are two major ways to introduce connectivity on a raster grid. The
first one is called 4-connectivity. Pixels (x0 , y0 ) and (x1 , y1 ) are connected if
and only if |x0 − x1 | + |y0 − y1 | ≤ 1 (note that all coordinates are integer). If
two pixels are 4-connected they can differ in only one coordinate – x or y (see
Figure 5.2, left).
Another variant of connectivity on a raster grid is called 8-connectivity
and defined in the following way – pixels (x0 , y0 ) and (x1 , y1 ) are connected if
and only if |x0 − x1 | ≤ 1 and |y0 − y1 | ≤ 1 (see Figure 5.2, right).

FIGURE 5.2: Types of connectivity on a raster grid.

Note that if two pixels are 4-connected then they are 8-connected, but the
opposite is not true – pixels (0, 0) and (1, 1) are 8-connected, but they are not
4-connected.
A set of pixels on a raster grid is 4- or 8-connected if for any two pixels from
this set there exists a sequence of pixels starting at the first pixel and ending
at the second such that any two subsequent pixels are 4- or 8-connected.
When we rasterize lines, arcs and other linear shapes (which are connected
in 2D-/3D-space) we must require that pixels resulting from this rasteriza-
tion also form a connected set according to the chosen connectedness criteria.
Thus, we come to raster algorithms which generate 4-connected pixels and
8-connected pixels. Note that the same shape can be rasterized differently
depending on the type of connectivity used.
Basic raster algorithms 83

5.2 Bresenheim’s line algorithm


One of the basic raster algorithms is Bresenheim’s algorithm for rasterizing
a line segment. It was originally developed in 1962 to control digital plotters.
But due to its efficiency it became widespread in computer graphics. It was
shown that this algorithm gives the approximation to lines which minimizes
the distance to the line (approximation error).
We describe the algorithm in a form called midpoint algorithm. All major
steps in deriving the algorithm will be done for an 8-connected line, but the
end of this section a C++ code for a 4-connected version will be presented.
Let’s assume A(xa , ya ) and B(xb , yb ) are two points with integer coordi-
nates. To create a raster image of the line segment AB we will start with the
case where 0 ≤ yb − ya ≤ xb − xa (see Fig 5.3).

FIGURE 5.3: Rasterizing line segment AB.

Later we will show how a generic case can be built from this case.
We will consider that the segment is a part of the line with an equation
y = kx + b where the coefficient k is in the range from 0 to 1. Then for every
integer value xi starting with xa and up to xb there will be exactly one pixel
(xi , yi ) with that x-coordinate.
Since we need some way to access individual pixels for the rest of the
chapter we will assume that there are two functions – putP ixel(x, y, c) and
getP ixel(x, y). First one draws a pixel at position (x, y) with color c and the
second one reads the color of the pixel at the given position (x, y).
Using these functions we can write the simplest (very inefficient) code to
draw segment AB.
void drawLine ( i nt xa , i nt ya , i nt xb , i nt yb ,
i nt c o l o r )
{
f l o a t k = ( f l o a t ) ( yb−ya ) / ( f l o a t ) ( xb − xa ) ;
f l o a t b = ya − k ∗ xa ;
84 Computer Graphics : From Pixels to Programmable Graphics Hardware

for ( i nt x = xa ; x <= xb ; x++ )


p u t P i x e l ( x , round ( k∗x + b ) , c o l o r ) ;
}
Here the function round is used to round the floating point number to
the nearest integer comparing its fractional part with 1/2. We can optimize
the function by noting that line y-value will be changed by k on each loop
iteration.
void drawLine ( i nt xa , i nt ya , i nt xb , i nt yb ,
i nt c o l o r )
{
f l o a t k = ( f l o a t ) ( yb−ya ) / ( f l o a t ) ( xb − xa ) ;
f l o a t b = ya − k∗ xa ;
f l o a t y = ya ;

for ( i nt x = xa ; x <= xb ; x++, y += k )


p u t P i x e l ( x , round ( y ) , c o l o r ) ;
}
We can get rid of the round function by tracking how the fractional part
of the line equation changes when we come to the next x-value

xi = xa + i,
(5.1)
ci = {kxi + b}.

If for i-th pixel ci ≤ 1/2 we will round kxi + b down and set yi = kxi + b,
otherwise we set yi = kxi + b + 1.

FIGURE 5.4: Line segment and fractional parts ci for three consecutive
pixels.
Basic raster algorithms 85

Let’s look at how the value of ci is changed when we iterate through


x0 = xa , x1 , x2 , · · ·.
When we know the coordinates (xi , yi ) of the current pixel (along with
the ci ) values for next pixel are found in the following way: ci+1 = ci + k
and yi+1 = yi (y is rounded down) if ci ≤ 1/2, and ci+1 = ci + k − 1 and
yi+1 = yi + 1 if ci > 1/2( see Figure 5.4).
Note that for x0 = xa and y0 = ya we have c0 = 0.
We can now get rid of the round function from our code.
void drawLine ( i nt xa , i nt ya , i nt xb , i nt yb ,
i nt c o l o r )
{
f l o a t k = ( f l o a t ) ( yb−ya ) / ( f l o a t ) ( xb − xa ) ;
f l o a t y = ya ;
float c = 0;
i nt y = ya ;

p u t P i x e l ( xa , ya , c o l o r ) ;
for ( i nt x = xa +1; x <= xb ; x++ )
{
i f ( ( c += k ) > 0 . 5 f )
{
c−−;
y++;
}

putPixel ( x , y , color ) ;
}
}
Note that comparing ci with zero is much easier than with 1/2. So we can
use di = 2ci − 1 instead of ci and compare it with zero. Since c0 = 0 and
c1 = k we have d0 = −1 and d1 = 2k − 1.
void drawLine ( i nt xa , i nt ya , i nt xb , i nt yb ,
i nt c o l o r )
{
f l o a t k = ( f l o a t ) ( yb−ya ) / ( f l o a t ) ( xb − xa ) ;
f l o a t y = ya ;
f l o a t d = 2∗k − 1 ;
i nt y = ya ;

p u t P i x e l ( xa , ya , c o l o r ) ;
for ( i nt x = xa +1; x <= xb ; x++ )
{
if ( d > 0 )
{
86 Computer Graphics : From Pixels to Programmable Graphics Hardware

d += 2∗ k + 2 ;
y++;
}
else
d += 2∗ k ;

putPixel ( x , y , color ) ;
}
}
But all these functions heavily use floating-point mathematics which can
be expensive. So the final step is to completely get rid of all floating-point
numbers. All input parameters are integer and for simple CPUs using integer-
only arithmetic is highly efficient. Since k = (yb − ya )/(xb − xa ) and all other
floating-point values are computed by adding multiples of k and integer values
n
then all di are in the form of fractions xb −x a
.
So if we multiply di by xb − xa we will get integer-only arithmetic (note
that deltas we are using should be multiplied by it too).
void drawLine ( i nt xa , i nt ya , i nt xb , i nt yb ,
i nt c o l o r )
{
i nt dx = xb − xa ;
i nt dy = yb − ya ;
i nt d = ( dy<<1) − dx ;
i nt d1 = dy << 1 ;
i nt d2 = ( dy − dx ) << 1 ;
i nt y = ya ;

p u t P i x e l ( xa , ya , c o l o r ) ;
for ( i nt x = xa +1; x <= xb ; x++ )
{
if ( d > 0 )
{
d += d2 ;
y++;
}
else
d += d1 ;

putPixel ( x , y , color ) ;
}
}
This code can be easily converted to get a 4-connected line rasterization.
void drawLine4 ( i nt xa , i nt ya , i nt xb , i nt yb ,
Basic raster algorithms 87

i nt c o l o r )
{
i nt dx = xb − xa ;
i nt dy = yb − ya ;
i nt d = ( dy<<1) − dx ;
i nt d1 = dy << 1 ;
i nt d2 = ( dy − dx ) << 1 ;
i nt x = xa ;
i nt y = ya ;

p u t P i x e l ( xa , ya , c o l o r ) ;
for ( i nt i = 1 ; i <= dy + dx ; i++ )
{
if ( d > 0 )
{
d += d2 ;
y++;
}
else
{
d += d1 ;
x++;
}

putPixel ( x , y , color ) ;
}
}
Now that we have a line rasterization algorithm for the case 0 ≤ yb − ya ≤
xb − xa we can adapt it the to the general case.
Note that the case 0 ≤ −(yb − ya ) ≤ xb − xa is symmetric to the already
considered case – we need to change y by −1 instead of 1 and the case 0 ≤
yb − ya > xb − xa is reduced to a previously considered case by swapping x
and y variables.
So we get the following C++ code for rendering any line segments (note
that it may be more efficient to detect horizontal and vertical lines and process
them separately).
void drawLine ( i nt xa , i nt ya , i nt xb , i nt yb ,
i nt c o l o r )
{
i nt dx = abs ( xb − xa ) ;
i nt dy = abs ( yb − ya ) ;
i nt sx = xb >= xa ? 1 : −1; // s i g n o f xb−xa
i nt sy = yb >= xy ? 1 : −1; // s i g n o f yb−ya
88 Computer Graphics : From Pixels to Programmable Graphics Hardware

i f ( dy <= dx )
{
i nt d = ( dy<<1) − dx ;
i nt d1 = dy << 1 ;
i nt d2 = ( dy − dx ) << 1 ;
i nt x = xa + sx ;
i nt y = ya ;

p u t P i x e l ( xa , ya , c o l o r ) ;
for ( i nt i = 1 ; i <= dx ; i ++, x += sx )
{
if ( d > 0 )
{
d += d2 ;
y += sy ;
}
else
d += d1 ;

putPixel ( x , y , color ) ;
}
}
else
{
i nt d = ( dx<<1) − dy ;
i nt d1 = dx << 1 ;
i nt d2 = ( dx − dy ) << 1 ;
i nt x = xa ;
i nt y = ya + sy ;

p u t P i x e l ( xa , ya , c o l o r ) ;
for ( i nt i = 1 ; i <= dy ; i ++, y += sy )
{
if ( d > 0 )
{
d += d2 ;
x += sx ;
}
else
d += d1 ;

putPixel ( x , y , color ) ;
}
}
}
Basic raster algorithms 89

The code contains only integer arithmetic and has no multiplications and
divisions. Because of it the code may be easily implemented even on very
simple processors.

5.3 Bresenheim’s circle algorithm


The algorithm described above can be modified to rasterize circles.

FIGURE 5.5: Part of the circle used for rasterizing.

First we write down an algorithm for a circle with radius R centered at a


coordinate origin and then apply a translation to get an arbitrary circle.
Due to circle symmetry it is sufficient to rasterize only 1/8 of the circle
(see Figure 5.5). That part of the circle corresponds to x-values in the range
[0, √R2 ] and y-value from √R2 to R and we will be rasterizing it.
An important property of this part of the circle is that the slope at any of
its points lies in the range [−1, 0].
The function drawCirclePoints draws 8 pixels per one call – it will apply
symmetry to get all pixels from using pixel coordinates (from the arc) and
circle center (xc, yc).
void d r a w C i r c l e P o i n t s ( i nt xc , i nt yc , i nt x , i nt y ,
i nt c o l o r )
{
p u t P i x e l ( xc + x , yc + y , c o l o r ) ;
p u t P i x e l ( xc + y , yc + x , c o l o r ) ;
90 Computer Graphics : From Pixels to Programmable Graphics Hardware

putPixel ( xc + y, yc − x, color );
putPixel ( xc + x, yc − y, color );
putPixel ( xc − x, yc − y, color );
putPixel ( xc − y, yc − x, color );
putPixel ( xc − y, yc + x, color );
putPixel ( xc − x, yc + y, color );
}
To check points for being inside/outside the circle we introduce the test
function

F (x, y) = x2 + y 2 − R2 . (5.2)
The function is equal to zero on the circle, negative inside circle and posi-
tive outside it.
Now we can start building a sequence of pixels. The first pixel is obvious
– x0 = 0, y0 = R.
Next, the x-value of a pixel based on the previous one is obvious too –
xi+1 = xi + 1. For yi+1 we have only two possible choices (due to the slope
being in [−1, 0] range) – yi or yi −1. We check the midpoint between these two
values to see whether it is inside or outside the circle. To do so we introduce
the decision variable

di = F (xi + 1, yi − 1/2) = (xi + 1)2 + (yi − 1/2)2 − R2 . (5.3)

FIGURE 5.6: Several produced pixels and the corresponding midpoints.

If di < 0 the midpoint (xi + 1, yi − 1/2) is inside our circle, and we set
yi+1 = yi . Otherwise (di ≥ 0) we set yi+1 = yi − 1. Now we need to see how
our decision variable changes depending on our choice.
If di < 0 we have the following equation for di+1

di+1 = F (xi+2 , yi+1 − 1/2) = F (xi + 2, yi − 1/2). (5.4)


Therefore,
Basic raster algorithms 91

di+1 − di = 2xi + 3. (5.5)


If di ≥ 0 since yi+1 = yi − 1 we have

di+1 = F (xi + 2, yi − 3/2) = (xi + 2)2 + (yi − 3/2)2 − R2 . (5.6)


This gives us an increment for decision variable

di+1 − di = 2(xi − yi ) + 5. (5.7)


Using Equations (5.5) and (5.7) we can easily update our decision variable
when we are moving to the next pixel. The initial value of decision variable
d0 = F (0 + 1, R − 1/2) = 5/4 − R.
We can write the following C++ function to plot the circle.
void d r a w C i r c l e ( i nt xc , i nt yc , i nt r , i nt c o l o r )
{
i nt x = 0;
i nt y = r;
float d = 1.25 f − r ;

d r a w C i r c l e P o i n t s ( xc , yc , x , y , c o l o r ) ;

while ( y > x )
{
if ( d < 0 )
{
d += 2∗ x + 3 ;
x++;
}
else
{
d += 2 ∗ ( x−y )+ 5 ;
x++;
y−−;
}
d r a w C i r c l e P o i n t s ( xc , yc , x , y , c o l o r ) ;
}
}
Note that we always change our decision variable by an integer value and
since its initial value has the fractional part of 1/4, it means that for every
pixel the fractional part of a decision variable will still be 1/4. Because of it we
can simply ignore the fractional part of di and di becomes an integer variable.
Thus, we get the following integer-only code.
void d r a w C i r c l e ( i nt xc , i nt yc , i nt r , i nt c o l o r )
92 Computer Graphics : From Pixels to Programmable Graphics Hardware

{
i nt x = 0;
i nt y = r;
i nt d = 1 − r;
i nt delta1 = 3;
i nt delta2 = −2∗ r +5;

d r a w C i r c l e P o i n t s ( xc , yc , x , y , c o l o r ) ;

while ( y > x )
{
if ( d < 0 )
{
d += d e l t a 1 ;
d e l t a 1 += 2 ;
d e l t a 2 += 2 ;
x++;
}
else
{
d += d e l t a 2 ;
d e l t a 1 += 2 ;
d e l t a 2 += 4 ;
x++;
y−−;
}

d r a w C i r c l e P o i n t s ( xc , yc , x , y , c o l o r ) ;
}
}

5.4 Triangle filling


Usually all objects in 3D-computer graphics are composed of triangles so
the triangle rasterization is used very often. As a rule, graphics hardware
supports only triangles or convex polygons easily decomposed into triangles
(as in OpenGL).
One of the simplest ways to rasterize a triangle is to rasterize its edges
and find extreme x-coordinates (a span) for every y-coordinate and fill it.
Note that using traditional line rasterization code can result in pixels some of
which can be outside of the triangle (see Figure 5.7).
Basic raster algorithms 93

FIGURE 5.7: Rasterization of triangle edges can produce pixels which are
outside of the triangle.

Usually it is undesirable to include such pixels because they can conflict


with the rasterization of adjacent triangles – overlapping pixels can cause
artifacts. Another problem may appear when we rasterize a triangle mesh
where one triangle shares common vertices with other triangles. Rasterization
of such a mesh should give no holes in the resulting pixel set. But it is also
very undesirable to have duplicate pixels, i.e., when the same pixel is produced
by rasterization of several non-overlapping triangles (see Figure 5.8).

FIGURE 5.8: Triangle mesh.

To cope with this problem the top-left rule is often used. According to this
rule the pixel is produced if its center lies inside the triangle or it lies on the
top or left edge.
Here a top edge is the horizontal edge above all other edges of the triangle.
A left edge is a non-horizontal edge on the left side of the triangle (see Figure
5.9). Note that the triangle can have either one or two left edges.
In Figure 5.10 the rasterization of several triangles are shown.
Figure 5.11 shows rasterizations of two triangles and a rectangle, composed
of two such triangles. As you can see, there are no duplicate pixels or holes in
94 Computer Graphics : From Pixels to Programmable Graphics Hardware

FIGURE 5.9: Top and left edges.

FIGURE 5.10: Triangle rasterizations.

the rectangle rasterization. Note that if you have a group of such rectangles
touching each other then there also will be no holes nor duplicate pixels.

FIGURE 5.11: Rectangle rasterization.

Here for triangle rasterization we will use a simple but effective algorithm
often used by graphics hardware.
Basic raster algorithms 95

The idea of an algorithm is very simple – let Ax + By + C = 0 be an


equation of the line coming through some edge of the triangle. This line splits
a whole plane into two parts, positive (where Ax + By + C > 0) and negative
(where Ax + By + C < 0).
Our triangle lies in one of these half-planes. We can always make it so that
it lies in the positive half-plane (if it lies in a negative half-plane then the
triangle will be in a positive half-plane for equation (−A)x + (−B)y + (−C) =
0).
Then if we have such line equations for all three edges the triangle itself
is an intersection of the corresponding three positive half-planes. If we have a
point (x0 , y0 ) and we want to check whether it lies inside the triangle we can
simply substitute it into the three line equations and check whether we will
get three non-negative numbers.
Thus, we can write for a triangle a system of inequalities fully describing
it in the following way


⎨f1 (x, y) = A1 x + B1 y + C1 ≥ 0,
f2 (x, y) = A2 x + B2 y + C2 ≥ 0, (5.8)


f3 (x, y) = A3 x + B3 y + C3 ≥ 0.
We can easily find a bounding box of the triangle xmin , ymin , xmax , ymax
and then for every point of this box we can check whether it lies inside our
triangle. In the process of checking we will use Equations (5.8) are all based
on linear functions.
So we have come to the following code.
First, we need a function that will compute line equation coefficients from
two points the line is passing through. This function will receive one more
point – point that should be in the positive half-plane. These three points
uniquely define the half-plane.
i n l i n e void b u i l d P l a n e (
const p o i n t& p1 , const p o i n t& p2 ,
const p o i n t& p3 ,
i nt& a , i nt& b , i nt&c )
{
// f i n d l i n e e q u a t i o n s from p1 and p2
a = p2 . y − p1 . y ;
b = p1 . x − p2 . x ;
c = p1 . x∗ p2 . y − p1 . y∗ p2 . x ;

// c h e c k w h e t h e r p3 i s i n p o s i t i v e
i f ( a ∗p3 . x+b∗ p3 . y+c < 0 )
{
a = −a ;
b = −b ;
c = −c ;
96 Computer Graphics : From Pixels to Programmable Graphics Hardware

}
}
Then the rasterizing code will look as shown below.
void r a s t e r i z e T r i a n g l e ( p o i n t p [ ] )
{
i nt xMin = p [ 0 ] . x ;
i nt yMin = p [ 0 ] . y ;
i nt xMax = p [ 0 ] . x ;
i nt yMax = p [ 0 ] . y ;
i nt a [ 3 ] , b [ 3 ] , c [ 3 ] ;

// f i n d bou n din g box


for ( i nt i = 1 ; i < 3 ; i++ )
{
i f ( p [ i ] . x < xMin )
xMin = p [ i ] . x ;
else
i f ( p [ i ] . x > xMax )
xMax = p [ i ] . x ;

if ( p [ i ] . y < yMin )
yMin = p[ i ]. y;
else
if ( p [ i ] . y > yMax )
yMax = p[ i ]. y;
}

// b u i l d l i n e equations
buildEquation (p [ 0 ] , p[1] , p[2] , a[0] , b[0] , c [2]);
buildEquation (p [ 0 ] , p[2] , p[1] , a[1] , b[1] , c [1]);
buildEquation (p [ 1 ] , p[2] , p[0] , a[2] , b[2] , c [2]);

// f i n d f u n c t i o n s a t lower − l e f t c o r n e r
i nt d0 = a [ 0 ] ∗ xMin+b [ 0 ] ∗ yMin+c [ 0 ] ;
i nt d1 = a [ 1 ] ∗ xMin+b [ 1 ] ∗ yMin+c [ 1 ] ;
i nt d2 = a [ 2 ] ∗ xMin+b [ 2 ] ∗ yMin+c [ 2 ] ;

// c h e c k p o i n t s
for ( i nt y = yMin ; y <= yMax ; y++ )
{
i nt f 0 = d0 ,
f 1 = d1 ,
f 2 = d2 ;
Basic raster algorithms 97

d0 += b [ 0 ] ;
d1 += b [ 1 ] ;
d2 += b [ 2 ] ;

for ( i nt x = xMin ; x <= xMax ; x++ )


{
i f ( f 0 >= 0 && f 1 >= 0 && f 2 >= 0 )
putPixel ( x , y ) ;

f 0 += a [ 0 ] ;
f 1 += a [ 1 ] ;
f 2 += a [ 2 ] ;
}
}
}
Note that sometimes the requirement to include only pixels inside the
triangle can lead to undesirable artifacts. Figure 5.12 shows a triangle where
this requirement results in missing pixels and a raster representation of the
triangle is not connected.

FIGURE 5.12: For thin angles we get “lost” pixels.

5.5 Flood fill algorithm


Another type of shape filling is more common to 2D-graphics and is usually
called a flood or boundary fill. It is the case where a figure (area) to be filled is
given by it’s boundary color and we know some point (seed ) inside the area.
98 Computer Graphics : From Pixels to Programmable Graphics Hardware

The area may be complex and have holes in it, but all its pixels must be
reachable from the seed pixel (see Figure 5.13).

FIGURE 5.13: Flood or boundary fill.

The simplest boundary fill algorithm can be written in just a few lines of
code.
void b o u n d a r y F i l l ( i nt x , i nt y , i nt b o r d e r C o l o r ,
i nt f i l l C o l o r )
{
i nt c = g e t P i x e l ( x , y ) ;

i f ( ( c != b o r d e r C o l o r ) && ( c != f i l l C o l o r ) )
{
putPixel ( x, y, fillColor );
boundaryFill ( x − 1 , y , borderColor , f i l l C o l o r );
boundaryFill ( x + 1 , y , borderColor , f i l l C o l o r );
boundaryFill ( x , y − 1 , borderColor , f i l l C o l o r );
boundaryFill ( x , y + 1 , borderColor , f i l l C o l o r );
}
}
Despite its simplicity this algorithm can handle a very complex area. How-
ever, it’s very inefficient and leads to very deep recursion, so it can be used
only for small areas.
To build an efficient boundary filling algorithm we should utilize coherence
– if some pixel (x, y) is inside the area to be filled then its neighboring pixels
are probably inside the area too.
We can fill the area by horizontal spans – for the current point (x, y) inside
the area we find a maximum span (xl , y) − (xr , y) , which is completely inside
the area and contains our point. After the found span is filled we move down
– all pixels on the next line are checked which can result in the forming of
several non-overlapping spans (see Figure 5.14).
Basic raster algorithms 99

FIGURE 5.14: Sequence of horizontal spans used to fill an area.

We should not only go down, but also go up. To do this, it is sufficient to


check pixels above the current span which were not in the previous span (see
Figure 5.15)

FIGURE 5.15: For some spans we may find an unfilled area above it.

The algorithm below leads to the following code (note that the algorithm
is recursive but the level of recursion is not very deep).
i nt l i n e F i l l (
i nt x , i nt y , i nt d i r , i nt prevXl , i nt prevXr ,
i nt b o r d e r C o l o r , i nt f i l l C o l o r )
{
i nt x l = x ;
i nt xr = x ;
i nt c ;

do
c = g e t P i x e l ( −−xl , y ) ;
while ( ( c != b o r d e r C o l o r ) && ( c != f i l l C o l o r ) ) ;
100 Computer Graphics : From Pixels to Programmable Graphics Hardware

do
c = g e t P i x e l ( ++xl , y ) ;
while ( ( c != b o r d e r C o l o r ) && ( c != f i l l C o l o r ) ) ;

x l ++;
xr −−;

drawLine ( xl , y , xr , y ) ; // f i l l t h e span

for ( x = x l ; x <= xr ; x++ )


{
c = getPixel ( x , y + dir );
i f ( ( c != b o r d e r C o l o r ) && ( c != f i l l C o l o r ) )
x = l i n e F i l l ( x , y + d i r , d i r , xl , xr ,
borderColor , f i l l C o l o r ) ;
}

for ( x = x l ; x < pr evXl ; x++ )


{
c = getPixel ( x , y − dir );
i f ( ( c != b o r d e r C o l o r ) && ( c != f i l l C o l o r ) )
x = l i n e F i l l ( x , y − d i r , −d i r , xl , xr ,
borderColor , f i l l C o l o r ) ;
}

for ( x = prevXr ; x < xr ; x++ )


{
c = getPixel ( x , y − dir );
i f ( ( c != b o r d e r C o l o r ) && ( c != f i l l C o l o r ) )
x = l i n e F i l l ( x , y − d i r , −d i r , xl , xr ,
borderColor , f i l l C o l o r ) ;
}

return xr ;
}
Then the resulting boundary fill function will be very simple and is shown
below.
void b o u n d a r y F i l l ( i nt x , i nt y , i nt b o r d e r C o l o r ,
i nt f i l l C o l o r )
{
l i n e F i l l ( x , y , 1 , x , x , borderColor , f i l l C o l o r ) ;
}

You might also like