home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1990-1992 Michael Davidson.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software
- * and its documentation for any purpose and without fee is hereby
- * granted, provided that the above copyright notice appear in all
- * copies and that both that copyright notice and this permission
- * notice appear in supporting documentation.
- *
- * This software is provided "as is" without express or implied warranty.
- */
-
- /*
- * The Graphics Interchange Format(c) is the Copyright property
- * of CompuServe Incorporated.
- *
- * GIF(sm) is a Service Mark property of CompuServe Incorporated.
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- #include "image.h"
-
- #ifndef STATIC
- #define STATIC static
- #endif
-
- /*
- * GIF file definitions
- */
- #define GIF_IMAGE_SEPARATOR ','
- #define GIF_EXTENSION '!'
- #define GIF_TERMINATOR ';'
-
- #define GIF_PLAIN_TEXT_EXT 0x01
- #define GIF_GRAPHICS_CONTROL_EXT 0xf9
- #define GIF_COMMENT_EXT 0xfe
- #define GIF_APPLICATION_EXT 0xff
-
- /*
- * GIF flags
- */
- #define GIF_INTERLACE 0x40
-
- /*
- * globals describing the GIF file
- */
-
- /*
- * GIF Screen Descriptor
- */
- STATIC int GIFScreenWidth;
- STATIC int GIFScreenHeight;
- STATIC int GIFScreenDepth;
- STATIC int GIFScreenFlags;
- STATIC int GIFScreenBackground;
- STATIC int GIFScreenAspectRatio;
-
- /*
- * GIF Global Color Map
- */
- STATIC int GIFGlobalColors;
- STATIC unsigned char GIFGlobalColorMap[256][3];
-
- /*
- * GIF Local Color Map
- */
- STATIC int GIFLocalColors;
- STATIC unsigned char GIFLocalColorMap[256][3];
-
- /*
- * GIF Graphics controls
- */
- STATIC unsigned DisposalMethod;
- STATIC unsigned UserInputFlag;
- STATIC unsigned TransparentColorFlag;
- STATIC unsigned DelayTime;
- STATIC unsigned TransparentColorIndex;
-
- /*
- * Disposal methods
- */
- #define NO_DISPOSAL 0
- #define LEAVE_IN_PLACE 1
- #define RESTORE_BG 2
- #define RESTORE_PREVIOUS 3
-
- /*
- * the GIF image itself
- */
- STATIC image_t *GIFImage;
-
- STATIC int gifReadImage(FILE *fp);
- STATIC int gifReadExtension(FILE *fp);
- STATIC int gifDecode(FILE *fp, int, int, int, int, int, int (*)());
- STATIC int gifComment(FILE *fp);
- STATIC int gifPlainText(FILE *fp);
- STATIC int gifApplication(FILE *fp);
- STATIC void gifSkipBlocks(FILE *fp);
- STATIC int gifGraphicsControl(FILE *fp);
- STATIC int gifReadByte(FILE *fp);
- STATIC int gifReadWord(FILE *fp);
-
- char *
- gifInfo(
- FILE *fp
- )
- {
- static char info[40];
- int width;
- int height;
- int colors;
- int flags;
-
- if (fread(info, 6, 1, fp) != 1)
- return NULL;
-
- if (strncmp(info, "GIF87a", 6) != 0 && strncmp(info, "GIF89a", 6) != 0)
- return NULL;
-
- width = gifReadWord(fp);
- height = gifReadWord(fp);
- flags = gifReadByte(fp);
- colors = 1 << ((flags & 7) + 1);
-
- sprintf(&info[6], " %dx%d %d colors", width, height, colors);
-
- return info;
- }
-
- int
- gifRead(
- char *name,
- FILE *fp
- )
- {
- int c;
- char header[6];
- int r;
-
- if (fread(header, sizeof(header), 1, fp) != 1)
- return imageError("can't read GIF header");
-
- if ( strncmp(header, "GIF87a", sizeof(header)) != 0
- && strncmp(header, "GIF89a", sizeof(header)) != 0 )
- return imageError("not a GIF file");
-
- GIFScreenWidth = gifReadWord(fp);
- GIFScreenHeight = gifReadWord(fp);
- GIFScreenFlags = gifReadByte(fp);
- GIFScreenBackground = gifReadByte(fp);
- GIFScreenAspectRatio= gifReadByte(fp);
-
- GIFScreenDepth = (GIFScreenFlags & 7) + 1;
- GIFGlobalColors = (GIFScreenFlags & 0x80) ? (1 << GIFScreenDepth) : 0;
-
- if ((GIFImage = imageStart(name, GIFScreenWidth, GIFScreenHeight,
- GIFScreenDepth, GIFScreenBackground)) == NULL)
- return imageError("can't allocate %d x %d GIF image",
- GIFScreenWidth, GIFScreenHeight);
-
- if (GIFGlobalColors != 0)
- {
- if (fread(GIFGlobalColorMap, 3, GIFGlobalColors, fp) != GIFGlobalColors)
- return imageError("can't read global color map");
-
- imageSetColorMap(0, GIFGlobalColorMap, GIFGlobalColors);
- }
-
- imageShowInfo(1);
-
- /*
- * set default graphics controls
- */
- DisposalMethod = NO_DISPOSAL;
- UserInputFlag = 0;
- TransparentColorFlag = 0;
- DelayTime = 0;
- TransparentColorIndex = 0;
-
- while ((c = gifReadByte(fp)) != GIF_TERMINATOR)
- {
- switch (c)
- {
- case GIF_IMAGE_SEPARATOR:
- if ( (r = gifReadImage(fp)) != 0)
- return r;
- break;
-
- case GIF_EXTENSION:
- if ( (r = gifReadExtension(fp)) != 0)
- return r;
- break;
-
- case EOF:
- return gifCorruptFile();
-
- default:
- return gifCorruptFile();
- }
- }
-
- return imageEnd();
- }
-
- gifCorruptFile()
- {
- int r;
-
- if ( (r = imageWarning("corrupt or truncated file")) != 0)
- return r;
- else
- return imageEnd();
- }
-
- int
- gifPutPixels(
- int x,
- int y,
- unsigned char *pixels,
- int count
- )
- {
- if (TransparentColorFlag)
- {
- register unsigned char c, *p, *q;
- register int n;
-
- c = TransparentColorIndex;
- n = count;
-
- for (p = pixels, q = &GIFImage->pixels[y][x]; --n >= 0; p++, q++)
- if (*p != c)
- *q = *p;
- }
- else
- {
- memcpy(&GIFImage->pixels[y][x], pixels, count);
- }
-
- return imageRefresh(x, y, count, 1);
- }
-
- int
- gifReadImage(
- FILE *fp
- )
- {
- int GIFImageLeft;
- int GIFImageTop;
- int GIFImageWidth;
- int GIFImageHeight;
- int GIFImageDepth;
- int GIFImageFlags;
- int r;
-
- GIFImageLeft = gifReadWord(fp);
- GIFImageTop = gifReadWord(fp);
- GIFImageWidth = gifReadWord(fp);
- GIFImageHeight = gifReadWord(fp);
- GIFImageFlags = gifReadByte(fp);
-
- /*
- * check that the image fits within the screen
- */
- if (GIFImageWidth > GIFScreenWidth || GIFImageHeight > GIFScreenHeight)
- return imageError("GIF image exceeds screen dimensions");
-
- /*
- * check that the image position is OK and adjust if necessary
- * (some GIF encoders get this wrong ......)
- */
- if (GIFImageLeft + GIFImageWidth > GIFScreenWidth)
- GIFImageLeft = GIFScreenWidth - GIFImageWidth;
-
- if (GIFImageTop + GIFImageHeight > GIFScreenHeight)
- GIFImageTop = GIFScreenHeight - GIFImageHeight;
-
- GIFImageDepth = (GIFImageFlags & 7) + 1;
- GIFLocalColors = (GIFImageFlags & 0x80) ? (1 << GIFImageDepth) : 0;
-
- if (GIFLocalColors != 0)
- {
- if (fread(GIFLocalColorMap, 3, GIFLocalColors, fp) != GIFLocalColors)
- return imageError("can't read local color map");
-
- imageSetColorMap(1, GIFLocalColorMap, GIFLocalColors);
- }
-
- r = gifDecode(fp, GIFImageLeft, GIFImageTop, GIFImageWidth, GIFImageHeight,
- GIFImageFlags & 0x40, gifPutPixels);
-
- return (r < 0) ? gifCorruptFile() : r;
- }
-
- /*
- * LZW decoding routines
- */
-
- #define MAXCODESIZE 12
- #define TABLESIZE (1 << MAXCODESIZE)
-
- struct code
- {
- struct code *previous;
- short length;
- unsigned char c;
- };
-
-
- #define GIFGetCode() ( (*(unsigned long *)(Bits + (BitIndex >> 3))) \
- >> (BitIndex & 7) )
- /*
- * GIFDecode() - decodes the actual image data
- */
-
- gifDecode(
- FILE *fp,
- int left,
- int top,
- int width,
- int height,
- int interlace,
- int (*putpixels)()
- )
- {
- unsigned char Bits[260];
- unsigned BitIndex;
- unsigned BitCount;
-
- unsigned InitCodeSize;
- unsigned CodeSize;
- unsigned CodeMask;
- struct code Codes[TABLESIZE+2];
- register struct code *CodeP;
- struct code *NextCodeP;
- struct code *PrevCodeP;
- struct code *LastCodeP;
- struct code *ClearCodeP;
- struct code *EOFCodeP;
- unsigned char buf[8192];
- register unsigned char *BufP;
- unsigned char *limit;
-
- unsigned Code;
- int x, y;
- int step;
- int npixels;
- int r;
-
- /*
- * get initial code size
- */
- if ((r = gifReadByte(fp)) < 1 || r > 8)
- return -1;
-
- InitCodeSize = r + 1;
- CodeSize = InitCodeSize;
-
- /*
- * set up pointers to special code values
- */
- ClearCodeP = &Codes[1 << (CodeSize - 1)];
- EOFCodeP = ClearCodeP + 1;
- NextCodeP = EOFCodeP;
- PrevCodeP = ClearCodeP;
- LastCodeP = &Codes[1 << CodeSize];
- CodeMask = (1 << CodeSize) - 1;
-
- /*
- * set up all of the single byte entries in the decode table
- */
- for (Code = 0, CodeP = Codes; CodeP < ClearCodeP; Code++, CodeP++)
- {
- CodeP->previous = NULL;
- CodeP->length = 1;
- CodeP->c = (unsigned char) Code;
- }
-
- ClearCodeP->previous= NULL;
- ClearCodeP->length = 0;
- ClearCodeP->c = 0;
-
- EOFCodeP->previous = NULL;
- EOFCodeP->length = 0;
- EOFCodeP->c = 0;
-
- BitIndex =
- BitCount = 0;
-
- x = 0;
- y = 0;
- step = interlace ? 8 : 1;
- BufP = &buf[0];
- limit = BufP + 4096;
-
- for (;;)
- {
- while (BitCount >= CodeSize && BufP < limit)
- {
- Code = GIFGetCode();
- Code &= CodeMask;
- BitCount -= CodeSize;
- BitIndex += CodeSize;
- CodeP = &Codes[Code];
-
- if (CodeP < ClearCodeP)
- {
- NextCodeP->previous = PrevCodeP;
- NextCodeP->length = PrevCodeP->length + 1;
- NextCodeP->c = (unsigned char) Code;
- *BufP++ = (unsigned char) Code;
- PrevCodeP = CodeP;
-
- if (++NextCodeP >= LastCodeP)
- {
- if (CodeSize < MAXCODESIZE)
- {
- ++CodeSize;
- LastCodeP = &Codes[1 << CodeSize];
- CodeMask = (1 << CodeSize) - 1;
- }
- else
- NextCodeP = LastCodeP;
- }
- }
- else if (CodeP > EOFCodeP)
- {
- unsigned char *p;
- int length;
-
- NextCodeP->previous = PrevCodeP;
- NextCodeP->length = PrevCodeP->length + 1;
-
- if (CodeP >= NextCodeP)
- {
- CodeP = NextCodeP;
- CodeP->c = (CodeP-1)->c;
- }
-
- PrevCodeP = CodeP;
- length = CodeP->length;
- p = BufP;
- BufP += length;
-
- switch (length & 7)
- {
- do
- {
- case 0: *--BufP = CodeP->c; CodeP = CodeP->previous;
- case 7: *--BufP = CodeP->c; CodeP = CodeP->previous;
- case 6: *--BufP = CodeP->c; CodeP = CodeP->previous;
- case 5: *--BufP = CodeP->c; CodeP = CodeP->previous;
- case 4: *--BufP = CodeP->c; CodeP = CodeP->previous;
- case 3: *--BufP = CodeP->c; CodeP = CodeP->previous;
- case 2: *--BufP = CodeP->c; CodeP = CodeP->previous;
- case 1: *--BufP = CodeP->c; CodeP = CodeP->previous;
- } while (BufP > p);
- }
-
- NextCodeP->c = *BufP; /* now set real suffix value */
- BufP += length;
-
- if (++NextCodeP >= LastCodeP)
- {
- if (CodeSize < MAXCODESIZE)
- {
- ++CodeSize;
- LastCodeP = &Codes[1 << CodeSize];
- CodeMask = (1 << CodeSize) - 1;
- }
- else
- NextCodeP = LastCodeP;
- }
- }
- else if (CodeP == ClearCodeP)
- {
- CodeSize = InitCodeSize;
- LastCodeP = &Codes[1 << CodeSize];
- CodeMask = (1 << CodeSize) - 1;
- NextCodeP = EOFCodeP;
- PrevCodeP = ClearCodeP;
- }
- else if (CodeP == EOFCodeP)
- break;
- }
-
- /*
- * get here if:
- * Bitcount < Codesize -> need more bits
- * BufP >= limit -> need to flush buffer
- * code == eof -> all done
- */
-
- /*
- * first put out any pixels in case we just hit EOF
- */
- npixels = BufP - buf;
- BufP = buf;
-
- while (npixels > 0)
- {
- int n;
-
- n = width - x;
- if (npixels < n)
- n = npixels;
-
- if ((r = putpixels(left + x, top + y, BufP, n)) != 0)
- return r;
-
- if ((x += n) >= width)
- {
- x = 0;
- if ((y += step) >= height)
- {
- step = step - (y & (step - 1));
- y = step / 2;
- }
- }
- npixels -= n;
- BufP += n;
- }
- BufP = buf;
-
- /*
- * if we are at EOF bail out
- */
- if (CodeP == EOFCodeP)
- break;
-
- /*
- * now get more code bits if necessary
- */
- if (BitCount < CodeSize)
- {
- int len;
- int i;
-
- len = (BitCount + 7) >> 3;
- for (i = 0; i < len; i++)
- Bits[i] = Bits[(BitIndex >> 3) + i];
- BitIndex &= 7;
-
- do
- {
- if ((len = getc(fp)) == EOF)
- return -1;
-
- if (len == 0 || fread(&Bits[i], 1, len, fp) != len)
- return -1;
-
- BitCount += len * 8;
- i += len;
- } while (BitCount < CodeSize);
- }
-
- }
- /*
- * seen EOF code - check for 0 length data block at end of encoded data
- */
-
- if (getc(fp) != 0)
- return -1;
-
- return 0;
- }
-
- int
- gifReadExtension(
- FILE *fp
- )
- {
- int c;
-
- c = gifReadByte(fp);
-
- switch (c)
- {
- case 0xf9: /* graphics control */
- gifGraphicsControl(fp);
- break;
-
- case 0xfe: /* comment */
- gifComment(fp);
- break;
-
- case 0x01: /* plain text */
- gifPlainText(fp);
- break;
-
- case 0xff: /* application */
- gifApplication(fp);
- break;
-
- default: /* unknown extension */
- gifSkipBlocks(fp);
- break;
-
- }
- return 0;
- }
-
- STATIC int
- gifGraphicsControl(
- FILE *fp
- )
- {
- int flags;
-
- if (gifReadByte(fp) != 4) /* check block size */
- return -1;
-
- flags = gifReadByte(fp);
-
- DisposalMethod = (flags >> 2) & 7;
- UserInputFlag = flags & 0x02;
- TransparentColorFlag= flags & 0x01;
-
- DelayTime = gifReadWord(fp);
- TransparentColorIndex= gifReadByte(fp);
-
- if (gifReadByte(fp) != 0 || ferror(fp)) /* check terminator */
- return -1;
-
- return 0;
- }
-
- STATIC int
- gifComment(
- FILE *fp
- )
- {
- int len;
- char buf[255];
-
- while ((len = gifReadByte(fp)) != 0 && len != EOF)
- {
- if (fread(buf, 1, len, fp) != len)
- break;
-
- #if 0
- PutComment(buf, len);
- #endif
- }
-
- return (len == 0) ? 0 : -1;
- }
-
- STATIC int
- gifPlainText(
- FILE *fp
- )
- {
- int TextGridLeft;
- int TextGridTop;
- int TextGridWidth;
- int TextGridHeight;
- int CharCellWidth;
- int CharCellHeight;
- int TextForeground;
- int TextBackground;
- int len;
- char buf[255];
-
- if (gifReadByte(fp) != 12) /* check block size */
- return -1;
-
- TextGridLeft = gifReadWord(fp);
- TextGridTop = gifReadWord(fp);
- TextGridWidth = gifReadWord(fp);
- TextGridHeight = gifReadWord(fp);
- CharCellWidth = gifReadByte(fp);
- CharCellHeight = gifReadByte(fp);
- TextForeground = gifReadByte(fp);
- TextBackground = gifReadByte(fp);
-
- while ((len = gifReadByte(fp)) != 0 && len != EOF)
- {
- if (fread(buf, 1, len, fp) != len)
- break;
- /*
- * do text rendering here
- */
- }
-
- return (len == 0) ? 0 : -1;
- }
-
- STATIC int
- gifApplication(
- FILE *fp
- )
- {
- char AppID[8];
- char AppAuthCode[3];
- char AppData[255];
- int len;
-
- if (gifReadByte(fp) != 11) /* check block size */
- return -1;
-
- if (fread(AppID, sizeof(AppID), 1, fp) != 1)
- return -1;
-
- if (fread(AppAuthCode, sizeof(AppAuthCode), 1, fp) != 1)
- return -1;
-
- while ((len = gifReadByte(fp)) != 0 && len != EOF)
- {
- if (fread(AppData, 1, len, fp) != len)
- break;
- }
-
- return (len == 0) ? 0 : -1;
- }
-
- STATIC void
- gifSkipBlocks(fp)
- FILE *fp;
- {
- int len;
-
- while ((len = gifReadByte(fp)) != 0 && len != EOF)
- fseek(fp, (long)len, 1);
- }
-
- STATIC int
- gifReadByte(fp)
- FILE *fp;
- {
- return getc(fp);
- }
-
- STATIC int
- gifReadWord(fp)
- FILE *fp;
- {
- int w;
-
- w = getc(fp);
- w |= getc(fp) << 8;
-
- return w;
- }
-