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