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