::[ PART 05 - LINE PLOTTING ]::[ Source ]::[ Printable Version ]::
::[ DISCLAIMER ]::
I will hold no responsibility to whatever happens to you, your computer, your sanity, your pet, or whatever that may happen to your existence for your reading these texts and for the outcome of the various source code(s) given in each tutorial. So in short, read at your own risk!

 
::[ INTRODUCTION ]::
Now that we know how to display pixels on various screen resolutions and bit-depths, we would want to learn the next important thing: LINES!

Obviously, if you've played a computer game or two, you'll know immediately why lines are important in computer graphics. Just imagine being an architect trying to create a masterpiece without using any lines...difficult huh?...;).

SideNote: One of the nice concepts I learned in our university is to use codes / methods that are available already. They are tested and tend to be better than the ones that will be created from scratch... I hope this way of thinking would show from now on...:).

 
::[ TUTORIAL ]::
There're various ways you could draw a line on a 2D surface. I'll discuss to you guys just one (SLOW) way today...plus one (FAST) 'bonus' algorithm courtesy of Bresenham...:).

One way to draw a line is through the use of sine/cosine. Let's refresh ourselves with both of them first (Oh yeah, using sine and cosine is SLOWWww, so avoid them...I'm using it here just as an introduction...:) ):

      cosine(angle) = X / R
      sine  (angle) = Y / R

  where:

      Y is the length of our line vertically
      X is the length of our line horizontally
      R is the length of our line from
        endpoint(x1,y1) to endpoint(x2,y2)

We get R through the Pythagorean Theorem:

      R = square root(X^2 + Y^2)

And the angle through either one of these:

      angle = ArcCosine(X / R)      or
      angle = ArcSine  (Y / R)

Getting the length of X and Y is easy. Just subtract x2 to x1 for X, and y2 to y1 for Y.

But what we're interested is X and Y because those are what we will use as our coordinates to plot to the screen. So through algebra, we manipulate our original equation to:

      X = cosine(angle) * R
      Y = sine  (angle) * R

Check also if (x2-x1) is negative (as well as (y2-y1)) because you'll be looping through them (if you choose to implement it).

I won't bother implementing what I just said above since it's not a good way to draw lines (but it WILL work, trust me).

Another way would be through the equation:

      Y = mX + B

If you don't know this equation, try finding someone who does. I believe many people have heard about this equation in their lifetime?

Now the 'bonus' algorithm I was telling you about is based on this equation. If you're clever enough, you'll be able to create a line routine from the above equation without any division and multiplications, and you'll be using only integer variables...something quite hard to do, but was achieved by a guy name Bresenham already...:).

Here's the code for Bresenham's algorithm:

  //----8<----[ CODE BEGIN ]--------//
    void line_bres(int x1,int y1, int x2,int y2,
                   unsigned int color)
    { int ctr,sdx,sdy;
      int deltax,deltay,x,y;
      int plotx = x1, ploty = y1;
   
      deltax = abs(x2-x1);
      deltay = abs(y2-y1);
   
      if(x2-x1 < 0) sdx = -1; else 
      if(x2-x1 > 0) sdx =  1; else sdx = 0;

      if(y2-y1 < 0) sdy = -1; else
      if(y2-y1 > 0) sdy =  1; else sdy = 0;

      x=deltay>>1;
      y=deltax>>1;

      pixel_highcolor(plotx, ploty, color);

      if (deltax >= deltay)
      { for(ctr=0; ctr<deltax; ctr++) {
          y += deltay;
          if (y >= deltax)
          { y -= deltax; 
            ploty += sdy; 
          }
          plotx += sdx;
          pixel_highcolor(plotx, ploty, color);
        }
      } else
      { for(ctr=0; ctr<deltay; ctr++) {
          x += deltax;
          if (x >= deltay)
          { x -= deltay; 
            plotx += sdx;  
          }
          ploty += sdy;
          pixel_highcolor(plotx, ploty, color);
        }
      }
    }
  //----8<----[ CODE END   ]--------//

As you can see, there's no multiplication nor division in Bresenham's Line Algorithm. It may look long and slow, but upon closer scrutiny, you'll notice that not all of the codes are executed...the if-statements controls which lines are executed...if deltax >= deltay, execute these set of codes, else execute another set of codes...:).

 
::[ ENDING ]::
And that's that! Not hard eh? If there is anything that's not clear to you, your mind, or to that part of you that lets you comprehend the things here, you may email me at:

