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