Annotation of loncom/cgi/mimeTeX/gifsave.c, revision 1.2
1.1 albertel 1: /* $Id: gifsave.c,v 1.2 1998/07/05 16:29:56 sverrehu 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 */
1.2 ! albertel 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 */
1.1 albertel 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 */
1.2 ! albertel 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 */
1.1 albertel 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
1.2 ! albertel 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
1.1 albertel 166: *
167: * RETURNS GIF_OK - OK
168: * GIF_ERRWRITE - Error opening the file
169: */
170: static int
171: Create(const char *filename)
172: {
1.2 ! albertel 173: OutBuffer = NULL; /* (added by j.forkosh) */
! 174: isCloseOutFile = 0; /* " */
! 175: gifSize = 0; /* " */
! 176: if ( filename == NULL ) /* " */
1.1 albertel 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 /* " */
1.2 ! albertel 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; /* " */
1.1 albertel 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: {
1.2 ! albertel 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; /* " */
1.1 albertel 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: {
1.2 ! albertel 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++; /* " */
1.1 albertel 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: {
1.2 ! albertel 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; /* " */
1.1 albertel 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);
1.2 ! albertel 298: OutBuffer = NULL; /* (added by j.forkosh) */
! 299: isCloseOutFile = 0; /* " */
1.1 albertel 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>