| 1 | /****************************************************************************** |
| 2 | |
| 3 | egif_lib.c - GIF encoding |
| 4 | |
| 5 | The functions here and in dgif_lib.c are partitioned carefully so that |
| 6 | if you only require one of read and write capability, only one of these |
| 7 | two modules will be linked. Preserve this property! |
| 8 | |
| 9 | SPDX-License-Identifier: MIT |
| 10 | |
| 11 | *****************************************************************************/ |
| 12 | |
| 13 | #include <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@*/ |
| 24 | static const GifPixelType CodeMask[] = { |
| 25 | 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff |
| 26 | }; |
| 27 | /*@-charint@*/ |
| 28 | |
| 29 | static int EGifPutWord(int Word, GifFileType * GifFile); |
| 30 | static int EGifSetupCompress(GifFileType * GifFile); |
| 31 | static int EGifCompressLine(GifFileType * GifFile, GifPixelType * Line, |
| 32 | int LineLen); |
| 33 | static int EGifCompressOutput(GifFileType * GifFile, int Code); |
| 34 | static 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 | ******************************************************************************/ |
| 54 | GifFileType * |
| 55 | EGifOpenFileName(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 | ******************************************************************************/ |
| 86 | GifFileType * |
| 87 | EGifOpenFileHandle(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 | ******************************************************************************/ |
| 140 | GifFileType * |
| 141 | EGifOpen(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 | ******************************************************************************/ |
| 192 | const char * |
| 193 | EGifGetGifVersion(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 | ******************************************************************************/ |
| 239 | void 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 | ******************************************************************************/ |
| 249 | static 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 | ******************************************************************************/ |
| 263 | int |
| 264 | EGifPutScreenDesc(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 | ******************************************************************************/ |
| 357 | int |
| 358 | EGifPutImageDesc(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 | ******************************************************************************/ |
| 446 | int |
| 447 | EGifPutLine(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 | ******************************************************************************/ |
| 479 | int |
| 480 | EGifPutPixel(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 | ******************************************************************************/ |
| 506 | int |
| 507 | (GifFileType *GifFile, const char *) |
| 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 | ******************************************************************************/ |
| 549 | int |
| 550 | EGifPutExtensionLeader(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 | ******************************************************************************/ |
| 571 | int |
| 572 | EGifPutExtensionBlock(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 | ******************************************************************************/ |
| 595 | int |
| 596 | EGifPutExtensionTrailer(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 | ******************************************************************************/ |
| 620 | int |
| 621 | EGifPutExtension(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 | |
| 654 | size_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 | |
| 671 | int 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 | ******************************************************************************/ |
| 707 | int |
| 708 | EGifPutCode(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 | ******************************************************************************/ |
| 735 | int |
| 736 | EGifPutCodeNext(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 | ******************************************************************************/ |
| 762 | int |
| 763 | EGifCloseFile(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 | ******************************************************************************/ |
| 820 | static int |
| 821 | EGifPutWord(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 | ******************************************************************************/ |
| 836 | static int |
| 837 | EGifSetupCompress(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 | ******************************************************************************/ |
| 883 | static int |
| 884 | EGifCompressLine(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 | ******************************************************************************/ |
| 971 | static int |
| 972 | EGifCompressOutput(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 | ******************************************************************************/ |
| 1019 | static int |
| 1020 | EGifBufferedOutput(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 | |
| 1057 | static int |
| 1058 | EGifWriteExtensions(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 | |
| 1081 | int |
| 1082 | EGifSpew(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 | |