Copy Link
Add to Bookmark
Report

The Bresenham algorithm

DrWatson's profile picture
Published in 
atari
 · 10 Nov 2023

:EARchaeopteryx says:

ÿÿf1!FUCK BRESENHAM!ÿÿf0

After that powerful title that could start world war III, you probably expect something mindboggling... something cunning.... something like:

A replacement for the bresenham algorithm?!?!

For those of you that don't know what the hell that is, I should say: "And you call yourself a coder!" ;-)

The bresenham algorithm relies on first calculating a few simple discriminants. In the main loop these discriminants added to/subtracted from each other. This is quite efficient, because the loop only consists of some simple and fast instructions.

Let's cut the crap and get to it. I found out a quite simple algorithm that beats the old bresenham to it. It is based on the following incredibly simple math >>>> steepness = dX/dY >>>> Where dX is the difference between the x-coordinate of point 1 and point 2 of the line and dY is the same for the y-coordinate.

OK, let's get to the code then, shall we? Here is it in dumb Pascal. And all you speedfreaks: don't worry, I know this isn't necessarily faster than Bresenham, but the assembler version is!!

------------------------------ òsourcecode 1 ð--------------------------------- 

Procedure DrawLine(integer: X1, X2, Y1, Y2)
var
X, Y, dX, dY: integer;
steepness, d: real;
begin
dX:=X2-X1;
dY:=Y2-Y1;
if dX > dY then
{The loop for the situation where dX > dY}
begin
steepness:=dY/dX
d:=Y1;
for X:=X1 to X2 do
begin
PutPixel(X, Trunc(d), 1); {plot the pixel by using the integer part}
d:=d+steepness; {add steepness to get the new Y}
end;
end;
else
{The same loop for the situation where dX =< DY}
begin
steepness:=dX/dY;
d:=X1;
for Y:=Y1 to Y2 do
begin
PutPixel(Trunc(d), Y, 1) {plot the pixel by using the integer part}
d:=d+steepness; {add steepness to get the new X}
end;
end;
end;

--------------------------- òend of sourcecode 1 ð-----------------------------

There it was in Pascal. Of course this still is slow. And I'm not talking about the normal Pascal compilers that are crap, but about the instructions used. There is a floating-point division at the initilializing part of the procedure, so this is quite slow when the line we want to draw is short (i.e. the main loop is done only a few times).

The good thing however, is that only very simple instructions are used in the main loop (for..do statement). And of course the main loop is executed every time a pixel is drawn so that mostly outweights the executing time of the initializing part.

The big difference with bresenham algo is that this uses floating-point numbers whereas bresenham only uses integers....

...What's that??......I hear a voice screaming from far away....

Y0u S0d!! th@t'5 th3 wh0le p0inT: N0t us1nG 3xpeNs1vE floating-point op3raTi0n5!

Now that might be true, but in the assembler version for the beautiful 680x0 series of processors, we don't need that shit!! We can do it with fairly cheap fixed point numbers! HARHAR!

The "< Trunc(d) >" statement from the Pascal-source can easily be translated into: ñ< swap d0 >ð on the 68000 :-) Comprendez? Non? Well.. Let's say you have a 68000 register like d0. This contains 32 bits. Now you can divide this up into two spaces: the upper 16 bits for the integer part and the lower 16 bits for the fractational part. What the ñ< swap >ð instruction does is only swap the upper part with lower part so you can use the integer part of the number!

This is more or less the principle my new routine relies on. It uses the same technique as the Pascal algorithm, but only now with a bit of fixed point techniques and further optimisations. Of course we can't get rid of the division instruction, but we can make a "< divu.w >" out of this with some effort.

The routine I'm about to show here has NO CLIPPING so don't do anything weird with it and uses Falcon truecolor mode. (320 pixels wide!) It is a subroutine that is called by putting some values in the data-/address-registers.

------------------------------- òSourcecode 2ð -------------------------------- 

* INPUT: d0.w: X1
* d1.w: Y1
* d2.w: X2
* d3.w: Y2
* d6.w: highcolor word (color of line)
* a0: start of screenaddress
DRAW_TRUELINE
move.l d2,d4
move.l d3,d5
sub.w d0,d2 ; / calculate the absolute
bpl.s .ok ; | value of the difference
neg.w d2 ; \ between X1 and X2
.ok sub.w d1,d3 ; / calculate the absolute
bpl.s .ok2 ; | value of the difference
neg.w d3 ; \ between Y1 and Y2
.ok2 cmp.w d2,d3
bhi.s .ver
* Part for dX > dY
cmp.w d0,d4 ; / Get the
bhs.s .do2 ; | heighest
exg d0,d4 ; | X and Y
exg d1,d5 ; \ in d4.w and d5.w
.do2 moveq #10,d2 ; \put #640
lsl.l #6,d2 ; /in d2.l (bytes in scanline)
sub.w d0,d4
sub.w d1,d5
add.l d0,d0
adda.l d0,a0
mulu.w d2,d1
adda.l d1,a0
tst.w d5
bpl.s .shit
neg.w d5 ; / make the
neg.l d2 ; | dX absolute
.shit swap d5 ; | and negate the scanline-
addq.w #1,d4 ; \ offset if needed
divu.w d4,d5 ; d5.w: steepness
moveq #0,d0
subq.w #1,d4 ; d4.w: number of times to loop

.lp2 add.w d5,d0 ; / check if you need to jump to
bcc.s .mov ; \ the next scanline
adda.l d2,a0 ; jump to the next scanline
.mov move.w d6,(a0)+ ; plot and go to next pixel
dbra d4,.lp2
rts

* Part for dX =< dY
.ver cmp.w d0,d4
bhs.s .do
exg d0,d4
exg d1,d5
.do moveq #10,d2 ; \put #640
lsl.l #6,d2 ; /in d2.l (bytes in scanline)
sub.w d0,d4
sub.w d1,d5
add.l d0,d0
adda.l d0,a0
mulu.w d2,d1
adda.l d1,a0 ; a0: address of first pixel
tst.w d5 ; / make the
bpl.s .shitt ; | dY absolute
neg.w d5 ; | and negate the scanline-
neg.l d2 ; \ offset if needed
.shitt swap d4
addq.w #1,d5
divu.w d5,d4 ; d4.w: steepness
moveq #0,d0
subq.w #1,d5 ; d5.w: number of times to loop

.lp add.w d4,d0 ; / check if you need to jump to
bcc.s .movie ; \ the next pixel in the scanline
addq.l #2,a0 ; go to next pixel in scanline
.movie move.w d6,(a0) ; plot the pixel
adda.l d2,a0 ; go to next scanline
dbra d5,.lp
rts

---------------------------- òend of sourcecode 2 ð----------------------------

Well.. There you have it then. I tried this and put it up against some of the best versions of bresenham linerouts I had and it still came out victorious!

The future: This routine is almost as fast as it gets. There is only two things you could do to make it even faster:

  1. Code specific main loops for special steepnesses. ( steepness<1/8 , steepness<1/4, etc..) I really want to do this. This could boost the speed by 20-30% (!)
  2. Draw the line from both ends, so you decrease the number of loops by 50%! But this looks a bit awkward if you ask me: you get a little knot in the middle of the line. So this is fast, but not always pretty.

EarX/fUn

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT