www.xbdev.net xbdev - software development
Friday December 14, 2018
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; y++)

            {

                  DrawPixel(px, py, colour);

                  x+=deltax;

                  if( x >= 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 <math.h>

 

 

#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; y++)

            {

                  DrawPixel(px, py, colour);

                  x+=deltax;

                  if( x >= 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-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.