Dec 14, 2011

Graphics Programming I



Undoubtedly, the issue of graphics programming is one of the most sought after judging by the messages that have kindly come to my mailbox, so vast is the topic that we could gather material for an entire site devoted exclusively to graphics, for this reason, the material presented here consider it in constant growth, also will not be possible to cover everything in a single section, in this case "Programming in C", as they also consider it necessary to cover terms for graphics programming Windows 9x, issue you will find in the section Programming Windows 9x . In its first review, this article refers to the programming of the video controller card, particularly the type VGA, then will include material relating to the type and standard SVGA VESA.

BIOS services  
Generally speaking, when we talk about graphics programming we refer to the different techniques we can use to display different information to display plain text, including those instructions written directly to memory addresses that correspond to the RAM of the controller board video of the computer. This statement is not necessarily true in programming for Windows since in this environment even the text is considered as a graph. 
Normally is the BIOS of the computer who is in charge of access to the connected hardware on your computer, this includes for example, disk drives, ports and of particular interest to this article, the video controller card, which acts as interface between the computer and monitor.
 The first thing to consider then, are the BIOS services, which are software routines that allow us to access different sections of our PC, to use one of these routines must generate an interrupt 
to the appropriate BIOS, according to the following list:
  • 5h screen printing operations.
  • Services 10h video display.
  • 11h determines the equipment installed.
  • 12h determines the memory size.
  • Servcios 13h for disk drives.
  • Services 14h I / S serial
  • Miscellaneous Services 14h.
  • Services 16h keyboard.
  • 17h Printer Services.
  • 18h access the BASIC language.
  • Reboot the PC 19h.
  • 1Ah Services real-time clock.
Looking at the list we realize that to have access to services related to video display must generate the BIOS interrupt 10h, this involves passing multiple values ​​to registers ax, bx, cx, dx, lx and is: bp. This article does not give an explanation of the different records because this information can be found in the section on basic concepts . Also, if you want more information about BIOS interrupts can consult the website of Ralf Brown is truly a "gift for DOS programmers."
Hello World

Video modes  
Today, virtually all adapter cards are VGA-type video, an acronym that means Video Graphics Array, but soon rename the term by the following: Video Graphics Adaptor, either way we are referring to the video controller card the PC. There are different types of video adapters, monochrome, color medium resolution (CGA and MCGA) and high resolution color (EGA and VGA). For each video adapters are different types of video that are used to display either text or graphics, this is the list:
Mode
Resolution
Type
Adapter
0h
40 x 25 B. and N.
Alphanumeric
CGA / EGA / VGA
1h
40 x 25 Color
Alphanumeric
CGA / EGA / VGA
2h
80 x 25 B. and N.
Alphanumeric
CGA / EGA / VGA
3h
80 x 25 Color
Alphanumeric
CGA / EGA / VGA
4h
320 x 200 Color
Graphic
CGA / EGA / VGA
5h
320 x 200 B. and N.
Graphic
CGA / EGA / VGA
6h
640 x 200 B. and N.
Graphic
CGA / EGA / VGA
7h
80 x 25 B. and N.
Alphanumeric
EGA / VGA (mono)
13h
320 x 200 Color
Graphic
EGA / VGA
14h
640 x 200 Color
Graphic
EGA / VGA
15h
640 x 350 Color
Graphic
EGA / VGA (mono)
16h
640 x 350 Color
Graphic
EGA / VGA
As we can see, you can use all types of video if we have a VGA-type adapter. For the purpose of this article, particularly interested in video mode 13h in which the display has a resolution of 320 pixels wide by 200 pixels height so as to establish a system of coordinates, the point (0, 0) is located in the upper left corner of the monitor screen, with the opposite side of the screen corresponding to the coordinate (319, 199), (Fig. 1). As mentioned, the resolution is 320 x 200 pixels while the color capacity is 256 different colors, ie, we can represent each of the different colors using a byte of memory. As the resolution demand an amount equal to 320x200 = 64000 pixels, so we need 64,000 bytes of video RAM.


