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