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