Graphics Programming Basics

Table of Contents:
Some Assumptions
Setting the Video Mode
A Pixel
A example program for pixels


Some Assumptions

First, if you haven't figured it out, this series of articles will be directed towards the PC. Starting with the basics, we will work towards faster, more effiecient routines. I will focus on providing FAST graphics routines that can be used by anyone. I'm writing examples in Turbo Pascal (Which is easily ported to C) with some inline assembler later on. I will progress from basic algorithms (the easiest) to complex algorithms which are much more effiecient. As a final step, I'll convert vital parts to assembler. The beginner will hopefully find this valuable as a resource, and will learn something. Working source code will be used as examples which can be placed into existing programs, or be used as the basis for more complex subjects. A more experienced programmer may find new ways to do something, and can point out errors. I'll try to be as accurate as possible, but as everyone knows, perfection is impossible and imperfection is probable. If you have an idea for an article or I'm unclear about something, email me, and I'll work on it!


Setting the Video Mode

This series of articles will use a mode commonly refered to as "Mode 13h". It provides 256 colors, a flat address space, and ease of use. It is the perfect mode to learn graphics programming.

To initialize Mode 13h, you must use the BIOS interrupt 10h, service 0:

PROCEDURE InitGraph;
BEGIN
  ASM
    mov AX, 0013h    { Function 0, mode 13h }
    int 10h 
  END;
END;
This function initializes the screen, clears the display buffer, and returns control. I usually put this function in a PROCEDURE called "InitGraph". For people who have used the BGI routines, this is very intuitive. This procedure will continue to evolve as more functionality is added.

To close down the graphics mode when we are done, we use the same BIOS interrupt to switch back to text mode. 80x25x16 color text mode is mode number 3. This is the procedure we'll use:

PROCEDURE CloseGraph; ASSEMBLER;
ASM
  mov AX, 0003h      { Function 0, Mode 3}
  int 10h
END;

A Pixel

Since this is the basics, the most logical place to start is at the pixel level. The resolution of the screen is 320x200. This provides, not stunning levels of detail, but the mode that is compatible with the most monitors out there, without any of the worries of working with SuperVGA (Maybe future articles). The screen buffer takes therefore 320*200*1 bytes = 64000 bytes. Since there are 256 colors in this mode, each pixel takes a convienient one byte.

I said before that memory is organized linearly. What does this mean? Well, the first pixel is at address 0, the second is at address 1, the third is at address 2... This loops around at the end of each line, and keeps counting. The first pixel on the second line is 320, the first pixel on the third line is 640...

X=0X=1X=2X=3X=4X=6X=7X=8 ..X=318X=319
Y=0 01234678 ..318319
Y=1 320321322323324326327328 .. 638639
Y=2 640641642643644646647648 .. 958959
.............
Y=199 6368063681636826368363684636866368763688 .. 6399863999

This is all great and everything, but how do I set a pixel? Well all of these addresses are relative to segment A000h. The first pixel is at A000:0, second at A000:1... You also know that each line is 320 bytes apart. So we can use this formula to get the address with a X and a Y coordinate:

Address = (Y*320)+X;
We can now add a few data structures:

TYPE
  ScreenBufferType = ARRAY[0..63999] OF BYTE;
  ScreenBufferPtr = ^ScreenBufferType;

VAR
  Screen : ScreenBufferPtr; 
Edit our InitGraph procedure slightly:

PROCEDURE InitGraph;
BEGIN
  ASM
    mov AX, 0013h    { Function 0, mode 13h }
    int 10h
  END;
  Screen := PTR($A000, 0);   { Set up the screen buffer pointer }
END;
And add our new SetPixel routine:

PROCEDURE SetPixel(X, Y : WORD; Color : BYTE);
VAR
  Address : WORD;
BEGIN
  Address := Y*320+X;
  Screen^[Address] := Color;
END;
To use this routine, you simply set the screen mode, and call this routine with a specific X and Y coordinate. See the
Example.


A Pixel Example

The program below demonstrates everything that we have learned up 'til now. It fills the screen with pixels with random colors at random positions. Here it is:

-----------------] Example Starts here [-----------------
PROGRAM PixelExample;
USES CRT;   { Include the "KeyPressed" function }
TYPE
  ScreenBufferType = ARRAY[0..63999] OF BYTE;
  ScreenBufferPtr = ^ScreenBufferType;

VAR
  Screen : ScreenBufferPtr; 

PROCEDURE InitGraph;
BEGIN
  ASM
    mov AX, 0013h    { Function 0, mode 13h }
    int 10h
  END;
  Screen := PTR($A000, 0);   { Set up the screen buffer pointer }
END;

PROCEDURE CloseGraph; ASSEMBLER;
ASM
  mov AX, 0003h      { Function 0, Mode 3}
  int 10h
END;

PROCEDURE SetPixel(X, Y : INTEGER; Color : BYTE);
VAR
  Address : WORD;
BEGIN
  Address := Y*320+X;
  Screen^[Address] := Color;
END;

BEGIN
  InitGraph;
  WHILE NOT KeyPressed DO
    SetPixel(
       RANDOM(320),     { Random X (Range 0-319)     }
       RANDOM(200),     { Random Y (Range 0-199)     }
       RANDOM(256));    { Random Color (Range 0-255) } 
  CloseGraph;
END.
-----------------] Example Ends here [-----------------


  • Created by Chris Lattner