Friday June 5, 2020
 home | about | contact | Donations

 XBOX Programming More than just a hobby...

Drawing a line... its easy when you know how

One of the first...and of course simplest things you usually learn to do, is draw a line... now there the first way...y=mx+c... then the tricker optimised way...which will improve our drawing speed a bit :)

How do we draw a line?  Any ideas?  Well when I did my very first line drawing algorithm, I used y=mx+c... where m is the gradient and c is what value y is when x is zero.

So to draw a line function we need two essential things... a starting point and an ending point...  Am I forgetting anything?  Oh yeah, and a colour..

 Code: SimpleA.cpp void DrawLine( int x0, int y0, int x1, int y1, unsigned int dwColourRGB ) {       /* code to draw our line */ }

So there's our simple function....its not got any code in it yet.... which is what we'll do now...  A thing to notice is the starting point is x0,y0...and the finishing point is x1,y1... I always try and start from 0 when I count... as pc's do it ....so do I :)

Lets hit the code...and see what a very simple line drawing function would look like....

 Code: SimpleB.cpp // This is our super cool line drawing function - of course I'm neglecting // boundary checking (e.g. clipping), but I'm sure you can add that in later :) void DrawLine( int x0, int y0, int x1, int y1, unsigned int dwColourRGB ) {       int deltax = x1-x0;       int deltay = y1-y0;       double m = deltay/deltax;         for( int x=0; x<=deltax; x++ )       {             double y=m*x;             DrawPixel( x0+x, y0+round(y), dwColour);             // or framebuffer[ x+x0 + (y+y0)*640 ]       } }

I think its pretty good... but we can do better of course... as if we set x to x0, our starting point... well instead of doing y=mx all the time we can just do y+=m;  ... and change our loop so that it stops when x is at x1.

 Code: SimpleC.cpp void DrawLine( int x0, int y0, int x1, int y1, unsigned int dwColourRGB ) {       int deltax = x1-x0;       int deltay = y1-y0;       double m = deltay/deltax;             double y=y0;       for( int x=x0; x<=x1; x++ )       {             y+=m;             DrawPixel( x0+x, y0+round(y), dwColour);       } }

But we can do a lot better... the thing thats slowing this function down, is the decimals... all that rounding and division... of course a person a long time ago, came up with a line drawing algorithm that uses only integers.... and of course its named after him: "Bresenham Line Algorthm".

How does it work?  Its principle is based on how a numberator/denominator works... as let me write the section of code below..

...

int y=y0;

for( int x=x0; x<=x1; x++ )

{

y+= deltay/deltax;

DrawPixel( x0+x, y0+round(y), dwColour);

}

...

Now that deltay/deltax thing is the problem... so if we just increment the deltay...and only increment the y, when deltay is greater then deltax....that will be a big improvment?  Of course its hard to see at first... but in a minute...due to my super simple tutorial, you'll shout out lout!..."wahooo...I've got it"..

Lets re-write those few lines of code as follows:

...

int y = 0;

int py = y0; // our y point

for( int x=x0; x<=x1; x++ )

{

y +=deltay;

if( y >= deltax )

{

py += deltay;

y -= deltax;

}

DrawPixel( x0+x, py, dwColour);

}

Just keep remembering how a fraction works.... do a small example... if we keep adding values to our numberator (top value)...then we only need to increment our y then... I put the y point in py.

Take a deep breath, as I'm going to show you the final function... its a lot.. and for the weak, it might make you faint.  But I've exaplained how it works... the reason its a little longer, is because a line can have a positive or negative gradient... it can also be in the right or left, top or bottom of the y-x axis....

 Code: Drawline.cpp #define SCREEN_WIDTH    640 #define SCREEN_HEIGHT   480   // based on the "bresenham line" principle of drawing a line with // only integers...no decimals etc   // 8 types of lines // deltax >=0     deltax <0   deltax>=0   deltax<0 // deltay >=0     deltay>=0   deltay <0   deltay<0 // // deltax >= deltay           deltax< deltay void DrawLine( int x0, int y0,                      int x1, int y1,                      unsigned int colour) {       // Now you could leave these lines out, it just means that your lines will be       // upside down, as the 0 for y, is the top left, so we need to invert our line       y1 = (SCREEN_HEIGHT)-y1;       y0 = (SCREEN_HEIGHT)-y0;         int deltax = x1-x0;       int deltay = y1-y0;         int y=0;       int x=0;         int sdx = (deltax < 0) ? -1 : 1; // sign of deltax (e.g. +ve or -ve)       int sdy = (deltay < 0) ? -1 : 1;       // sdx is the line direction... for x or y... e.g. if the x value is going left to       // right or right to left... e.g       // if deltax >0  ...then of course sdx=1       // so our line is going from left to right    ------>x +ve       // and alternatively       // if deltax <0 ... we get sdx= -1;       // and our line is     x<-------     -ve         // We only want our delta's to be positive....keep with positive numbers of       // incrment...so using our sdx/sdy value we set our delta values to +ve numbers       deltax = sdx * deltax + 1;       deltay = sdy * deltay + 1; // (-1*-6)+1) = 7         int px = x0; // starting point (x0,y0)       int py = y0;         //float ynum = deltax/2; // num stands for the numerator starting value         if( deltax >= deltay )   // comment this one, but the else side is almost the same       {             for (x = 0; x < deltax; x++)             {                   DrawPixel( px, py,  colour);                   y += deltay;     // y=mx... but if y starts at y0                                    // then we can do y+=m                   if (y >= deltax) // m=deltax/deltay  and py+=m                         {            // if the numberator is greator than the denomiator                         y -= deltax; // we increment, and subract from the numerator.                         py += sdy;                         }                   px += sdx; // x is going from x0 to x1... we just increment as we                              // move along             }       }       else       {             for( y=0; y= deltay )                   {                         x-= deltay;                         px+=sdx;                   }                   py+=sdy;             }       } }

