Complete Derivation of the Algorithm
Sample Output
Bresenham's line-algorithm source code
The first routine was written strictly for clarity and is a direct implementation from my derivation paper. The other two routines were optimized for a linear framebuffer and the instructions used were reduced in complexity to allow easy implementation into assembly language.
Midpoint Problem with Double-Ended Bresenham Algorithm
With close inspection of the Bresenham algorithm that draws from both ends simultaneously, it becomes quite apparent that this line drawing routine forces symmetry about the line's midpoint. For lines whose number of steps in the independent variable is even (variable we are stepping in to find dependent values), this is not a problem; however, when it is odd we are confronted with a line that can not be drawn symmetrically about the midpoint. Here is an example of a line that has an odd number of steps in the independent variable, clearly showing the midpoint problem:
from both ends from A to B from B to A . . . . B . . . . B . . . . B . . X X . . . . X . . . X X . . X X . . . X X . . . X . . . A . . . . A . . . . A . . . .
The first section of the line drawing from point A could be given one more point than the section drawing from point B. So in our example, the independent variable is X and the looping variable (half of the change in the independent variable) becomes floor(dX/2) or floor(4/2) or 2; each section will draw two points and then section A will be allowed to draw the next point, as opposed to the current double-ended version, which calculates a looping variable of floor(5/2) or 2, but instead allows both sections to draw three points, thus causing the overlap. It is important to remember that the change in the independent variable and the number of steps in the independent variable required to draw the line differ by one (dX is one less, since dX is Bx-Ax or Ax-Bx).
Even Faster Without the Midpoint Problem?
The "FASTEST" third version of the algorithm is clearly the right approach (see performance comparisons below), except that it has two glaring weaknesses: a large constant term (37 cycles) that is required for all lines and worst of all, the problem at the midpoint where the two line segments meet. With close inspection of version 3, we should be able to implement the midpoint corrections AND reduce the amount of overhead (the constant term). How?
First of all, the absolute values of dX and dY can be easily found within the calculations of the FrameBuffer increments. Then, several cycles can be trimmed out of the inner-loops by utilizing the fact that most assembly languages have operations that perform an arithmetic calculation, compare the result to zero, and then branch; even if it doesn't it is still faster to perform an arithmetic operation (that sets the sign bit) and then branch on the condition of the sign bit. So instead of doing this:
dX--; if (dX>=0) goto XLOOP;Perform the calculation, compare, and branch in one or two operations:
if ((dX=dX-1) >= 0) goto XLOOP;In C, the advantages of this organization may not be apparent, but in assembly, it is a clear optimization. This strategy was applied to dX, dY, and to the decision variable (with a little alterations). But what about the MidPoint problem? This one's easy. We can simply cut both ends off one pixel short of finishing its line segment. Then, depending on whether there are an odd or even number of steps in the independent variable, we can draw one or both of the remmaining points. We can safely plot one of the endpoints, but we must only plot the other if the number of steps is even (or in our case if the change in the independent variable is odd; remember dX or dY = #steps-1).
Well, here it is: THE FASTEST VERSION
Performance of Bresenham's Algorithm
The equations for the average number of cycles required for a line of length N are derived from the following tables (only versions 2 and 3 were included):
The number of cycles were computed from the average number of cycles needed to create a line in a "starburst" or "spoked wheel" of lines originating from a central point with a radius of line-length N. This allows calculation of the average number of cycles needed for lines of any slope for a particular line length (radius of the startburst). The cycles per unitlength were computed by dividing the average number of cycles for a line of length N (NumCycles) by the length N.The formula for the average number of computation cycles for a line of length N can be found quite easily from the table data if we plot the NumCycles as a function of Length (N). If we let Length(N) be represented by the X-axis and NumCycles by the Y-axis, we should be able to derive a linear equation of the form
NumCycles for Line Length N = f(N) = m*N+Constantwhere m is the slope of the line (ChangeInNumCycles / ChangeInLength) and the constant is the Y-intercept that represents the number of cycles needed in preparation of the line routine for lines of any length (cycles outside of "inner-loop"). This constant can be found by calculating the number of cycles required for a theoretical line of length 0. The slope for a particular number of cycles is found by solving for the line equation m = (NumCycles-Constant) / Length N. Basically, the line that is plotted from the table data is translated so that it intersects the origin and then the slope is easily calculated. Once we obtained a list of slopes, we were able to find the average slope. Now with this slope and the constant Y-intercept value, we have the equation for the number of cycles for a line of length N:
NumCycles = AverageSlope * Length + Constant
Complete Source Code
This the complete line drawing test source code