Graphics 1 CMP-5010B
Dr. David Greenwood
Spring 2022
Improving the efficiency of the DDA line drawing algorithm.
Let’s make clear some assumptions:
Following these assumptions, the simplest algorithm is:
for x = x0 to x1:
decide y value
draw(x, y)
What is an efficient way to decide the \(y\) value?
As we step in the \(x\) direction, we observe that:
We can include this observation in our algorithm:
x = x0
y = y0
draw(x, y)
while x < x1:
x = x + 1
if y should increment:
y = y + 1
draw(x, y)
Assuming the line is given by \(y = mx + c\):
y = round(mx) + c
Let fraction
be the amount \(y\) has increased since the last \(y\) increase.
fraction
is \(\geq \frac{1}{2}\).x = x0
y = y0
fraction = start_value
fraction_step = (y1 - y0) / (x1 - x0)
draw(x, y)
while x < x1:
x = x + 1
fraction = fraction + fraction_step
if fraction >= 1/2:
y = y + 1
fraction = fraction - 1
draw(x, y)
First we have: \(m = \frac{y_1 - y_0}{x_1 - x_0}\)
hence:
\[ \begin{aligned} fraction\_step &= \frac{y1 - y0}{x1 - x0} \times (x1 - x0) \times 2 \\ &= 2 (y1 - y0) \end{aligned} \]
We also want to set a start_value
for fraction
:
\[ start\_value = 2(y_1 - y_0) - (x_1 - x_0) \]
x = x0
y = y0
fraction = 2 * (y1 - y0) - (x1 - x0)
fraction_step = 2 * (y1 - y0)
draw(x, y)
while x < x1:
x = x + 1
fraction = fraction + fraction_step
if fraction >= 0:
y = y + 1
fraction = fraction - 2 * (x1 - x0)
draw(x, y)
There are other approaches to deriving the Bresenham Line Algorithm. The parts are the same, but some details are presented differently.
The course text makes the decision to move up in y
based on the distance between the true line and the nearest pixel.
Midpoint is a variation of Bresenham’s Line Algorithm.
Same improvement goals:
The midpoint algorithm uses 8 compass points to describe the next pixel to draw:
We will describe the algorithm just for the upper right octant.
For a previous pixel p
in the upper right octant, we label the two candidate pixels E and NE.
We will define criteria based on the midpoint between the two candidates.
The algorithm decides if a true line passes either above, below or through the midpoint.
IF the true line is below or on the midpoint: pick the E pixel.
ELSE: pick the NE pixel.
We will use the implicit line equation:
\[ ax + by + c = 0 \]
We know that:
\[ a = \Delta y~, b = -\Delta x~ \Rightarrow f(x, y) = x \Delta y - y \Delta x + c = 0 \]
N.B. henceforth we will assume \(c=0\), and remove from the derivations.
IF the line goes exactly through the midpoint then we have the decision variable:
\[ \begin{aligned} D &= f(x_p + 1, y_p + \tfrac{1}{2}) \\ &= a_{(m)} (x_p + 1) + b_{(m)} (y_p + \tfrac{1}{2}) \\ &= 0 \end{aligned} \]
recall, in the upper right octant: \(a > 0, ~b < 0\)
IF the line goes below the midpoint:
\[a<a_{(m)} \land b>b_{(m)} \Rightarrow D < 0 \Rightarrow E\]
The actual value of \(D(E)\) is:
\[ \begin{aligned} D(E) &= f(x_p + 1, y_p) \\ &= a(x_p + 1) + b y_p \\ &= a x_p + a + b y_p \\ &= f(x_p, y_p) + a \end{aligned} \]
ELSE the line goes above the midpoint:
\[a>a_{(m)} \land b<b_{(m)} \Rightarrow D > 0 \Rightarrow NE\]
The actual value of \(D(NE)\) is:
\[ \begin{aligned} D(NE) &= f(x_p + 1, y_p + 1) \\ &= a(x_p + 1) + b(y_p + 1) \\ &= a x_p + a + b y_p + b \\ &= f(x_p, y_p) + a + b \end{aligned} \]
To avoid having to recalculate actual decision variable values each time we move one pixel in x, we can derive a decision variable increment instead.
We do this by looking ahead to the next pixel.
If we have chosen the E pixel then the next midpoint will be at:
\[ \begin{aligned} D_{mE} &= f(x_p + 2, y_p + \tfrac{1}{2}) \\ &= a (x_p + 2) + b (y_p + \tfrac{1}{2}) \end{aligned} \]
Subtracting the original \(D\) gives:
\[ \begin{aligned} \Delta E &= D_{mE} - D \\ &= a \\ &= \Delta y \end{aligned} \]
If we have chosen the NE pixel then the next midpoint will be at:
\[ \begin{aligned} D_{mNE} &= f(x_p + 2, y_p + \tfrac{3}{2}) \\ &= a (x_p + 2) + b (y_p + \tfrac{3}{2}) \end{aligned} \]
Subtracting the original \(D\) gives:
\[ \begin{aligned} \Delta NE &= D_{mNE} - D \\ &= a+b \\ &= \Delta y - \Delta x \end{aligned} \]
If the decision variable relies on the previous pixel, what is the decision variable for the first pixel?
Since the start point is on the line:
\[ f(x_0, y_0) = 0 \]
Substituting into the decision variable gives:
\[ \begin{aligned} D_{init} &= f(x_0 + 1, y_0 + \tfrac{1}{2}) \\ &= a (x_0 + 1) + b (y_0 + \tfrac{1}{2}) \\ &= a x_0 + b y_0 + a + \tfrac{1}{2} b \\ &= f(x_0, y_0) + a + \tfrac{1}{2} b \end{aligned} \]
This yields:
\[D_{init} = a + \tfrac{b}{2}\]
We want to remove floating point arithmetic, so we can multiply by \(2\), however, we must also do this to the decision variable increments.
Finally, our initial decision variable is:
\[ D_{init} = 2a + b = 2 \Delta y - \Delta x \]
and the decision variable increments are:
\[ \Delta E = 2 \Delta y,~ \Delta NE = 2 (\Delta y - \Delta x) \]
void lineMid(int x0, int y0, int xEnd, int yEnd){
int dx=xEnd-x0, dy=yEnd-y0, x=x0, y=y0;
int E_inc = 2*dy, NE_inc = 2*(dy-dx), D = 2*dy-dx;
setPixel(x,y);
while(x<xEnd){
if (D > 0){
D += NE_inc;
x++; y++;
} else {
D += E_inc;
x++;
}
setPixel(x,y);
} }
Aliasing is a distortion artifact when representing a high-resolution image at a lower resolution.
To mitigate aliasing, we can use a technique called anti-aliasing.
We will consider a few possible approaches.
The first approach is to consider a higher resolution display.
We can render an artificially thick line.
We can render to a sub-pixel grid.
We can filter the image.
Reading: