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