Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 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 * |
| 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 * |
| 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 | } |
Leon Scroggins III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 108 | /*@i1@*/memset(Private, '\0', sizeof(GifFilePrivateType)); |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 109 | 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 III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 127 | Private->gif89 = false; |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 128 | |
| 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 | ******************************************************************************/ |
| 141 | GifFileType * |
| 142 | EGifOpen(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 III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 164 | memset(Private, '\0', sizeof(GifFilePrivateType)); |
| 165 | |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 166 | 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 III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 193 | const char * |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 194 | EGifGetGifVersion(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 | ******************************************************************************/ |
| 240 | void 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 | ******************************************************************************/ |
| 250 | static 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 | ******************************************************************************/ |
| 264 | int |
| 265 | EGifPutScreenDesc(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 III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 274 | const char *write_version; |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 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) { |
Leon Scroggins III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 386 | if (GifFile->Image.ColorMap != NULL) { |
| 387 | GifFreeMapObject(GifFile->Image.ColorMap); |
| 388 | GifFile->Image.ColorMap = NULL; |
| 389 | } |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 390 | 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 | ******************************************************************************/ |
| 444 | int |
| 445 | EGifPutLine(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 | ******************************************************************************/ |
| 477 | int |
| 478 | EGifPutPixel(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 | ******************************************************************************/ |
| 504 | int |
| 505 | EGifPutComment(GifFileType *GifFile, const char *Comment) |
| 506 | { |
Leon Scroggins III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 507 | unsigned int length; |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 508 | 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 | ******************************************************************************/ |
| 547 | int |
| 548 | EGifPutExtensionLeader(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 | ******************************************************************************/ |
| 569 | int |
| 570 | EGifPutExtensionBlock(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 | ******************************************************************************/ |
| 593 | int |
| 594 | EGifPutExtensionTrailer(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 | ******************************************************************************/ |
| 618 | int |
| 619 | EGifPutExtension(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 | |
| 652 | size_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 | |
| 669 | int 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 | ******************************************************************************/ |
| 705 | int |
| 706 | EGifPutCode(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 | ******************************************************************************/ |
| 733 | int |
| 734 | EGifPutCodeNext(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 | ******************************************************************************/ |
| 760 | int |
Leon Scroggins III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 761 | EGifCloseFile(GifFileType *GifFile, int *ErrorCode) |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 762 | { |
| 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 III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 775 | if (ErrorCode != NULL) |
| 776 | *ErrorCode = E_GIF_ERR_NOT_WRITEABLE; |
| 777 | free(GifFile); |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 778 | 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 III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 798 | free((char *) Private); |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 799 | } |
| 800 | |
| 801 | if (File && fclose(File) != 0) { |
Leon Scroggins III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 802 | if (ErrorCode != NULL) |
| 803 | *ErrorCode = E_GIF_ERR_CLOSE_FAILED; |
| 804 | free(GifFile); |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 805 | return GIF_ERROR; |
| 806 | } |
| 807 | |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 808 | free(GifFile); |
Leon Scroggins III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 809 | if (ErrorCode != NULL) |
| 810 | *ErrorCode = E_GIF_SUCCEEDED; |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 811 | return GIF_OK; |
| 812 | } |
| 813 | |
| 814 | /****************************************************************************** |
| 815 | Put 2 bytes (a word) into the given file in little-endian order: |
| 816 | ******************************************************************************/ |
| 817 | static int |
| 818 | EGifPutWord(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 | ******************************************************************************/ |
| 833 | static int |
| 834 | EGifSetupCompress(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 | ******************************************************************************/ |
| 880 | static int |
| 881 | EGifCompressLine(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 | ******************************************************************************/ |
| 969 | static int |
| 970 | EGifCompressOutput(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 | ******************************************************************************/ |
| 1017 | static int |
| 1018 | EGifBufferedOutput(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 | |
| 1055 | static int |
| 1056 | EGifWriteExtensions(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 | |
| 1080 | int |
| 1081 | EGifSpew(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 III | df596d3 | 2017-03-13 14:03:21 -0400 | [diff] [blame] | 1150 | if (EGifCloseFile(GifFileOut, NULL) == GIF_ERROR) |
Keith Platfoot | 9b8f860 | 2015-01-07 10:16:21 -0800 | [diff] [blame] | 1151 | return (GIF_ERROR); |
| 1152 | |
| 1153 | return (GIF_OK); |
| 1154 | } |
| 1155 | |
| 1156 | /* end */ |