home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / gbmsrc.zip / gbmgif.c < prev    next >
C/C++ Source or Header  |  1999-01-17  |  21KB  |  897 lines

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