File:  [LON-CAPA] / loncom / cgi / mimeTeX / gifsave.c
Revision 1.4: download - view: text, annotated - select for diffs
Thu Dec 4 12:17:13 2008 UTC (15 years, 5 months ago) by riegler
Branches: MAIN
CVS tags: version_2_9_X, version_2_9_99_0, version_2_9_1, version_2_9_0, version_2_8_X, version_2_8_99_1, version_2_8_99_0, version_2_8_2, version_2_8_1, version_2_8_0, version_2_7_99_1, version_2_7_99_0, version_2_10_X, version_2_10_1, version_2_10_0_RC2, version_2_10_0_RC1, version_2_10_0, loncapaMITrelate_1, language_hyphenation_merge, language_hyphenation, bz6209-base, bz6209, bz5969, bz2851, PRINT_INCOMPLETE_base, PRINT_INCOMPLETE, HEAD, GCI_3, GCI_2, GCI_1, BZ5971-printing-apage, BZ5434-fox, BZ4492-merge, BZ4492-feature_horizontal_radioresponse, BZ4492-feature_Support_horizontal_radioresponse, BZ4492-Support_horizontal_radioresponse
upgrade to mimetex version 1.7
This version will be needed in order to align formulas neatly.
noticable change is the % in the path specified in "commands".

    1: /* $Id: gifsave.c,v 1.4 2008/12/04 12:17:13 riegler Exp $ */
    2: /**************************************************************************
    3:  *
    4:  *  FILE            gifsave.c
    5:  *
    6:  *  DESCRIPTION     Routines to create a GIF-file. See README for
    7:  *                  a description.
    8:  *
    9:  *                  The functions were originally written using Borland's
   10:  *                  C-compiler on an IBM PC -compatible computer, but they
   11:  *                  are compiled and tested on Linux and SunOS as well.
   12:  *
   13:  *  WRITTEN BY      Sverre H. Huseby <sverrehu@online.no>
   14:  *
   15:  **************************************************************************/
   16: 
   17: #include <stdlib.h>
   18: #include <stdio.h>
   19: /* #include <unistd.h> */	/* (added by j.forkosh) to get STDOUT_FILENO*/
   20: #include <string.h>		/* " */
   21: /* --- windows-specific header info --- */
   22: #ifndef WINDOWS			/* -DWINDOWS not supplied by user */
   23:   #if defined(_WINDOWS) || defined(_WIN32) || defined(WIN32) \
   24:   ||  defined(DJGPP)		/* try to recognize windows compilers */ \
   25:   ||  defined(_USRDLL)		/* must be WINDOWS if compiling for DLL */
   26:     #define WINDOWS		/* signal windows */
   27:   #endif
   28: #endif
   29: #ifdef WINDOWS			/* " if filename=NULL passed to GIF_Create()*/
   30:   #include <fcntl.h>		/* " OutFile=stdout used.  But Windows opens*/
   31:   #include <io.h>		/* " stdout in char mode, and precedes every*/
   32: 				/* " 0x0A with spurious 0x0D. */
   33:   #if defined(_O_BINARY) && !defined(O_BINARY)  /* only have _O_BINARY */
   34:     #define O_BINARY _O_BINARY	/* make O_BINARY available, etc... */
   35:     #define setmode  _setmode
   36:     #define fileno   _fileno
   37:   #endif
   38:   #if defined(_O_BINARY) || defined(O_BINARY)  /* setmode() now available */
   39:     #define HAVE_SETMODE	/* so we'll use setmode() */
   40:   #endif
   41: #endif
   42: 
   43: /* #include "gifsave.h" */	/* (j.forkosh) explcitly include header */
   44: enum GIF_Code {
   45:     GIF_OK = 0,
   46:     GIF_ERRCREATE,
   47:     GIF_ERRWRITE,
   48:     GIF_OUTMEM
   49: };
   50: 
   51: int  GIF_Create(const char *filename, int width, int height,
   52: 		int numcolors, int colorres);
   53: void GIF_SetColor(int colornum, int red, int green, int blue);
   54: void GIF_SetTransparent(int colornum);	/* (added by j.forkosh) */
   55: int  GIF_CompressImage(int left, int top, int width, int height,
   56: 		       int (*getpixel)(int x, int y));
   57: int  GIF_Close(void);
   58: /* --- end-of-header gifsave.h --- */
   59: 
   60: 
   61: /**************************************************************************
   62:  *                                                                        *
   63:  *                       P R I V A T E    D A T A                         *
   64:  *                                                                        *
   65:  **************************************************************************/
   66: 
   67: typedef unsigned Word;          /* at least two bytes (16 bits) */
   68: typedef unsigned char Byte;     /* exactly one byte (8 bits) */
   69: 
   70: /* used by IO-routines */
   71: static FILE *OutFile = NULL;    /* file to write to */
   72: static Byte *OutBuffer = NULL;	/* (added by j.forkosh) */
   73: static int isCloseOutFile = 0;	/* " */
   74: #if !defined(MAXGIFSZ)		/* " */
   75:   #define MAXGIFSZ 131072	/* " max #bytes comprising gif image */
   76: #endif				/* " */
   77: int gifSize = 0;		/* " #bytes comprising gif */
   78: int maxgifSize = MAXGIFSZ;	/* " max #bytes written to OutBuffer */
   79: extern int  iscachecontenttype;	/* " true to cache mime content-type */
   80: extern char contenttype[2048];	/* " content-type:, etc. buffer */
   81: 
   82: /* used when writing to a file bitwise */
   83: static Byte Buffer[256];        /* there must be one more than `needed' */
   84: static int  Index,              /* current byte in buffer */
   85:             BitsLeft;           /* bits left to fill in current byte. These
   86:                                  * are right-justified */
   87: 
   88: /* used by routines maintaining an LZW string table */
   89: #define RES_CODES 2
   90: 
   91: #define HASH_FREE 0xFFFF
   92: #define NEXT_FIRST 0xFFFF
   93: 
   94: #define MAXBITS 12
   95: #define MAXSTR (1 << MAXBITS)
   96: 
   97: #define HASHSIZE 9973
   98: #define HASHSTEP 2039
   99: 
  100: #define HASH(index, lastbyte) (((lastbyte << 8) ^ index) % HASHSIZE)
  101: 
  102: static Byte *StrChr = NULL;
  103: static Word *StrNxt = NULL,
  104:             *StrHsh = NULL,
  105:             NumStrings;
  106: 
  107: /* used in the main routines */
  108: typedef struct {
  109:     Word LocalScreenWidth,
  110:          LocalScreenHeight;
  111:     Byte GlobalColorTableSize : 3,
  112:          SortFlag             : 1,
  113:          ColorResolution      : 3,
  114:          GlobalColorTableFlag : 1;
  115:     Byte BackgroundColorIndex;
  116:     Byte PixelAspectRatio;
  117: } ScreenDescriptor;
  118: 
  119: typedef struct {
  120:     Byte Separator;
  121:     Word LeftPosition,
  122:          TopPosition;
  123:     Word Width,
  124:          Height;
  125:     Byte LocalColorTableSize : 3,
  126:          Reserved            : 2,
  127:          SortFlag            : 1,
  128:          InterlaceFlag       : 1,
  129:          LocalColorTableFlag : 1;
  130: } ImageDescriptor;
  131: 
  132: static int  BitsPrPrimColor,    /* bits pr primary color */
  133:             NumColors;          /* number of colors in color table */
  134: static int  TransparentColorIndex=(-1); /* (added by j.forkosh) */
  135: static Byte *ColorTable = NULL;
  136: static Word ScreenHeight,
  137:             ScreenWidth,
  138:             ImageHeight,
  139:             ImageWidth,
  140:             ImageLeft,
  141:             ImageTop,
  142:             RelPixX, RelPixY;   /* used by InputByte() -function */
  143: static int  (*GetPixel)(int x, int y);
  144: 
  145: 
  146: 
  147: /**************************************************************************
  148:  *                                                                        *
  149:  *                   P R I V A T E    F U N C T I O N S                   *
  150:  *                                                                        *
  151:  **************************************************************************/
  152: 
  153: /*========================================================================*
  154:  =                         Routines to do file IO                         =
  155:  *========================================================================*/
  156: 
  157: /*-------------------------------------------------------------------------
  158:  *
  159:  *  NAME          Create
  160:  *
  161:  *  DESCRIPTION   Creates a new file, and enables referencing using the
  162:  *                global variable OutFile. This variable is only used
  163:  *                by these IO-functions, making it relatively simple to
  164:  *                rewrite file IO.
  165:  *
  166:  *  INPUT         filename
  167:  *                        name of file to create,
  168:  *                        or NULL for stdout,
  169:  *                        or if *filename='\000' then it's the address of
  170:  *                           a memory buffer to which gif will be written
  171:  *
  172:  *  RETURNS       GIF_OK       - OK
  173:  *                GIF_ERRWRITE - Error opening the file
  174:  */
  175: static int
  176: Create(const char *filename)
  177: {
  178:     OutBuffer = NULL;				/* (added by j.forkosh) */
  179:     isCloseOutFile = 0;				/* " */
  180:     gifSize = 0;				/* " */
  181:     if ( filename == NULL )			/* " */
  182:       {	OutFile = stdout;			/* " */
  183: 	/*OutFile = fdopen(STDOUT_FILENO,"wb");*/ /* " doesn't work, */
  184: 	#ifdef WINDOWS				/* "   so instead... */
  185: 	  #ifdef HAVE_SETMODE			/* "   try to use setmode()*/
  186: 	    if ( setmode ( fileno (stdout), O_BINARY) /* to  set stdout */
  187: 	    == -1 ) ; /* handle error */	/* " to binary mode */
  188: 	  #else					/* " setmode not available */
  189: 	    #if 1				/* " */
  190: 	      freopen ("CON", "wb", stdout);	/* " freopen stdout binary */
  191: 	    #else				/* " */
  192: 	      stdout = fdopen (STDOUT_FILENO, "wb"); /*fdopen stdout binary*/
  193: 	    #endif				/* " */
  194: 	  #endif				/* " */
  195: 	#endif					/* " */
  196:       }						/* " */
  197:     else					/* " */
  198:       if ( *filename != '\000' )		/* " */
  199: 	{ if ((OutFile = fopen(filename, "wb")) == NULL)
  200: 	    return GIF_ERRCREATE;
  201: 	  isCloseOutFile = 1;			/* (added by j.forkosh) */
  202:           if ( iscachecontenttype )		/* " cache headers in file */
  203:             if ( *contenttype != '\000' )	/* " have headers in buffer*/
  204:               fputs(contenttype,OutFile); }	/* " write buffered headers*/
  205:       else					/* " */
  206: 	OutBuffer = (Byte *)filename;		/* " */
  207:     return GIF_OK;
  208: }
  209: 
  210: 
  211: 
  212: /*-------------------------------------------------------------------------
  213:  *
  214:  *  NAME          Write
  215:  *
  216:  *  DESCRIPTION   Output bytes to the current OutFile.
  217:  *
  218:  *  INPUT         buf     pointer to buffer to write
  219:  *                len     number of bytes to write
  220:  *
  221:  *  RETURNS       GIF_OK       - OK
  222:  *                GIF_ERRWRITE - Error writing to the file
  223:  */
  224: static int
  225: Write(const void *buf, unsigned len)
  226: {
  227:     if ( OutBuffer == NULL )			/* (added by j.forkosh) */
  228:       {	if (fwrite(buf, sizeof(Byte), len, OutFile) < len)
  229: 	  return GIF_ERRWRITE; }
  230:     else					/* (added by j.forkosh) */
  231:       {	if ( gifSize+len <= maxgifSize )	/* " */
  232: 	  memcpy(OutBuffer+gifSize,buf,len); }	/* " */
  233:     gifSize += len;				/* " */
  234:     return GIF_OK;
  235: }
  236: 
  237: 
  238: 
  239: /*-------------------------------------------------------------------------
  240:  *
  241:  *  NAME          WriteByte
  242:  *
  243:  *  DESCRIPTION   Output one byte to the current OutFile.
  244:  *
  245:  *  INPUT         b       byte to write
  246:  *
  247:  *  RETURNS       GIF_OK       - OK
  248:  *                GIF_ERRWRITE - Error writing to the file
  249:  */
  250: static int
  251: WriteByte(Byte b)
  252: {
  253:     if ( OutBuffer == NULL )			/* (added by j.forkosh) */
  254:       {	if (putc(b, OutFile) == EOF)
  255: 	  return GIF_ERRWRITE; }
  256:     else					/* (added by j.forkosh) */
  257:       {	if ( gifSize < maxgifSize )		/* " */
  258: 	  OutBuffer[gifSize] = b; }		/* " */
  259:     gifSize++;					/* " */
  260:     return GIF_OK;
  261: }
  262: 
  263: 
  264: 
  265: /*-------------------------------------------------------------------------
  266:  *
  267:  *  NAME          WriteWord
  268:  *
  269:  *  DESCRIPTION   Output one word (2 bytes with byte-swapping, like on
  270:  *                the IBM PC) to the current OutFile.
  271:  *
  272:  *  INPUT         w       word to write
  273:  *
  274:  *  RETURNS       GIF_OK       - OK
  275:  *                GIF_ERRWRITE - Error writing to the file
  276:  */
  277: static int
  278: WriteWord(Word w)
  279: {
  280:     if ( OutBuffer == NULL )			/* (added by j.forkosh) */
  281:       {	if (putc(w & 0xFF, OutFile) == EOF)
  282: 	  return GIF_ERRWRITE;
  283: 	if (putc((w >> 8), OutFile) == EOF)
  284: 	  return GIF_ERRWRITE; }
  285:     else					/* (added by j.forkosh) */
  286:       if ( gifSize+1 < maxgifSize )		/* " */
  287: 	{ OutBuffer[gifSize] = (Byte)(w & 0xFF);  /* " */
  288: 	  OutBuffer[gifSize+1] = (Byte)(w >> 8); }  /* " */
  289:     gifSize += 2;				/* " */
  290:     return GIF_OK;
  291: }
  292: 
  293: 
  294: 
  295: /*-------------------------------------------------------------------------
  296:  *
  297:  *  NAME          Close
  298:  *
  299:  *  DESCRIPTION   Close current OutFile.
  300:  */
  301: static void
  302: Close(void)
  303: {
  304:     if ( isCloseOutFile )			/* (added by j.forkosh) */
  305:       fclose(OutFile);
  306:     OutBuffer = NULL;				/* (added by j.forkosh) */
  307:     isCloseOutFile = 0;				/* " */
  308: }
  309: 
  310: 
  311: 
  312: 
  313: 
  314: /*========================================================================*
  315:  =                                                                        =
  316:  =                      Routines to write a bit-file                      =
  317:  =                                                                        =
  318:  *========================================================================*/
  319: 
  320: /*-------------------------------------------------------------------------
  321:  *
  322:  *  NAME          InitBitFile
  323:  *
  324:  *  DESCRIPTION   Initiate for using a bitfile. All output is sent to
  325:  *                  the current OutFile using the I/O-routines above.
  326:  */
  327: static void
  328: InitBitFile(void)
  329: {
  330:     Buffer[Index = 0] = 0;
  331:     BitsLeft = 8;
  332: }
  333: 
  334: 
  335: 
  336: /*-------------------------------------------------------------------------
  337:  *
  338:  *  NAME          ResetOutBitFile
  339:  *
  340:  *  DESCRIPTION   Tidy up after using a bitfile
  341:  *
  342:  *  RETURNS       0 - OK, -1 - error
  343:  */
  344: static int
  345: ResetOutBitFile(void)
  346: {
  347:     Byte numbytes;
  348: 
  349:     /* how much is in the buffer? */
  350:     numbytes = Index + (BitsLeft == 8 ? 0 : 1);
  351: 
  352:     /* write whatever is in the buffer to the file */
  353:     if (numbytes) {
  354:         if (WriteByte(numbytes) != GIF_OK)
  355:             return -1;
  356: 
  357:         if (Write(Buffer, numbytes) != GIF_OK)
  358:             return -1;
  359: 
  360:         Buffer[Index = 0] = 0;
  361:         BitsLeft = 8;
  362:     }
  363:     return 0;
  364: }
  365: 
  366: 
  367: 
  368: /*-------------------------------------------------------------------------
  369:  *
  370:  *  NAME          WriteBits
  371:  *
  372:  *  DESCRIPTION   Put the given number of bits to the outfile.
  373:  *
  374:  *  INPUT         bits    bits to write from (right justified)
  375:  *                numbits number of bits to write
  376:  *
  377:  *  RETURNS       bits written, or -1 on error.
  378:  *
  379:  */
  380: static int
  381: WriteBits(int bits, int numbits)
  382: {
  383:     int  bitswritten = 0;
  384:     Byte numbytes = 255;
  385: 
  386:     do {
  387:         /* if the buffer is full, write it */
  388:         if ((Index == 254 && !BitsLeft) || Index > 254) {
  389:             if (WriteByte(numbytes) != GIF_OK)
  390:                 return -1;
  391: 
  392:             if (Write(Buffer, numbytes) != GIF_OK)
  393:                 return -1;
  394: 
  395:             Buffer[Index = 0] = 0;
  396:             BitsLeft = 8;
  397:         }
  398: 
  399:         /* now take care of the two specialcases */
  400:         if (numbits <= BitsLeft) {
  401:             Buffer[Index] |= (bits & ((1 << numbits) - 1)) << (8 - BitsLeft);
  402:             bitswritten += numbits;
  403:             BitsLeft -= numbits;
  404:             numbits = 0;
  405:         } else {
  406:             Buffer[Index] |= (bits & ((1 << BitsLeft) - 1)) << (8 - BitsLeft);
  407:             bitswritten += BitsLeft;
  408:             bits >>= BitsLeft;
  409:             numbits -= BitsLeft;
  410: 
  411:             Buffer[++Index] = 0;
  412:             BitsLeft = 8;
  413:         }
  414:     } while (numbits);
  415: 
  416:     return bitswritten;
  417: }
  418: 
  419: 
  420: 
  421: /*========================================================================*
  422:  =                Routines to maintain an LZW-string table                =
  423:  *========================================================================*/
  424: 
  425: /*-------------------------------------------------------------------------
  426:  *
  427:  *  NAME          FreeStrtab
  428:  *
  429:  *  DESCRIPTION   Free arrays used in string table routines
  430:  */
  431: static void
  432: FreeStrtab(void)
  433: {
  434:     if (StrHsh) {
  435:         free(StrHsh);
  436:         StrHsh = NULL;
  437:     }
  438:     if (StrNxt) {
  439:         free(StrNxt);
  440:         StrNxt = NULL;
  441:     }
  442:     if (StrChr) {
  443:         free(StrChr);
  444:         StrChr = NULL;
  445:     }
  446: }
  447: 
  448: 
  449: 
  450: /*-------------------------------------------------------------------------
  451:  *
  452:  *  NAME          AllocStrtab
  453:  *
  454:  *  DESCRIPTION   Allocate arrays used in string table routines
  455:  *
  456:  *  RETURNS       GIF_OK     - OK
  457:  *                GIF_OUTMEM - Out of memory
  458:  */
  459: static int
  460: AllocStrtab(void)
  461: {
  462:     /* just in case */
  463:     FreeStrtab();
  464: 
  465:     if ((StrChr = (Byte *) malloc(MAXSTR * sizeof(Byte))) == 0) {
  466:         FreeStrtab();
  467:         return GIF_OUTMEM;
  468:     }
  469:     if ((StrNxt = (Word *) malloc(MAXSTR * sizeof(Word))) == 0) {
  470:         FreeStrtab();
  471:         return GIF_OUTMEM;
  472:     }
  473:     if ((StrHsh = (Word *) malloc(HASHSIZE * sizeof(Word))) == 0) {
  474:         FreeStrtab();
  475:         return GIF_OUTMEM;
  476:     }
  477:     return GIF_OK;
  478: }
  479: 
  480: 
  481: 
  482: /*-------------------------------------------------------------------------
  483:  *
  484:  *  NAME          AddCharString
  485:  *
  486:  *  DESCRIPTION   Add a string consisting of the string of index plus
  487:  *                the byte b.
  488:  *
  489:  *                If a string of length 1 is wanted, the index should
  490:  *                be 0xFFFF.
  491:  *
  492:  *  INPUT         index   index to first part of string, or 0xFFFF is
  493:  *                        only 1 byte is wanted
  494:  *                b       last byte in new string
  495:  *
  496:  *  RETURNS       Index to new string, or 0xFFFF if no more room
  497:  */
  498: static Word
  499: AddCharString(Word index, Byte b)
  500: {
  501:     Word hshidx;
  502: 
  503:     /* check if there is more room */
  504:     if (NumStrings >= MAXSTR)
  505:         return 0xFFFF;
  506: 
  507:     /* search the string table until a free position is found */
  508:     hshidx = HASH(index, b);
  509:     while (StrHsh[hshidx] != 0xFFFF)
  510:         hshidx = (hshidx + HASHSTEP) % HASHSIZE;
  511: 
  512:     /* insert new string */
  513:     StrHsh[hshidx] = NumStrings;
  514:     StrChr[NumStrings] = b;
  515:     StrNxt[NumStrings] = (index != 0xFFFF) ? index : NEXT_FIRST;
  516: 
  517:     return NumStrings++;
  518: }
  519: 
  520: 
  521: 
  522: /*-------------------------------------------------------------------------
  523:  *
  524:  *  NAME          FindCharString
  525:  *
  526:  *  DESCRIPTION   Find index of string consisting of the string of index
  527:  *                plus the byte b.
  528:  *
  529:  *                If a string of length 1 is wanted, the index should
  530:  *                be 0xFFFF.
  531:  *
  532:  *  INPUT         index   index to first part of string, or 0xFFFF is
  533:  *                        only 1 byte is wanted
  534:  *                b       last byte in string
  535:  *
  536:  *  RETURNS       Index to string, or 0xFFFF if not found
  537:  */
  538: static Word
  539: FindCharString(Word index, Byte b)
  540: {
  541:     Word hshidx, nxtidx;
  542: 
  543:     /* check if index is 0xFFFF. in that case we need only return b,
  544:      * since all one-character strings has their bytevalue as their
  545:      * index */
  546:     if (index == 0xFFFF)
  547:         return b;
  548: 
  549:     /* search the string table until the string is found, or we find
  550:      * HASH_FREE. in that case the string does not exist. */
  551:     hshidx = HASH(index, b);
  552:     while ((nxtidx = StrHsh[hshidx]) != 0xFFFF) {
  553:         if (StrNxt[nxtidx] == index && StrChr[nxtidx] == b)
  554:             return nxtidx;
  555:         hshidx = (hshidx + HASHSTEP) % HASHSIZE;
  556:     }
  557: 
  558:     /* no match is found */
  559:     return 0xFFFF;
  560: }
  561: 
  562: 
  563: 
  564: /*-------------------------------------------------------------------------
  565:  *
  566:  *  NAME          ClearStrtab
  567:  *
  568:  *  DESCRIPTION   Mark the entire table as free, enter the 2**codesize
  569:  *                one-byte strings, and reserve the RES_CODES reserved
  570:  *                codes.
  571:  *
  572:  *  INPUT         codesize
  573:  *                        number of bits to encode one pixel
  574:  */
  575: static void
  576: ClearStrtab(int codesize)
  577: {
  578:     int q, w;
  579:     Word *wp;
  580: 
  581:     /* no strings currently in the table */
  582:     NumStrings = 0;
  583: 
  584:     /* mark entire hashtable as free */
  585:     wp = StrHsh;
  586:     for (q = 0; q < HASHSIZE; q++)
  587:         *wp++ = HASH_FREE;
  588: 
  589:     /* insert 2**codesize one-character strings, and reserved codes */
  590:     w = (1 << codesize) + RES_CODES;
  591:     for (q = 0; q < w; q++)
  592:         AddCharString(0xFFFF, q);
  593: }
  594: 
  595: 
  596: 
  597: /*========================================================================*
  598:  =                        LZW compression routine                         =
  599:  *========================================================================*/
  600: 
  601: /*-------------------------------------------------------------------------
  602:  *
  603:  *  NAME          LZW_Compress
  604:  *
  605:  *  DESCRIPTION   Perform LZW compression as specified in the
  606:  *                GIF-standard.
  607:  *
  608:  *  INPUT         codesize
  609:  *                         number of bits needed to represent
  610:  *                         one pixelvalue.
  611:  *                inputbyte
  612:  *                         function that fetches each byte to compress.
  613:  *                         must return -1 when no more bytes.
  614:  *
  615:  *  RETURNS       GIF_OK     - OK
  616:  *                GIF_OUTMEM - Out of memory
  617:  */
  618: static int
  619: LZW_Compress(int codesize, int (*inputbyte)(void))
  620: {
  621:     register int c;
  622:     register Word index;
  623:     int  clearcode, endofinfo, numbits, limit, errcode;
  624:     Word prefix = 0xFFFF;
  625: 
  626:     /* set up the given outfile */
  627:     InitBitFile();
  628: 
  629:     /* set up variables and tables */
  630:     clearcode = 1 << codesize;
  631:     endofinfo = clearcode + 1;
  632: 
  633:     numbits = codesize + 1;
  634:     limit = (1 << numbits) - 1;
  635: 
  636:     if ((errcode = AllocStrtab()) != GIF_OK)
  637:         return errcode;
  638:     ClearStrtab(codesize);
  639: 
  640:     /* first send a code telling the unpacker to clear the stringtable */
  641:     WriteBits(clearcode, numbits);
  642: 
  643:     /* pack image */
  644:     while ((c = inputbyte()) != -1) {
  645:         /* now perform the packing. check if the prefix + the new
  646:          *  character is a string that exists in the table */
  647:         if ((index = FindCharString(prefix, c)) != 0xFFFF) {
  648:             /* the string exists in the table. make this string the
  649:              * new prefix.  */
  650:             prefix = index;
  651:         } else {
  652:             /* the string does not exist in the table. first write
  653:              * code of the old prefix to the file. */
  654:             WriteBits(prefix, numbits);
  655: 
  656:             /* add the new string (the prefix + the new character) to
  657:              * the stringtable */
  658:             if (AddCharString(prefix, c) > limit) {
  659:                 if (++numbits > 12) {
  660:                     WriteBits(clearcode, numbits - 1);
  661:                     ClearStrtab(codesize);
  662:                     numbits = codesize + 1;
  663:                 }
  664:                 limit = (1 << numbits) - 1;
  665:             }
  666: 
  667:             /* set prefix to a string containing only the character
  668:              * read. since all possible one-character strings exists
  669:              * int the table, there's no need to check if it is found. */
  670:             prefix = c;
  671:         }
  672:     }
  673: 
  674:     /* end of info is reached. write last prefix. */
  675:     if (prefix != 0xFFFF)
  676:         WriteBits(prefix, numbits);
  677: 
  678:     /* erite end of info -mark, flush the buffer, and tidy up */
  679:     WriteBits(endofinfo, numbits);
  680:     ResetOutBitFile();
  681:     FreeStrtab();
  682: 
  683:     return GIF_OK;
  684: }
  685: 
  686: 
  687: 
  688: /*========================================================================*
  689:  =                              Other routines                            =
  690:  *========================================================================*/
  691: 
  692: /*-------------------------------------------------------------------------
  693:  *
  694:  *  NAME          BitsNeeded
  695:  *
  696:  *  DESCRIPTION   Calculates number of bits needed to store numbers
  697:  *                between 0 and n - 1
  698:  *
  699:  *  INPUT         n       number of numbers to store (0 to n - 1)
  700:  *
  701:  *  RETURNS       Number of bits needed
  702:  */
  703: static int
  704: BitsNeeded(Word n)
  705: {
  706:     int ret = 1;
  707: 
  708:     if (!n--)
  709:         return 0;
  710:     while (n >>= 1)
  711:         ++ret;
  712:     return ret;
  713: }
  714: 
  715: 
  716: 
  717: /*-------------------------------------------------------------------------
  718:  *
  719:  *  NAME          InputByte
  720:  *
  721:  *  DESCRIPTION   Get next pixel from image. Called by the
  722:  *                LZW_Compress()-function
  723:  *
  724:  *  RETURNS       Next pixelvalue, or -1 if no more pixels
  725:  */
  726: static int
  727: InputByte(void)
  728: {
  729:     int ret;
  730: 
  731:     if (RelPixY >= ImageHeight)
  732:         return -1;
  733:     ret = GetPixel(ImageLeft + RelPixX, ImageTop + RelPixY);
  734:     if (++RelPixX >= ImageWidth) {
  735:         RelPixX = 0;
  736:         ++RelPixY;
  737:     }
  738:     return ret;
  739: }
  740: 
  741: 
  742: 
  743: /*-------------------------------------------------------------------------
  744:  *
  745:  *  NAME          WriteScreenDescriptor
  746:  *
  747:  *  DESCRIPTION   Output a screen descriptor to the current GIF-file
  748:  *
  749:  *  INPUT         sd      pointer to screen descriptor to output
  750:  *
  751:  *  RETURNS       GIF_OK       - OK
  752:  *                GIF_ERRWRITE - Error writing to the file
  753:  */
  754: static int
  755: WriteScreenDescriptor(ScreenDescriptor *sd)
  756: {
  757:     Byte tmp;
  758: 
  759:     if (WriteWord(sd->LocalScreenWidth) != GIF_OK)
  760:         return GIF_ERRWRITE;
  761:     if (WriteWord(sd->LocalScreenHeight) != GIF_OK)
  762:         return GIF_ERRWRITE;
  763:     tmp = (sd->GlobalColorTableFlag << 7)
  764:           | (sd->ColorResolution << 4)
  765:           | (sd->SortFlag << 3)
  766:           | sd->GlobalColorTableSize;
  767:     if (WriteByte(tmp) != GIF_OK)
  768:         return GIF_ERRWRITE;
  769:     if (WriteByte(sd->BackgroundColorIndex) != GIF_OK)
  770:         return GIF_ERRWRITE;
  771:     if (WriteByte(sd->PixelAspectRatio) != GIF_OK)
  772:         return GIF_ERRWRITE;
  773: 
  774:     return GIF_OK;
  775: }
  776: 
  777: 
  778: 
  779: /*-------------------------------------------------------------------------
  780:  *
  781:  *  NAME          WriteTransparentColorIndex (added by j.forkosh)
  782:  *
  783:  *  DESCRIPTION   Output a graphic extension block setting transparent
  784:  *		  colormap index
  785:  *
  786:  *  INPUT         colornum       colormap index of color to be transparent
  787:  *
  788:  *  RETURNS       GIF_OK       - OK
  789:  *                GIF_ERRWRITE - Error writing to the file
  790:  */
  791: static int
  792: WriteTransparentColorIndex(int colornum)
  793: {
  794:     if ( colornum < 0 ) return GIF_OK;		/*no transparent color set*/
  795:     if (WriteByte((Byte)(0x21)) != GIF_OK)	/*magic:Extension Introducer*/
  796:         return GIF_ERRWRITE;
  797:     if (WriteByte((Byte)(0xf9)) != GIF_OK)     /*magic:Graphic Control Label*/
  798:         return GIF_ERRWRITE;
  799:     if (WriteByte((Byte)(4)) != GIF_OK)		/* #bytes in block */
  800:         return GIF_ERRWRITE;
  801:     if (WriteByte((Byte)(1)) != GIF_OK)        /*transparent index indicator*/
  802:         return GIF_ERRWRITE;
  803:     if (WriteWord((Word)(0)) != GIF_OK)		/* delay time */
  804:         return GIF_ERRWRITE;
  805:     if (WriteByte((Byte)(colornum)) != GIF_OK)	/* transparent color index */
  806:         return GIF_ERRWRITE;
  807:     if (WriteByte((Byte)(0)) != GIF_OK)        /* terminator */
  808:         return GIF_ERRWRITE;
  809: 
  810:     return GIF_OK;
  811: }
  812: 
  813: 
  814: 
  815: /*-------------------------------------------------------------------------
  816:  *
  817:  *  NAME          WriteImageDescriptor
  818:  *
  819:  *  DESCRIPTION   Output an image descriptor to the current GIF-file
  820:  *
  821:  *  INPUT         id      pointer to image descriptor to output
  822:  *
  823:  *  RETURNS       GIF_OK       - OK
  824:  *                GIF_ERRWRITE - Error writing to the file
  825:  */
  826: static int
  827: WriteImageDescriptor(ImageDescriptor *id)
  828: {
  829:     Byte tmp;
  830: 
  831:     if (WriteByte(id->Separator) != GIF_OK)
  832:         return GIF_ERRWRITE;
  833:     if (WriteWord(id->LeftPosition) != GIF_OK)
  834:         return GIF_ERRWRITE;
  835:     if (WriteWord(id->TopPosition) != GIF_OK)
  836:         return GIF_ERRWRITE;
  837:     if (WriteWord(id->Width) != GIF_OK)
  838:         return GIF_ERRWRITE;
  839:     if (WriteWord(id->Height) != GIF_OK)
  840:         return GIF_ERRWRITE;
  841:     tmp = (id->LocalColorTableFlag << 7)
  842:           | (id->InterlaceFlag << 6)
  843:           | (id->SortFlag << 5)
  844:           | (id->Reserved << 3)
  845:           | id->LocalColorTableSize;
  846:     if (WriteByte(tmp) != GIF_OK)
  847:         return GIF_ERRWRITE;
  848: 
  849:     return GIF_OK;
  850: }
  851: 
  852: 
  853: 
  854: /**************************************************************************
  855:  *                                                                        *
  856:  *                    P U B L I C    F U N C T I O N S                    *
  857:  *                                                                        *
  858:  **************************************************************************/
  859: 
  860: /*-------------------------------------------------------------------------
  861:  *
  862:  *  NAME          GIF_Create
  863:  *
  864:  *  DESCRIPTION   Create a GIF-file, and write headers for both screen
  865:  *                and image.
  866:  *
  867:  *  INPUT         filename
  868:  *                        name of file to create (including extension)
  869:  *                width   number of horisontal pixels on screen
  870:  *                height  number of vertical pixels on screen
  871:  *                numcolors
  872:  *                        number of colors in the colormaps
  873:  *                colorres
  874:  *                        color resolution. Number of bits for each
  875:  *                        primary color
  876:  *
  877:  *  RETURNS       GIF_OK        - OK
  878:  *                GIF_ERRCREATE - Couldn't create file
  879:  *                GIF_ERRWRITE  - Error writing to the file
  880:  *                GIF_OUTMEM    - Out of memory allocating color table
  881:  */
  882: int
  883: GIF_Create(const char *filename, int width, int height,
  884: 	   int numcolors, int colorres)
  885: {
  886:     int q, tabsize;
  887:     Byte *bp;
  888:     ScreenDescriptor SD;
  889: 
  890:     /* initiate variables for new GIF-file */
  891:     NumColors = numcolors ? (1 << BitsNeeded(numcolors)) : 0;
  892:     BitsPrPrimColor = colorres;
  893:     ScreenHeight = height;
  894:     ScreenWidth = width;
  895: 
  896:     /* create file specified */
  897:     if (Create(filename) != GIF_OK)
  898:         return GIF_ERRCREATE;
  899: 
  900:     /* write GIF signature */
  901:     if ((Write("GIF87a", 6)) != GIF_OK)
  902:         return GIF_ERRWRITE;
  903: 
  904:     /* initiate and write screen descriptor */
  905:     SD.LocalScreenWidth = width;
  906:     SD.LocalScreenHeight = height;
  907:     if (NumColors) {
  908:         SD.GlobalColorTableSize = BitsNeeded(NumColors) - 1;
  909:         SD.GlobalColorTableFlag = 1;
  910:     } else {
  911:         SD.GlobalColorTableSize = 0;
  912:         SD.GlobalColorTableFlag = 0;
  913:     }
  914:     SD.SortFlag = 0;
  915:     SD.ColorResolution = colorres - 1;
  916:     SD.BackgroundColorIndex = 0;
  917:     SD.PixelAspectRatio = 0;
  918:     if (WriteScreenDescriptor(&SD) != GIF_OK)
  919:         return GIF_ERRWRITE;
  920: 
  921:     /* allocate color table */
  922:     if (ColorTable) {
  923:         free(ColorTable);
  924:         ColorTable = NULL;
  925:     }
  926:     if (NumColors) {
  927:         tabsize = NumColors * 3;
  928:         if ((ColorTable = (Byte *) malloc(tabsize * sizeof(Byte))) == NULL)
  929:             return GIF_OUTMEM;
  930:         else {
  931:             bp = ColorTable;
  932:             for (q = 0; q < tabsize; q++)
  933:                 *bp++ = 0;
  934:         }
  935:     }
  936:     return 0;
  937: }
  938: 
  939: 
  940: 
  941: /*-------------------------------------------------------------------------
  942:  *
  943:  *  NAME          GIF_SetColor
  944:  *
  945:  *  DESCRIPTION   Set red, green and blue components of one of the
  946:  *                colors. The color components are all in the range
  947:  *                [0, (1 << BitsPrPrimColor) - 1]
  948:  *
  949:  *  INPUT         colornum
  950:  *                        color number to set. [0, NumColors - 1]
  951:  *                red     red component of color
  952:  *                green   green component of color
  953:  *                blue    blue component of color
  954:  */
  955: void
  956: GIF_SetColor(int colornum, int red, int green, int blue)
  957: {
  958:     long maxcolor;
  959:     Byte *p;
  960: 
  961:     maxcolor = (1L << BitsPrPrimColor) - 1L;
  962:     p = ColorTable + colornum * 3;
  963:     *p++ = (Byte) ((red * 255L) / maxcolor);
  964:     *p++ = (Byte) ((green * 255L) / maxcolor);
  965:     *p++ = (Byte) ((blue * 255L) / maxcolor);
  966: }
  967: 
  968: 
  969: 
  970: /*-------------------------------------------------------------------------
  971:  *
  972:  *  NAME          GIF_SetTransparent (added by j.forkosh)
  973:  *
  974:  *  DESCRIPTION   Set colormap index of color to be transparent
  975:  *
  976:  *  INPUT         colornum
  977:  *                        color number to set transparent. [0, NumColors - 1]
  978:  */
  979: void
  980: GIF_SetTransparent(int colornum)
  981: {
  982:     TransparentColorIndex = colornum;
  983: }
  984: 
  985: 
  986: 
  987: /*-------------------------------------------------------------------------
  988:  *
  989:  *  NAME          GIF_CompressImage
  990:  *
  991:  *  DESCRIPTION   Compress an image into the GIF-file previousely
  992:  *                created using GIF_Create(). All color values should
  993:  *                have been specified before this function is called.
  994:  *
  995:  *                The pixels are retrieved using a user defined callback
  996:  *                function. This function should accept two parameters,
  997:  *                x and y, specifying which pixel to retrieve. The pixel
  998:  *                values sent to this function are as follows:
  999:  *
 1000:  *                    x : [ImageLeft, ImageLeft + ImageWidth - 1]
 1001:  *                    y : [ImageTop, ImageTop + ImageHeight - 1]
 1002:  *
 1003:  *                The function should return the pixel value for the
 1004:  *                point given, in the interval [0, NumColors - 1]
 1005:  *
 1006:  *  INPUT         left    screen-relative leftmost pixel x-coordinate
 1007:  *                        of the image
 1008:  *                top     screen-relative uppermost pixel y-coordinate
 1009:  *                        of the image
 1010:  *                width   width of the image, or -1 if as wide as
 1011:  *                        the screen
 1012:  *                height  height of the image, or -1 if as high as
 1013:  *                        the screen
 1014:  *                getpixel
 1015:  *                        address of user defined callback function.
 1016:  *                        (see above)
 1017:  *
 1018:  *  RETURNS       GIF_OK       - OK
 1019:  *                GIF_OUTMEM   - Out of memory
 1020:  *                GIF_ERRWRITE - Error writing to the file
 1021:  */
 1022: int
 1023: GIF_CompressImage(int left, int top, int width, int height,
 1024: 		  int (*getpixel)(int x, int y))
 1025: {
 1026:     int codesize, errcode;
 1027:     ImageDescriptor ID;
 1028: 
 1029:     if (width < 0) {
 1030:         width = ScreenWidth;
 1031:         left = 0;
 1032:     }
 1033:     if (height < 0) {
 1034:         height = ScreenHeight;
 1035:         top = 0;
 1036:     }
 1037:     if (left < 0)
 1038:         left = 0;
 1039:     if (top < 0)
 1040:         top = 0;
 1041: 
 1042:     /* write global colortable if any */
 1043:     if (NumColors)
 1044:         if ((Write(ColorTable, NumColors * 3)) != GIF_OK)
 1045:             return GIF_ERRWRITE;
 1046: 
 1047:     /* write graphic extension block with transparent color index */
 1048:     if ( TransparentColorIndex >= 0 )     /* (added by j.forkosh) */
 1049:       if ( WriteTransparentColorIndex(TransparentColorIndex)
 1050:       !=   GIF_OK ) return GIF_ERRWRITE;
 1051: 
 1052:     /* initiate and write image descriptor */
 1053:     ID.Separator = ',';
 1054:     ID.LeftPosition = ImageLeft = left;
 1055:     ID.TopPosition = ImageTop = top;
 1056:     ID.Width = ImageWidth = width;
 1057:     ID.Height = ImageHeight = height;
 1058:     ID.LocalColorTableSize = 0;
 1059:     ID.Reserved = 0;
 1060:     ID.SortFlag = 0;
 1061:     ID.InterlaceFlag = 0;
 1062:     ID.LocalColorTableFlag = 0;
 1063: 
 1064:     if (WriteImageDescriptor(&ID) != GIF_OK)
 1065:         return GIF_ERRWRITE;
 1066: 
 1067:     /* write code size */
 1068:     codesize = BitsNeeded(NumColors);
 1069:     if (codesize == 1)
 1070:         ++codesize;
 1071:     if (WriteByte(codesize) != GIF_OK)
 1072:         return GIF_ERRWRITE;
 1073: 
 1074:     /* perform compression */
 1075:     RelPixX = RelPixY = 0;
 1076:     GetPixel = getpixel;
 1077:     if ((errcode = LZW_Compress(codesize, InputByte)) != GIF_OK)
 1078:         return errcode;
 1079: 
 1080:     /* write terminating 0-byte */
 1081:     if (WriteByte(0) != GIF_OK)
 1082:         return GIF_ERRWRITE;
 1083: 
 1084:     return GIF_OK;
 1085: }
 1086: 
 1087: 
 1088: 
 1089: /*-------------------------------------------------------------------------
 1090:  *
 1091:  *  NAME          GIF_Close
 1092:  *
 1093:  *  DESCRIPTION   Close the GIF-file
 1094:  *
 1095:  *  RETURNS       GIF_OK       - OK
 1096:  *                GIF_ERRWRITE - Error writing to file
 1097:  */
 1098: int
 1099: GIF_Close(void)
 1100: {
 1101:     ImageDescriptor ID;
 1102: 
 1103:     /* initiate and write ending image descriptor */
 1104:     ID.Separator = ';';
 1105:     ID.LeftPosition = 0;	/* (added by j.forkosh) */
 1106:     ID.TopPosition = 0;		/* " initialize entire ID structure */
 1107:     ID.Width = 0;		/* " and ditto for other ID.x=0; below */
 1108:     ID.Height = 0;
 1109:     ID.LocalColorTableSize = 0;
 1110:     ID.Reserved = 0;
 1111:     ID.SortFlag = 0;
 1112:     ID.InterlaceFlag = 0;
 1113:     ID.LocalColorTableFlag = 0;
 1114: 
 1115:     if (WriteImageDescriptor(&ID) != GIF_OK)
 1116:         return GIF_ERRWRITE;
 1117: 
 1118:     /* close file */
 1119:     Close();
 1120: 
 1121:     /* release color table */
 1122:     if (ColorTable) {
 1123:         free(ColorTable);
 1124:         ColorTable = NULL;
 1125:     }
 1126: 
 1127:     return GIF_OK;
 1128: }
 1129: /* --- end-of-file gifsave.c --- */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>