1 /******************************************************************************
2 * "Gif-Lib" - Yet another gif library.
3 *
4 * Written by: Gershon Elber Ver 1.1, Aug. 1990
5 ******************************************************************************
6 * The kernel of the GIF Encoding process can be found here.
7 ******************************************************************************
8 * History:
9 * 14 Jun 89 - Version 1.0 by Gershon Elber.
10 * 3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names).
11 * 26 Jun 96 - Version 3.0 by Eric S. Raymond (Full GIF89 support)
12 *****************************************************************************/
13
14 #if defined(__sun__) && __STDC_VERSION__ >= 201112L && _XOPEN_SOURCE < 600
15 #ifdef _XOPEN_SOURCE
16 #undef _XOPEN_SOURCE
17 #endif
18 #define _XOPEN_SOURCE 600
19 #endif
20
21 #if defined(_MSC_VER) || defined(__MSDOS__)
22 # include <io.h>
23 # include <sys\stat.h>
24 # ifndef _MSC_VER
25 # include <alloc.h>
26 # endif
27 #else
28 # include <sys/types.h>
29 # include <sys/stat.h>
30 # ifdef R6000
31 # include <sys/mode.h>
32 # endif
33 #endif /* _MSC_VER || __MSDOS__ */
34
35 #if defined(unix) || defined(__unix__) || defined(__sun__) || defined(__APPLE__)
36 #include <unistd.h>
37 #endif
38
39 #include <fcntl.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include "gif_lib.h"
44 #include "gif_lib_private.h"
45
46 #if !defined(S_IREAD) && defined(S_IRUSR)
47 # define S_IREAD S_IRUSR
48 # define S_IWRITE S_IRUSR
49 #endif
50
51 /* #define DEBUG_NO_PREFIX Dump only compressed data. */
52
53 /* Masks given codes to BitsPerPixel, to make sure all codes are in range: */
54 static GifPixelType CodeMask[] = {
55 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
56 };
57
58 static char GifVersionPrefix[GIF_STAMP_LEN + 1] = GIF87_STAMP;
59
60 #define WRITE(_gif,_buf,_len) \
61 (((GifFilePrivateType*)_gif->Private)->Write ? \
62 ((GifFilePrivateType*)_gif->Private)->Write(_gif,_buf,(int)(_len)) : \
63 fwrite(_buf, 1, _len, ((GifFilePrivateType*)_gif->Private)->File))
64
65 static int EGifPutWord(int Word, GifFileType * GifFile);
66 static int EGifSetupCompress(GifFileType * GifFile);
67 static int EGifCompressLine(GifFileType * GifFile, GifPixelType * Line,
68 int LineLen);
69 static int EGifCompressOutput(GifFileType * GifFile, int Code);
70 static int EGifBufferedOutput(GifFileType * GifFile, GifByteType * Buf,
71 int c);
72
73 /******************************************************************************
74 * Open a new gif file for write, given by its name. If TestExistence then
75 * if the file exists this routines fails (returns NULL).
76 * Returns GifFileType pointer dynamically allocated which serves as the gif
77 * info record. _GifError is cleared if successfully.
78 *****************************************************************************/
79 GifFileType *
EGifOpenFileName(const char * FileName,int TestExistence)80 EGifOpenFileName(const char *FileName,
81 int TestExistence) {
82
83 int FileHandle;
84 GifFileType *GifFile;
85
86 if (TestExistence)
87 FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL
88 #if defined(O_BINARY)
89 | O_BINARY
90 #endif /* __MSDOS__ */
91 , S_IREAD | S_IWRITE);
92 else
93 FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC
94 #if defined(O_BINARY)
95 | O_BINARY
96 #endif /* __MSDOS__ */
97 , S_IREAD | S_IWRITE);
98
99 if (FileHandle == -1) {
100 _GifError = E_GIF_ERR_OPEN_FAILED;
101 return NULL;
102 }
103 GifFile = EGifOpenFileHandle(FileHandle);
104 if (GifFile == (GifFileType *) NULL)
105 close(FileHandle);
106 return GifFile;
107 }
108
109 /******************************************************************************
110 * Update a new gif file, given its file handle, which must be opened for
111 * write in binary mode.
112 * Returns GifFileType pointer dynamically allocated which serves as the gif
113 * info record. _GifError is cleared if successfully.
114 *****************************************************************************/
115 GifFileType *
EGifOpenFileHandle(int FileHandle)116 EGifOpenFileHandle(int FileHandle) {
117
118 GifFileType *GifFile;
119 GifFilePrivateType *Private;
120 FILE *f;
121
122 GifFile = (GifFileType *) malloc(sizeof(GifFileType));
123 if (GifFile == NULL) {
124 _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
125 return NULL;
126 }
127
128 memset(GifFile, '\0', sizeof(GifFileType));
129
130 Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
131 if (Private == NULL) {
132 free(GifFile);
133 _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
134 return NULL;
135 }
136 if ((Private->HashTable = _InitHashTable()) == NULL) {
137 free(GifFile);
138 free(Private);
139 _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
140 return NULL;
141 }
142
143 #if defined(O_BINARY)
144 setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */
145 #endif /* __MSDOS__ */
146
147 f = fdopen(FileHandle, "wb"); /* Make it into a stream: */
148
149 #if defined (__MSDOS__) || defined(WIN32)
150 setvbuf(f, NULL, _IOFBF, GIF_FILE_BUFFER_SIZE); /* And inc. stream
151 * buffer. */
152 #endif /* __MSDOS__ */
153
154 GifFile->Private = (VoidPtr)Private;
155 Private->FileHandle = FileHandle;
156 Private->File = f;
157 Private->FileState = FILE_STATE_WRITE;
158
159 Private->Write = (OutputFunc) 0; /* No user write routine (MRB) */
160 GifFile->UserData = (VoidPtr) 0; /* No user write handle (MRB) */
161
162 _GifError = 0;
163
164 return GifFile;
165 }
166
167 /******************************************************************************
168 * Output constructor that takes user supplied output function.
169 * Basically just a copy of EGifOpenFileHandle. (MRB)
170 *****************************************************************************/
171 GifFileType *
EGifOpen(void * userData,OutputFunc writeFunc)172 EGifOpen(void *userData,
173 OutputFunc writeFunc) {
174
175 GifFileType *GifFile;
176 GifFilePrivateType *Private;
177
178 GifFile = (GifFileType *)malloc(sizeof(GifFileType));
179 if (GifFile == NULL) {
180 _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
181 return NULL;
182 }
183
184 memset(GifFile, '\0', sizeof(GifFileType));
185
186 Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
187 if (Private == NULL) {
188 free(GifFile);
189 _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
190 return NULL;
191 }
192
193 Private->HashTable = _InitHashTable();
194 if (Private->HashTable == NULL) {
195 free (GifFile);
196 free (Private);
197 _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
198 return NULL;
199 }
200
201 GifFile->Private = (VoidPtr) Private;
202 Private->FileHandle = 0;
203 Private->File = (FILE *) 0;
204 Private->FileState = FILE_STATE_WRITE;
205
206 Private->Write = writeFunc; /* User write routine (MRB) */
207 GifFile->UserData = userData; /* User write handle (MRB) */
208
209 _GifError = 0;
210
211 return GifFile;
212 }
213
214 /******************************************************************************
215 * Routine to set current GIF version. All files open for write will be
216 * using this version until next call to this routine. Version consists of
217 * 3 characters as "87a" or "89a". No test is made to validate the version.
218 *****************************************************************************/
219 void
EGifSetGifVersion(const char * Version)220 EGifSetGifVersion(const char *Version) {
221 strncpy(GifVersionPrefix + GIF_VERSION_POS, Version, 3);
222 }
223
224 /******************************************************************************
225 * This routine should be called before any other EGif calls, immediately
226 * follows the GIF file opening.
227 *****************************************************************************/
228 int
EGifPutScreenDesc(GifFileType * GifFile,int Width,int Height,int ColorRes,int BackGround,const ColorMapObject * ColorMap)229 EGifPutScreenDesc(GifFileType * GifFile,
230 int Width,
231 int Height,
232 int ColorRes,
233 int BackGround,
234 const ColorMapObject * ColorMap) {
235
236 int i;
237 GifByteType Buf[3];
238 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
239
240 if (Private->FileState & FILE_STATE_SCREEN) {
241 /* If already has screen descriptor - something is wrong! */
242 _GifError = E_GIF_ERR_HAS_SCRN_DSCR;
243 return GIF_ERROR;
244 }
245 if (!IS_WRITEABLE(Private)) {
246 /* This file was NOT open for writing: */
247 _GifError = E_GIF_ERR_NOT_WRITEABLE;
248 return GIF_ERROR;
249 }
250
251 /* First write the version prefix into the file. */
252 #ifndef DEBUG_NO_PREFIX
253 if (WRITE(GifFile, (unsigned char *)GifVersionPrefix,
254 (int)strlen(GifVersionPrefix)) != strlen(GifVersionPrefix)) {
255 _GifError = E_GIF_ERR_WRITE_FAILED;
256 return GIF_ERROR;
257 }
258 #endif /* DEBUG_NO_PREFIX */
259
260 GifFile->SWidth = Width;
261 GifFile->SHeight = Height;
262 GifFile->SColorResolution = ColorRes;
263 GifFile->SBackGroundColor = BackGround;
264 if (ColorMap) {
265 GifFile->SColorMap = MakeMapObject(ColorMap->ColorCount,
266 ColorMap->Colors);
267 if (GifFile->SColorMap == NULL) {
268 _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
269 return GIF_ERROR;
270 }
271 } else
272 GifFile->SColorMap = NULL;
273
274 /*
275 * Put the logical screen descriptor into the file:
276 */
277 /* Logical Screen Descriptor: Dimensions */
278 EGifPutWord(Width, GifFile);
279 EGifPutWord(Height, GifFile);
280
281 /* Logical Screen Descriptor: Packed Fields */
282 /* Note: We have actual size of the color table default to the largest
283 * possible size (7+1 == 8 bits) because the decoder can use it to decide
284 * how to display the files.
285 */
286 Buf[0] = (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */
287 ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */
288 (ColorMap ? ColorMap->BitsPerPixel - 1 : 0x07 ); /* Actual size of the
289 color table. */
290 Buf[1] = BackGround; /* Index into the ColorTable for background color */
291 Buf[2] = 0; /* Pixel Aspect Ratio */
292 #ifndef DEBUG_NO_PREFIX
293 WRITE(GifFile, Buf, 3);
294 #endif /* DEBUG_NO_PREFIX */
295
296 /* If we have Global color map - dump it also: */
297 #ifndef DEBUG_NO_PREFIX
298 if (ColorMap != NULL)
299 for (i = 0; i < ColorMap->ColorCount; i++) {
300 /* Put the ColorMap out also: */
301 Buf[0] = ColorMap->Colors[i].Red;
302 Buf[1] = ColorMap->Colors[i].Green;
303 Buf[2] = ColorMap->Colors[i].Blue;
304 if (WRITE(GifFile, Buf, 3) != 3) {
305 _GifError = E_GIF_ERR_WRITE_FAILED;
306 return GIF_ERROR;
307 }
308 }
309 #endif /* DEBUG_NO_PREFIX */
310
311 /* Mark this file as has screen descriptor, and no pixel written yet: */
312 Private->FileState |= FILE_STATE_SCREEN;
313
314 return GIF_OK;
315 }
316
317 /******************************************************************************
318 * This routine should be called before any attempt to dump an image - any
319 * call to any of the pixel dump routines.
320 *****************************************************************************/
321 int
EGifPutImageDesc(GifFileType * GifFile,int Left,int Top,int Width,int Height,int Interlace,const ColorMapObject * ColorMap)322 EGifPutImageDesc(GifFileType * GifFile,
323 int Left,
324 int Top,
325 int Width,
326 int Height,
327 int Interlace,
328 const ColorMapObject * ColorMap) {
329
330 int i;
331 GifByteType Buf[3];
332 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
333
334 if (Private->FileState & FILE_STATE_IMAGE &&
335 #if defined(__MSDOS__) || defined(WIN32) || defined(__GNUC__)
336 Private->PixelCount > 0xffff0000UL) {
337 #else
338 Private->PixelCount > 0xffff0000) {
339 #endif /* __MSDOS__ */
340 /* If already has active image descriptor - something is wrong! */
341 _GifError = E_GIF_ERR_HAS_IMAG_DSCR;
342 return GIF_ERROR;
343 }
344 if (!IS_WRITEABLE(Private)) {
345 /* This file was NOT open for writing: */
346 _GifError = E_GIF_ERR_NOT_WRITEABLE;
347 return GIF_ERROR;
348 }
349 GifFile->Image.Left = Left;
350 GifFile->Image.Top = Top;
351 GifFile->Image.Width = Width;
352 GifFile->Image.Height = Height;
353 GifFile->Image.Interlace = Interlace;
354 if (ColorMap) {
355 GifFile->Image.ColorMap = MakeMapObject(ColorMap->ColorCount,
356 ColorMap->Colors);
357 if (GifFile->Image.ColorMap == NULL) {
358 _GifError = E_GIF_ERR_NOT_ENOUGH_MEM;
359 return GIF_ERROR;
360 }
361 } else {
362 GifFile->Image.ColorMap = NULL;
363 }
364
365 /* Put the image descriptor into the file: */
366 Buf[0] = ','; /* Image separator character. */
367 #ifndef DEBUG_NO_PREFIX
368 WRITE(GifFile, Buf, 1);
369 #endif /* DEBUG_NO_PREFIX */
370 EGifPutWord(Left, GifFile);
371 EGifPutWord(Top, GifFile);
372 EGifPutWord(Width, GifFile);
373 EGifPutWord(Height, GifFile);
374 Buf[0] = (ColorMap ? 0x80 : 0x00) |
375 (Interlace ? 0x40 : 0x00) |
376 (ColorMap ? ColorMap->BitsPerPixel - 1 : 0);
377 #ifndef DEBUG_NO_PREFIX
378 WRITE(GifFile, Buf, 1);
379 #endif /* DEBUG_NO_PREFIX */
380
381 /* If we have Global color map - dump it also: */
382 #ifndef DEBUG_NO_PREFIX
383 if (ColorMap != NULL)
384 for (i = 0; i < ColorMap->ColorCount; i++) {
385 /* Put the ColorMap out also: */
386 Buf[0] = ColorMap->Colors[i].Red;
387 Buf[1] = ColorMap->Colors[i].Green;
388 Buf[2] = ColorMap->Colors[i].Blue;
389 if (WRITE(GifFile, Buf, 3) != 3) {
390 _GifError = E_GIF_ERR_WRITE_FAILED;
391 return GIF_ERROR;
392 }
393 }
394 #endif /* DEBUG_NO_PREFIX */
395 if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) {
396 _GifError = E_GIF_ERR_NO_COLOR_MAP;
397 return GIF_ERROR;
398 }
399
400 /* Mark this file as has screen descriptor: */
401 Private->FileState |= FILE_STATE_IMAGE;
402 Private->PixelCount = (long)Width *(long)Height;
403
404 EGifSetupCompress(GifFile); /* Reset compress algorithm parameters. */
405
406 return GIF_OK;
407 }
408
409 /******************************************************************************
410 * Put one full scanned line (Line) of length LineLen into GIF file.
411 *****************************************************************************/
412 int
413 EGifPutLine(GifFileType * GifFile,
414 GifPixelType * Line,
415 int LineLen) {
416
417 int i;
418 GifPixelType Mask;
419 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
420
421 if (!IS_WRITEABLE(Private)) {
422 /* This file was NOT open for writing: */
423 _GifError = E_GIF_ERR_NOT_WRITEABLE;
424 return GIF_ERROR;
425 }
426
427 if (!LineLen)
428 LineLen = GifFile->Image.Width;
429 if (Private->PixelCount < (unsigned)LineLen) {
430 _GifError = E_GIF_ERR_DATA_TOO_BIG;
431 return GIF_ERROR;
432 }
433 Private->PixelCount -= LineLen;
434
435 /* Make sure the codes are not out of bit range, as we might generate
436 * wrong code (because of overflow when we combine them) in this case: */
437 Mask = CodeMask[Private->BitsPerPixel];
438 for (i = 0; i < LineLen; i++)
439 Line[i] &= Mask;
440
441 return EGifCompressLine(GifFile, Line, LineLen);
442 }
443
444 /******************************************************************************
445 * Put one pixel (Pixel) into GIF file.
446 *****************************************************************************/
447 int
448 EGifPutPixel(GifFileType * GifFile,
449 GifPixelType Pixel) {
450
451 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
452
453 if (!IS_WRITEABLE(Private)) {
454 /* This file was NOT open for writing: */
455 _GifError = E_GIF_ERR_NOT_WRITEABLE;
456 return GIF_ERROR;
457 }
458
459 if (Private->PixelCount == 0) {
460 _GifError = E_GIF_ERR_DATA_TOO_BIG;
461 return GIF_ERROR;
462 }
463 --Private->PixelCount;
464
465 /* Make sure the code is not out of bit range, as we might generate
466 * wrong code (because of overflow when we combine them) in this case: */
467 Pixel &= CodeMask[Private->BitsPerPixel];
468
469 return EGifCompressLine(GifFile, &Pixel, 1);
470 }
471
472 /******************************************************************************
473 * Put a comment into GIF file using the GIF89 comment extension block.
474 *****************************************************************************/
475 int
476 EGifPutComment(GifFileType * GifFile,
477 const char *Comment) {
478
479 unsigned int length;
480 char *buf;
481
482 length = (unsigned int)strlen(Comment);
483 if (length <= 255) {
484 return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE,
485 length, Comment);
486 } else {
487 buf = (char *)Comment;
488 if (EGifPutExtensionFirst(GifFile, COMMENT_EXT_FUNC_CODE, 255, buf)
489 == GIF_ERROR) {
490 return GIF_ERROR;
491 }
492 length -= 255;
493 buf = buf + 255;
494
495 /* Break the comment into 255 byte sub blocks */
496 while (length > 255) {
497 if (EGifPutExtensionNext(GifFile, 0, 255, buf) == GIF_ERROR) {
498 return GIF_ERROR;
499 }
500 buf = buf + 255;
501 length -= 255;
502 }
503 /* Output any partial block and the clear code. */
504 if (EGifPutExtensionLast(GifFile, 0, length, buf) == GIF_ERROR) {
505 return GIF_ERROR;
506 }
507 }
508 return GIF_OK;
509 }
510
511 /******************************************************************************
512 * Put a first extension block (see GIF manual) into gif file. Here more
513 * extensions can be dumped using EGifPutExtensionNext until
514 * EGifPutExtensionLast is invoked.
515 *****************************************************************************/
516 int
517 EGifPutExtensionFirst(GifFileType * GifFile,
518 int ExtCode,
519 int ExtLen,
520 const VoidPtr Extension) {
521
522 GifByteType Buf[3];
523 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
524
525 if (!IS_WRITEABLE(Private)) {
526 /* This file was NOT open for writing: */
527 _GifError = E_GIF_ERR_NOT_WRITEABLE;
528 return GIF_ERROR;
529 }
530
531 if (ExtCode == 0) {
532 WRITE(GifFile, (GifByteType *)&ExtLen, 1);
533 } else {
534 Buf[0] = '!';
535 Buf[1] = ExtCode;
536 Buf[2] = ExtLen;
537 WRITE(GifFile, Buf, 3);
538 }
539
540 WRITE(GifFile, Extension, ExtLen);
541
542 return GIF_OK;
543 }
544
545 /******************************************************************************
546 * Put a middle extension block (see GIF manual) into gif file.
547 *****************************************************************************/
548 int
549 EGifPutExtensionNext(GifFileType * GifFile,
550 int ExtCode,
551 int ExtLen,
552 const VoidPtr Extension) {
553
554 GifByteType Buf;
555 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
556
557 if (!IS_WRITEABLE(Private)) {
558 /* This file was NOT open for writing: */
559 _GifError = E_GIF_ERR_NOT_WRITEABLE;
560 return GIF_ERROR;
561 }
562
563 Buf = ExtLen;
564 WRITE(GifFile, &Buf, 1);
565 WRITE(GifFile, Extension, ExtLen);
566
567 return GIF_OK;
568 }
569
570 /******************************************************************************
571 * Put a last extension block (see GIF manual) into gif file.
572 *****************************************************************************/
573 int
574 EGifPutExtensionLast(GifFileType * GifFile,
575 int ExtCode,
576 int ExtLen,
577 const VoidPtr Extension) {
578
579 GifByteType Buf;
580 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
581
582 if (!IS_WRITEABLE(Private)) {
583 /* This file was NOT open for writing: */
584 _GifError = E_GIF_ERR_NOT_WRITEABLE;
585 return GIF_ERROR;
586 }
587
588 /* If we are given an extension sub-block output it now. */
589 if (ExtLen > 0) {
590 Buf = ExtLen;
591 WRITE(GifFile, &Buf, 1);
592 WRITE(GifFile, Extension, ExtLen);
593 }
594
595 /* Write the block terminator */
596 Buf = 0;
597 WRITE(GifFile, &Buf, 1);
598
599 return GIF_OK;
600 }
601
602 /******************************************************************************
603 * Put an extension block (see GIF manual) into gif file.
604 * Warning: This function is only useful for Extension blocks that have at
605 * most one subblock. Extensions with more than one subblock need to use the
606 * EGifPutExtension{First,Next,Last} functions instead.
607 *****************************************************************************/
608 int
609 EGifPutExtension(GifFileType * GifFile,
610 int ExtCode,
611 int ExtLen,
612 const VoidPtr Extension) {
613
614 GifByteType Buf[3];
615 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
616
617 if (!IS_WRITEABLE(Private)) {
618 /* This file was NOT open for writing: */
619 _GifError = E_GIF_ERR_NOT_WRITEABLE;
620 return GIF_ERROR;
621 }
622
623 if (ExtCode == 0)
624 WRITE(GifFile, (GifByteType *)&ExtLen, 1);
625 else {
626 Buf[0] = '!'; /* Extension Introducer 0x21 */
627 Buf[1] = ExtCode; /* Extension Label */
628 Buf[2] = ExtLen; /* Extension length */
629 WRITE(GifFile, Buf, 3);
630 }
631 WRITE(GifFile, Extension, ExtLen);
632 Buf[0] = 0;
633 WRITE(GifFile, Buf, 1);
634
635 return GIF_OK;
636 }
637
638 /******************************************************************************
639 * Put the image code in compressed form. This routine can be called if the
640 * information needed to be piped out as is. Obviously this is much faster
641 * than decoding and encoding again. This routine should be followed by calls
642 * to EGifPutCodeNext, until NULL block is given.
643 * The block should NOT be freed by the user (not dynamically allocated).
644 *****************************************************************************/
645 int
646 EGifPutCode(GifFileType * GifFile,
647 int CodeSize,
648 const GifByteType * CodeBlock) {
649
650 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
651
652 if (!IS_WRITEABLE(Private)) {
653 /* This file was NOT open for writing: */
654 _GifError = E_GIF_ERR_NOT_WRITEABLE;
655 return GIF_ERROR;
656 }
657
658 /* No need to dump code size as Compression set up does any for us: */
659 /*
660 * Buf = CodeSize;
661 * if (WRITE(GifFile, &Buf, 1) != 1) {
662 * _GifError = E_GIF_ERR_WRITE_FAILED;
663 * return GIF_ERROR;
664 * }
665 */
666
667 return EGifPutCodeNext(GifFile, CodeBlock);
668 }
669
670 /******************************************************************************
671 * Continue to put the image code in compressed form. This routine should be
672 * called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If
673 * given buffer pointer is NULL, empty block is written to mark end of code.
674 *****************************************************************************/
675 int
676 EGifPutCodeNext(GifFileType * GifFile,
677 const GifByteType * CodeBlock) {
678
679 GifByteType Buf;
680 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
681
682 if (CodeBlock != NULL) {
683 if (WRITE(GifFile, CodeBlock, CodeBlock[0] + 1)
684 != (unsigned)(CodeBlock[0] + 1)) {
685 _GifError = E_GIF_ERR_WRITE_FAILED;
686 return GIF_ERROR;
687 }
688 } else {
689 Buf = 0;
690 if (WRITE(GifFile, &Buf, 1) != 1) {
691 _GifError = E_GIF_ERR_WRITE_FAILED;
692 return GIF_ERROR;
693 }
694 Private->PixelCount = 0; /* And local info. indicate image read. */
695 }
696
697 return GIF_OK;
698 }
699
700 /******************************************************************************
701 * This routine should be called last, to close GIF file.
702 *****************************************************************************/
703 int
704 EGifCloseFile(GifFileType * GifFile) {
705
706 GifByteType Buf;
707 GifFilePrivateType *Private;
708 FILE *File;
709
710 if (GifFile == NULL || GifFile->Private == NULL)
711 return GIF_ERROR;
712
713 Private = (GifFilePrivateType *) GifFile->Private;
714 if (!IS_WRITEABLE(Private)) {
715 /* This file was NOT open for writing: */
716 _GifError = E_GIF_ERR_NOT_WRITEABLE;
717 free(GifFile);
718 return GIF_ERROR;
719 }
720
721 File = Private->File;
722
723 Buf = ';';
724 WRITE(GifFile, &Buf, 1);
725
726 if (GifFile->Image.ColorMap) {
727 FreeMapObject(GifFile->Image.ColorMap);
728 GifFile->Image.ColorMap = NULL;
729 }
730 if (GifFile->SColorMap) {
731 FreeMapObject(GifFile->SColorMap);
732 GifFile->SColorMap = NULL;
733 }
734
735 if (Private->HashTable) {
736 free((char *) Private->HashTable);
737 }
738
739 if (File && fclose(File) != 0) {
740 _GifError = E_GIF_ERR_CLOSE_FAILED;
741 free((char *) Private);
742 free(GifFile);
743 return GIF_ERROR;
744 }
745
746 free((char *) Private);
747 free(GifFile);
748
749 return GIF_OK;
750 }
751
752 /******************************************************************************
753 * Put 2 bytes (word) into the given file:
754 *****************************************************************************/
755 static int
756 EGifPutWord(int Word,
757 GifFileType * GifFile) {
758
759 unsigned char c[2];
760
761 c[0] = Word & 0xff;
762 c[1] = (Word >> 8) & 0xff;
763 #ifndef DEBUG_NO_PREFIX
764 if (WRITE(GifFile, c, 2) == 2)
765 return GIF_OK;
766 else
767 return GIF_ERROR;
768 #else
769 return GIF_OK;
770 #endif /* DEBUG_NO_PREFIX */
771 }
772
773 /******************************************************************************
774 * Setup the LZ compression for this image:
775 *****************************************************************************/
776 static int
777 EGifSetupCompress(GifFileType * GifFile) {
778
779 int BitsPerPixel;
780 GifByteType Buf;
781 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
782
783 /* Test and see what color map to use, and from it # bits per pixel: */
784 if (GifFile->Image.ColorMap)
785 BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel;
786 else if (GifFile->SColorMap)
787 BitsPerPixel = GifFile->SColorMap->BitsPerPixel;
788 else {
789 _GifError = E_GIF_ERR_NO_COLOR_MAP;
790 return GIF_ERROR;
791 }
792
793 Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel);
794 WRITE(GifFile, &Buf, 1); /* Write the Code size to file. */
795
796 Private->Buf[0] = 0; /* Nothing was output yet. */
797 Private->BitsPerPixel = BitsPerPixel;
798 Private->ClearCode = (1 << BitsPerPixel);
799 Private->EOFCode = Private->ClearCode + 1;
800 Private->RunningCode = Private->EOFCode + 1;
801 Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */
802 Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */
803 Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */
804 Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */
805 Private->CrntShiftDWord = 0;
806
807 /* Clear hash table and send Clear to make sure the decoder do the same. */
808 _ClearHashTable(Private->HashTable);
809
810 if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) {
811 _GifError = E_GIF_ERR_DISK_IS_FULL;
812 return GIF_ERROR;
813 }
814 return GIF_OK;
815 }
816
817 /******************************************************************************
818 * The LZ compression routine:
819 * This version compresses the given buffer Line of length LineLen.
820 * This routine can be called a few times (one per scan line, for example), in
821 * order to complete the whole image.
822 ******************************************************************************/
823 static int
824 EGifCompressLine(GifFileType * GifFile,
825 GifPixelType * Line,
826 int LineLen) {
827
828 int i = 0, CrntCode, NewCode;
829 unsigned long NewKey;
830 GifPixelType Pixel;
831 GifHashTableType *HashTable;
832 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
833
834 HashTable = Private->HashTable;
835
836 if (Private->CrntCode == FIRST_CODE) /* Its first time! */
837 CrntCode = Line[i++];
838 else
839 CrntCode = Private->CrntCode; /* Get last code in compression. */
840
841 while (i < LineLen) { /* Decode LineLen items. */
842 Pixel = Line[i++]; /* Get next pixel from stream. */
843 /* Form a new unique key to search hash table for the code combines
844 * CrntCode as Prefix string with Pixel as postfix char.
845 */
846 NewKey = (((UINT32) CrntCode) << 8) + Pixel;
847 if ((NewCode = _ExistsHashTable(HashTable, (UINT32)NewKey)) >= 0) {
848 /* This Key is already there, or the string is old one, so
849 * simple take new code as our CrntCode:
850 */
851 CrntCode = NewCode;
852 } else {
853 /* Put it in hash table, output the prefix code, and make our
854 * CrntCode equal to Pixel.
855 */
856 if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) {
857 _GifError = E_GIF_ERR_DISK_IS_FULL;
858 return GIF_ERROR;
859 }
860 CrntCode = Pixel;
861
862 /* If however the HashTable if full, we send a clear first and
863 * Clear the hash table.
864 */
865 if (Private->RunningCode >= LZ_MAX_CODE) {
866 /* Time to do some clearance: */
867 if (EGifCompressOutput(GifFile, Private->ClearCode)
868 == GIF_ERROR) {
869 _GifError = E_GIF_ERR_DISK_IS_FULL;
870 return GIF_ERROR;
871 }
872 Private->RunningCode = Private->EOFCode + 1;
873 Private->RunningBits = Private->BitsPerPixel + 1;
874 Private->MaxCode1 = 1 << Private->RunningBits;
875 _ClearHashTable(HashTable);
876 } else {
877 /* Put this unique key with its relative Code in hash table: */
878 _InsertHashTable(HashTable, (UINT32)NewKey, Private->RunningCode++);
879 }
880 }
881
882 }
883
884 /* Preserve the current state of the compression algorithm: */
885 Private->CrntCode = CrntCode;
886
887 if (Private->PixelCount == 0) {
888 /* We are done - output last Code and flush output buffers: */
889 if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) {
890 _GifError = E_GIF_ERR_DISK_IS_FULL;
891 return GIF_ERROR;
892 }
893 if (EGifCompressOutput(GifFile, Private->EOFCode) == GIF_ERROR) {
894 _GifError = E_GIF_ERR_DISK_IS_FULL;
895 return GIF_ERROR;
896 }
897 if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) {
898 _GifError = E_GIF_ERR_DISK_IS_FULL;
899 return GIF_ERROR;
900 }
901 }
902
903 return GIF_OK;
904 }
905
906 /******************************************************************************
907 * The LZ compression output routine:
908 * This routine is responsible for the compression of the bit stream into
909 * 8 bits (bytes) packets.
910 * Returns GIF_OK if written successfully.
911 *****************************************************************************/
912 static int
913 EGifCompressOutput(GifFileType * GifFile,
914 int Code) {
915
916 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
917 int retval = GIF_OK;
918
919 if (Code == FLUSH_OUTPUT) {
920 while (Private->CrntShiftState > 0) {
921 /* Get Rid of what is left in DWord, and flush it. */
922 if (EGifBufferedOutput(GifFile, Private->Buf,
923 Private->CrntShiftDWord & 0xff) == GIF_ERROR)
924 retval = GIF_ERROR;
925 Private->CrntShiftDWord >>= 8;
926 Private->CrntShiftState -= 8;
927 }
928 Private->CrntShiftState = 0; /* For next time. */
929 if (EGifBufferedOutput(GifFile, Private->Buf,
930 FLUSH_OUTPUT) == GIF_ERROR)
931 retval = GIF_ERROR;
932 } else {
933 Private->CrntShiftDWord |= ((long)Code) << Private->CrntShiftState;
934 Private->CrntShiftState += Private->RunningBits;
935 while (Private->CrntShiftState >= 8) {
936 /* Dump out full bytes: */
937 if (EGifBufferedOutput(GifFile, Private->Buf,
938 Private->CrntShiftDWord & 0xff) == GIF_ERROR)
939 retval = GIF_ERROR;
940 Private->CrntShiftDWord >>= 8;
941 Private->CrntShiftState -= 8;
942 }
943 }
944
945 /* If code cannot fit into RunningBits bits, must raise its size. Note */
946 /* however that codes above 4095 are used for special signaling. */
947 if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) {
948 Private->MaxCode1 = 1 << ++Private->RunningBits;
949 }
950
951 return retval;
952 }
953
954 /******************************************************************************
955 * This routines buffers the given characters until 255 characters are ready
956 * to be output. If Code is equal to -1 the buffer is flushed (EOF).
957 * The buffer is Dumped with first byte as its size, as GIF format requires.
958 * Returns GIF_OK if written successfully.
959 *****************************************************************************/
960 static int
961 EGifBufferedOutput(GifFileType * GifFile,
962 GifByteType * Buf,
963 int c) {
964
965 if (c == FLUSH_OUTPUT) {
966 /* Flush everything out. */
967 if (Buf[0] != 0
968 && WRITE(GifFile, Buf, Buf[0] + 1) != (unsigned)(Buf[0] + 1)) {
969 _GifError = E_GIF_ERR_WRITE_FAILED;
970 return GIF_ERROR;
971 }
972 /* Mark end of compressed data, by an empty block (see GIF doc): */
973 Buf[0] = 0;
974 if (WRITE(GifFile, Buf, 1) != 1) {
975 _GifError = E_GIF_ERR_WRITE_FAILED;
976 return GIF_ERROR;
977 }
978 } else {
979 if (Buf[0] == 255) {
980 /* Dump out this buffer - it is full: */
981 if (WRITE(GifFile, Buf, Buf[0] + 1) != (unsigned)(Buf[0] + 1)) {
982 _GifError = E_GIF_ERR_WRITE_FAILED;
983 return GIF_ERROR;
984 }
985 Buf[0] = 0;
986 }
987 Buf[++Buf[0]] = c;
988 }
989
990 return GIF_OK;
991 }
992
993 /******************************************************************************
994 * This routine writes to disk an in-core representation of a GIF previously
995 * created by DGifSlurp().
996 *****************************************************************************/
997 int
998 EGifSpew(GifFileType * GifFileOut) {
999
1000 int i, j, gif89 = FALSE;
1001 int bOff; /* Block Offset for adding sub blocks in Extensions */
1002 char SavedStamp[GIF_STAMP_LEN + 1];
1003
1004 for (i = 0; i < GifFileOut->ImageCount; i++) {
1005 for (j = 0; j < GifFileOut->SavedImages[i].ExtensionBlockCount; j++) {
1006 int function =
1007 GifFileOut->SavedImages[i].ExtensionBlocks[j].Function;
1008
1009 if (function == COMMENT_EXT_FUNC_CODE
1010 || function == GRAPHICS_EXT_FUNC_CODE
1011 || function == PLAINTEXT_EXT_FUNC_CODE
1012 || function == APPLICATION_EXT_FUNC_CODE)
1013 gif89 = TRUE;
1014 }
1015 }
1016
1017 memcpy(SavedStamp, GifVersionPrefix, GIF_STAMP_LEN);
1018 if (gif89) {
1019 memcpy(GifVersionPrefix, GIF89_STAMP, GIF_STAMP_LEN);
1020 } else {
1021 memcpy(GifVersionPrefix, GIF87_STAMP, GIF_STAMP_LEN);
1022 }
1023 if (EGifPutScreenDesc(GifFileOut,
1024 GifFileOut->SWidth,
1025 GifFileOut->SHeight,
1026 GifFileOut->SColorResolution,
1027 GifFileOut->SBackGroundColor,
1028 GifFileOut->SColorMap) == GIF_ERROR) {
1029 memcpy(GifVersionPrefix, SavedStamp, GIF_STAMP_LEN);
1030 return (GIF_ERROR);
1031 }
1032 memcpy(GifVersionPrefix, SavedStamp, GIF_STAMP_LEN);
1033
1034 for (i = 0; i < GifFileOut->ImageCount; i++) {
1035 SavedImage *sp = &GifFileOut->SavedImages[i];
1036 int SavedHeight = sp->ImageDesc.Height;
1037 int SavedWidth = sp->ImageDesc.Width;
1038 ExtensionBlock *ep;
1039
1040 /* this allows us to delete images by nuking their rasters */
1041 if (sp->RasterBits == NULL)
1042 continue;
1043
1044 if (sp->ExtensionBlocks) {
1045 for (j = 0; j < sp->ExtensionBlockCount; j++) {
1046 ep = &sp->ExtensionBlocks[j];
1047 if (j == sp->ExtensionBlockCount - 1 || (ep+1)->Function != 0) {
1048 /*** FIXME: Must check whether outputting
1049 * <ExtLen><Extension> is ever valid or if we should just
1050 * drop anything with a 0 for the Function. (And whether
1051 * we should drop here or in EGifPutExtension)
1052 */
1053 if (EGifPutExtension(GifFileOut,
1054 (ep->Function != 0) ? ep->Function : '\0',
1055 ep->ByteCount,
1056 ep->Bytes) == GIF_ERROR) {
1057 return (GIF_ERROR);
1058 }
1059 } else {
1060 EGifPutExtensionFirst(GifFileOut, ep->Function, ep->ByteCount, ep->Bytes);
1061 for (bOff = j+1; bOff < sp->ExtensionBlockCount; bOff++) {
1062 ep = &sp->ExtensionBlocks[bOff];
1063 if (ep->Function != 0) {
1064 break;
1065 }
1066 EGifPutExtensionNext(GifFileOut, 0,
1067 ep->ByteCount, ep->Bytes);
1068 }
1069 EGifPutExtensionLast(GifFileOut, 0, 0, NULL);
1070 j = bOff-1;
1071 }
1072 }
1073 }
1074
1075 if (EGifPutImageDesc(GifFileOut,
1076 sp->ImageDesc.Left,
1077 sp->ImageDesc.Top,
1078 SavedWidth,
1079 SavedHeight,
1080 sp->ImageDesc.Interlace,
1081 sp->ImageDesc.ColorMap) == GIF_ERROR)
1082 return (GIF_ERROR);
1083
1084 for (j = 0; j < SavedHeight; j++) {
1085 if (EGifPutLine(GifFileOut,
1086 sp->RasterBits + j * SavedWidth,
1087 SavedWidth) == GIF_ERROR)
1088 return (GIF_ERROR);
1089 }
1090 }
1091
1092 if (EGifCloseFile(GifFileOut) == GIF_ERROR)
1093 return (GIF_ERROR);
1094
1095 return (GIF_OK);
1096 }
1097