Part 6 we abandon DOS and move to MS Windows! We'll learn how to create a window under MS Windows and how "messages" are handled. We will be dealing with the Win32 API.

This parting from DOS is good for us because we won't be very productive staying inside a cave are we? And after learning how to create windows and stuffs, we'll learn the basics of the OpenGL API. After that, maybe we'll probe some nitty-gritty stuffs (such as generating and walking through landscapes, loading 3D model files, etc....all in due time...;) ).

Briefly...uhh, no, there's no 'briefly' section here, you can't make a 'brief' summary about the Win32 API...:).

 
::[ NOMAD.C ]::

  #include <DOS.H>
  #include <MEM.H>
  #include <STDLIB.H>
  #include <STDIO.H>

  //--[ VESA STRUCTURE ]---------------------//
  typedef struct {
    unsigned char VESAsignature[4];     
    unsigned char VersionMinor;
    unsigned char VersionMajor;
    unsigned char far *OEM;
    long int      Capabilities;
    unsigned int  far *VideoModes;      
    unsigned int  TotalMemory;
    unsigned char Reserved[235];
  } VESA_STRUCT;

  //--[ MODE STRUCTURE ]---------------------//
  typedef struct {
    unsigned int  ModeAttribute;
    unsigned char WindowA;
    unsigned char WindowB;
    unsigned int  Granularity;    // Granularity and WinSize are
    unsigned int  WinSize;        //   used to calc bankmult
    unsigned int  WinAseg;        // Window A's starting segment
    unsigned int  WinBseg;        // Window B's starting segment
    void  (far *bankSwitch)(void);
    unsigned int  Bytesperline;   // Bytes per line
    unsigned int  Xres;           // Width of the mode
    unsigned int  Yres;           // Height of the mode
    unsigned char charWidth;      // For VESA text-modes only
    unsigned char charHeight;     // For VESA text-modes only
    unsigned char bitplanes;
    unsigned char bitsperpixel;   // If 8 then 256 colors
    unsigned char banks;
    unsigned char memorymodel;
    unsigned char banksize;
    unsigned char imagepages;
    unsigned char reserved1;
    unsigned char redmasksize;        // For 15/16-bit modes 
    unsigned char redfieldposition;   // For 15/16-bit modes 
    unsigned char greenmasksize;      // For 15/16-bit modes
    unsigned char greenfieldposition; // For 15/16-bit modes
    unsigned char bluemasksize;       // For 15/16-bit modes
    unsigned char bluefieldposition;  // For 15/16-bit modes
    unsigned char rsvdmasksize;       // For 15/16-bit modes
    unsigned char rsvdfieldposition;  // For 15/16-bit modes
    unsigned char directcolormode;
    unsigned char Reserved[216];      // Reserved
  } MODE_STRUCT;

  static int   current_bank;
  static int   bankmult;
  VESA_STRUCT  VesaINFO;
  MODE_STRUCT  ModeINFO;

  //--[ SETS SCREEN TO GIVEN MODE ]---------//
  int setGFXMODE(int mode_num)
  { _AH = 0x4F;
    _AL = 0x02;      // VBE FUNCTION 0x02!!!
    _BX = mode_num;

    geninterrupt(0x10);

    //--[ INITIALIZE GLOBAL VARIABLES ]--//
    current_bank = 0;
    bankmult = ModeINFO.WinSize / ModeINFO.Granularity;

    return _AX;     // zero if successful
  }

  //--[ SETS SCREEN TO TEXT MODE ]----------//
  void setTXTMODE() {
    _AX = 0x0003;
    geninterrupt(0x10);
  }

  //--[ SETS BANK USING BIOS ]--------------//
  void setbank_SLOW(int bank_num)
  { bank_num = bank_num * bankmult;
    if (current_bank == bank_num) return;

    _AH = 0x4F;
    _AL = 0x05;      // VBE FUNCTION 0x05!!!
    _BX = 0x0;       
    _DX = bank_num;

    geninterrupt(0x10);

    if (_AX==0x4F)   // SUCCESS
      current_bank = bank_num; 
  }

  //--[ GETS VESA INFO ]--------------------//
  char getVesaInfo(VESA_STRUCT *vesainfo)
  { _AH = 0x4F;         
    _AL = 0x00;      // VBE FUNCTION 00!!!

    asm les di,[vesainfo]
    geninterrupt(0x10);

    if(_AX==0x4F) return(1);     
    return(0);
  }

  //--[ GETS MODE INFO ]--------------------//
  char getVesaModeInfo(unsigned int mode, 
                         MODE_STRUCT *modeinfo)
  { _AH = 0x4F;
    _AL = 0x01;      // VBE FUNCTION 01!!!
    _CX = mode;
      
    asm les di,[modeinfo]
    geninterrupt(0x10); 

    if(_AX==0x4F) {
      return(1);
    }
    return(0);
  }

  //--[ PACKS RGB TO AN UNSIGNED INT ]------//
  unsigned int RGB15bit(unsigned char R,
                        unsigned char G,
                        unsigned char B)
  { unsigned int Red,Green,Blue;
    unsigned int Final_Color = 0;

    R     = R >> 1; 
    Red   = R << 10;

    G     = G >> 1;
    Green = G << 5;

    B     = B >> 1;
    Blue  = B << 0;     
    Final_Color = Final_Color + Red + Green + Blue;
    
    return(Final_Color);
  }

  //--[ PLOTS PIXEL DIRECTLY TO MEMORY ]---//
  void pixel_highcolor(int x, int y, unsigned int colornum)
  { long int offset = (long)y*ModeINFO.Bytesperline + x + x;
    unsigned int    temp = colornum << 8;
    unsigned char   ch1;
    unsigned char   ch2;

    ch1  = temp     >> 8;
    ch2  = colornum >> 8;

    setbank_SLOW(offset >> 16);
    memset(MK_FP(0xA000,offset),   ch1, 1);
    memset(MK_FP(0xA000,offset+1), ch2, 1);
  }

  //--[ BRESENHAM'S LINE PLOTTING ALGO ]---//
  void line_bres(int x1,int y1, int x2,int y2,
                 unsigned int color)
  { int ctr,sdx,sdy;
    int deltax,deltay,x,y;
    int plotx = x1, ploty = y1;
   
    deltax = abs(x2-x1);
    deltay = abs(y2-y1);
   
    if(x2-x1 < 0) sdx = -1; else 
    if(x2-x1 > 0) sdx =  1; else sdx = 0;

    if(y2-y1 < 0) sdy = -1; else
    if(y2-y1 > 0) sdy =  1; else sdy = 0;

    x=deltay>>1;       
    y=deltax>>1;

    pixel_highcolor(plotx, ploty, color);

    if (deltax >= deltay)
    { for(ctr=0; ctr= deltax)
        { y -= deltax; 
          ploty += sdy; 
        }
        plotx += sdx;
        pixel_highcolor(plotx, ploty, color);
      }
    } else
    { for(ctr=0; ctr= deltay)
        { x -= deltay; 
          plotx += sdx;  
        }
        ploty += sdy;
        pixel_highcolor(plotx, ploty, color);
      }
    }
  }

  void main() {
    unsigned int *runner;
    unsigned int found = 0;
    long int     ctr;

    setTXTMODE();

    printf("\n\nNOMAD GRAPHICS TUTORIAL - [ PART 5 ]\n");
    printf("\n  This is a sample code included with GFXTUT5.TXT");
    printf("\n  to show how to implement the sample codes that");
    printf("\n  were given in the said tutorial.\n");
    printf("\n  [ Press Any Key to Begin ]");

    (void)getch();

    if(getVesaInfo(&VesaINFO)==1) {
      runner = VesaINFO.VideoModes;

      while((*runner)!=0xFFFF) {
        if((*runner)==0x110) found = 1;
	runner++;
      }

      if(found==1) {
	getVesaModeInfo(0x110,&ModeINFO);
  	setGFXMODE(0x110);
	for(ctr=0; ctr<100000; ctr++)
          line_bres(random(640), random(480), 
                    random(640), random(480),
                    RGB15bit(random(63),random(63),random(63))) 

        setTXTMODE();

        printf("\n\nNOMAD GRAPHICS TUTORIAL - [ PART 5 ]\n");
        printf("\n  That's all for now...:)\n");
        printf("\n  email: [ willietang@hehe.com ]\n");

      } else
      { setTXTMODE();

        printf("\n\nNOMAD GRAPHICS TUTORIAL - [ PART 5 ]\n");
        printf("\n  SORRY! It seems your monitor doesn't support the");
        printf("\n  screen mode 640x480-15bit through VESA.\n");
        printf("\n  That's all for now...:)\n");
        printf("\n  email: [ willietang@hehe.com ]\n");
      }
    }
  }
This tutorial was written on: 10.22.2001 | Updated: 12.01.2001
:.Site.:.Menu.:
:// .site..news.
:// .tutorials..1.
:// .tutorials..2.
:// .projects.
:// .forums.
:// .gbook.
:// .links.