/*************************************************************************** * GIF Encoder from arbitrary image storage format * * Credits: * * Lempel-Ziv Compression * GIF(sm)is the property of CompuServe Incorporated * IEEE Computer, June 1984 * Spencer W. Thomas * Jim McKie * Steve Davies * Ken Turkowski * James A. Woods * Joe Orost * Donald Knuth, (vol 3. sec. 6.4) * G. Knott * David Rowley * Marcel Wijkstra * PBMPlus Copyright (C) 1989 by Jef Poskanzer * Bitmap BMP/DIB format structures (C) Microsoft Corporation * Glenn Slayden 1996 * * **************************************************************************/ #include #include #include #include #include #include /*************************************************************************** * * Windows BMP format definitions. Cannot use the actual structures * for two reasons: first, I couldn't get the #pragma pack to work * in GCC, and second, the byte order is different between Intel/Sun. * **************************************************************************/ /* BITMAPFILEHEADER offsets */ #define w_bfType 0x00 #define d_bfSize 0x02 #define w_bfReserved1 0x06 #define w_bfReserved2 0x08 #define d_bfOffBits 0x0A /* BITMAPINFOHEADER offsets */ #define d_biSize 0x0E #define l_biWidth 0x12 #define l_biHeight 0x16 #define w_biPlanes 0x1A #define w_biBitCount 0x1C #define d_biCOmpression 0x1E #define d_biSizeImage 0x22 #define l_biXPelsPerMeter 0x26 #define l_biYPelsPerMeter 0x2A #define d_biClrUsed 0x2E #define d_biClrImportant 0x32 #define offs_start_rgb 0x36 #define sizeof_rgb 4 #define _swapword(x) (x[0] + (x[1]<<8)) #define GetIntelWord(a) ((unsigned short)_swapword(((unsigned char *)(a)))) #define GetIntelDWord(a) ((unsigned long)GetIntelWord(a) + \ (unsigned long)(GetIntelWord(a+2)<<16)) /*************************************************************************** * * PIMG can be a pointer to any user-defined image type. You must * pass it into GIFEncode() in the main() function, and add your * code to handle it in the GetPixel() function * **************************************************************************/ typedef char *PIMG; void GIFEncode(FILE *fp,int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, PIMG pi); /*************************************************************************** * * A code_int must be able to hold 1<Usage\n"); fprintf(fpOut,""); fprintf(fpOut,"Usage: gifencode.cgi?win3bmp.bmp\n"); fprintf(fpOut,""); exit(0); } #else fpOut = fopen("mygif.gif","w"); fp = fopen("image/blobule.bmp","r"); #endif if (!fp) exit(0); /* preread 6 bytes from the file to get its size */ fread(preread,1,6,fp); /* Check for proper signature */ if (GetIntelWord(preread + w_bfType) != 0x4D42) exit(0); cbFile = GetIntelDWord(preread + d_bfSize); /* Read the rest of the file into pBmp */ pBmp = malloc(cbFile); fread(&pBmp[6],1,cbFile-6,fp); /* Make sure it's not an old-style OS/2 bitmap */ if (GetIntelDWord(pBmp+d_biSize) != 0x28) exit(0); #ifndef CGI printf("bfType: %x\n", GetIntelWord(preread+w_bfType)); printf("cbFile: %lx\n", cbFile); printf("bfSize: %lx\n", GetIntelDWord(preread+d_bfSize)); printf("bfOffBits: %lx\n",GetIntelDWord(pBmp+d_bfOffBits)); printf("biSize: %lx\n", GetIntelDWord(pBmp+d_biSize)); printf("biWidth: %ld\n", GetIntelDWord(pBmp+l_biWidth)); printf("biHeight: %ld\n", GetIntelDWord(pBmp+l_biHeight)); printf("biPlanes: %x\n", GetIntelWord(pBmp+w_biPlanes)); printf("biBitCount: %x\n",GetIntelWord(pBmp+w_biBitCount)); #endif bpp = GetIntelWord(pBmp + w_biBitCount); width = GetIntelDWord(pBmp + l_biWidth); height = GetIntelDWord(pBmp + l_biHeight); cColors = (1 << bpp); pRgb = pBmp + offs_start_rgb; for (i=0; i= Height) { ++Pass; cury = 4; } break; case 1: cury += 8; if (cury >= Height) { ++Pass; cury = 2; } break; case 2: cury += 4; if (cury >= Height) { ++Pass; cury = 1; } break; case 3: cury += 2; break; } } } } /*************************************************************************** * * Return the next pixel from the image * **************************************************************************/ int GIFNextPixel(PIMG pi) { int r; if (CountDown == 0) return EOF; --CountDown; r = GetPixel(pi, curx, cury); BumpPixel(); return r; } /*************************************************************************** * * GIF block size related globals * **************************************************************************/ static char accum[256]; /* packet accumulator */ static int a_count = 0; /* Number of character in the accumulator */ /*************************************************************************** * * Flush a 254 byte packet to disk and reset the accumulator * **************************************************************************/ void flush_char(void) { if (a_count > 0) { fputc( a_count, g_outfile ); fwrite( accum, 1, a_count, g_outfile ); a_count = 0; } } /*************************************************************************** * * Add a character to the end of the current packet, and if it is 254 * characters, flush the packet to disk. * **************************************************************************/ void char_out(int c) { accum[a_count++] = c; if (a_count >= 254) flush_char(); } /*************************************************************************** * * Output the given code * **************************************************************************/ void output(code_int code) { /* Statics */ static unsigned long cur_accum = 0; static int cur_bits = 0; static unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; /* Code */ cur_accum &= masks[cur_bits]; if (cur_bits > 0) cur_accum |= ((long)code << cur_bits); else cur_accum = code; cur_bits += n_bits; while (cur_bits >= 8) { char_out((unsigned int)(cur_accum & 0xff)); cur_accum >>= 8; cur_bits -= 8; } /* * If the next entry is going to be too big for the code size, * then increase it, if possible. */ if (free_ent > maxcode || clear_flg) { if (clear_flg) { maxcode = MAXCODE(n_bits = g_init_bits); clear_flg = 0; } else { ++n_bits; if (n_bits == maxbits) maxcode = maxmaxcode; else maxcode = MAXCODE(n_bits); } } if( code == EOFCode ) { /* At EOF, write the rest of the buffer. */ while (cur_bits > 0) { char_out((unsigned int)(cur_accum & 0xff)); cur_accum >>= 8; cur_bits -= 8; } flush_char(); fflush(g_outfile); if (ferror(g_outfile)) return; } } /*************************************************************************** * * Clear the hash table * **************************************************************************/ void cl_hash(register count_int hsize) { register count_int *htab_p = htab+hsize; register long i; register long m1 = -1; i = hsize - 16; do { *(htab_p-16) = m1; *(htab_p-15) = m1; *(htab_p-14) = m1; *(htab_p-13) = m1; *(htab_p-12) = m1; *(htab_p-11) = m1; *(htab_p-10) = m1; *(htab_p-9) = m1; *(htab_p-8) = m1; *(htab_p-7) = m1; *(htab_p-6) = m1; *(htab_p-5) = m1; *(htab_p-4) = m1; *(htab_p-3) = m1; *(htab_p-2) = m1; *(htab_p-1) = m1; htab_p -= 16; } while ((i -= 16) >= 0); for (i+=16; i > 0; --i) *--htab_p = m1; } /*************************************************************************** * * Clear out the codes table * **************************************************************************/ void cl_block(void) { cl_hash((count_int)hsize); free_ent = ClearCode + 2; clear_flg = 1; output((code_int)ClearCode); } /*************************************************************************** * * compress - according to original documentation, "use open addressing * double hashing (no chaining) on the prefix code / next character * combination. Do a variant of Knuth's algorithm D (vol 3. sec. 6.4) * along with G. Knott's relatively-prime secondary probe. Here, the * modular division first probe gives way to a faster exclusive-or * manipulation. Also do block compression with an adaptive reset, * whereby the code table is cleared when the compression ratio decreases, * but after the table fills. The variable-length output codes are re- * sized at this point, and a special CLEAR code is generated for the * decompressor. * **************************************************************************/ void compress( int init_bits, FILE *outfile, PIMG pi) { register long fcode; register code_int i /* = 0 */; register int c; register code_int ent; register code_int disp; register code_int hsize_reg; register int hshift; g_init_bits = init_bits; g_outfile = outfile; clear_flg = 0; maxcode = MAXCODE(n_bits = g_init_bits); ClearCode = (1 << (init_bits - 1)); EOFCode = ClearCode + 1; free_ent = ClearCode + 2; ent = GIFNextPixel(pi); hshift = 0; for (fcode = (long)hsize; fcode < 65536L; fcode *= 2L) ++hshift; /* set hash code range bound */ hshift = 8 - hshift; hsize_reg = hsize; /* clear hash table */ cl_hash((count_int) hsize_reg); output((code_int)ClearCode); while ((c = GIFNextPixel(pi)) != EOF) { fcode = (long) (((long) c << maxbits) + ent); i = (((code_int)c << hshift) ^ ent); /* xor hashing */ if (htab[i] == fcode) { ent = ctab[i]; continue; } else if ((long)htab[i] < 0) /* empty slot */ goto nomatch; disp = hsize_reg - i; /* secondary hash (after G. Knott) */ if (i == 0) disp = 1; probe: if ((i -= disp) < 0) i += hsize_reg; if (htab[i] == fcode) { ent = ctab[i]; continue; } if ((long)htab[i] > 0) goto probe; nomatch: output((code_int)ent); ent = c; if (free_ent < maxmaxcode) { ctab[i] = free_ent++; /* code -> hashtable */ htab[i] = fcode; } else cl_block(); } /* Put out the final code */ output((code_int)ent); output((code_int)EOFCode); } /*************************************************************************** * * Write out a word to the GIF file * **************************************************************************/ void Putword( int w, FILE *fp) { fputc(w & 0xff, fp); fputc((w / 256) & 0xff, fp); } /*************************************************************************** * * Write out a GIF file * **************************************************************************/ void GIFEncode( FILE *fp, /* Writeable file handle for output */ int GWidth, /* Pixel width of the source image */ int GHeight, /* Pixel height of the source image */ int GInterlace, /* Interlace flag */ int Background, /* Background color index */ int Transparent, /* -1 or Transparent color index (causes GIF89a) */ int BitsPerPixel, /* Bits per pixel of source and output */ int *Red, /* 2^bpp entries of red component */ int *Green, int *Blue, PIMG pi) /* pointer to source image */ { int B; int RWidth, RHeight; int LeftOfs, TopOfs; int Resolution; int ColorMapSize; int InitCodeSize; int i; Interlace = GInterlace; ColorMapSize = 1 << BitsPerPixel; RWidth = Width = GWidth; RHeight = Height = GHeight; LeftOfs = TopOfs = 0; Resolution = BitsPerPixel; /* Calculate number of bits we are expecting */ CountDown = (long)Width * (long)Height; /* Indicate which pass we are on (if interlace) */ Pass = 0; /* The initial code size */ if (BitsPerPixel <= 1) InitCodeSize = 2; else InitCodeSize = BitsPerPixel; /* Set up the current x and y position */ curx = cury = 0; /* Write the GIF header */ fwrite( Transparent < 0 ? "GIF87a" : "GIF89a", 1, 6, fp ); /* Write out the screen width and height */ Putword( RWidth, fp ); Putword( RHeight, fp ); /* Indicate that there is a global colour map */ B = 0x80; /* OR in the resolution */ B |= (Resolution - 1) << 5; /* OR in the Bits per Pixel */ B |= (BitsPerPixel - 1); /* Write it out */ fputc( B, fp ); /* Write out the Background colour */ fputc( Background, fp ); /* Byte of 0's (future expansion) */ fputc( 0, fp ); /* Write out the Global Color Map */ for (i=0; i= 0) { fputc( '!', fp ); fputc( 0xf9, fp ); fputc( 4, fp ); fputc( 1, fp ); fputc( 0, fp ); fputc( 0, fp ); fputc( (unsigned char) Transparent, fp ); fputc( 0, fp ); } /* Write an Image separator */ fputc( ',', fp ); /* Write the Image header */ Putword( LeftOfs, fp ); Putword( TopOfs, fp ); Putword( Width, fp ); Putword( Height, fp ); /* Write out whether or not the image is interlaced */ if (Interlace) fputc( 0x40, fp ); else fputc( 0x00, fp ); /* Write out the initial code size */ fputc( InitCodeSize, fp ); /* Write out the compressed image */ compress( InitCodeSize+1, fp, pi ); /* Write out a Zero-length packet (to end the series) */ fputc( 0, fp ); /* Write the GIF file terminator */ fputc( ';', fp ); }