home *** CD-ROM | disk | FTP | other *** search
/ PC Home 5 / PC_HOME_5.iso / megadisk / 4.exe / PCH3.C next >
Encoding:
C/C++ Source or Header  |  1993-11-24  |  16.0 KB  |  409 lines

  1. /***************************************************************************
  2.  *                                                                         *
  3.  *  Program:          PCH3, Load & display a 256-colour, 320x200 picture.  *
  4.  *  Description:      Example program 3 for the PC Home series on PC       *
  5.  *                    graphics for C programmers.                          *
  6.  *  Notes:            This program ONLY displays 256-colour VGA images.    *
  7.  *                    Don't ask it to display any other kind of picture    *
  8.  *                    because it will fall over.  I have written this      *
  9.  *                    program to show the effectiveness of screen buffers. *
  10.  *                    If you pass parameter 2 as "N", the program will load*
  11.  *                    the file directly to screen; if you pass it as "Y",  *
  12.  *                    the program will load the file to an off-screen      *
  13.  *                    buffer, and then copy it to the screen.  If you've   *
  14.  *                    got a very fast machine (486/33+) with a fast SCSI   *
  15.  *                    hard drive or very large cache, you might not notice *
  16.  *                    any difference - lucky old you.  Everyone else will  *
  17.  *                    immediately see one reason for buffering screens.    *
  18.  *                                                                         *
  19.  *        NOTE!!      LARGE MODEL ONLY                                     *
  20.  *                                                                         *
  21.  *        NOTE!!      We're doing some quite involved stuff now, so this   *
  22.  *                    source file is quite big.  Starting next month, I'll *
  23.  *                    provide all the "support" functions in a separate    *
  24.  *                    .OBJ file, so you can concentrate on the new stuff.  *
  25.  *                    So you don't feel cheated, I'll continue to provide  *
  26.  *                    the source to these functions, but in a different .C *
  27.  *                    file.  Later in the series, I'll bundle all these    *
  28.  *                    functions into a proper .LIB file, so you can use all*
  29.  *                    these lovely facilities we've developed as if they   *
  30.  *                    were a part of the base functionality of your        *
  31.  *                    compiler.                                            *
  32.  *                                                                         *
  33.  *   LAST NOTE!!      Unless it's been excluded for reasons of space or    *
  34.  *                    copyright, you'll find a file called ROSE.PCX on the *
  35.  *                    coverdisk.  This is useful for two reasons: firstly, *
  36.  *                    it's a lovely picture; secondly, it's an example of  *
  37.  *                    of a PCX file that is larger in it's "compressed"    *
  38.  *                    form than it would be as a straightforward bitmap.   *
  39.  *                    See, I told you...                                   *
  40.  *                                                                         *
  41.  ***************************************************************************/
  42.  
  43. #include "stdio.h"        /* For standard disk file functions */
  44. #include "alloc.h"        /* We need to allocate a few memory blocks */
  45. #include "dos.h"          /* For PC-specific things like MK_FP */
  46. #include "string.h"       /* String functions to understand the parameters */
  47.  
  48. #define OK 0
  49. #define VGA_mem  (char *)0xA0000000  // The start seg/off of VGA memory
  50.  
  51.  
  52. /* This is the standard PCX header.  As I mentioned in the article, most of
  53.    this stuff is useless to us, because we're only interested in 256-colour
  54.    files.                           */
  55.  
  56. typedef struct {
  57.         char PCX_id;         /* ALWAYS 0Ah (10 decimal), for some reason */
  58.         char version;        /* If this is not 5, you're not interested */
  59.         char encoding;       /* Ignore */
  60.         char bits_per_pixel; /* Either 1 or 8, or you're not interested */
  61.         int xmin, ymin;      /* The top-left co-ords of the original image */
  62.         int xmax, ymax;      /* The bottom-right co-ords of the original
  63.                                 image.  XMAX-XMIN=Height, YMAX-YMIN=Width.
  64.                                 BUT!!  You must add 1 to the result, because
  65.                                 PCX will store, for example, 0-319, which
  66.                                 is a total of 320 pixels.  Sorry: I didn't
  67.                                 design the bloody thing! */
  68.         int hscreen;         // If these values aren't 320 and 200, you aren't
  69.         int vscreen;         // interested in this file.  Throw it out.
  70.         char palette[48];    /* For 16-colour images.  Ignore it */
  71.         char filler1;        /* Ignore */
  72.         char colour_planes;  /* If "bits_per_pixel"=1, this must be 8.  If
  73.                                 it's not, you don't want it. */
  74.         int bytes_per_ln;    /* Always 320, or you aren't interested. */
  75.         int pal_type;        /* Ignore */
  76.         char filler2[58];    /* Ignore */
  77.         } PCXHDR;
  78.  
  79. PCXHDR hdr;                  /* Define a PCX header structure called hdr */
  80. unsigned int width, depth;   /* ... of the image */
  81. unsigned int bytes, bits;
  82. char *palette;               /* Pointer to some memory to store our palette */
  83. char pcxfnam[13];            /* PCX file name (XXXXXXXX.XXX\0 = 13 chars) */
  84. char far *sbptr;             /* If you're using a buffer, this its pointer */
  85.  
  86. char far *g_FPtr(char far *ptr, unsigned int l);
  87.  
  88. main(int argc, char *argv[])
  89. {
  90.  
  91.    FILE *fp;                 /* Pointer to FILE structure for PCX file */
  92.    char pcxfnam[13], *s1, buffer_flag;
  93.    unsigned ss;
  94.  
  95.    if (argc <= 2)
  96.       {
  97.       printf("\nUsage:  PCH3 {PCX-file-name} {Buffer-screen-flag}\n");
  98.       exit(0);
  99.       }
  100.  
  101.    strcpy(pcxfnam, argv[1]);    /* String pcxfnam = 1st argument */
  102.  
  103.    buffer_flag = *argv[2];      /* Convert the string "argv[2]" to a char */
  104.  
  105.    if (buffer_flag == 'Y' || buffer_flag == 'y')
  106.       {
  107.       sbptr = calloc(64000, 1); /* If you've no calloc, use malloc & clear it*/
  108.       sbptr = g_FPtr(sbptr, 0);
  109.       }
  110.  
  111.    palette = malloc(768);  /* These should work with all C compilers, but if
  112.                               you've got a funny 'un, use whatever function
  113.                               your compiler provides to allocate the correct
  114.                               amount of memory, and then assign the pointers
  115.                               to point to the start of this memory */
  116.  
  117.    fp = fopen(pcxfnam, "rb");   /* Open PCX file */
  118.    if (fp == NULL)
  119.       {
  120.       printf("\n%s:  File does not exist, or cannot be opened\n", pcxfnam);
  121.       exit(0);
  122.       }
  123.  
  124.    if (fread( (char *)&hdr, 1, sizeof(PCXHDR), fp) != sizeof(PCXHDR))
  125.       {
  126.       printf("\n%s:  File is not a PCX file\n", pcxfnam);
  127.       exit(0);
  128.       }
  129.  
  130.   /* A note on the above, for less experienced C programmers.  The bit that
  131.      says "(char *)&hdr" is rather opaque.  It says "Take structure 'hdr',
  132.      find its address in memory (that's the &), and then "typecast" this
  133.      address into a 'char *', that is, a character pointer."  Clear as mud?
  134.      Never mind, just use it as is if you don't follow.  */
  135.  
  136.    if (hdr.PCX_id != 0x0a)       /* The famous check - byte 1 must be 0Ah! */
  137.       {
  138.       printf("\n%s:  File is not a PCX file\n", pcxfnam);
  139.       exit(0);
  140.       }
  141.  
  142. /*  OK.  If you've got this far, you've either got a PCX file, or you've got
  143.     some other bloody file that is longer than 128 bytes and has 0Ah as its
  144.     first byte.  Not much of a check, is it?  Let's check out some of the
  145.     other values.         */
  146.  
  147.    if (hdr.version != 5)    /* I don't know why 5, but there you go.  If
  148.                                it's not version 5, then either it's not 256-
  149.                                colour, or it's been written by a package
  150.                                that isn't following the rules... */
  151.       {
  152.       printf("\n%s:  File is not a 256-colour PCX file\n");
  153.       exit(0);
  154.       }
  155.  
  156.    if (hdr.bits_per_pixel == 1)
  157.       bits = hdr.colour_planes;
  158.    else
  159.       bits = hdr.bits_per_pixel;
  160.  
  161.    if (bits != 8)   /* Because PCX 256-colour has 8 colour bits per pixel */
  162.       {
  163.       printf("\n%s:  File is not a 256-colour PCX file\n");
  164.       exit(0);
  165.       }
  166.  
  167.  
  168. /* The last check we can do before unpacking the file is check that it has a
  169.    valid palette chunk on the end.  The PCX palette data for 256-colour
  170.    images is simply a lump of data tagged on the end of the file, separated
  171.    from the image data by - for some reason - the character 0Ch.  You read
  172.    the 769th character from the end of file; if it's 12 (0Ch), you can carry
  173.    on.    */
  174.  
  175.  
  176.    if ( !fseek(fp, -769L, SEEK_END) )
  177.  
  178. /*  So, in English, if you can read from the end of file (SEEK_END), backwards
  179.     769 positions, you can carry on.  If your compiler doesn't have SEEK_END
  180.     defined, use 2 (it's #defined in the Turbo C include files).    */
  181.       {
  182.       if ( fgetc(fp) != 0x0c || fread(palette, 1, 768, fp) != 768 )
  183.          {
  184.          printf("\n%s:  Invalid palette information\n", pcxfnam);
  185.          exit(0);
  186.          }
  187.       }
  188.    else
  189.       {
  190.       printf("\n%s:  Could not find palette information\n", pcxfnam);
  191.       exit(0);
  192.       }
  193.  
  194. /*  Got this far?  Good!  You can now be pretty sure that you've got a valid
  195.     256-colour PCX file (or someone extremely determined to see you fail).
  196.     Let's get back to the start of the image data.  */
  197.  
  198.    fseek(fp, 128L, SEEK_SET);   /* Point to just after the header block  */
  199.  
  200.    width = (hdr.xmax - hdr.xmin) + 1;     /* REMEMBER TO ADD 1!  */
  201.    depth = (hdr.ymax - hdr.ymin) + 1;    /*     "     "   "  "   */
  202.    bytes = hdr.bytes_per_ln;
  203.  
  204. /* We've checked everything we can check, and we've read the palette.  Let's
  205.    go the whole hog and unpack the data.  Pass argument 2 along, so that the
  206.    function knows whether to unpack direct to screen or to a buffer.  */
  207.  
  208.  
  209.    unpackPCXfile(fp, buffer_flag);
  210.  
  211.    getch();        /*  Press any key when you're bored with the picture... */
  212.  
  213. /*   Close the file, switch back to text mode and end the program  */
  214.    fclose(fp);
  215.    g_SetTxt();
  216.    return(0);
  217.  
  218. }
  219.  
  220.  
  221. unpackPCXfile(FILE *fp, char buffer_flag)
  222. {
  223.  
  224.    int i;
  225.  
  226.    g_SetVGA();            /* Switch the hardware over, same as last month */
  227.  
  228.    g_SetVGAPalette(palette);  /* We have to use a DOS service to set the VGA
  229.                                  palette to that stored with the image.  If
  230.                                  we don't, we'll probably end up with a very
  231.                                  strange-looking picture indeed. */
  232.  
  233.    for (i=0; i<depth; i++)   /* Outer loop - do "depth" (height) times */
  234.        {
  235.        if (i >= 200)         // A quick check - shouldn't be necessary - to
  236.           break;             // ensure we don't run off the bottom of the
  237.                              // screen - this is an almost foolproof way of
  238.                              // crashing the computer.
  239.  
  240.        if (buffer_flag == 'Y' || buffer_flag == 'y')
  241.           readPCXline(g_FPtr(sbptr, i*320) , fp);   // Unpack to buffer
  242.        else
  243.           readPCXline(g_FPtr( VGA_mem, i*320), fp);  // Unpack direct to screen
  244.        }
  245.  
  246.    if (buffer_flag == 'Y' || buffer_flag == 'y')   // If you have written to
  247.       g_SwapScr();                                 // a buffer, you now need
  248.                                                    // to copy it to the
  249.                                                    // screen.
  250.  
  251. return(OK);
  252.  
  253. }
  254.  
  255.  
  256. readPCXline(char far *x, FILE *fp)
  257. {
  258.  
  259.    int n=0, i;
  260.    unsigned char c;
  261.    char far *p;
  262.  
  263.    p = x;
  264.  
  265.    memset(p, 0, width);  /* Set the current line to colour 0 */
  266.  
  267.    do
  268.       {
  269.       c = fgetc(fp);   /* Read a character from file */
  270.  
  271.  
  272. /* OK.  The unpacking algorithm, in case my article is too boring for you to
  273.    bother reading, is this:-
  274.       1)  If the character is less than 192, great.  Simply copy it to the
  275.           screen and read the next character.
  276.       2)  If the character is greater than or equal to 192, subtract 192 from
  277.           it, and store the result in "repeat value".
  278.       3)  Read the next character from file.
  279.       4)  Place this character "repeat value" times into the screen.
  280.       5)  Read the next character.
  281. */
  282.       if ( c >= 192 )     /* The check in step 1 */
  283.          {
  284.          i = c - 192;    /* Step 2 */
  285.          c = fgetc(fp);  /* Step 3 */
  286.          while (i--)     /* Step 4 */
  287.             p[n++] = c;  /*  "   " */
  288.          }
  289.       else
  290.          p[n++] = c;     /* Ordinary character copy, stick it on screen */
  291.  
  292.       } while (n < width);    /* Until the last byte in this line */
  293.  
  294.    return(OK);
  295.  
  296. }
  297.  
  298. g_SetVGA()
  299. {
  300.     union REGS regs;
  301.     regs.h.al = 0x13;                    /* 256-colour VGA */
  302.     regs.h.ah = 0x00;                    /* Set graphics mode */
  303.     int86(0x10, ®s, ®s);           /* Request DOS service 10h */
  304.  
  305. return(OK);
  306.  
  307. }
  308.  
  309.  
  310. g_SetTxt()
  311. {
  312.     union REGS regs;
  313.     regs.h.al = 0x03;                    /* Text mode */
  314.     regs.h.ah = 0x00;                    /* Set graphics mode */
  315.     int86(0x10, ®s, ®s);           /* Request DOS service 10h */
  316.  
  317. return(0);
  318.  
  319. }
  320.  
  321.  
  322. g_SetVGAPalette(char *p)
  323. {
  324.  
  325.    union REGS regs;
  326.    struct SREGS sr;
  327.    int i;
  328.  
  329.    for (i=1; i<=768; i++)
  330.        p[i] = p[i] >> 2;       /*  Although VGA-256 palette values are held
  331.                                    in whole bytes, they are really only 6
  332.                                    bits long.  Therefore, to get them to
  333.                                    their proper values, we have to right-
  334.                                    shift them by two bits (that is, divide
  335.                                    them by four).          */
  336.  
  337.    regs.x.ax = 0x1012;         /* Interrupt 10h, service 12h */
  338.    regs.x.bx = 0;
  339.    regs.x.cx = 256;
  340.    regs.x.dx = FP_OFF(p);
  341.    sr.es     = FP_SEG(p);
  342.    int86x(0x10, ®s, ®s, &sr);
  343.  
  344. return(OK);
  345.  
  346. }
  347.  
  348. g_SwapScr()
  349. {
  350.  
  351. unsigned s1, s2, o1, o2;
  352.  
  353. sbptr = g_FPtr(sbptr, 0);  // This rationalises the pointer - see the notes
  354.                            // at the top of function g_FPtr.
  355.  
  356. s1 = FP_SEG(sbptr);     /* Buffer segment... */
  357. o1 = FP_OFF(sbptr);     /* ... and offset */
  358. s2 = 0xA000;            /* VGA display memory starts at A000h */
  359. o2 = 0;
  360.  
  361.  
  362. movedata(s1, o1, s2, o2, 64000);
  363.  
  364. return(OK);
  365. }
  366.  
  367. /* Function g_FPtr() REALLY needs some explanation!  The purpose of it is to
  368.    "rationalise" a pointer and a fudging value into the most efficient
  369.    possible configuration.
  370.  
  371.    Say you've got a pointer set to 93CB:C350.  This is the same as A000:0000.
  372.    They both point to the top of the VGA screen.  If you try to copy 30000
  373.    bytes to the first pointer, however, you'll only get the first 15535; the
  374.    "offset" portion of the pointer will wrap around to 0 when it reaches FFFF.
  375.    The upshot is that the latter 14465 bytes will overwrite the memory area
  376.    immediately after 93CB:0000 - in English, you'll crash the machine.
  377.  
  378.    Function g_FPtr() exists to turn 93CB:C350 into A000:0000; therefore, it
  379.    allows you to copy blocks upto 64K in size without worries.  It works by
  380.    loading as much of the offset value as it can onto the segment value.  It
  381.    really needs an entire article in itself, so if you don't understand it,
  382.    do one of the following things:-
  383.    1)  Buy a book on assembler/advanced C.
  384.    2)  Just use the function and don't worry about it (simplest by far).
  385.    3)  Use the HUGE memory model for everything (this is slow, however).
  386.    4)  Write to Matthew McGrath, PC Home's editor, and beg him for an article
  387.        on the subject.  If he gets enough letters, he might agree, and I'm all
  388.        for that because then I get extra money!  Great, isn't it?       */
  389.  
  390.  
  391.  
  392. char *g_FPtr(char far *ptr, unsigned int l)
  393. {
  394.  
  395.    unsigned int seg, off;
  396.  
  397.    seg = FP_SEG(ptr);
  398.    off = FP_OFF(ptr);
  399.  
  400.    seg += (off/16);
  401.    off &= 0x000f;
  402.    off += (unsigned int)(l & 0x000fL);
  403.    seg += (l / 16L);
  404.    ptr = MK_FP(seg, off);
  405.  
  406.    return(ptr);
  407.  
  408. }
  409.