File:  [LON-CAPA] / loncom / cgi / mimeTeX / gifsave.c
Revision 1.2: download - view: text, annotated - select for diffs
Fri Mar 24 23:08:33 2006 UTC (18 years, 3 months ago) by albertel
Branches: MAIN
CVS tags: version_2_5_X, version_2_5_2, version_2_5_1, version_2_5_0, version_2_4_X, version_2_4_99_0, version_2_4_2, version_2_4_1, version_2_4_0, version_2_3_X, version_2_3_99_0, version_2_3_2, version_2_3_1, version_2_3_0, version_2_2_X, version_2_2_99_1, version_2_2_99_0, version_2_2_2, version_2_2_1, version_2_2_0, version_2_1_99_3, version_2_1_99_2, version_2_1_99_1, version_2_1_99_0, HEAD
-latest versions of mimtex

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

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