File:  [LON-CAPA] / loncom / cgi / mimeTeX / gifsave.c
Revision 1.1: download - view: text, annotated - select for diffs
Mon Feb 28 19:08:11 2005 UTC (19 years, 4 months ago) by albertel
Branches: MAIN
CVS tags: version_2_1_X, version_2_1_3, version_2_1_2, version_2_1_1, version_2_1_0, version_2_0_X, version_2_0_99_1, version_2_0_2, version_2_0_1, version_2_0_0, version_1_99_3, version_1_99_2, version_1_99_1_tmcc, version_1_99_1, version_1_99_0_tmcc, version_1_99_0, HEAD
- adding tex->image convertor (mimeTeX)

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

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