Hello World
Plotting pixels  
To display graphics on the monitor screen we need to place the video mode to a value equal to 13h, for this use BIOS interrupt 10h 0x00 specifying the value in the AX register and the desired video mode recording at (0x13h) as shown in lines 17 through 19 of the following program, called grafico1.c that displays random pixels 250.000.
 /************************************************* ********* * * * layout grafico1.c pixels using the BIOS * * (c) 1999, Virgilio Gomez Jaime Negrete * ***************** # include # <stdio.h> *****************************************/ <dos.h> <stdlib.h> include # include int main () {int x, y, color, long i, Union REGS pixel / * Video mode 13 * / pixel.h.ah = 0x00; pixel. h.al = 0x13; int86 (0x10, & pixel, & pixel) for (i = 0; i <250000; i + +) {x = rand ()% 320; y = rand ()% 200; color = rand ()% 256; pixel.h.ah = 0x0C, / * function to print a pixel * / pixel.h.al = color; pixel.x.cx = x; pixel.x.dx = y; int86 (0x10, & pixel, & pixel );} / * return to video mode 3 * / pixel.h.ah = 0x00; pixel.h.al = 0x03; int86 (0x10, & pixel, & pixel) return 0;}
After specifying the desired video mode, in this case, mode 13h, we use a loop to plot pixels. The easiest way is to use the BIOS specified in register 0x0C ah. To use this function specify the coordinate x in the register cx and y coordinate record dx, while the value corresponding to the color specified in the Register. See lines 27 through 31 of the program. As can be seen, to draw pixels using the BIOS is relatively simple, but as the interest is implicit programming graphics speed, as this technique is insufficient, as we shall see in the next paragraph, there are alternatives ...
Writing in the video RAM
As already mentioned, the memory required to work in graphical mode 13h is 64000 bytes. If we check the properties of the PC in the section on memory, we can see that the video memory is located in the segment 0xA000h, therefore, writing to this memory segment in turn will be writing on the monitor screen, the color displayed depends on the value written to memory. The video memory is linear which involves using a special mechanism to specify the values ​​for the coordinates x and y: take first the value of the y and multiply by the width of the screen (320), the latter result we add the value of the coordinate x and thus obtain an offset value, which in Spanish would be something like an offset value, but to better understand the C language, we use the word offset. 
We also need to declare an
 unsigned char pointer that points to video memory segment 0xA000h and finally, using clock () 
function we can write a program that will help us to compare the time it would take random graph 256.000 pixels, using first the BIOS interrupt and then writing directly to video RAM:

  /************************************************* *********  * Grafico2.c *  * This program compares the speed graph *  * Using the BIOS and the video RAM *  * (C) 1999, Virgilio Gómez Jaime Negrete *  ************************************************** ********/   # Include <stdio.h>  Stdlib.h  # Include <dos.h>  # Include <time.h>   / * Pointer to video memory * /  unsigned char * VGA = (unsigned char *) 0xA0000000L;   int main ()  {      int x, y, color;       float t1, t2;       long i;       REGS junction pixel;       clock_t clock Clock2;        / * Set the video mode 13h * /       pixel.h.ah = 0x00;       pixel.h.al = 0x13;       int86 (0x10, & pixel, & pixel);        clock = clock () / * record boot time * /       / * Graph using the BIOS 256,000 pixels * /       for (i = 0; i <256000; i + +)       {           pixel.h.ah = 0x0C;           pixel.h.al = rand ()% 256;           pixel.x.cx = rand ()% 320;           pixel.x.dx = rand ()% 200;           int86 (0x10, & pixel, & pixel);       }       Clock2 = clock () / * record the final time * /       t1 = ((float) Clock2-(float) clock) / CLOCKS_PER_SEC;        / * Called again for cleaning mode 13h screen * /       pixel.h.ah = 0x00;       pixel.h.al = 0x13;       int86 (0x10, & pixel, & pixel);        ();/* clock = clock records the time of initiation * /       / * 256,000 pixels using graphic video memory * /       for (i = 0; i <256000; i + +)       {           x = rand ()% 320;           y = rand ()% 200;           color = rand ()% 256;           VGA [y * 320 + x] = color;           / * VGA [(y <<8) + (y <<6) + x] = color; * /       }       Clock2 = clock ();       t2 = ((float) Clock2-(float) clock) / CLOCKS_PER_SEC;        / * Return to the 3h mode to display text * /       pixel.h.ah = 0x00;       pixel.h.al = 0x03;       int86 (0x10, & pixel, & pixel);        / * We display results * /       printf ("Plotting with the BIOS took% f seconds. \ n", t1);
      printf ("Plot in memory took% f seconds. \ n", t2);       printf ("Plot in memory was% f times faster. \ n", T1/T2);        return 0;   }

