1 /****************************************************************************
2     gif.c - read and write gif images using libgif/libungif.
3     Distributed with Xplanet.
4     Copyright (C) 2002 Hari Nair <hari@alumni.caltech.edu>
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 ****************************************************************************/
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include <gif_lib.h>
26 
27 /*
28   Based on the gif2rgb and rgb2gif codes in the libgif distribution.
29 */
30 
31 int
read_gif(const char * filename,int * width,int * height,unsigned char ** rgb)32 read_gif(const char *filename, int *width, int *height, unsigned char **rgb)
33 {
34     int	BackGround = 0;
35     /* The way Interlaced image should be read - offsets and
36        jumps... */
37     int InterlacedOffset[] = { 0, 4, 2, 1 };
38     int InterlacedJumps[] = { 8, 8, 4, 2 };
39     ColorMapObject *ColorMap;
40 
41     int	i, j, Size, Row, Col, Width, Height, ExtCode, Count;
42     GifRecordType RecordType;
43     GifByteType *Extension;
44     GifRowType *ScreenBuffer;
45 
46     GifRowType GifRow;
47     GifColorType *ColorMapEntry;
48     unsigned char *BufferP;
49 
50     GifFileType *GifFile;
51 #if GIFLIB_MAJOR >= 5
52     GifFile = DGifOpenFileName(filename, NULL);
53 #else
54     GifFile = DGifOpenFileName(filename);
55 #endif
56     if (GifFile == NULL) {
57 	fprintf(stderr, "Can't open GIF file %s\n", filename);
58 	return(0);
59     }
60     /* (Following comment was probably written on a 286 running under DOS.)  */
61     /* Allocate the screen as vector of column of rows. We can't allocate the*/
62     /* whole screen at once, as this broken minded CPU can allocate up to    */
63     /* 64k at a time and our image can be bigger than that:		     */
64     /* Note this screen is device independent - it's the screen defined by   */
65     /* the GIF file parameters.						     */
66     if ((ScreenBuffer = (GifRowType *)
67 	 malloc(GifFile->SHeight * sizeof(GifRowType))) == NULL)
68     {
69 	fprintf(stderr, "Failed to allocate memory required, aborted.");
70 	return(0);
71     }
72 
73     Size = GifFile->SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/
74     if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */
75     {
76 	fprintf(stderr, "Failed to allocate memory required, aborted.");
77 	return(0);
78     }
79 
80     for (i = 0; i < GifFile->SWidth; i++)  /* Set its color to BackGround. */
81 	ScreenBuffer[0][i] = GifFile->SBackGroundColor;
82     for (i = 1; i < GifFile->SHeight; i++) {
83 	/* Allocate the other rows, and set their color to background too: */
84 	if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL)
85 	{
86 	    fprintf(stderr, "Failed to allocate memory required, aborted.\n");
87 	    return(0);
88 	}
89 
90 	memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
91     }
92 
93     /* Scan the content of the GIF file and load the image(s) in: */
94     do {
95 	if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
96 	    fprintf(stderr, "Can't read GIF file\n");
97 	    return(0);
98 	}
99 	switch (RecordType) {
100 	case IMAGE_DESC_RECORD_TYPE:
101 	    if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
102 		return(0);
103 	    }
104 	    Row = GifFile->Image.Top; /* Image Position relative to Screen. */
105 	    Col = GifFile->Image.Left;
106 	    Width = GifFile->Image.Width;
107 	    Height = GifFile->Image.Height;
108 	    if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
109 		GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
110 		return(0);
111 	    }
112 	    if (GifFile->Image.Interlace) {
113 		/* Need to perform 4 passes on the images: */
114 		for (Count = i = 0; i < 4; i++)
115 		    for (j = Row + InterlacedOffset[i]; j < Row + Height;
116 			 j += InterlacedJumps[i]) {
117 			if (DGifGetLine(GifFile, &ScreenBuffer[j][Col],
118 					Width) == GIF_ERROR) {
119 			    return(0);
120 			}
121 		    }
122 	    }
123 	    else {
124 		for (i = 0; i < Height; i++) {
125 		    if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
126 				    Width) == GIF_ERROR) {
127 			return(0);
128 		    }
129 		}
130 	    }
131 	    break;
132 	case EXTENSION_RECORD_TYPE:
133 	    /* Skip any extension blocks in file: */
134 	    if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
135 		return(0);
136 	    }
137 	    while (Extension != NULL) {
138 		if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
139 		    return(0);
140 		}
141 	    }
142 	    break;
143 	case TERMINATE_RECORD_TYPE:
144 	    break;
145 	default:		    /* Should be traps by DGifGetRecordType. */
146 	    break;
147 	}
148     } while (RecordType != TERMINATE_RECORD_TYPE);
149 
150     /* Lets dump it - set the global variables required and do it: */
151     BackGround = GifFile->SBackGroundColor;
152     ColorMap = (GifFile->Image.ColorMap
153 		? GifFile->Image.ColorMap
154 		: GifFile->SColorMap);
155     if (ColorMap == NULL) {
156 	fprintf(stderr, "Gif Image does not have a colormap\n");
157 	return(0);
158     }
159 
160     *width = GifFile->SWidth;
161     *height = GifFile->SHeight;
162 
163     rgb[0] = malloc(3 * *width * *height);
164     if (rgb[0] == NULL)
165     {
166 	fprintf(stderr, "Can't allocate memory for GIF file.\n");
167 	return(0);
168     }
169 
170     BufferP = rgb[0];
171 
172     for (i = 0; i < *height; i++) {
173 	GifRow = ScreenBuffer[i];
174 	for (j = 0; j < *width; j++) {
175 	    ColorMapEntry = &ColorMap->Colors[GifRow[j]];
176 	    *BufferP++ = ColorMapEntry->Red;
177 	    *BufferP++ = ColorMapEntry->Green;
178 	    *BufferP++ = ColorMapEntry->Blue;
179 	}
180     }
181 
182     if (DGifCloseFile(GifFile, NULL) == GIF_ERROR) {
183 	return(0);
184     }
185 
186     return(1);
187 }
188 
189 /* Make some variables global, so we could access them faster: */
190 static int
191     ExpNumOfColors = 8,
192     ColorMapSize = 256;
193 
194 /******************************************************************************
195  * Code to quantize high resolution image into lower one. You may want to
196  * peek into the following article this code is based on:
197  * "Color Image Quantization for frame buffer Display", by Paul Heckbert
198  * SIGGRAPH 1982 page 297-307.
199  ******************************************************************************
200  * History:
201  * 5 Jan 90 - Version 1.0 by Gershon Elber.
202  *****************************************************************************/
203 
204 #define ABS(x)    ((x) > 0 ? (x) : (-(x)))
205 
206 #define COLOR_ARRAY_SIZE 32768
207 #define BITS_PER_PRIM_COLOR 5
208 #define MAX_PRIM_COLOR      0x1f
209 
210 static int SortRGBAxis;
211 
212 typedef struct QuantizedColorType {
213     GifByteType RGB[3];
214     GifByteType NewColorIndex;
215     long Count;
216     struct QuantizedColorType *Pnext;
217 } QuantizedColorType;
218 
219 typedef struct NewColorMapType {
220     GifByteType RGBMin[3], RGBWidth[3];
221     unsigned int NumEntries; /* # of QuantizedColorType in linked list below */
222     unsigned long Count; /* Total number of pixels in all the entries */
223     QuantizedColorType *QuantizedColors;
224 } NewColorMapType;
225 
226 static int SubdivColorMap(NewColorMapType * NewColorSubdiv,
227                           unsigned int ColorMapSize,
228                           unsigned int *NewColorMapSize);
229 static int SortCmpRtn(const void *Entry1, const void *Entry2);
230 
231 /******************************************************************************
232  * Quantize high resolution image into lower one. Input image consists of a
233  * 2D array for each of the RGB colors with size Width by Height. There is no
234  * Color map for the input. Output is a quantized image with 2D array of
235  * indexes into the output color map.
236  *   Note input image can be 24 bits at the most (8 for red/green/blue) and
237  * the output has 256 colors at the most (256 entries in the color map.).
238  * ColorMapSize specifies size of color map up to 256 and will be updated to
239  * real size before returning.
240  *   Also non of the parameter are allocated by this routine.
241  *   This function returns GIF_OK if succesfull, GIF_ERROR otherwise.
242  ******************************************************************************/
243 static int
QuantizeBuffer(unsigned int Width,unsigned int Height,int * ColorMapSize,GifByteType * RedInput,GifByteType * GreenInput,GifByteType * BlueInput,GifByteType * OutputBuffer,GifColorType * OutputColorMap)244 QuantizeBuffer(unsigned int Width,
245                unsigned int Height,
246                int *ColorMapSize,
247                GifByteType * RedInput,
248                GifByteType * GreenInput,
249                GifByteType * BlueInput,
250                GifByteType * OutputBuffer,
251                GifColorType * OutputColorMap) {
252 
253     unsigned int Index, NumOfEntries;
254     int i, j, MaxRGBError[3];
255     unsigned int NewColorMapSize;
256     long Red, Green, Blue;
257     NewColorMapType NewColorSubdiv[256];
258     QuantizedColorType *ColorArrayEntries, *QuantizedColor;
259 
260     ColorArrayEntries = (QuantizedColorType *)malloc(
261                            sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE);
262     if (ColorArrayEntries == NULL) {
263         return GIF_ERROR;
264     }
265 
266     for (i = 0; i < COLOR_ARRAY_SIZE; i++) {
267         ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR);
268         ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) &
269            MAX_PRIM_COLOR;
270         ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR;
271         ColorArrayEntries[i].Count = 0;
272     }
273 
274     /* Sample the colors and their distribution: */
275     for (i = 0; i < (int)(Width * Height); i++) {
276         Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
277                   (2 * BITS_PER_PRIM_COLOR)) +
278                 ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
279                   BITS_PER_PRIM_COLOR) +
280                 (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
281         ColorArrayEntries[Index].Count++;
282     }
283 
284     /* Put all the colors in the first entry of the color map, and call the
285      * recursive subdivision process.  */
286     for (i = 0; i < 256; i++) {
287         NewColorSubdiv[i].QuantizedColors = NULL;
288         NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0;
289         for (j = 0; j < 3; j++) {
290             NewColorSubdiv[i].RGBMin[j] = 0;
291             NewColorSubdiv[i].RGBWidth[j] = 255;
292         }
293     }
294 
295     /* Find the non empty entries in the color table and chain them: */
296     for (i = 0; i < COLOR_ARRAY_SIZE; i++)
297         if (ColorArrayEntries[i].Count > 0)
298             break;
299     QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i];
300     NumOfEntries = 1;
301     while (++i < COLOR_ARRAY_SIZE)
302         if (ColorArrayEntries[i].Count > 0) {
303             QuantizedColor->Pnext = &ColorArrayEntries[i];
304             QuantizedColor = &ColorArrayEntries[i];
305             NumOfEntries++;
306         }
307     QuantizedColor->Pnext = NULL;
308 
309     NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */
310     NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */
311     NewColorMapSize = 1;
312     if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) !=
313        GIF_OK) {
314         free((char *)ColorArrayEntries);
315         return GIF_ERROR;
316     }
317     if (NewColorMapSize < *ColorMapSize) {
318         /* And clear rest of color map: */
319         for (i = NewColorMapSize; i < *ColorMapSize; i++)
320             OutputColorMap[i].Red = OutputColorMap[i].Green =
321                 OutputColorMap[i].Blue = 0;
322     }
323 
324     /* Average the colors in each entry to be the color to be used in the
325      * output color map, and plug it into the output color map itself. */
326     for (i = 0; i < NewColorMapSize; i++) {
327         if ((j = NewColorSubdiv[i].NumEntries) > 0) {
328             QuantizedColor = NewColorSubdiv[i].QuantizedColors;
329             Red = Green = Blue = 0;
330             while (QuantizedColor) {
331                 QuantizedColor->NewColorIndex = i;
332                 Red += QuantizedColor->RGB[0];
333                 Green += QuantizedColor->RGB[1];
334                 Blue += QuantizedColor->RGB[2];
335                 QuantizedColor = QuantizedColor->Pnext;
336             }
337             OutputColorMap[i].Red = (Red << (8 - BITS_PER_PRIM_COLOR)) / j;
338             OutputColorMap[i].Green = (Green << (8 - BITS_PER_PRIM_COLOR)) / j;
339             OutputColorMap[i].Blue = (Blue << (8 - BITS_PER_PRIM_COLOR)) / j;
340         } else
341             fprintf(stderr,
342                     "Null entry in quantized color map - that's weird.\n");
343     }
344 
345     /* Finally scan the input buffer again and put the mapped index in the
346      * output buffer.  */
347     MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0;
348     for (i = 0; i < (int)(Width * Height); i++) {
349         Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
350                  (2 * BITS_PER_PRIM_COLOR)) +
351                 ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) <<
352                  BITS_PER_PRIM_COLOR) +
353                 (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR));
354         Index = ColorArrayEntries[Index].NewColorIndex;
355         OutputBuffer[i] = Index;
356         if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i]))
357             MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]);
358         if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i]))
359             MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]);
360         if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i]))
361             MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]);
362     }
363 
364     free((char *)ColorArrayEntries);
365 
366     *ColorMapSize = NewColorMapSize;
367 
368     return GIF_OK;
369 }
370 
371 /******************************************************************************
372  * Routine to subdivide the RGB space recursively using median cut in each
373  * axes alternatingly until ColorMapSize different cubes exists.
374  * The biggest cube in one dimension is subdivide unless it has only one entry.
375  * Returns GIF_ERROR if failed, otherwise GIF_OK.
376  ******************************************************************************/
377 static int
SubdivColorMap(NewColorMapType * NewColorSubdiv,unsigned int ColorMapSize,unsigned int * NewColorMapSize)378 SubdivColorMap(NewColorMapType * NewColorSubdiv,
379                unsigned int ColorMapSize,
380                unsigned int *NewColorMapSize) {
381 
382     int MaxSize;
383     unsigned int i, j, Index = 0, NumEntries, MinColor, MaxColor;
384     long Sum, Count;
385     QuantizedColorType *QuantizedColor, **SortArray;
386 
387     while (ColorMapSize > *NewColorMapSize) {
388         /* Find candidate for subdivision: */
389         MaxSize = -1;
390         for (i = 0; i < *NewColorMapSize; i++) {
391             for (j = 0; j < 3; j++) {
392                 if ((((int)NewColorSubdiv[i].RGBWidth[j]) > MaxSize) &&
393                       (NewColorSubdiv[i].NumEntries > 1)) {
394                     MaxSize = NewColorSubdiv[i].RGBWidth[j];
395                     Index = i;
396                     SortRGBAxis = j;
397                 }
398             }
399         }
400 
401         if (MaxSize == -1)
402             return GIF_OK;
403 
404         /* Split the entry Index into two along the axis SortRGBAxis: */
405 
406         /* Sort all elements in that entry along the given axis and split at
407          * the median.  */
408         SortArray = (QuantizedColorType **)malloc(
409                       sizeof(QuantizedColorType *) *
410                       NewColorSubdiv[Index].NumEntries);
411         if (SortArray == NULL)
412             return GIF_ERROR;
413         for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors;
414              j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL;
415              j++, QuantizedColor = QuantizedColor->Pnext)
416             SortArray[j] = QuantizedColor;
417 
418         qsort(SortArray, NewColorSubdiv[Index].NumEntries,
419               sizeof(QuantizedColorType *), SortCmpRtn);
420 
421         /* Relink the sorted list into one: */
422         for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++)
423             SortArray[j]->Pnext = SortArray[j + 1];
424         SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL;
425         NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0];
426         free((char *)SortArray);
427 
428         /* Now simply add the Counts until we have half of the Count: */
429         Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count;
430         NumEntries = 1;
431         Count = QuantizedColor->Count;
432         while (QuantizedColor->Pnext != NULL &&
433 	       (Sum -= QuantizedColor->Pnext->Count) >= 0 &&
434                QuantizedColor->Pnext->Pnext != NULL) {
435             QuantizedColor = QuantizedColor->Pnext;
436             NumEntries++;
437             Count += QuantizedColor->Count;
438         }
439         /* Save the values of the last color of the first half, and first
440          * of the second half so we can update the Bounding Boxes later.
441          * Also as the colors are quantized and the BBoxes are full 0..255,
442          * they need to be rescaled.
443          */
444         MaxColor = QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */
445 	/* coverity[var_deref_op] */
446         MinColor = QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */
447         MaxColor <<= (8 - BITS_PER_PRIM_COLOR);
448         MinColor <<= (8 - BITS_PER_PRIM_COLOR);
449 
450         /* Partition right here: */
451         NewColorSubdiv[*NewColorMapSize].QuantizedColors =
452            QuantizedColor->Pnext;
453         QuantizedColor->Pnext = NULL;
454         NewColorSubdiv[*NewColorMapSize].Count = Count;
455         NewColorSubdiv[Index].Count -= Count;
456         NewColorSubdiv[*NewColorMapSize].NumEntries =
457            NewColorSubdiv[Index].NumEntries - NumEntries;
458         NewColorSubdiv[Index].NumEntries = NumEntries;
459         for (j = 0; j < 3; j++) {
460             NewColorSubdiv[*NewColorMapSize].RGBMin[j] =
461                NewColorSubdiv[Index].RGBMin[j];
462             NewColorSubdiv[*NewColorMapSize].RGBWidth[j] =
463                NewColorSubdiv[Index].RGBWidth[j];
464         }
465         NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] =
466            NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] +
467            NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor;
468         NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor;
469 
470         NewColorSubdiv[Index].RGBWidth[SortRGBAxis] =
471            MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis];
472 
473         (*NewColorMapSize)++;
474     }
475 
476     return GIF_OK;
477 }
478 
479 /****************************************************************************
480  * Routine called by qsort to compare two entries.
481  ****************************************************************************/
482 static int
SortCmpRtn(const void * Entry1,const void * Entry2)483 SortCmpRtn(const void *Entry1,
484            const void *Entry2) {
485 
486     return (*((QuantizedColorType **) Entry1))->RGB[SortRGBAxis] -
487        (*((QuantizedColorType **) Entry2))->RGB[SortRGBAxis];
488 }
489 
490 /******************************************************************************
491 * Close output file (if open), and exit.
492 ******************************************************************************/
QuitGifError(GifFileType * GifFile)493 static void QuitGifError(GifFileType *GifFile)
494 {
495     fprintf(stderr, "Error writing GIF file\n");
496     if (GifFile != NULL) EGifCloseFile(GifFile, NULL);
497 }
498 
499 int
write_gif(const char * filename,int width,int height,char * rgb)500 write_gif(const char *filename, int width, int height, char *rgb)
501 {
502     int i;
503     GifByteType *red, *green, *blue;
504 
505     GifByteType *OutputBuffer = NULL;
506     ColorMapObject *OutputColorMap = NULL;
507 
508     GifFileType *GifFile;
509     GifByteType *Ptr;
510 
511     bool GifTestExistance = false;
512 
513     ColorMapSize = 1 << ExpNumOfColors;
514 
515 #if GIFLIB_MAJOR >= 5
516     OutputColorMap = GifMakeMapObject(ColorMapSize, NULL);
517 #else
518     OutputColorMap = MakeMapObject(ColorMapSize, NULL);
519 #endif
520 
521     if (OutputColorMap == NULL ||
522 	(OutputBuffer = (GifByteType *) malloc(width * height *
523 					       sizeof(GifByteType))) == NULL)
524     {
525 	fprintf(stderr,"Failed to allocate memory required, aborted.");
526 	return(0);
527     }
528 
529     red = malloc(width * height * sizeof(GifByteType));
530     green = malloc(width * height * sizeof(GifByteType));
531     blue = malloc(width * height * sizeof(GifByteType));
532     if (red == NULL || green == NULL || blue == NULL)
533     {
534 	fprintf(stderr, "Can't allocate memory for GIF file.\n");
535 	return(0);
536     }
537     for (i = 0; i < width * height; i++)
538     {
539 	red[i]   = (GifByteType) rgb[3*i  ];
540 	green[i] = (GifByteType) rgb[3*i+1];
541 	blue[i]  = (GifByteType) rgb[3*i+2];
542     }
543 
544     if (QuantizeBuffer(width, height, &ColorMapSize,
545 		       red, green, blue,
546 		       OutputBuffer, OutputColorMap->Colors) == GIF_ERROR)
547 
548     {
549 	QuitGifError(NULL);
550 	return(0);
551     }
552 
553     free(red);
554     free(green);
555     free(blue);
556 
557     Ptr = OutputBuffer;
558 
559 #if GIFLIB_MAJOR >= 5
560     GifFile = EGifOpenFileName(filename, GifTestExistance, NULL);
561 #else
562     GifFile = EGifOpenFileName(filename, GifTestExistance);
563 #endif
564     /* Open stdout for the output file: */
565     if (GifFile == NULL)
566     {
567 	QuitGifError(GifFile);
568 	return(0);
569     }
570 
571     if (EGifPutScreenDesc(GifFile,
572 			  width, height, ExpNumOfColors, 0,
573 			  OutputColorMap) == GIF_ERROR ||
574 	EGifPutImageDesc(GifFile,
575 			 0, 0, width, height, false, NULL) == GIF_ERROR)
576 
577     {
578 	QuitGifError(GifFile);
579 	return(0);
580     }
581 
582     for (i = 0; i < height; i++) {
583 	if (EGifPutLine(GifFile, Ptr, width) == GIF_ERROR)
584 
585 	{
586 	    QuitGifError(GifFile);
587 	    return(0);
588 	}
589 	Ptr += width;
590     }
591 
592     if (EGifCloseFile(GifFile, NULL) == GIF_ERROR)
593 
594     {
595 	QuitGifError(GifFile);
596 	return(0);
597     }
598 
599     return(1);
600 }
601