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