This program is similar to grafico1.c except that I have modified it to allow us to measure the time it takes to plot 256.000 pixels (4 times the resolution of the video mode 13h) using first BIOS functions and then, by writing directly to video RAM, of particular interest is the loop that we use for this purpose, lines 49 to 56 in line 54 we see that is assigned to an array called VGA [] color value that is plotted on pixel on the screen position specified by the offset value indicated in the brackets of the array, according to what explained in the previous paragraph. At line 55 is commented out an alternate way to calculate the offset that uses the left shift of bits, taking a number n and any bit shifting their position on the left is the same effect as multiplying that number by n 2. In general, if a number n it move x spaces on the left the result is 2 x n. In the specific case and value is 320, and not a multiple of 2, what we do is to factor 320 in parts that are multiples of 2, ie, 256 and 64. 
It is important to note that these programs must be compiled using a standard medium or large memory, remember that the video mode 13h bytes 64.000 requires so little memory model (small) is insufficient to run these programs.
Hello World
Graphing lines  
At this time we know how to plot pixels, well, to deploy a straight line does not substantially change the procedure, in fact do exactly the same, we put a series of pixels, aligned according to the equation of the straight line. We know that two points are sufficient to define a straight line, referring to the picture below we can see a straight line (in red) from a point of coordinates P1 (X1, Y1) and ends at the point P2 with coordinates (X2, Y2). In the picture we see in the gray area that would occupy the monitor screen during the video mode 13h. Remember that the coordinate origin (0, 0) is located in the upper left corner of the monitor screen.

To graph a line we need first to calculate the slope of the line, for this we use the shape of the slope and a point of the equation of a line, which is the equation of the same when you know a point P (x1, y1 ) on the straight and its slope, m.
y = m (x - x 1) + and 1
On the other hand, knowing the two points defining the straight line, we know the slope m of the same, using the following equation:
m = (y 2 - y 1) / (x 2 - x 1)
As mentioned earlier, the coordinate origin is at the top left corner of the monitor screen, this means that we can not handle a Cartesian coordinate system, so we should implement a mechanism that tells us whether the line to draw is more horizontal than vertical or vice versa, this information is provided by the sign of the value of horizontal and vertical distances for this using a macro defined in line 13 the following program:

  /************************************************* *********  * Grafico3.c *  * Draw a straight line *  * (C) 1999, Virgilio Gómez Jaime Negrete *  ************************************************** ********/   # Include <stdio.h>  Stdlib.h  # Include <dos.h>  # Include <time.h>   / * If x = 0 then x = 0, if x <0 then x = -1, if x> 0 then x = 1 * /  # Define sign (x) ((x <0)? -1: ((X> 0)? 1:0))   int main ()  {      int x1 = 0, x2 = 319, y1 = 0, y2 = 199, dx, dy, dxabs, dyabs, SDX, SDY, i;       REGS Union line;       float slope;       clock_t reloj1, Clock2;        / * 13h video mode * /       linea.h.ah = 0x00;       linea.h.al = 0x13;       int86 (0x10, & line, & line);        dx = x2-x1 / * horizontal distance * /       dy = y2-y1 / * vertical distance * /       dxabs = abs (dx);       dyabs = abs (dy);       sdx = sign (dx);       SDY = sign (d);        if (dxabs> = dyabs)       {           slope = (float) dy / (float) dx;           for (i = 0; i! = dx; i + = sdx)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 2, / * green * /               linea.x.cx = i + x1;               linea.x.dx = (slope * i) + y1;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }       }       else       {           slope = (float) dx / (float) d;           for (i = 0; i! = d;   i + = SDY)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 4, / * red * /               linea.x.cx = (slope * i) + x1;               linea.x.dx = i + y1;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }       }        / * Implements a time delay of 2 seconds * /       reloj1 = clock ();       do       Clock2 = clock ();       while ((Clock2-reloj1) <2000);        / * Return to video mode 3 * /       linea.h.ah = 0x00;       linea.h.al = 0x03;       int86 (0x10, & line, & line);        return 0;   }

