home *** CD-ROM | disk | FTP | other *** search
/ Frostbyte's 1980s DOS Shareware Collection / floppyshareware.zip / floppyshareware / FORTH / FRASRC11.ZIP / ENCODER.C < prev    next >
C/C++ Source or Header  |  1989-11-26  |  13KB  |  414 lines

  1.  
  2. /*
  3.     encoder.c - GIF Encoder and associated routines
  4.  
  5. */
  6.  
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11.  
  12. #include "fractint.h"
  13.  
  14. extern struct fractal_info save_info;    /*  for saving data in GIF file */
  15.  
  16. extern    int    oktoprint;        /* 0 if printf() won't work */
  17. extern    int    xdots, ydots;        /* # of dots on the screen  */
  18. extern    int    colors;            /* maximum colors available */
  19. extern    int    dotmode;        /* so we can detect disk-video */
  20. extern    int    warn;            /* warnings on/off */
  21.  
  22. extern unsigned char dacbox[256][3];    /* Video-DAC (filled in by SETVIDEO) */
  23. extern int    daclearn, daccount;    /* used by the color-cyclers */
  24. extern int    reallyega;        /* "reall-an-EGA" flag */
  25. extern int    extraseg;        /* used by Save-to-GIF routines */
  26. extern char potfile[];      /* potential file name TW 7/19/89 */
  27.  
  28. /*
  29.             Save-To-Disk Routines (GIF)
  30.  
  31. GIF and 'Graphics Interchange Format' are trademarks (tm) of Compuserve
  32. Incorporated, an H&R Block Company.
  33.  
  34.  
  35. The following routines perform the GIF encoding when the 's' key is pressed.
  36. The routines refer to several variables that are declared elsewhere
  37. [colors, xdots, ydots, and 'dacbox'], and rely on external routines to
  38. actually read and write screen pixels [getcolor(x,y) and putcolor(x,y,color)].
  39. (Writing pixels is just stuffed in here as a sort of visual status report,
  40. and has nothing to do with any GIF function.)   They also rely on the
  41. existence of an externally-defined 64K dataspace and they use the routines
  42. 'toextra()' and 'cmpextra()' to deal with that dataspace (in the same manner
  43. as 'memcpy()' and 'memcmp()' would).   Otherwise, they perform a generic
  44. GIF-encoder function.
  45.  
  46. Note that these routines use small string- and hash-tables, and "flush"
  47. the GIF entries whenever the hash-table gets two-thirds full or the string
  48. table gets full.   They also use the GIF encoding technique of limiting the
  49. encoded string length to a specific size, "adding" a string to the hash table
  50. at that point even if a matching string exists ("adding" is in quotes, because
  51. if a matching string exists we can increment the code counter but safely throw
  52. the duplicate string away, saving both string space and a hash table entry).
  53.  
  54.    This results in relatively good speed and small data space, but at the
  55. expense of compression efficiency (filesize).   These trade-offs could be
  56. adjusted by modifying the #DEFINEd variables below.
  57.  
  58. Note that the 'strlocn' and 'teststring' routines are declared
  59. to be external just so that they can be defined (and the space re-used)
  60. elsewhere.  The actual declarations are in the assembler code.
  61.  
  62. */
  63.  
  64. #define MAXTEST   100        /* maximum single string length */
  65. #define MAXSTRING 64000        /* total space reserved for strings */
  66.                 /* maximum number of strings available */
  67. #define MAXENTRY  5003        /* (a prime number is best for hashing) */
  68.  
  69. extern unsigned int strlocn[MAXENTRY];
  70. extern unsigned char teststring[MAXTEST];
  71.  
  72. static int numsaves = 0;    /* For adjusting 'save-to-disk' filenames */
  73.  
  74. static FILE *out;
  75.  
  76. static int lentest, lastentry, numentries, numrealentries;
  77. static unsigned int nextentry;
  78. static int clearcode, endcode;
  79. static unsigned int hashcode;
  80.  
  81. static unsigned char blockcount, block[266];
  82. static int startbits, codebits, bytecount, bitcount;
  83.  
  84. static char paletteBW[] = {            /* B&W palette */
  85.       0,  0,  0, 63, 63, 63,
  86.     };
  87. static char paletteCGA[] = {            /* 4-color (CGA) palette  */
  88.       0,  0,  0, 21, 63, 63, 63, 21, 63, 63, 63, 63,
  89.     };
  90. static char paletteEGA[] = {            /* 16-color (EGA/CGA) pal */
  91.       0,  0,  0,  0,  0, 42,  0, 42,  0,  0, 42, 42,
  92.      42,  0,  0, 42,  0, 42, 42, 21,  0, 42, 42, 42,
  93.      21, 21, 21, 21, 21, 63, 21, 63, 21, 21, 63, 63,
  94.      63, 21, 21, 63, 21, 63, 63, 63, 21, 63, 63, 63,
  95.     };
  96. int paletteVGA[] = {            /* VGA palette - to DAC registers */
  97.     0, 1, 2, 3, 4, 5,20, 7,56,57,58,59,60,61,62,63,
  98.     };
  99.  
  100. savetodisk(filename)            /* save-to-disk routine */
  101. char *filename;
  102. {
  103. char openfile[80], openfiletype[10];
  104. int i, j, ydot, xdot, color, outcolor1, outcolor2;
  105. unsigned int hashentry;
  106. unsigned char bitsperpixel, x;
  107. int entrynum;
  108.  
  109. if (extraseg == 0) {            /* not enough memory for this */
  110.     buzzer(2);
  111.     return;
  112.     }
  113.  
  114. restart:
  115.  
  116. strcpy(openfile,filename);        /* decode and open the filename */
  117. strcpy(openfiletype,DEFAULTFRACTALTYPE);/* determine the file extension */
  118. for (i = 0; i < strlen(openfile); i++)
  119.     if (openfile[i] == '.') {
  120.         strcpy(openfiletype,&openfile[i]);
  121.         openfile[i] = 0;
  122.         }
  123. if (++numsaves > 1) {
  124.     updatesavename(openfile);       /* secondary saves? new filename */
  125.     strncpy(filename, openfile, strlen(openfile));
  126.     }
  127.  
  128. strcat(openfile,openfiletype);
  129. if (warn && (out=fopen(openfile,"r")) != NULL) {
  130.     fclose(out);
  131.     goto restart;
  132.     }
  133. if ((out=fopen(openfile,"wb")) == NULL) {
  134.     if (oktoprint)
  135.         printf(" ?? Couldn't create file %s \n",openfile);
  136.     return;
  137.     }
  138.  
  139. bitsperpixel = 0;            /* calculate bits / pixel */
  140. for (i = colors; i >= 2; i /= 2 )
  141.     bitsperpixel++;
  142.  
  143. startbits = bitsperpixel+1;        /* start coding with this many bits */
  144. if (colors == 2)
  145.     startbits++;            /* B&W Klooge */
  146.  
  147. clearcode = 1 << (startbits - 1);    /* set clear and end codes */
  148. endcode = clearcode+1;
  149.  
  150. outcolor1 = 0;                /* use these colors to show progress */
  151. outcolor2 = 1;                /* (this has nothing to do with GIF) */
  152. if (colors > 2) {
  153.     outcolor1 = 2;
  154.     outcolor2 = 3;
  155.     }
  156. if (numsaves && 1 == 0) {        /* reverse the colors on alt saves */
  157.     i = outcolor1;
  158.     outcolor1 = outcolor2;
  159.     outcolor2 = i;
  160.     }
  161.  
  162. fwrite("GIF87a",1,6,out);        /* GIF Signature */
  163.  
  164. fwrite(&xdots,2,1,out);            /* screen descriptor */
  165. fwrite(&ydots,2,1,out);
  166. x = 128 + ((6-1)<<4) + (bitsperpixel-1); /* color resolution == 6 bits worth */
  167. fwrite(&x,1,1,out);
  168. i = 0;
  169. fwrite(&i,1,1,out);
  170. fwrite(&i,1,1,out);
  171.  
  172. if (colors == 256) {            /* write out the 256-color palette */
  173.     if (dacbox[0][0] != 255)    /* got a DAC - must be a VGA */
  174.         shftwrite(dacbox,colors);
  175.     else                /* uh oh - better fake it */
  176.         for (i = 0; i < 256; i += 16)
  177.             shftwrite(paletteEGA,16);
  178.     }
  179. if (colors == 2)            /* write out the B&W palette */
  180.     shftwrite(paletteBW,colors);
  181. if (colors == 4)            /* write out the CGA palette */
  182.     shftwrite(paletteCGA,colors);
  183. if (colors == 16)            /* Either EGA or VGA */
  184.     if (dacbox[0][0] != 255) {    /* got a  DAC - must be a VGA */
  185.         if (reallyega)        /* well, maybe really an EGA */
  186.             shftwrite(dacbox,colors);
  187.         else
  188.             for (i = 0; i < colors; i++)
  189.                 shftwrite(dacbox[paletteVGA[i]],1);
  190.         }
  191.         else            /* no DAC - must be an EGA */
  192.             shftwrite(paletteEGA,colors);
  193.  
  194. fwrite(",",1,1,out);            /* Image Descriptor */
  195. i = 0;
  196. fwrite(&i,2,1,out);
  197. fwrite(&i,2,1,out);
  198. fwrite(&xdots,2,1,out);
  199. fwrite(&ydots,2,1,out);
  200. i = 0;
  201. fwrite(&i,1,1,out);
  202.  
  203. bitsperpixel = startbits - 1;        /* raster data starts here */
  204. fwrite(&bitsperpixel,1,1,out);
  205.  
  206. codebits = startbits;            /* start encoding */
  207.  
  208. raster(9999);                /* initialize the raster routine */
  209.  
  210. inittable();                /* initialize the LZW tables */
  211.  
  212. for (ydot = 0; ydot < ydots; ydot++) {    /* scan through the dots */
  213.     for (xdot = 0; xdot < xdots; xdot++) {
  214.         color = getcolor(xdot,ydot);    /* get the next dot */
  215.         teststring[0] = ++lentest;
  216.         teststring[lentest] = color;
  217.         if (lentest == 1) {        /* root entry? */
  218.             lastentry = color;
  219.             continue;
  220.             }
  221.         if (lentest == 2)        /* init   the hash code */
  222.             hashcode = 301 * (teststring[1]+1);
  223.         hashcode *= (color + lentest);  /* update the hash code */
  224.         hashentry = ++hashcode % MAXENTRY;
  225.         for( i = 0; i < MAXENTRY; i++) {
  226.             if (++hashentry >= MAXENTRY) hashentry = 0;
  227.             if (cmpextra(strlocn[hashentry]+2,
  228.                 teststring,lentest+1) == 0)
  229.                     break;
  230.             if (strlocn[hashentry] == 0) i = MAXENTRY;
  231.             }
  232.         /* found an entry and string length isn't too bad */
  233.         if (strlocn[hashentry] != 0 && lentest < MAXTEST-3) {
  234.             fromextra(strlocn[hashentry],&entrynum,2);
  235.             lastentry = entrynum;
  236.             continue;
  237.             }
  238.         raster(lastentry);            /* write entry */
  239.         numentries++;        /* act like you added one, anyway */
  240.         if (strlocn[hashentry] == 0) {    /* add new string, if any */
  241.             entrynum = numentries+endcode;
  242.             strlocn[hashentry] = nextentry;
  243.             toextra(nextentry, &entrynum,2);
  244.             toextra(nextentry+2,
  245.                 teststring,lentest+1);
  246.             nextentry += lentest+3;
  247.             numrealentries++;
  248.             }
  249.         teststring[0] = 1;        /* reset current entry */
  250.         teststring[1] = color;
  251.         lentest = 1;
  252.         lastentry = color;
  253.  
  254.         if ((numentries+endcode) == (1<<codebits))
  255.             codebits++;         /* use longer encoding */
  256.  
  257.         if ( numentries + endcode > 4093 ||    /* out of room? */
  258.              numrealentries > (MAXENTRY*2)/3 ||
  259.             nextentry > MAXSTRING-MAXTEST-5) {
  260.             raster(lastentry);        /* flush & restart */
  261.             inittable();
  262.             }
  263.         }
  264.     if (dotmode != 11)            /* supress this on disk-video */
  265.         for (i = 0; 250*i < xdots; i++) {    /* display vert status bars */
  266.             putcolor(      i,ydot,outcolor1);    /*   (this is NOT   */
  267.             putcolor(xdots-1-i,ydot,outcolor2);    /*    GIF-related)  */
  268.             }
  269.     if (kbhit())                /* keyboard hit - bail out */
  270.         ydot = 9999;
  271.     }
  272.  
  273. raster(lastentry);            /* tidy up - dump the last code */
  274.  
  275. raster(endcode);            /* finish the map */
  276.  
  277. i = 0;                    /* raster data ends here */
  278. fwrite(&i,1,1,out);
  279.  
  280. fwrite(";",1,1,out);                    /* GIF Terminator */
  281.  
  282. if (strcmp(openfiletype,".gif") != 0) {        /* non-standard fractal info */
  283.     fwrite("!",1,1,out);            /* extension block identifier */
  284.     i = 255; fwrite(&i,1,1,out);        /* extension block #255 */
  285.     i =  11; fwrite(&i,1,1,out);        /* size of Identifier Block */
  286.     fwrite("fractint001",11,1,out);        /* Application Identifier */
  287.     i = sizeof(save_info); fwrite(&i,1,1,out); /* size of fractal info */
  288.     fwrite(&save_info,sizeof(save_info),1,out); /* fractal info */
  289.     i =   0; fwrite(&i,1,1,out);        /* block terminator */
  290.     fwrite(";",1,1,out);            /* GIF terminator */
  291.     }
  292. fclose(out);
  293. if (ydot < 9999) {            /* signal normal or interrupted end */
  294.     buzzer(0);
  295.     if (oktoprint) 
  296.         printf(" File saved as %s \n",openfile);
  297.         }
  298. else    {
  299.     buzzer(1);
  300.     if (oktoprint)
  301.         printf(" ** INTERRUPTED ** File %s \n", openfile);
  302.     getakey();            /* read the (interrupt) key-press */
  303.     }
  304. }
  305.  
  306. shftwrite(color,numcolors)        /* shift IBM colors to GIF format */
  307. unsigned char color[];
  308. int numcolors;
  309. {
  310. unsigned char thiscolor;
  311. int i,j;
  312. for (i = 0; i < numcolors; i++)
  313.     for (j = 0; j < 3; j++) {
  314.         thiscolor = color[3*i+j];
  315.         thiscolor = thiscolor << 2;
  316.         thiscolor += (thiscolor >> 6);
  317.         fwrite(&thiscolor,1,1,out);
  318.         }
  319. }
  320.  
  321. inittable()                /* routine to init tables */
  322. {
  323. int i;
  324.  
  325. raster(clearcode);            /* signal that table is initialized */
  326.  
  327. numentries = 0;                /* initialize the table */
  328. numrealentries = 0;
  329. nextentry = 1;
  330. lentest = 0;
  331. codebits = startbits;
  332.  
  333. toextra(0,"\0",1);            /* clear the hash entries */
  334. for (i = 0; i < MAXENTRY; i++)
  335.     strlocn[i] = 0;
  336.  
  337. }
  338.  
  339. raster(code)                /* routine to block and output codes */
  340. unsigned int code;
  341. {
  342. unsigned int icode, i, j;
  343.  
  344. if (code == 9999) {            /* special start-up signal */
  345.     bytecount = 0;
  346.     bitcount = 0;
  347.     for (i = 0; i < 266; i++)
  348.         block[i] = 0;
  349.     return;
  350.     }
  351.  
  352. icode = code << bitcount;        /* update the bit string */
  353. block[bytecount  ] |= (icode & 255);
  354. block[bytecount+1] |= ((icode>>8) & 255);
  355. icode = (code>>8) << bitcount;
  356. block[bytecount+2] |= ((icode>>8) & 255);
  357. bitcount += codebits;
  358. while (bitcount >= 8) {            /* locate next starting point */
  359.     bitcount -= 8;
  360.     bytecount++;
  361.     }
  362.  
  363. if (bytecount > 250 || code == endcode) {    /* time to write a block */
  364.     if (code == endcode) 
  365.         while (bitcount > 0) {        /* if EOF, find the real end */
  366.             bitcount -= 8;
  367.             bytecount++;
  368.             }
  369.     i = bytecount;
  370.     blockcount = i;
  371.     fwrite(&blockcount,1,1,out);        /* write the block */
  372.     fwrite(block,i,1,out);
  373.     bytecount = 0;                /* now re-start the block */
  374.     for (j = 0; j < 5; j++)            /* (may have leftover bits) */
  375.         block[j] = block[j+i];
  376.     for (j = 5; j < 266; j++)
  377.         block[j] = 0;
  378.     }
  379. }
  380.  
  381.  
  382. updatesavename(name)                     /* go to the next name */
  383. char *name;
  384. {
  385.   char *save, *hold = name + strlen(name) - 1;        /* start at the end */
  386.   int i =  0;
  387.  
  388.   while(isdigit(*hold))                      /* skip backwards */
  389.     hold--;
  390.   
  391.   hold++;                         /* recover first digit */
  392.   
  393.   while (*hold == '0')                      /* skip leading zeros */
  394.     hold++;
  395.   
  396.   save = hold;
  397.   
  398.   while (*save)                         /* check for all nines */
  399.   {
  400.     if (*save != '9')
  401.       break;
  402.     save++;
  403.   }
  404.     
  405.   if (!*save)               /* if the whole thing is nines then back */
  406.     save = hold - 1;           /* up one place. Note that this will eat */
  407.                    /* your last letter if you go to far.    */
  408.   else
  409.     save = hold;
  410.  
  411.   itoa(atoi(hold) + 1, save, 10);            /* increment the number */
  412. }
  413.   
  414.