0
|
1 /* Synched up with: Not in FSF. */
|
|
2
|
|
3 /******************************************************************************
|
|
4 * "Gif-Lib" - Yet another gif library. *
|
|
5 * *
|
|
6 * Written by: Gershon Elber IBM PC Ver 1.1, Aug. 1990 *
|
|
7 *******************************************************************************
|
|
8 * The kernel of the GIF Decoding process can be found here. *
|
|
9 *******************************************************************************
|
|
10 * History: *
|
|
11 * 16 Jun 89 - Version 1.0 by Gershon Elber. *
|
|
12 * 3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names). *
|
|
13 ******************************************************************************/
|
|
14
|
74
|
15 #ifdef emacs
|
|
16 #include <config.h>
|
|
17 #endif /* emacs */
|
|
18
|
0
|
19 #ifdef __MSDOS__
|
|
20 #include <io.h>
|
|
21 #include <alloc.h>
|
|
22 #include <stdlib.h>
|
|
23 #include <sys\stat.h>
|
|
24 #else
|
|
25 #include <sys/types.h>
|
|
26 #include <sys/stat.h>
|
|
27 #endif /* __MSDOS__ */
|
|
28
|
|
29 #include <fcntl.h>
|
|
30 #include <stdio.h>
|
|
31 #include <string.h>
|
|
32
|
|
33 #include "gif_lib.h"
|
|
34 #ifndef emacs
|
|
35 #include "gif_hash.h"
|
|
36 #endif
|
|
37
|
187
|
38 #ifdef emacs
|
|
39 void *xmalloc (size_t size);
|
|
40 void *xrealloc (void *ptr, size_t size);
|
|
41 #ifdef ERROR_CHECK_MALLOC
|
|
42 void *xfree_1 (void *);
|
|
43 #define xfree xfree_1
|
|
44 #else
|
|
45 void *xfree (void *);
|
|
46 #endif
|
|
47 #endif /* emacs */
|
|
48
|
|
49
|
0
|
50 #define PROGRAM_NAME "GIF_LIBRARY"
|
|
51
|
|
52 #define COMMENT_EXT_FUNC_CODE 0xfe /* Extension function code for comment. */
|
|
53 #define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */
|
|
54 #define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1
|
|
55 #define GIF_VERSION_POS 3 /* Version first character in stamp. */
|
|
56
|
|
57 #define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
|
|
58 #define LZ_BITS 12
|
|
59
|
|
60 #define FILE_STATE_READ 0x01/* 1 write, 0 read - EGIF_LIB compatible.*/
|
|
61
|
|
62 #define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */
|
|
63 #define FIRST_CODE 4097 /* Impossible code, to signal first. */
|
|
64 #define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */
|
|
65
|
|
66 #define IS_READABLE(Private) (!(Private->FileState & FILE_STATE_READ))
|
|
67
|
|
68 typedef struct GifFilePrivateType {
|
|
69 int FileState,
|
|
70 FileHandle, /* Where all this data goes to! */
|
|
71 BitsPerPixel, /* Bits per pixel (Codes uses at list this + 1). */
|
|
72 ClearCode, /* The CLEAR LZ code. */
|
|
73 EOFCode, /* The EOF LZ code. */
|
|
74 RunningCode, /* The next code algorithm can generate. */
|
|
75 RunningBits,/* The number of bits required to represent RunningCode. */
|
|
76 MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */
|
|
77 LastCode, /* The code before the current code. */
|
|
78 CrntCode, /* Current algorithm code. */
|
|
79 StackPtr, /* For character stack (see below). */
|
|
80 CrntShiftState; /* Number of bits in CrntShiftDWord. */
|
|
81 unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */
|
|
82 long PixelCount; /* Number of pixels in image. */
|
|
83 FILE *File; /* File as stream. */
|
|
84 GifByteType Buf[256]; /* Compressed input is buffered here. */
|
|
85 GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */
|
|
86 GifByteType Suffix[LZ_MAX_CODE+1]; /* So we can trace the codes. */
|
|
87 unsigned int Prefix[LZ_MAX_CODE+1];
|
|
88 } GifFilePrivateType;
|
|
89
|
|
90 #ifndef emacs
|
|
91 #ifdef SYSV
|
|
92 static char *VersionStr =
|
|
93 "Gif library module,\t\tGershon Elber\n\
|
|
94 (C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
|
|
95 #else
|
|
96 static char *VersionStr =
|
|
97 PROGRAM_NAME
|
|
98 " IBMPC "
|
|
99 GIF_LIB_VERSION
|
|
100 " Gershon Elber, "
|
|
101 __DATE__ ", " __TIME__ "\n"
|
|
102 "(C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
|
|
103 #endif /* SYSV */
|
|
104 #endif /* !emacs */
|
|
105
|
|
106 extern int _GifError;
|
|
107
|
|
108 static int DGifGetWord(FILE *File, int *Word);
|
|
109 static int DGifSetupDecompress(GifFileType *GifFile);
|
|
110 static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line,
|
|
111 int LineLen);
|
|
112 static int DGifGetPrefixChar(unsigned int *Prefix, int Code, int ClearCode);
|
|
113 static int DGifDecompressInput(GifFilePrivateType *Private, int *Code);
|
|
114 static int DGifBufferedInput(FILE *File, GifByteType *Buf,
|
|
115 GifByteType *NextByte);
|
|
116
|
|
117 /******************************************************************************
|
|
118 * Open a new gif file for read, given by its name. *
|
|
119 * Returns GifFileType pointer dynamically allocated which serves as the gif *
|
|
120 * info record. _GifError is cleared if succesfull. *
|
|
121 ******************************************************************************/
|
203
|
122 GifFileType *DGifOpenFileName(CONST char *FileName)
|
0
|
123 {
|
|
124 int FileHandle;
|
|
125
|
|
126 if ((FileHandle = open(FileName, O_RDONLY
|
|
127 #ifdef __MSDOS__
|
|
128 | O_BINARY
|
|
129 #endif /* __MSDOS__ */
|
|
130 )) == -1) {
|
|
131 _GifError = D_GIF_ERR_OPEN_FAILED;
|
|
132 return NULL;
|
|
133 }
|
|
134
|
|
135 return DGifOpenFileHandle(FileHandle);
|
|
136 }
|
|
137
|
|
138 /******************************************************************************
|
|
139 * Update a new gif file, given its file handle. *
|
|
140 * Returns GifFileType pointer dynamically allocated which serves as the gif *
|
|
141 * info record. _GifError is cleared if succesfull. *
|
|
142 ******************************************************************************/
|
|
143 GifFileType *DGifOpenFileHandle(int FileHandle)
|
|
144 {
|
|
145 char Buf[GIF_STAMP_LEN+1];
|
|
146 GifFileType *GifFile;
|
|
147 GifFilePrivateType *Private;
|
|
148 FILE *f;
|
|
149
|
|
150 #ifdef __MSDOS__
|
|
151 setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */
|
|
152 f = fdopen(FileHandle, "rb"); /* Make it into a stream: */
|
|
153 setvbuf(f, NULL, _IOFBF, GIF_FILE_BUFFER_SIZE);/* And inc. stream buffer.*/
|
|
154 #else
|
|
155 f = fdopen(FileHandle, "r"); /* Make it into a stream: */
|
|
156 #endif /* __MSDOS__ */
|
|
157
|
|
158 if ((GifFile = (GifFileType *) xmalloc(sizeof(GifFileType))) == NULL) {
|
|
159 _GifError = D_GIF_ERR_NOT_ENOUGH_MEM;
|
|
160 return NULL;
|
|
161 }
|
|
162
|
|
163 memset(GifFile, '\0', sizeof(GifFileType));
|
|
164
|
|
165 if ((Private = (GifFilePrivateType *) xmalloc(sizeof(GifFilePrivateType)))
|
|
166 == NULL) {
|
|
167 _GifError = D_GIF_ERR_NOT_ENOUGH_MEM;
|
|
168 xfree((char *) GifFile);
|
|
169 return NULL;
|
|
170 }
|
|
171 GifFile->Private = (VoidPtr) Private;
|
|
172 Private->FileHandle = FileHandle;
|
|
173 Private->File = f;
|
|
174 Private->FileState = 0; /* Make sure bit 0 = 0 (File open for read). */
|
|
175
|
|
176 /* Lets see if this is a GIF file: */
|
|
177 if (fread(Buf, 1, GIF_STAMP_LEN, Private->File) != GIF_STAMP_LEN) {
|
|
178 _GifError = D_GIF_ERR_READ_FAILED;
|
|
179 xfree((char *) Private);
|
|
180 xfree((char *) GifFile);
|
|
181 return NULL;
|
|
182 }
|
|
183
|
|
184 /* The GIF Version number is ignored at this time. Maybe we should do */
|
|
185 /* something more useful with it. */
|
|
186 Buf[GIF_STAMP_LEN] = 0;
|
|
187 if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) {
|
|
188 _GifError = D_GIF_ERR_NOT_GIF_FILE;
|
|
189 xfree((char *) Private);
|
|
190 xfree((char *) GifFile);
|
|
191 return NULL;
|
|
192 }
|
|
193
|
|
194 if (DGifGetScreenDesc(GifFile) == GIF_ERROR) {
|
|
195 xfree((char *) Private);
|
|
196 xfree((char *) GifFile);
|
|
197 return NULL;
|
|
198 }
|
|
199
|
|
200 _GifError = 0;
|
|
201
|
|
202 return GifFile;
|
|
203 }
|
|
204
|
|
205 /******************************************************************************
|
|
206 * This routine should be called before any other DGif calls. Note that *
|
|
207 * this routine is called automatically from DGif file open routines. *
|
|
208 ******************************************************************************/
|
|
209 int DGifGetScreenDesc(GifFileType *GifFile)
|
|
210 {
|
|
211 int i, BitsPerPixel;
|
|
212 GifByteType Buf[3];
|
|
213 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
214
|
|
215 if (!IS_READABLE(Private)) {
|
|
216 /* This file was NOT open for reading: */
|
|
217 _GifError = D_GIF_ERR_NOT_READABLE;
|
|
218 return GIF_ERROR;
|
|
219 }
|
|
220
|
|
221 /* Put the screen descriptor into the file: */
|
|
222 if (DGifGetWord(Private->File, &GifFile->SWidth) == GIF_ERROR ||
|
|
223 DGifGetWord(Private->File, &GifFile->SHeight) == GIF_ERROR)
|
|
224 return GIF_ERROR;
|
|
225
|
|
226 if (fread(Buf, 1, 3, Private->File) != 3) {
|
|
227 _GifError = D_GIF_ERR_READ_FAILED;
|
|
228 return GIF_ERROR;
|
|
229 }
|
|
230 GifFile->SColorResolution = ((int) ((Buf[0] & 0x70) + 1) >> 4) + 1;
|
|
231 BitsPerPixel = (Buf[0] & 0x07) + 1;
|
|
232 GifFile->SBackGroundColor = Buf[1];
|
|
233 if (Buf[0] & 0x80) { /* Do we have global color map? */
|
|
234
|
|
235 GifFile->SColorMap = MakeMapObject(1 << BitsPerPixel, NULL);
|
|
236
|
|
237 /* Get the global color map: */
|
|
238 for (i = 0; i < GifFile->SColorMap->ColorCount; i++) {
|
|
239 if (fread(Buf, 1, 3, Private->File) != 3) {
|
|
240 _GifError = D_GIF_ERR_READ_FAILED;
|
|
241 return GIF_ERROR;
|
|
242 }
|
|
243 GifFile->SColorMap->Colors[i].Red = Buf[0];
|
|
244 GifFile->SColorMap->Colors[i].Green = Buf[1];
|
|
245 GifFile->SColorMap->Colors[i].Blue = Buf[2];
|
|
246 }
|
129
|
247 } else {
|
|
248 /* XEmacs assumes we always have a colormap */
|
|
249 GifFile->SColorMap = MakeMapObject(2, NULL);
|
|
250 GifFile->SColorMap->Colors[0].Red = 0;
|
|
251 GifFile->SColorMap->Colors[0].Green = 0;
|
|
252 GifFile->SColorMap->Colors[0].Blue = 0;
|
|
253 GifFile->SColorMap->Colors[1].Red = 0xff;
|
|
254 GifFile->SColorMap->Colors[1].Green = 0xff;
|
|
255 GifFile->SColorMap->Colors[1].Blue = 0xff;
|
0
|
256 }
|
|
257
|
|
258 return GIF_OK;
|
|
259 }
|
|
260
|
|
261 /******************************************************************************
|
|
262 * This routine should be called before any attemp to read an image. *
|
|
263 ******************************************************************************/
|
|
264 int DGifGetRecordType(GifFileType *GifFile, GifRecordType *Type)
|
|
265 {
|
|
266 GifByteType Buf;
|
|
267 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
268
|
|
269 if (!IS_READABLE(Private)) {
|
|
270 /* This file was NOT open for reading: */
|
|
271 _GifError = D_GIF_ERR_NOT_READABLE;
|
|
272 return GIF_ERROR;
|
|
273 }
|
|
274
|
|
275 if (fread(&Buf, 1, 1, Private->File) != 1) {
|
|
276 _GifError = D_GIF_ERR_READ_FAILED;
|
|
277 return GIF_ERROR;
|
|
278 }
|
|
279
|
|
280 switch (Buf) {
|
|
281 case ',':
|
|
282 *Type = IMAGE_DESC_RECORD_TYPE;
|
|
283 break;
|
|
284 case '!':
|
|
285 *Type = EXTENSION_RECORD_TYPE;
|
|
286 break;
|
|
287 case ';':
|
|
288 *Type = TERMINATE_RECORD_TYPE;
|
|
289 break;
|
|
290 default:
|
|
291 *Type = UNDEFINED_RECORD_TYPE;
|
|
292 _GifError = D_GIF_ERR_WRONG_RECORD;
|
|
293 return GIF_ERROR;
|
|
294 }
|
|
295
|
|
296 return GIF_OK;
|
|
297 }
|
|
298
|
|
299 /******************************************************************************
|
|
300 * This routine should be called before any attemp to read an image. *
|
|
301 * Note it is assumed the Image desc. header (',') has been read. *
|
|
302 ******************************************************************************/
|
|
303 int DGifGetImageDesc(GifFileType *GifFile)
|
|
304 {
|
|
305 int i, BitsPerPixel;
|
|
306 GifByteType Buf[3];
|
|
307 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
308
|
|
309 if (!IS_READABLE(Private)) {
|
|
310 /* This file was NOT open for reading: */
|
|
311 _GifError = D_GIF_ERR_NOT_READABLE;
|
|
312 return GIF_ERROR;
|
|
313 }
|
|
314
|
|
315 if (DGifGetWord(Private->File, &GifFile->Image.Left) == GIF_ERROR ||
|
|
316 DGifGetWord(Private->File, &GifFile->Image.Top) == GIF_ERROR ||
|
|
317 DGifGetWord(Private->File, &GifFile->Image.Width) == GIF_ERROR ||
|
|
318 DGifGetWord(Private->File, &GifFile->Image.Height) == GIF_ERROR)
|
|
319 return GIF_ERROR;
|
|
320 if (fread(Buf, 1, 1, Private->File) != 1) {
|
|
321 _GifError = D_GIF_ERR_READ_FAILED;
|
|
322 return GIF_ERROR;
|
|
323 }
|
|
324 BitsPerPixel = (Buf[0] & 0x07) + 1;
|
|
325 GifFile->Image.Interlace = (Buf[0] & 0x40);
|
|
326 if (Buf[0] & 0x80) { /* Does this image have local color map? */
|
|
327
|
|
328 if (GifFile->Image.ColorMap && GifFile->SavedImages == NULL)
|
|
329 FreeMapObject(GifFile->Image.ColorMap);
|
|
330
|
|
331 GifFile->Image.ColorMap = MakeMapObject(1 << BitsPerPixel, NULL);
|
185
|
332
|
0
|
333 /* Get the image local color map: */
|
|
334 for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) {
|
|
335 if (fread(Buf, 1, 3, Private->File) != 3) {
|
|
336 _GifError = D_GIF_ERR_READ_FAILED;
|
|
337 return GIF_ERROR;
|
|
338 }
|
|
339 GifFile->Image.ColorMap->Colors[i].Red = Buf[0];
|
|
340 GifFile->Image.ColorMap->Colors[i].Green = Buf[1];
|
|
341 GifFile->Image.ColorMap->Colors[i].Blue = Buf[2];
|
|
342 }
|
|
343 }
|
|
344
|
|
345 if (GifFile->SavedImages) {
|
|
346 SavedImage *sp;
|
|
347
|
|
348 if ((GifFile->SavedImages = (SavedImage *)xrealloc(GifFile->SavedImages,
|
|
349 sizeof(SavedImage) * (GifFile->ImageCount + 1))) == NULL) {
|
|
350 _GifError = D_GIF_ERR_NOT_ENOUGH_MEM;
|
|
351 return GIF_ERROR;
|
|
352 }
|
|
353
|
|
354 sp = &GifFile->SavedImages[GifFile->ImageCount];
|
|
355 memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc));
|
|
356 if (GifFile->Image.ColorMap)
|
|
357 {
|
|
358 sp->ImageDesc.ColorMap =
|
|
359 MakeMapObject (GifFile->Image.ColorMap->ColorCount,
|
|
360 GifFile->Image.ColorMap->Colors);
|
|
361 }
|
|
362 sp->RasterBits = (GifPixelType *)NULL;
|
|
363 sp->ExtensionBlockCount = 0;
|
|
364 sp->ExtensionBlocks = (ExtensionBlock *)NULL;
|
|
365 }
|
|
366
|
|
367 GifFile->ImageCount++;
|
|
368
|
|
369 Private->PixelCount = (long) GifFile->Image.Width *
|
|
370 (long) GifFile->Image.Height;
|
|
371
|
|
372 DGifSetupDecompress(GifFile); /* Reset decompress algorithm parameters. */
|
|
373
|
|
374 return GIF_OK;
|
|
375 }
|
|
376
|
|
377 /******************************************************************************
|
|
378 * Get one full scanned line (Line) of length LineLen from GIF file. *
|
|
379 ******************************************************************************/
|
|
380 int DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
|
|
381 {
|
|
382 GifByteType *Dummy;
|
|
383 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
384
|
|
385 if (!IS_READABLE(Private)) {
|
|
386 /* This file was NOT open for reading: */
|
|
387 _GifError = D_GIF_ERR_NOT_READABLE;
|
|
388 return GIF_ERROR;
|
|
389 }
|
|
390
|
|
391 if (!LineLen) LineLen = GifFile->Image.Width;
|
|
392
|
|
393 #ifdef __MSDOS__
|
|
394 if ((Private->PixelCount -= LineLen) > 0xffff0000UL) {
|
|
395 #else
|
|
396 if ((unsigned) (Private->PixelCount -= LineLen) > 0xffff0000) {
|
|
397 #endif /* __MSDOS__ */
|
|
398 _GifError = D_GIF_ERR_DATA_TOO_BIG;
|
|
399 return GIF_ERROR;
|
|
400 }
|
|
401
|
|
402 if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) {
|
|
403 if (Private->PixelCount == 0) {
|
|
404 /* We probably would not be called any more, so lets clean */
|
|
405 /* everything before we return: need to flush out all rest of */
|
|
406 /* image until empty block (size 0) detected. We use GetCodeNext.*/
|
|
407 do if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
|
|
408 return GIF_ERROR;
|
|
409 while (Dummy != NULL);
|
|
410 }
|
|
411 return GIF_OK;
|
|
412 }
|
|
413 else
|
|
414 return GIF_ERROR;
|
|
415 }
|
|
416
|
|
417 /******************************************************************************
|
|
418 * Put one pixel (Pixel) into GIF file. *
|
|
419 ******************************************************************************/
|
|
420 int DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel)
|
|
421 {
|
|
422 GifByteType *Dummy;
|
|
423 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
424
|
|
425 if (!IS_READABLE(Private)) {
|
|
426 /* This file was NOT open for reading: */
|
|
427 _GifError = D_GIF_ERR_NOT_READABLE;
|
|
428 return GIF_ERROR;
|
|
429 }
|
|
430
|
|
431 #ifdef __MSDOS__
|
|
432 if (--Private->PixelCount > 0xffff0000UL)
|
|
433 #else
|
|
434 if ((unsigned) --Private->PixelCount > 0xffff0000)
|
|
435 #endif /* __MSDOS__ */
|
|
436 {
|
|
437 _GifError = D_GIF_ERR_DATA_TOO_BIG;
|
|
438 return GIF_ERROR;
|
|
439 }
|
|
440
|
|
441 if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) {
|
|
442 if (Private->PixelCount == 0) {
|
|
443 /* We probably would not be called any more, so lets clean */
|
|
444 /* everything before we return: need to flush out all rest of */
|
|
445 /* image until empty block (size 0) detected. We use GetCodeNext.*/
|
|
446 do if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
|
|
447 return GIF_ERROR;
|
|
448 while (Dummy != NULL);
|
|
449 }
|
|
450 return GIF_OK;
|
|
451 }
|
|
452 else
|
|
453 return GIF_ERROR;
|
|
454 }
|
|
455
|
|
456 /******************************************************************************
|
|
457 * Get an extension block (see GIF manual) from gif file. This routine only *
|
|
458 * returns the first data block, and DGifGetExtensionNext shouldbe called *
|
|
459 * after this one until NULL extension is returned. *
|
|
460 * The Extension should NOT be freed by the user (not dynamically allocated).*
|
|
461 * Note it is assumed the Extension desc. header ('!') has been read. *
|
|
462 ******************************************************************************/
|
|
463 int DGifGetExtension(GifFileType *GifFile, int *ExtCode,
|
|
464 GifByteType **Extension)
|
|
465 {
|
|
466 GifByteType Buf;
|
|
467 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
468
|
|
469 if (!IS_READABLE(Private)) {
|
|
470 /* This file was NOT open for reading: */
|
|
471 _GifError = D_GIF_ERR_NOT_READABLE;
|
|
472 return GIF_ERROR;
|
|
473 }
|
|
474
|
|
475 if (fread(&Buf, 1, 1, Private->File) != 1) {
|
|
476 _GifError = D_GIF_ERR_READ_FAILED;
|
|
477 return GIF_ERROR;
|
|
478 }
|
|
479 *ExtCode = Buf;
|
|
480
|
|
481 return DGifGetExtensionNext(GifFile, Extension);
|
|
482 }
|
|
483
|
|
484 /******************************************************************************
|
|
485 * Get a following extension block (see GIF manual) from gif file. This *
|
|
486 * routine sould be called until NULL Extension is returned. *
|
|
487 * The Extension should NOT be freed by the user (not dynamically allocated).*
|
|
488 ******************************************************************************/
|
|
489 int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **Extension)
|
|
490 {
|
|
491 GifByteType Buf;
|
|
492 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
493
|
|
494 if (fread(&Buf, 1, 1, Private->File) != 1) {
|
|
495 _GifError = D_GIF_ERR_READ_FAILED;
|
|
496 return GIF_ERROR;
|
|
497 }
|
|
498 if (Buf > 0) {
|
|
499 *Extension = Private->Buf; /* Use private unused buffer. */
|
|
500 (*Extension)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */
|
|
501 if (fread(&((*Extension)[1]), 1, Buf, Private->File) != Buf) {
|
|
502 _GifError = D_GIF_ERR_READ_FAILED;
|
|
503 return GIF_ERROR;
|
|
504 }
|
|
505 }
|
|
506 else
|
|
507 *Extension = NULL;
|
|
508
|
|
509 return GIF_OK;
|
|
510 }
|
|
511
|
|
512 /******************************************************************************
|
|
513 * This routine should be called last, to close the GIF file. *
|
|
514 ******************************************************************************/
|
|
515 int DGifCloseFile(GifFileType *GifFile)
|
|
516 {
|
|
517 GifFilePrivateType *Private;
|
|
518 FILE *File;
|
|
519
|
|
520 if (GifFile == NULL) return GIF_ERROR;
|
|
521
|
|
522 Private = (GifFilePrivateType *) GifFile->Private;
|
|
523
|
|
524 if (!IS_READABLE(Private)) {
|
|
525 /* This file was NOT open for reading: */
|
|
526 _GifError = D_GIF_ERR_NOT_READABLE;
|
|
527 return GIF_ERROR;
|
|
528 }
|
|
529
|
|
530 File = Private->File;
|
|
531
|
|
532 if (GifFile->Image.ColorMap)
|
|
533 {
|
|
534 FreeMapObject(GifFile->Image.ColorMap);
|
|
535 GifFile->Image.ColorMap = 0;
|
|
536 }
|
|
537 if (GifFile->SColorMap)
|
|
538 {
|
|
539 FreeMapObject(GifFile->SColorMap);
|
|
540 GifFile->SColorMap = 0;
|
|
541 }
|
|
542 if (Private)
|
|
543 {
|
|
544 xfree((char *) Private);
|
|
545 GifFile->Private = 0;
|
|
546 }
|
|
547 if (GifFile->SavedImages)
|
|
548 {
|
|
549 FreeSavedImages(GifFile);
|
|
550 GifFile->SavedImages = 0;
|
|
551 }
|
|
552 xfree(GifFile);
|
|
553
|
|
554 if (fclose(File) != 0) {
|
|
555 _GifError = D_GIF_ERR_CLOSE_FAILED;
|
|
556 return GIF_ERROR;
|
|
557 }
|
|
558 return GIF_OK;
|
|
559 }
|
|
560
|
|
561 /******************************************************************************
|
|
562 * Get 2 bytes (word) from the given file: *
|
|
563 ******************************************************************************/
|
|
564 static int DGifGetWord(FILE *File, int *Word)
|
|
565 {
|
|
566 unsigned char c[2];
|
|
567
|
|
568 if (fread(c, 1, 2, File) != 2) {
|
|
569 _GifError = D_GIF_ERR_READ_FAILED;
|
|
570 return GIF_ERROR;
|
|
571 }
|
|
572
|
|
573 *Word = (((unsigned int) c[1]) << 8) + c[0];
|
|
574 return GIF_OK;
|
|
575 }
|
|
576
|
|
577 /******************************************************************************
|
|
578 * Get the image code in compressed form. his routine can be called if the *
|
|
579 * information needed to be piped out as is. Obviously this is much faster *
|
|
580 * than decoding and encoding again. This routine should be followed by calls *
|
|
581 * to DGifGetCodeNext, until NULL block is returned. *
|
|
582 * The block should NOT be freed by the user (not dynamically allocated). *
|
|
583 ******************************************************************************/
|
|
584 int DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock)
|
|
585 {
|
|
586 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
587
|
|
588 if (!IS_READABLE(Private)) {
|
|
589 /* This file was NOT open for reading: */
|
|
590 _GifError = D_GIF_ERR_NOT_READABLE;
|
|
591 return GIF_ERROR;
|
|
592 }
|
|
593
|
|
594 *CodeSize = Private->BitsPerPixel;
|
|
595
|
|
596 return DGifGetCodeNext(GifFile, CodeBlock);
|
|
597 }
|
|
598
|
|
599 /******************************************************************************
|
|
600 * Continue to get the image code in compressed form. This routine should be *
|
|
601 * called until NULL block is returned. *
|
|
602 * The block should NOT be freed by the user (not dynamically allocated). *
|
|
603 ******************************************************************************/
|
|
604 int DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock)
|
|
605 {
|
|
606 GifByteType Buf;
|
|
607 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
608
|
|
609 if (fread(&Buf, 1, 1, Private->File) != 1) {
|
|
610 _GifError = D_GIF_ERR_READ_FAILED;
|
|
611 return GIF_ERROR;
|
|
612 }
|
|
613
|
|
614 if (Buf > 0) {
|
|
615 *CodeBlock = Private->Buf; /* Use private unused buffer. */
|
|
616 (*CodeBlock)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */
|
|
617 if (fread(&((*CodeBlock)[1]), 1, Buf, Private->File) != Buf) {
|
|
618 _GifError = D_GIF_ERR_READ_FAILED;
|
|
619 return GIF_ERROR;
|
|
620 }
|
|
621 }
|
|
622 else {
|
|
623 *CodeBlock = NULL;
|
|
624 Private->Buf[0] = 0; /* Make sure the buffer is empty! */
|
|
625 Private->PixelCount = 0; /* And local info. indicate image read. */
|
|
626 }
|
|
627
|
|
628 return GIF_OK;
|
|
629 }
|
|
630
|
|
631 /******************************************************************************
|
|
632 * Setup the LZ decompression for this image: *
|
|
633 ******************************************************************************/
|
|
634 static int DGifSetupDecompress(GifFileType *GifFile)
|
|
635 {
|
|
636 int i, BitsPerPixel;
|
|
637 GifByteType CodeSize;
|
|
638 unsigned int *Prefix;
|
|
639 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
640
|
|
641 fread(&CodeSize, 1, 1, Private->File); /* Read Code size from file. */
|
|
642 BitsPerPixel = CodeSize;
|
|
643
|
|
644 Private->Buf[0] = 0; /* Input Buffer empty. */
|
|
645 Private->BitsPerPixel = BitsPerPixel;
|
|
646 Private->ClearCode = (1 << BitsPerPixel);
|
|
647 Private->EOFCode = Private->ClearCode + 1;
|
|
648 Private->RunningCode = Private->EOFCode + 1;
|
|
649 Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */
|
|
650 Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */
|
|
651 Private->StackPtr = 0; /* No pixels on the pixel stack. */
|
|
652 Private->LastCode = NO_SUCH_CODE;
|
|
653 Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */
|
|
654 Private->CrntShiftDWord = 0;
|
|
655
|
|
656 Prefix = Private->Prefix;
|
|
657 for (i = 0; i <= LZ_MAX_CODE; i++) Prefix[i] = NO_SUCH_CODE;
|
|
658
|
|
659 return GIF_OK;
|
|
660 }
|
|
661
|
|
662 /******************************************************************************
|
|
663 * The LZ decompression routine: *
|
|
664 * This version decompress the given gif file into Line of length LineLen. *
|
|
665 * This routine can be called few times (one per scan line, for example), in *
|
|
666 * order the complete the whole image. *
|
|
667 ******************************************************************************/
|
|
668 static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line,
|
|
669 int LineLen)
|
|
670 {
|
|
671 int i = 0, j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr;
|
|
672 GifByteType *Stack, *Suffix;
|
|
673 unsigned int *Prefix;
|
|
674 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
675
|
|
676 StackPtr = Private->StackPtr;
|
|
677 Prefix = Private->Prefix;
|
|
678 Suffix = Private->Suffix;
|
|
679 Stack = Private->Stack;
|
|
680 EOFCode = Private->EOFCode;
|
|
681 ClearCode = Private->ClearCode;
|
|
682 LastCode = Private->LastCode;
|
|
683
|
|
684 if (StackPtr != 0) {
|
|
685 /* Let pop the stack off before continueing to read the gif file: */
|
|
686 while (StackPtr != 0 && i < LineLen) Line[i++] = Stack[--StackPtr];
|
|
687 }
|
|
688
|
|
689 while (i < LineLen) { /* Decode LineLen items. */
|
|
690 if (DGifDecompressInput(Private, &CrntCode) == GIF_ERROR)
|
|
691 return GIF_ERROR;
|
|
692
|
|
693 if (CrntCode == EOFCode) {
|
|
694 /* Note however that usually we will not be here as we will stop */
|
|
695 /* decoding as soon as we got all the pixel, or EOF code will */
|
|
696 /* not be read at all, and DGifGetLine/Pixel clean everything. */
|
|
697 if (i != LineLen - 1 || Private->PixelCount != 0) {
|
|
698 _GifError = D_GIF_ERR_EOF_TOO_SOON;
|
|
699 return GIF_ERROR;
|
|
700 }
|
|
701 i++;
|
|
702 }
|
|
703 else if (CrntCode == ClearCode) {
|
|
704 /* We need to start over again: */
|
|
705 for (j = 0; j <= LZ_MAX_CODE; j++) Prefix[j] = NO_SUCH_CODE;
|
|
706 Private->RunningCode = Private->EOFCode + 1;
|
|
707 Private->RunningBits = Private->BitsPerPixel + 1;
|
|
708 Private->MaxCode1 = 1 << Private->RunningBits;
|
|
709 LastCode = Private->LastCode = NO_SUCH_CODE;
|
|
710 }
|
|
711 else {
|
|
712 /* Its regular code - if in pixel range simply add it to output */
|
|
713 /* stream, otherwise trace to codes linked list until the prefix */
|
|
714 /* is in pixel range: */
|
|
715 if (CrntCode < ClearCode) {
|
|
716 /* This is simple - its pixel scalar, so add it to output: */
|
|
717 Line[i++] = CrntCode;
|
|
718 }
|
|
719 else {
|
|
720 /* Its a code to needed to be traced: trace the linked list */
|
|
721 /* until the prefix is a pixel, while pushing the suffix */
|
|
722 /* pixels on our stack. If we done, pop the stack in reverse */
|
|
723 /* (thats what stack is good for!) order to output. */
|
|
724 if (Prefix[CrntCode] == NO_SUCH_CODE) {
|
|
725 /* Only allowed if CrntCode is exactly the running code: */
|
|
726 /* In that case CrntCode = XXXCode, CrntCode or the */
|
|
727 /* prefix code is last code and the suffix char is */
|
|
728 /* exactly the prefix of last code! */
|
|
729 if (CrntCode == Private->RunningCode - 2) {
|
|
730 CrntPrefix = LastCode;
|
|
731 Suffix[Private->RunningCode - 2] =
|
|
732 Stack[StackPtr++] = DGifGetPrefixChar(Prefix,
|
|
733 LastCode, ClearCode);
|
|
734 }
|
|
735 else {
|
|
736 _GifError = D_GIF_ERR_IMAGE_DEFECT;
|
|
737 return GIF_ERROR;
|
|
738 }
|
|
739 }
|
|
740 else
|
|
741 CrntPrefix = CrntCode;
|
|
742
|
|
743 /* Now (if image is O.K.) we should not get an NO_SUCH_CODE */
|
|
744 /* During the trace. As we might loop forever, in case of */
|
|
745 /* defective image, we count the number of loops we trace */
|
|
746 /* and stop if we got LZ_MAX_CODE. obviously we can not */
|
|
747 /* loop more than that. */
|
|
748 j = 0;
|
|
749 while (j++ <= LZ_MAX_CODE &&
|
|
750 CrntPrefix > ClearCode &&
|
|
751 CrntPrefix <= LZ_MAX_CODE) {
|
|
752 Stack[StackPtr++] = Suffix[CrntPrefix];
|
|
753 CrntPrefix = Prefix[CrntPrefix];
|
|
754 }
|
|
755 if (j >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) {
|
|
756 _GifError = D_GIF_ERR_IMAGE_DEFECT;
|
|
757 return GIF_ERROR;
|
|
758 }
|
|
759 /* Push the last character on stack: */
|
|
760 Stack[StackPtr++] = CrntPrefix;
|
|
761
|
|
762 /* Now lets pop all the stack into output: */
|
|
763 while (StackPtr != 0 && i < LineLen)
|
|
764 Line[i++] = Stack[--StackPtr];
|
|
765 }
|
|
766 if (LastCode != NO_SUCH_CODE) {
|
|
767 Prefix[Private->RunningCode - 2] = LastCode;
|
|
768
|
|
769 if (CrntCode == Private->RunningCode - 2) {
|
|
770 /* Only allowed if CrntCode is exactly the running code: */
|
|
771 /* In that case CrntCode = XXXCode, CrntCode or the */
|
|
772 /* prefix code is last code and the suffix char is */
|
|
773 /* exactly the prefix of last code! */
|
|
774 Suffix[Private->RunningCode - 2] =
|
|
775 DGifGetPrefixChar(Prefix, LastCode, ClearCode);
|
|
776 }
|
|
777 else {
|
|
778 Suffix[Private->RunningCode - 2] =
|
|
779 DGifGetPrefixChar(Prefix, CrntCode, ClearCode);
|
|
780 }
|
|
781 }
|
|
782 LastCode = CrntCode;
|
|
783 }
|
|
784 }
|
|
785
|
|
786 Private->LastCode = LastCode;
|
|
787 Private->StackPtr = StackPtr;
|
|
788
|
|
789 return GIF_OK;
|
|
790 }
|
|
791
|
|
792 /******************************************************************************
|
|
793 * Routine to trace the Prefixes linked list until we get a prefix which is *
|
|
794 * not code, but a pixel value (less than ClearCode). Returns that pixel value.*
|
|
795 * If image is defective, we might loop here forever, so we limit the loops to *
|
|
796 * the maximum possible if image O.k. - LZ_MAX_CODE times. *
|
|
797 ******************************************************************************/
|
|
798 static int DGifGetPrefixChar(unsigned int *Prefix, int Code, int ClearCode)
|
|
799 {
|
|
800 int i = 0;
|
|
801
|
|
802 while (Code > ClearCode && i++ <= LZ_MAX_CODE) Code = Prefix[Code];
|
|
803 return Code;
|
|
804 }
|
|
805
|
|
806 /******************************************************************************
|
|
807 * Interface for accessing the LZ codes directly. Set Code to the real code *
|
|
808 * (12bits), or to -1 if EOF code is returned. *
|
|
809 ******************************************************************************/
|
|
810 int DGifGetLZCodes(GifFileType *GifFile, int *Code)
|
|
811 {
|
|
812 GifByteType *CodeBlock;
|
|
813 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
|
|
814
|
|
815 if (!IS_READABLE(Private)) {
|
|
816 /* This file was NOT open for reading: */
|
|
817 _GifError = D_GIF_ERR_NOT_READABLE;
|
|
818 return GIF_ERROR;
|
|
819 }
|
|
820
|
|
821 if (DGifDecompressInput(Private, Code) == GIF_ERROR)
|
|
822 return GIF_ERROR;
|
|
823
|
|
824 if (*Code == Private->EOFCode) {
|
|
825 /* Skip rest of codes (hopefully only NULL terminating block): */
|
|
826 do if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR)
|
|
827 return GIF_ERROR;
|
|
828 while (CodeBlock != NULL);
|
|
829
|
|
830 *Code = -1;
|
|
831 }
|
|
832 else if (*Code == Private->ClearCode) {
|
|
833 /* We need to start over again: */
|
|
834 Private->RunningCode = Private->EOFCode + 1;
|
|
835 Private->RunningBits = Private->BitsPerPixel + 1;
|
|
836 Private->MaxCode1 = 1 << Private->RunningBits;
|
|
837 }
|
|
838
|
|
839 return GIF_OK;
|
|
840 }
|
|
841
|
|
842 /******************************************************************************
|
|
843 * The LZ decompression input routine: *
|
|
844 * This routine is responsable for the decompression of the bit stream from *
|
|
845 * 8 bits (bytes) packets, into the real codes. *
|
|
846 * Returns GIF_OK if read succesfully. *
|
|
847 ******************************************************************************/
|
|
848 static int DGifDecompressInput(GifFilePrivateType *Private, int *Code)
|
|
849 {
|
|
850 GifByteType NextByte;
|
|
851 static unsigned int CodeMasks[] = {
|
|
852 0x0000, 0x0001, 0x0003, 0x0007,
|
|
853 0x000f, 0x001f, 0x003f, 0x007f,
|
|
854 0x00ff, 0x01ff, 0x03ff, 0x07ff,
|
|
855 0x0fff
|
|
856 };
|
|
857
|
|
858 while (Private->CrntShiftState < Private->RunningBits) {
|
|
859 /* Needs to get more bytes from input stream for next code: */
|
|
860 if (DGifBufferedInput(Private->File, Private->Buf, &NextByte)
|
|
861 == GIF_ERROR) {
|
|
862 return GIF_ERROR;
|
|
863 }
|
|
864 Private->CrntShiftDWord |=
|
|
865 ((unsigned long) NextByte) << Private->CrntShiftState;
|
|
866 Private->CrntShiftState += 8;
|
|
867 }
|
|
868 *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits];
|
|
869
|
|
870 Private->CrntShiftDWord >>= Private->RunningBits;
|
|
871 Private->CrntShiftState -= Private->RunningBits;
|
|
872
|
|
873 /* If code cannt fit into RunningBits bits, must raise its size. Note */
|
|
874 /* however that codes above 4095 are used for special signaling. */
|
|
875 if (++Private->RunningCode > Private->MaxCode1 &&
|
|
876 Private->RunningBits < LZ_BITS) {
|
|
877 Private->MaxCode1 <<= 1;
|
|
878 Private->RunningBits++;
|
|
879 }
|
|
880 return GIF_OK;
|
|
881 }
|
|
882
|
|
883 /******************************************************************************
|
|
884 * This routines read one gif data block at a time and buffers it internally *
|
|
885 * so that the decompression routine could access it. *
|
|
886 * The routine returns the next byte from its internal buffer (or read next *
|
|
887 * block in if buffer empty) and returns GIF_OK if succesful. *
|
|
888 ******************************************************************************/
|
|
889 static int DGifBufferedInput(FILE *File, GifByteType *Buf,
|
|
890 GifByteType *NextByte)
|
|
891 {
|
|
892 if (Buf[0] == 0) {
|
|
893 /* Needs to read the next buffer - this one is empty: */
|
|
894 if (fread(Buf, 1, 1, File) != 1)
|
|
895 {
|
|
896 _GifError = D_GIF_ERR_READ_FAILED;
|
|
897 return GIF_ERROR;
|
|
898 }
|
|
899 if (fread(&Buf[1], 1, Buf[0], File) != Buf[0])
|
|
900 {
|
|
901 _GifError = D_GIF_ERR_READ_FAILED;
|
|
902 return GIF_ERROR;
|
|
903 }
|
|
904 *NextByte = Buf[1];
|
|
905 Buf[1] = 2; /* We use now the second place as last char read! */
|
|
906 Buf[0]--;
|
|
907 }
|
|
908 else {
|
|
909 *NextByte = Buf[Buf[1]++];
|
|
910 Buf[0]--;
|
|
911 }
|
|
912
|
|
913 return GIF_OK;
|
|
914 }
|
|
915
|
|
916 /******************************************************************************
|
|
917 * This routine reads an entire GIF into core, hanging all its state info off *
|
|
918 * the GifFileType pointer. Call DGifOpenFileName() or DGifOpenFileHandle() *
|
|
919 * first to initialize I/O. Its inverse is EGifSpew(). *
|
|
920 ******************************************************************************/
|
|
921 int DGifSlurp(GifFileType *GifFile)
|
|
922 {
|
|
923 #if 0
|
|
924 int i, j, Error;
|
|
925 #endif /* 0 */
|
|
926 int ImageSize;
|
|
927 GifRecordType RecordType;
|
|
928 SavedImage *sp;
|
|
929 GifByteType *ExtData;
|
114
|
930 int ExtCode;
|
0
|
931
|
|
932 /* Some versions of malloc dislike 0-length requests */
|
|
933 GifFile->SavedImages = (SavedImage *)xmalloc(sizeof(SavedImage));
|
|
934
|
|
935 do {
|
|
936 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
|
|
937 return(GIF_ERROR);
|
|
938
|
|
939 switch (RecordType) {
|
|
940 case IMAGE_DESC_RECORD_TYPE:
|
|
941 if (DGifGetImageDesc(GifFile) == GIF_ERROR)
|
|
942 return(GIF_ERROR);
|
|
943
|
|
944 sp = &GifFile->SavedImages[GifFile->ImageCount-1];
|
|
945 ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;
|
|
946
|
|
947 sp->RasterBits
|
|
948 = (GifPixelType*) xmalloc(ImageSize * sizeof(GifPixelType));
|
|
949
|
|
950 if (DGifGetLine(GifFile, sp->RasterBits, ImageSize)
|
|
951 == GIF_ERROR)
|
|
952 return(GIF_ERROR);
|
|
953
|
|
954 break;
|
|
955
|
|
956 case EXTENSION_RECORD_TYPE:
|
114
|
957 /* This code fails if no image_desc record has been read
|
|
958 yet. I don't know if that's legal, but I've seen GIFs
|
|
959 that start with an extension record. XEmacs doesn't use
|
|
960 the extension records anyway, so we'll just ignore them.
|
|
961 - dkindred@cs.cmu.edu */
|
|
962 #if 0
|
0
|
963 if (DGifGetExtension(GifFile,&sp->Function,&ExtData)==GIF_ERROR)
|
|
964 return(GIF_ERROR);
|
|
965 else
|
|
966 {
|
175
|
967 ExtensionBlock *ep =
|
|
968 &sp->ExtensionBlocks[sp->ExtensionBlockCount++];
|
0
|
969
|
|
970 ep->ByteCount = ExtData[0];
|
|
971 ep->Bytes = (GifByteType *)xmalloc(ep->ByteCount * sizeof(GifByteType));
|
|
972 memcpy(ep->Bytes, ExtData, ep->ByteCount * sizeof(char));
|
|
973 }
|
|
974
|
|
975 while (ExtData != NULL) {
|
|
976 if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR)
|
|
977 return(GIF_ERROR);
|
|
978 else
|
|
979 {
|
175
|
980 ExtensionBlock *ep =
|
|
981 &sp->ExtensionBlocks[sp->ExtensionBlockCount++];
|
0
|
982
|
|
983 ep->ByteCount = ExtData[0];
|
|
984 ep->Bytes = (GifByteType *)xmalloc(ep->ByteCount * sizeof(GifByteType));
|
|
985 memcpy(ep->Bytes,ExtData,ep->ByteCount * sizeof(char));
|
|
986 }
|
|
987 }
|
114
|
988 #else
|
|
989 /* Skip any extension blocks in the file. */
|
185
|
990 if (DGifGetExtension (GifFile, &ExtCode, &ExtData)
|
114
|
991 == GIF_ERROR)
|
|
992 return GIF_ERROR;
|
|
993
|
|
994 while (ExtData != NULL) {
|
|
995 if (DGifGetExtensionNext (GifFile, &ExtData) == GIF_ERROR)
|
|
996 return GIF_ERROR;
|
|
997 }
|
|
998 #endif
|
0
|
999 break;
|
|
1000
|
|
1001 case TERMINATE_RECORD_TYPE:
|
|
1002 break;
|
|
1003
|
|
1004 default: /* Should be trapped by DGifGetRecordType */
|
|
1005 break;
|
|
1006 }
|
|
1007 }
|
|
1008 while
|
|
1009 (RecordType != TERMINATE_RECORD_TYPE);
|
|
1010
|
|
1011 return(GIF_OK);
|
|
1012 }
|
|
1013
|