home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / rexx / library2 / gbmrexx / gbm / gbmgif.c < prev    next >
C/C++ Source or Header  |  1993-11-10  |  22KB  |  924 lines

  1. /*
  2.  
  3. GBMGIF.C  Graphics Interchange Format support
  4.  
  5. Input options: index=# to get a given image in the file
  6.  
  7. */
  8.  
  9. /*...sincludes:0:*/
  10. #include <stdio.h>
  11. #include <ctype.h>
  12. #include <stddef.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <memory.h>
  16. #include <malloc.h>
  17. #ifdef AIX
  18. #include <unistd.h>
  19. #else
  20. #include <io.h>
  21. #endif
  22. #include <fcntl.h>
  23. #include <sys/types.h>
  24. #include <sys/stat.h>
  25. #include "standard.h"
  26. #include "gbm.h"
  27.  
  28. /*...vgbm\46\h:0:*/
  29. /*...e*/
  30.  
  31. /*...suseful:0:*/
  32. #define    low_byte(w)    ((byte)  ((w)&0x00ff)    )
  33. #define    high_byte(w)    ((byte) (((w)&0xff00)>>8))
  34. #define    make_word(a,b)    (((word)a) + (((word)b) << 8))
  35. /*...e*/
  36. /*...ssame:0:*/
  37. static BOOLEAN same(char *s1, char *s2, int n)
  38.     {
  39.     for ( ; n--; s1++, s2++ )
  40.         if ( tolower(*s1) != tolower(*s2) )
  41.             return ( FALSE );
  42.     return ( TRUE );
  43.     }
  44. /*...e*/
  45. /*...sfind_word:0:*/
  46. static char *find_word(char *str, char *substr)
  47.     {
  48.     char    buf [100+1], *s;
  49.     int    len = strlen(substr);
  50.  
  51.     for ( s  = strtok(strcpy(buf, str), " \t,");
  52.           s != NULL;
  53.           s  = strtok(NULL, " \t,") )
  54.         if ( same(s, substr, len) && s [len] == '\0' )
  55.             return ( str + (s - buf) );
  56.     return ( NULL );
  57.     }
  58. /*...e*/
  59. /*...sfind_word_prefix:0:*/
  60. static char *find_word_prefix(char *str, char *substr)
  61.     {
  62.     char    buf [100+1], *s;
  63.     int    len = strlen(substr);
  64.  
  65.     for ( s  = strtok(strcpy(buf, str), " \t,");
  66.           s != NULL;
  67.           s  = strtok(NULL, " \t,") )
  68.         if ( same(s, substr, len) )
  69.             return ( str + (s - buf) );
  70.     return ( NULL );
  71.     }
  72. /*...e*/
  73.  
  74. static GBMFT gif_gbmft =
  75.     {
  76.     "GIF",
  77.     "CompuServe Graphics Interchange Format",
  78.     "GIF",
  79.     GBM_FT_R1|GBM_FT_R4|GBM_FT_R8|
  80.     GBM_FT_W1|GBM_FT_W4|GBM_FT_W8,
  81.     };
  82.  
  83. #define    GBM_ERR_GIF_BPP        ((GBM_ERR) 1100)
  84. #define    GBM_ERR_GIF_TERM    ((GBM_ERR) 1101)
  85. #define    GBM_ERR_GIF_CODE_SIZE    ((GBM_ERR) 1102)
  86. #define    GBM_ERR_GIF_CORRUPT    ((GBM_ERR) 1103)
  87.  
  88. typedef struct
  89.     {
  90.     BOOLEAN ilace, errok;
  91.     int bpp;
  92.     byte pal [0x100 * 3];
  93.     } GIF_PRIV;
  94.  
  95. /*...sgif_qft:0:*/
  96. GBM_ERR gif_qft(GBMFT *gbmft)
  97.     {
  98.     *gbmft = gif_gbmft;
  99.     return ( GBM_ERR_OK );
  100.     }
  101. /*...e*/
  102. /*...sgif_rhdr:0:*/
  103. GBM_ERR gif_rhdr(char *fn, int fd, GBM *gbm, char *opt)
  104.     {
  105.     GIF_PRIV *gif_priv = (GIF_PRIV *) gbm -> priv;
  106.     byte signiture [6], scn_desc [7], image_desc [10];
  107.     char *index;
  108.     int img = 0, img_want = 1;
  109.     int bits_gct;
  110.  
  111.     fn=fn; /* Suppress 'unref arg' compiler warnings */
  112.  
  113.     /* Discover which image in GIF file we want */
  114.  
  115.     if ( (index = find_word_prefix(opt, "index=")) != NULL )
  116.         sscanf(index + 6, "%d", &img_want);
  117.  
  118.     gif_priv -> errok = ( find_word(opt, "errok") != NULL );
  119.  
  120.     /* Read and validate signiture block */
  121.  
  122.     lseek(fd, 0L, SEEK_SET);
  123.     if ( read(fd, signiture, 6) != 6 )
  124.         return ( GBM_ERR_READ );
  125.     if ( memcmp(signiture, "GIF87a", 6) &&
  126.          memcmp(signiture, "GIF89a", 6) )
  127.         return ( GBM_ERR_BAD_MAGIC );
  128.  
  129.     /* Read screen descriptor */
  130.  
  131.     if ( read(fd, scn_desc, 7) != 7 )
  132.         return ( GBM_ERR_READ );
  133.     gif_priv -> bpp = bits_gct = (scn_desc [4] & 7) + 1;
  134.  
  135.     if ( scn_desc [4] & 0x80 )
  136.         /* Global colour table follows screen descriptor */
  137.         {
  138.         if ( read(fd, gif_priv -> pal, 3 << bits_gct) != (3 << bits_gct) )
  139.             return ( GBM_ERR_READ );
  140.         }
  141.     else
  142.         /* Blank out palette, but make entry 1 white */
  143.         {
  144.         memset(gif_priv -> pal, 0, 3 << bits_gct);
  145.         gif_priv -> pal [3] =
  146.         gif_priv -> pal [4] =
  147.         gif_priv -> pal [5] = 0xff;
  148.         }
  149.  
  150.     /* Expected image descriptors / extension blocks / terminator */
  151.  
  152.     while ( img < img_want )
  153.         {
  154.         if ( read(fd, image_desc, 1) != 1 )
  155.             return ( GBM_ERR_READ );
  156.         switch ( image_desc [0] )
  157.             {
  158. /*...s0x2c \45\ image descriptor:24:*/
  159. case 0x2c:
  160.     if ( read(fd, image_desc + 1, 9) != 9 )
  161.         return ( GBM_ERR_READ );
  162.     gbm -> w = make_word(image_desc [5], image_desc [6]);
  163.     gbm -> h = make_word(image_desc [7], image_desc [8]);
  164.     gif_priv -> ilace = ( (image_desc [9] & 0x40) != 0 );
  165.  
  166.     if ( image_desc [9] & 0x80 )
  167.         /* Local colour table follows */
  168.         {
  169.         gif_priv -> bpp = (scn_desc [9] & 7) + 1;
  170.         if ( read(fd, gif_priv -> pal, 3 << gif_priv -> bpp) != (3 << gif_priv -> bpp) )
  171.             return ( GBM_ERR_READ );
  172.         }
  173.  
  174.     if ( ++img != img_want )
  175.         /* Skip the image data */
  176.         {
  177.         byte code_size, block_size;
  178.  
  179.         if ( read(fd, &code_size, 1) != 1 )
  180.             return ( GBM_ERR_READ );
  181.         do
  182.             {
  183.             if ( read(fd, &block_size, 1) != 1 )
  184.                 return ( GBM_ERR_READ );
  185.             lseek(fd, block_size, SEEK_SET);
  186.             }
  187.         while ( block_size );
  188.         }
  189.  
  190.     break;
  191. /*...e*/
  192. /*...s0x21 \45\ extension block:24:*/
  193. /* Ignore all extension blocks */
  194.  
  195. case 0x21:
  196.     {
  197.     byte func_code, byte_count;
  198.  
  199.     if ( read(fd, &func_code, 1) != 1 )
  200.         return ( GBM_ERR_READ );
  201.     do
  202.         {
  203.         if ( read(fd, &byte_count, 1) != 1 )
  204.             return ( GBM_ERR_READ );
  205.         lseek(fd, byte_count, SEEK_CUR);
  206.         }
  207.     while ( byte_count );
  208.     }
  209.     break;
  210. /*...e*/
  211. /*...s0x3b \45\ terminator:24:*/
  212. /* Oi, we were hoping to get an image descriptor! */
  213.  
  214. case 0x3b:
  215.     return ( GBM_ERR_GIF_TERM );
  216. /*...e*/
  217.             }
  218.         }
  219.  
  220.     switch ( gif_priv -> bpp )
  221.         {
  222.         case 1:        gbm -> bpp = 1;        break;
  223.         case 2:
  224.         case 3:
  225.         case 4:        gbm -> bpp = 4;        break;
  226.         case 5:
  227.         case 6:
  228.         case 7:
  229.         case 8:        gbm -> bpp = 8;        break;
  230.         default:    return ( GBM_ERR_GIF_BPP );
  231.         }
  232.  
  233.     return ( GBM_ERR_OK );
  234.     }
  235. /*...e*/
  236. /*...sgif_rpal:0:*/
  237. GBM_ERR gif_rpal(int fd, GBM *gbm, GBMRGB *gbmrgb)
  238.     {
  239.     GIF_PRIV *gif_priv = (GIF_PRIV *) gbm -> priv;
  240.     byte *pal = gif_priv -> pal;
  241.     int i;
  242.  
  243.     fd=fd; /* Suppress 'unref arg' compiler warning */
  244.  
  245.     memset(gbmrgb, 0x80, (sizeof(GBMRGB) << gbm -> bpp));
  246.  
  247.     for ( i = 0; i < (1 << gif_priv -> bpp); i++ )
  248.         {
  249.         gbmrgb [i].r = *pal++;
  250.         gbmrgb [i].g = *pal++;
  251.         gbmrgb [i].b = *pal++;
  252.         }
  253.  
  254.     return ( GBM_ERR_OK );
  255.     }
  256. /*...e*/
  257. /*...sgif_rdata:0:*/
  258. /*...scword \45\ code word:0:*/
  259. /*
  260. For some 32 bit systems, eg: Intel 386 processor, it may be quicker to use
  261. a 32 bit operation, rather than a 16 bit one. Hence the following :-
  262. */
  263.  
  264. #ifdef OS2
  265. typedef dword cword;
  266. #else
  267. typedef word cword;
  268. #endif
  269. /*...e*/
  270.  
  271. /*...sread context:0:*/
  272. typedef struct
  273.     {
  274.     int fd;                /* File descriptor to read */
  275.     int inx, size;            /* Index and size in bits */
  276.     byte buf [255+3];        /* Buffer holding bits */
  277.     int code_size;            /* Number of bits to return at once */
  278.     cword read_mask;        /* 2^code_size-1 */
  279.     } READ_CONTEXT;
  280.  
  281. static cword read_code(READ_CONTEXT *c)
  282.     {
  283.     int raw_code, byte_inx;
  284.  
  285.     if ( c -> inx + c -> code_size > c -> size )
  286. /*...snot enough bits in buffer\44\ refill it:16:*/
  287. /* Not very efficient, but infrequently called */
  288.  
  289. {
  290. int bytes_to_lose = (c -> inx >> 3);
  291. byte bytes;
  292.  
  293. /* Note biggest code size is 12 bits */
  294. /* And this can at worst span 3 bytes */
  295. memcpy(c -> buf, c -> buf + bytes_to_lose, 3);
  296. (c -> inx) &= 7;
  297. (c -> size) -= (bytes_to_lose << 3);
  298. if ( read(c -> fd, &bytes, 1) != 1 )
  299.     return ( 0xffff );
  300. if ( read(c -> fd, c -> buf + (c -> size >> 3), bytes) != bytes )
  301.     return ( 0xffff );
  302. (c -> size) += (bytes << 3);
  303. }
  304. /*...e*/
  305.  
  306.     byte_inx = (c -> inx >> 3);
  307.     raw_code = c -> buf [byte_inx] + ((c -> buf [byte_inx + 1]) << 8);
  308.     if ( c -> code_size > 8 )
  309.         raw_code += ((c -> buf [byte_inx + 2]) << 16);
  310.     raw_code >>= ((c -> inx) & 7);
  311.     (c -> inx) += (byte) (c -> code_size);
  312.  
  313.     return ( (cword) raw_code & c -> read_mask );
  314.     }
  315. /*...e*/
  316. /*...soutput context:0:*/
  317. typedef struct
  318.     {
  319.     int x, y, w, h, bpp, pass;
  320.     BOOLEAN ilace;
  321.     int stride;
  322.     byte *data, *data_this_line;
  323.     } OUTPUT_CONTEXT;
  324.  
  325. static void output(byte value, OUTPUT_CONTEXT *o)
  326.     {
  327.     if ( o -> y >= o -> h )
  328.         return;
  329.  
  330.     switch ( o -> bpp )
  331.         {
  332.         case 1:
  333.             if ( (o -> x) & 7 )
  334.                 o -> data_this_line [(o -> x) >> 3] |= (value << (7 - ((o -> x) & 7)));
  335.             else
  336.                 o -> data_this_line [(o -> x) >> 3] = (value << 7);
  337.             break;
  338.         case 4:
  339.             if ( (o -> x) & 1 )
  340.                 o -> data_this_line [(o -> x) >> 1] |= value;
  341.             else
  342.                 o -> data_this_line [(o -> x) >> 1] = (value << 4);
  343.             break;
  344.         case 8:
  345.             o -> data_this_line [o -> x] = value;
  346.             break;
  347.         }
  348.  
  349.     if ( ++(o -> x) < o -> w )
  350.         return;
  351.  
  352.     o -> x = 0;
  353.     if ( o -> ilace )
  354.         {
  355.         switch ( o -> pass )
  356.             {
  357.             case 0:
  358.                 (o -> y) += 8;
  359.                 if ( o -> y >= o -> h )
  360.                     {
  361.                     (o -> pass)++;
  362.                     o -> y = 4;
  363.                     }
  364.                 break;
  365.             case 1:
  366.                 (o -> y) += 8;
  367.                 if ( o -> y >= o -> h )
  368.                     {
  369.                     (o -> pass)++;
  370.                     o -> y = 2;
  371.                     }
  372.                 break;
  373.             case 2:
  374.                 (o -> y) += 4;
  375.                 if ( o -> y >= o -> h )
  376.                     {
  377.                     (o -> pass)++;
  378.                     o -> y = 1;
  379.                     }
  380.                 break;
  381.             case 3:
  382.                 (o -> y) += 2;
  383.                 break;
  384.             }
  385.         o -> data_this_line = o -> data + (o -> h - 1 - o -> y) * o -> stride;
  386.         }
  387.     else
  388.         {
  389.         (o -> y)++;
  390.         o -> data_this_line -= (o -> stride);
  391.         }
  392.     }
  393. /*...e*/
  394.  
  395. GBM_ERR gif_rdata(int fd, GBM *gbm, byte *data)
  396.     {
  397.     GIF_PRIV *gif_priv = (GIF_PRIV *) gbm -> priv;
  398.     byte min_code_size;        /* As read from the file */
  399.     int init_code_size;        /* Initial code size */
  400.     cword max_code;            /* 1 << code_size */
  401.     cword clear_code;        /* Code to clear table */
  402.     cword eoi_code;            /* End of information code */
  403.     cword first_free_code;        /* First free code */
  404.     cword free_code;        /* Next available free code slot */
  405.     word bit_mask;            /* Output pixel mask */ 
  406.     int i, out_count = 0;
  407.     cword code, cur_code, old_code, in_code, fin_char;
  408.     cword *prefix, *suffix, *outcode;
  409.     READ_CONTEXT c;
  410.     OUTPUT_CONTEXT o;
  411.     BOOLEAN table_full = FALSE;    /* To help implement deferred clear */
  412.  
  413.     if ( (prefix = (cword *) malloc(4096 * sizeof(cword))) == NULL )
  414.         return ( GBM_ERR_MEM );
  415.     if ( (suffix = (cword *) malloc(4096 * sizeof(cword))) == NULL )
  416.         {
  417.         free(prefix);
  418.         return ( GBM_ERR_MEM );
  419.         }
  420.     if ( (outcode = (cword *) malloc(4097 * sizeof(cword))) == NULL )
  421.         {
  422.         free(suffix);        
  423.         free(prefix);
  424.         return ( GBM_ERR_MEM );
  425.         }
  426.  
  427.     if ( read(fd, &min_code_size, 1) != 1 )
  428.         {    
  429.         free(outcode);
  430.         free(suffix);
  431.         free(prefix);
  432.         return ( GBM_ERR_READ );
  433.         }
  434.  
  435.     if ( min_code_size < 2 || min_code_size > 9 )
  436.         {    
  437.         free(outcode);
  438.         free(suffix);
  439.         free(prefix);
  440.         return ( GBM_ERR_GIF_CODE_SIZE );
  441.         }
  442.  
  443.     /* Initial read context */
  444.  
  445.     c.inx            = 0;
  446.     c.size           = 0;
  447.     c.fd             = fd;
  448.     c.code_size      = min_code_size + 1;
  449.     c.read_mask      = (cword) (( 1 << c.code_size ) - 1);
  450.  
  451.     /* Initialise pixel-output context */
  452.  
  453.     o.x              = 0;
  454.     o.y              = 0;
  455.     o.pass           = 0;
  456.     o.w              = gbm -> w;
  457.     o.h              = gbm -> h;
  458.     o.bpp            = gbm -> bpp;
  459.     o.ilace          = gif_priv -> ilace;
  460.     o.stride         = ( (gbm -> w * gbm -> bpp + 31) / 32 ) * 4;
  461.     o.data           = data;
  462.     o.data_this_line = data + (gbm -> h - 1) * o.stride;
  463.  
  464.     bit_mask = (word) ((1 << gif_priv -> bpp) - 1);
  465.  
  466.     /* 2^min_code size accounts for all colours in file */
  467.  
  468.     clear_code = (cword) ( 1 << min_code_size );
  469.     eoi_code = (cword) (clear_code + 1);
  470.     free_code = first_free_code = (cword) (clear_code + 2);
  471.  
  472.     /* 2^(min_code_size+1) includes clear and eoi code and space too */
  473.  
  474.     init_code_size = c.code_size;
  475.     max_code = (cword) ( 1 << c.code_size );
  476.  
  477.     while ( (code = read_code(&c)) != eoi_code && code != 0xffff && o.y < o.h )
  478.         {
  479.         if ( code == clear_code )
  480.             {
  481.             c.code_size = init_code_size;
  482.             max_code = (cword) ( 1 << c.code_size );
  483.             c.read_mask = (cword) (max_code - 1);
  484.             free_code = first_free_code;
  485.             cur_code = old_code = code = read_code(&c);
  486.             if ( code == 0xffff )
  487.                 break;
  488.             fin_char = (cur_code & bit_mask);
  489.             output(fin_char, &o);
  490.             table_full = FALSE;
  491.             }
  492.         else
  493.             {
  494.             cur_code = in_code = code;
  495.             if ( cur_code >= free_code )
  496.                 {
  497.                 cur_code = old_code;
  498.                 outcode [out_count++] = fin_char;
  499.                 }
  500.             while ( cur_code > bit_mask )
  501.                 {
  502.                 if ( out_count > 4096 )
  503.                     {
  504.                     free(outcode);
  505.                     free(suffix);
  506.                     free(prefix);
  507.                     return ( gif_priv -> errok ? GBM_ERR_OK : GBM_ERR_GIF_CORRUPT );
  508.                     }
  509.                 outcode [out_count++] = suffix [cur_code];
  510.                 cur_code = prefix [cur_code];
  511.                 }
  512.             fin_char = (cur_code & bit_mask);
  513.             outcode [out_count++] = fin_char;
  514.             for ( i = out_count - 1; i >= 0; i-- )
  515.                 output(outcode [i], &o);
  516.             out_count = 0;
  517.  
  518.             /* Update dictionary */
  519.  
  520.             if ( !table_full )
  521.                 {
  522.                 prefix [free_code] = old_code;
  523.                 suffix [free_code] = fin_char;
  524.  
  525.                 /* Advance to next free slot */
  526.  
  527.                 if ( ++free_code >= max_code )
  528.                     {
  529.                     if ( c.code_size < 12 )
  530.                         {
  531.                         c.code_size++;
  532.                         max_code <<= 1;
  533.                         c.read_mask = (cword) (( 1 << c.code_size ) - 1);
  534.                         }
  535.                     else
  536.                         table_full = TRUE;
  537.                     }
  538.                 }
  539.             old_code = in_code;
  540.             }
  541.         }
  542.  
  543.     free(outcode);
  544.     free(suffix);
  545.     free(prefix);
  546.  
  547.     if ( code == 0xffff )
  548.         return ( gif_priv -> errok ? GBM_ERR_OK : GBM_ERR_READ );
  549.  
  550.     return ( GBM_ERR_OK );
  551.     }
  552. /*...e*/
  553. /*...sgif_w:0:*/
  554. /*
  555. We won't write any GIF89a or higher extensions into file.
  556. Write palette as global colour table, not local.
  557.  
  558. strings holds code strings in the format
  559.     2 byte code number
  560.     1 byte length of string of pixels = N
  561.     N bytes of pixels
  562. hashcode is calculated from a string of pixels cumulatively.
  563. hashtable is searched starting at index hashcode for to find the entry.
  564. hashtable is big enough so that 1/2 of size >= max num of strings.
  565.  
  566. */
  567.  
  568. #define    INIT_HASH(p)    (((p)+3)*301)
  569.  
  570. /*...swrite context:0:*/
  571. typedef struct
  572.     {
  573.     int fd;                /* Open file descriptor to write to */
  574.     int inx;            /* Bit index into buf */
  575.     int code_size;            /* Code size in bits */
  576.     byte buf [255+2];        /* Biggest block + overflow space */
  577.     } WRITE_CONTEXT;
  578.  
  579. static BOOLEAN write_code(int code, WRITE_CONTEXT *w)
  580.     {
  581.     byte *buf = w -> buf + (w -> inx >> 3);
  582.  
  583.     code <<= ((w -> inx) & 7);
  584.     *buf++ |= (byte)  code       ;
  585. /*...scomment about IBM C\45\Set\47\2 optmiser bug:8:*/
  586. /* C-Set/2 with CSD0044 optimiser bug shows itself in following code :-
  587.     LEA    EDI,[EDX+01H]
  588.     MOV    CL,AL
  589.     SAR    CL,08H        ; What!
  590.     MOV    [EDI],CL
  591. */
  592. /*...e*/
  593.     *buf++  = (byte) (code >>  8);
  594.     *buf    = (byte) (code >> 16);
  595.  
  596.     (w -> inx) += (w -> code_size);
  597.     if ( w -> inx >= 255 * 8 )
  598.         /* Flush out full buffer */
  599.         {
  600.         byte bytes = 255;
  601.  
  602.         if ( write(w -> fd, &bytes, 1) != 1 )
  603.             return ( FALSE );
  604.         if ( write(w -> fd, w -> buf, 255) != 255 )
  605.             return ( FALSE );
  606.  
  607.         memcpy(w -> buf, w -> buf + 255, 2);
  608.         memset(w -> buf + 2, 0, 255);
  609.         (w -> inx) -= (255 * 8);
  610.         }
  611.  
  612.     return ( TRUE );
  613.     }
  614.  
  615. static BOOLEAN flush_code(WRITE_CONTEXT *w)
  616.     {
  617.     byte bytes = ((w -> inx + 7) >> 3);
  618.  
  619.     if ( bytes )
  620.         {
  621.         if ( write(w -> fd, &bytes, 1) != 1 )
  622.             return ( FALSE );
  623.         if ( write(w -> fd, w -> buf, bytes) != bytes )
  624.             return ( FALSE );
  625.         }
  626.  
  627.     /* Data block terminator - a block of zero size */
  628.  
  629.     bytes = 0;
  630.     return ( write(w -> fd, &bytes, 1) == 1 );
  631.     }
  632. /*...e*/
  633.  
  634. #define    MAX_STRING    64        /* Just to prevent huge strings */
  635. #define    MAX_STRINGS    64000        /* Small enough for 16 bit */
  636. #define    MAX_HASH    8193        /* Prime, and much > 4096 */
  637.  
  638. GBM_ERR gif_w(char *fn, int fd, GBM *gbm, GBMRGB *gbmrgb, byte *data, char *opt)
  639.     {
  640.     int xpos = 0, ypos = 0, xscreen = gbm -> w, yscreen = gbm -> h;
  641.     int inx_background = 0;
  642.     byte scn_desc [7], image_desc [10], term;
  643.     int stride = ((gbm -> w * gbm -> bpp + 31) / 32) * 4;
  644.     WRITE_CONTEXT w;
  645.     int init_code_size, x, y, p;
  646.     word clear_code, eoi_code, lastentry;
  647.     int nextstring, numentries, numrealentries;
  648.     unsigned int hashcode, lenstring;
  649.     byte min_code_size;
  650.     byte string [MAX_STRING-2], *strings, **hashtable, *pdata;
  651.     char *s;
  652.  
  653.     fn=fn; /* Suppress 'unref arg' compiler warnings */
  654.  
  655.     if ( gbm -> bpp != 1 && gbm -> bpp != 4 && gbm -> bpp != 8 )
  656.         return ( GBM_ERR_NOT_SUPP );
  657.  
  658.     if ( (s = find_word_prefix(opt, "xscreen=")) != NULL )
  659.         sscanf(s + 8, "%d", &xscreen);
  660.  
  661.     if ( (s = find_word_prefix(opt, "yscreen=")) != NULL )
  662.         sscanf(s + 8, "%d", &yscreen);
  663.  
  664.     if ( (s = find_word_prefix(opt, "background=")) != NULL )
  665.         sscanf(s + 11, "%d", &inx_background);
  666.  
  667.     if ( (s = find_word_prefix(opt, "xpos=")) != NULL )
  668.         sscanf(s + 5, "%d", &xpos);
  669.  
  670.     if ( (s = find_word_prefix(opt, "ypos=")) != NULL )
  671.         sscanf(s + 5, "%d", &ypos);
  672.  
  673.     /* Write signiture */
  674.  
  675.     if ( write(fd, "GIF87a", 6) != 6 )
  676.         return ( GBM_ERR_WRITE );
  677.  
  678.     /* Write screen descriptor */
  679.  
  680.     scn_desc [0] = low_byte(xscreen);
  681.     scn_desc [1] = high_byte(xscreen);
  682.     scn_desc [2] = low_byte(yscreen);
  683.     scn_desc [3] = high_byte(yscreen);
  684.     scn_desc [4] = (0x80 | ((gbm -> bpp - 1) * 0x11));
  685.                 /* Global colour table follows */
  686.                 /* Quality bpp == gct bpp == gbm -> bpp */
  687.     scn_desc [5] = (byte) inx_background;
  688.     scn_desc [6] = 0;
  689.     if ( write(fd, scn_desc, 7) != 7 )
  690.         return ( GBM_ERR_WRITE );
  691.  
  692.     /* Write global colour table */
  693.  
  694.     for ( p = 0; p < (1 << gbm -> bpp); p++ )
  695.         {
  696.         byte pal [3];
  697.  
  698.         pal [0] = gbmrgb [p].r;
  699.         pal [1] = gbmrgb [p].g;
  700.         pal [2] = gbmrgb [p].b;
  701.         if ( write(fd, pal, 3) != 3 )
  702.             return ( GBM_ERR_WRITE );
  703.         }
  704.  
  705.     /* Do image descriptor block */
  706.  
  707.     image_desc [0] = (byte) 0x2c;
  708.     image_desc [1] = low_byte(xpos);
  709.     image_desc [2] = high_byte(xpos);
  710.     image_desc [3] = low_byte(ypos);
  711.     image_desc [4] = high_byte(ypos);
  712.     image_desc [5] = low_byte(gbm -> w);
  713.     image_desc [6] = high_byte(gbm -> w);
  714.     image_desc [7] = low_byte(gbm -> h);
  715.     image_desc [8] = high_byte(gbm -> h);
  716.     image_desc [9] = gbm -> bpp - 1;
  717.         /* Non-interlaced, no local colour map, no sorted palette */
  718.     if ( write(fd, image_desc, 10) != 10 )
  719.         return ( GBM_ERR_WRITE );
  720.  
  721.     /* Now LZW encode data */
  722.  
  723.     if ( (strings = (byte *) malloc(MAX_STRINGS)) == NULL )
  724.         return ( GBM_ERR_MEM );
  725.  
  726.     if ( (hashtable = (byte **) malloc(MAX_HASH * sizeof(byte *))) == NULL )
  727.         {
  728.         free(strings);
  729.         return ( GBM_ERR_MEM );
  730.         }
  731.  
  732.     /* Initialise encoder variables */
  733.  
  734.     init_code_size = gbm -> bpp + 1;
  735.     if ( init_code_size == 2 )
  736.         /* Room for col0, col1, cc, eoi, but no others! */
  737.         init_code_size++;
  738.  
  739.     min_code_size = init_code_size - 1;
  740.     if ( write(fd, &min_code_size, 1) != 1 )
  741.         {
  742.         free(hashtable);
  743.         free(strings);
  744.         return ( GBM_ERR_WRITE );
  745.         }
  746.  
  747.     clear_code     = ( 1 << min_code_size );
  748.     eoi_code       = clear_code + 1;
  749.  
  750.     /* Setup write context */
  751.  
  752.     w.fd        = fd;
  753.     w.inx       = 0;
  754.     w.code_size = init_code_size;
  755.     memset(w.buf, 0, sizeof(w.buf));
  756.  
  757.     if ( !write_code(clear_code, &w) )
  758.         {
  759.         free(hashtable);
  760.         free(strings);
  761.         return ( GBM_ERR_WRITE );
  762.         }
  763.  
  764.     numentries     = 0;
  765.     numrealentries = 0;
  766.     nextstring     = 0;
  767.     lenstring      = 0;
  768.     for ( hashcode = 0; hashcode < MAX_HASH; hashcode++ )
  769.         hashtable [hashcode] = NULL;
  770.  
  771.     data += ( (gbm -> h - 1) * stride );
  772.     for ( y = gbm -> h - 1; y >= 0; y--, data -= stride )
  773.         for ( x = 0, pdata = data; x < gbm -> w; x++ )
  774.             {
  775.             byte col;
  776.  
  777. /*...sget col:24:*/
  778. switch ( gbm -> bpp )
  779.     {
  780.     case 8:
  781.         col = *pdata++;
  782.         break;
  783.     case 4:
  784.         if ( x & 1 )
  785.             col = (*pdata++ & 0x0f);
  786.         else
  787.             col = (*pdata >> 4);
  788.         break;
  789.     default: /* must be 1 */
  790.         if ( (x & 7) == 7 )
  791.             col = (*pdata++ & 0x01);
  792.         else
  793.             col = ((*pdata >> (7-(x&7))) & 0x01);
  794.         break;
  795.     }
  796. /*...e*/
  797. /*...slzw encode:24:*/
  798. string [0] = ++lenstring;
  799. string [lenstring] = col;
  800. if ( lenstring == 1 )
  801.     {
  802.     lastentry = col;
  803.     hashcode = INIT_HASH(col);
  804.     }
  805. else
  806.     {
  807.     unsigned int i, j;
  808.  
  809.     /* Find current string within strings, by generating hash of string,
  810.        and looking in strings for it starting at hashcode */
  811.  
  812.     hashcode *= ( col + lenstring );
  813.     j = (hashcode % MAX_HASH);
  814.     for ( i = 0; i < MAX_HASH; i++ )
  815.         {
  816.         if ( ++j >= MAX_HASH )
  817.             j = 0;
  818.         if ( hashtable [j] == NULL )
  819.             break;
  820.         if ( !memcmp(hashtable [j]+2, string, 1 + lenstring) )
  821.             break;
  822.         }
  823.     if ( hashtable [j] != NULL && 2 + 1 + lenstring < MAX_STRING )
  824.         /* Found in the strings table */
  825.         {
  826.         byte *ptr = hashtable [j];
  827.         lastentry = (word) ptr [0] + ((word) ptr [1] << 8);
  828.         }
  829.     else
  830.         /* Not found, or length too long */
  831.         {
  832.         if ( !write_code(lastentry, &w) )
  833.             {
  834.             free(hashtable);
  835.             free(strings);
  836.             return ( GBM_ERR_WRITE );
  837.             }
  838.         numentries++;
  839.         if ( hashtable [j] == NULL )
  840.             /* Add string */
  841.             {
  842.             word entry = (word) (eoi_code + numentries);
  843.             byte *ptr = strings + nextstring;
  844.             hashtable [j] = ptr;
  845.             ptr [0] = (byte)  entry;
  846.             ptr [1] = (byte) (entry >> 8);
  847.             memcpy(ptr + 2, string, 1 + lenstring);
  848.             nextstring += (2 + 1 + lenstring); 
  849.             numrealentries++;
  850.             }
  851.         lenstring = string [0] = (byte) 1;
  852.         lastentry = string [1] = (byte) col;
  853.         hashcode = INIT_HASH(col);
  854.  
  855.         if ( eoi_code + numentries == (1 << w.code_size) )
  856.             /* Next code will be written longer */
  857.             w.code_size++;
  858.  
  859.         if ( eoi_code + numentries > 4093 ||
  860.              nextstring + MAX_STRING > MAX_STRINGS )
  861.             /* Reset tables */
  862.             {
  863.             if ( !write_code(lastentry, &w) )
  864.                 {
  865.                 free(hashtable);
  866.                 free(strings);
  867.                 return ( GBM_ERR_WRITE );
  868.                 }
  869.             if ( !write_code(clear_code, &w) )
  870.                 {
  871.                 free(hashtable);
  872.                 free(strings);
  873.                 return ( GBM_ERR_WRITE );
  874.                 }
  875.             numentries     = 0;
  876.             numrealentries = 0;
  877.             nextstring     = 0;
  878.             lenstring      = 0;
  879.             w.code_size    = init_code_size;
  880.             for ( j = 0; j < MAX_HASH; j++ )
  881.                 hashtable [j] = NULL;
  882.             }
  883.         }
  884.     }
  885. /*...e*/
  886.             }
  887.  
  888.     free(hashtable);
  889.     free(strings);
  890.  
  891.     if ( !write_code(lastentry, &w) )
  892.         return ( GBM_ERR_WRITE );
  893.     if ( !write_code(eoi_code, &w) )
  894.         return ( GBM_ERR_WRITE );
  895.     if ( !flush_code(&w) )
  896.         return ( GBM_ERR_WRITE );
  897.  
  898.     /* Now write terminator */
  899.  
  900.     term = (byte) 0x3b;
  901.     if ( write(fd, &term, 1) != 1 )
  902.         return ( GBM_ERR_WRITE );
  903.  
  904.     return ( GBM_ERR_OK );
  905.     }
  906. /*...e*/
  907. /*...sgif_err:0:*/
  908. char *gif_err(GBM_ERR rc)
  909.     {
  910.     switch ( (int) rc )
  911.         {
  912.         case GBM_ERR_GIF_BPP:
  913.             return ( "bad bits per pixel" );
  914.         case GBM_ERR_GIF_TERM:
  915.             return ( "terminator found before requested image descriptor" );
  916.         case GBM_ERR_GIF_CODE_SIZE:
  917.             return ( "code size not in range 2 to 9" );
  918.         case GBM_ERR_GIF_CORRUPT:
  919.             return ( "encoded data is corrupt" );
  920.         }
  921.     return ( NULL );
  922.     }
  923. /*...e*/
  924.