In simple terms, the macro returns zero sign if x is zero, -1 if x is less than zero and 1 if x is greater than zero. This information is used in loops that serve to plot, pixel by pixel, the line defined by the points whose coordinates are defined by the values ​​of the variables x1, y1 and x2, y2. In lines 27 and 28 are calculated the respective horizontal and vertical distances to later calculate the slope of the straight lines 36 and 52. Depending on whether the line is more horizontal than vertical or vice versa using one of the two loops to plot the straight line on the monitor screen, if the line is then plotted as horizontal green as specified on line 41 On the other hand, if the line is vertical it is plotted in red, according to the instruction given in line 57.
The program includes a grafico3.c do-while loop to induce a time delay that allows us to observe the construction of the straight line and demonstrate that it is drawn pixel by pixel. At the end of the program saw another do-while loop that implements a two-second delay allows us to see for a moment the full straight line. The do-while loop above using clock () function. This program should carefully examine and experiment with different values ​​for the variables that define the points on the line.
Hello World
Graphing polygons  
According to the discussion in the previous sections, we conclude that draw a polygon is reduced to the following, except for the circle, the other we can simply draw polygons by drawing a series of straight lines, we saw that a straight line to your Once the path drawn pixel by pixel, a situation that also applies to the circles. In the most general possible, we say that you can draw any geometric figure, including those that simulate three-dimensional, simply by plotting a series of pixels according to an algorithm. In graphics programming speed plays a major role and is usually one of the objectives to be achieved by developing our algorithm for graphs.
The following program demonstrates how grafico4.c draw a rectangle, which is formed by four straight lines, each in turn drawing graphics pixel by pixel according to the conditions imposed in the respective loop.
   /************************************************* *********  * Grafico4.c *  * Draw polygons *  * (C) 1999, Virgilio Gómez Jaime Negrete *  ************************************************** ********/   # Include <stdio.h>  Stdlib.h  # Include <dos.h>  # Include <time.h>   int main ()  {      int x1 = 50 y1 = 50 x2 = 269, y2 = 149, dx, dy, dxabs, dyabs, i;
      REGS Union line;       clock_t reloj1, Clock2;        / * 13h video mode * /       linea.h.ah = 0x00;       linea.h.al = 0x13;       int86 (0x10, & line, & line);        dx = x2-x1 / * horizontal distance * /       dy = y2-y1 / * vertical distance * /       dxabs = abs (dx);       dyabs = abs (dy);        if (dxabs> = dyabs)       {           for (i = 0; i <dxabs i + +)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 2, / * green * /               linea.x.cx = i + x1;               linea.x.dx = y1;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }           for (i = 0; i <dyabs; i + +)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 2, / * green * /               linea.x.cx = x2;               linea.x.dx = i + y1;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }           for (i = dxabs;   i> 0; i -)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 2, / * green * /               linea.x.cx = i + x1;               linea.x.dx = y2;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }           for (i = dyabs;   i> 0; i -)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 2, / * green * /               linea.x.cx = x1;               linea.x.dx = i + y1;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }       }       else       {           for (i = 0; i <dxabs; i + +)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 4, / * red * /               linea.x.cx = i + x1;               linea.x.dx = y1;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }           for (i = 0; i <dyabs; i + +)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 4, / * red * /               linea.x.cx = x2;               linea.x.dx = i + y1;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }           for (i = dxabs;   i> 0; i--)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 4, / * red * /               linea.x.cx = i + x1;               linea.x.dx = y2;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }           for (i = dyabs;   i> 0; i -)           {               reloj1 = clock ();               linea.h.ah = 0x0C, / * function to print a pixel * /               linea.h.al = 4, / * red * /               linea.x.cx = x1;               linea.x.dx = i + y1;               int86 (0x10, & line, & line);               do               Clock2 = clock ();               while ((Clock2-reloj1) <25);           }       }        / * Implements a time delay of 2 seconds * /       reloj1 = clock ();       do       Clock2 = clock ();       while ((Clock2-reloj1) <2000);        / * Return to video mode 3 * /       linea.h.ah = 0x00;       linea.h.al = 0x03;       int86 (0x10, & line, & line);        return 0;   }

As seen, the program is similar to grafico3.c, except that instead of drawing a single line, four are plotted in green if the rectangle is more horizontal than vertical, or in red if the coordinates specified in variables x1, y1, x2, and y2 determine that the figure is more vertical than horizontal. Obviously the program does not show grafico4.c speed but the fact that the graphics are constructed from individual pixels. The student observer will have no difficulty to combine the programs described in this lesson and make a drawing routine as quickly as possible within their respective computer.

0 comments:

Post a Comment

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Premium Wordpress Themes