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