I personally think its a great function... with that function, I build one of my very first wire frame 3D engines... a long long long time ago :)

With a bit of creativity, I managed to put together a small piece of code that you can compile and see some flashing lines etc... So you can see your line drawing algorithm in action on your xbox :)

 Code: DownloadSourceCode /***************************************************************************/ /*                                                                         */ /* Drawing a simple line with an XBOX                                      */ /*                                                                         */ /***************************************************************************/     #include     #define SCREEN_WIDTH    640 #define SCREEN_HEIGHT   480   // Point in memory where we should start writing to. // int framebuffer = 0xf0040000; int framebuffer = 0xf0010000 + 640*320 + 80;       // Remember that at an address in memory..(for the xbox its 0xf0040000), our // pixels for the screen are arranged as BGR|BGR|BGR|BGR... etc.. 3 bytes // each. void DrawPixel(int x, int y, unsigned int dwPixelColourRGB) // AARRGGBB {       // Always remember the colours for or memory is BlueGreenRed :)         unsigned char red   = (dwPixelColourRGB & 0x00ff0000) >> 16;       unsigned char blue  = (dwPixelColourRGB & 0x000000ff);       unsigned char green = (dwPixelColourRGB & 0x0000ff00) >> 8;       ((unsigned char*)framebuffer)[(x + y*SCREEN_WIDTH)*4+0] = blue;       ((unsigned char*)framebuffer)[(x + y*SCREEN_WIDTH)*4+1] = green;       ((unsigned char*)framebuffer)[(x + y*SCREEN_WIDTH)*4+2] = red;   }   // FIRST AND SIMPLE METHOD OF DRAWING A LINE USING Y=MX+C // This is our super cool line drawing function - of course I'm neglecting // boundary checking (e.g. clipping), but I'm sure you can add that in later :) void DrawLine( int x0, int y0, int x1, int y1, unsigned int dwColour ) {       y1 = (SCREEN_HEIGHT)-y1;       y0 = (SCREEN_HEIGHT)-y0;         int deltax = x1-x0;       int deltay = y1-y0;       double m = (double)deltay/(double)deltax;         for( int x=0; x<=deltax; x++ )       {             double y=m*(double)x;             DrawPixel( x0+x, y0+y, dwColour);       } }     // EFFICENT ALGORITHM FOR DRAWING A LINE // based on the "Bresenham Line" principle of drawing a line with // only integers...no decimals etc   // 8 types of lines // deltax >=0     deltax <0   deltax>=0   deltax<0 // deltay >=0     deltay>=0   deltay <0   deltay<0 // // deltax >= deltay           deltax< deltay void LineR   ( int x0, int y0,                      int x1, int y1,                      unsigned int colour) {       y1 = (SCREEN_HEIGHT)-y1;       y0 = (SCREEN_HEIGHT)-y0;         int deltax = x1-x0;       int deltay = y1-y0;         int y=0;       int x=0;         int sdx = (deltax < 0) ? -1 : 1; // sign of deltax (e.g. +ve or -ve)       int sdy = (deltay < 0) ? -1 : 1;       // sdx is the line direction... for x or y... e.g. if the x value is going left to       // right or right to left... e.g       // if deltax >0  ...then of course sdx=1       // so our line is going from left to right    ------>x +ve       // and alternatively       // if deltax <0 ... we get sdx= -1;       // and our line is     x<-------     -ve         // We only want our delta's to be positive....keep with positive numbers of       // incrment...so using our sdx/sdy value we set our delta values to +ve numbers       deltax = sdx * deltax + 1;       deltay = sdy * deltay + 1; // (-1*-6)+1) = 7         int px = x0; // starting point (x0,y0)       int py = y0;         //float ynum = deltax/2; // num stands for the numerator starting value         if( deltax >= deltay )       {             for (x = 0; x < deltax; x++)             {                   DrawPixel( px, py,  colour);                   y += deltay;     // y=mx... but if y starts at y0                                    // then we can do y+=m                   if (y >= deltax) // m=deltax/deltay  and py+=m                         {            // if the numberator is greator than the denomiator                         y -= deltax; // we increment, and subract from the numerator.                         py += sdy;                         }                   px += sdx; // x is going from x0 to x1... we just increment as we                              // move along             }       }       else       {             for( y=0; y= deltay )                   {                         x-= deltay;                         px+=sdx;                   }                   py+=sdy;             }       } }   // Simple entry point. void main() {       unsigned int c=0;       while(true)       {             c+= 10;                         SimpleLine( 0,90, 300, 50, c ); // AARRGGBB             SimpleLine( 0,150, 639, 150, 0x0000ff00 );             SimpleLine( 0,350, 300, 350, 0x000000ff );             SimpleLine( 0,450, 300, 450, 0x00ffffff );             SimpleLine( 0,479, 300, 479, 0x0000ffff );               SimpleLine( 50,100, 200, 400, 0x00ffffff );               DrawLine( 0,0, 639, 479, c );             DrawLine( 639,0,0 ,479 , c );       } }

Thank goodness... there's a lot there... but usually you hide your line drawing and pixel drawing functions away in a secret .cpp/.h file for when you need them :)

{Under Construction}

 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2020 xbdev.net - All rights reserved. Designated tutorial and software are the property of their respective owners.