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