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