1/* 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3% % 4% % 5% % 6% GGGG IIIII FFFFF % 7% G I F % 8% G GG I FFF % 9% G G I F % 10% GGG IIIII F % 11% % 12% % 13% Read/Write Compuserv Graphics Interchange Format % 14% % 15% Software Design % 16% Cristy % 17% July 1992 % 18% % 19% % 20% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 21% dedicated to making software imaging solutions freely available. % 22% % 23% You may not use this file except in compliance with the License. You may % 24% obtain a copy of the License at % 25% % 26% http://www.imagemagick.org/script/license.php % 27% % 28% Unless required by applicable law or agreed to in writing, software % 29% distributed under the License is distributed on an "AS IS" BASIS, % 30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31% See the License for the specific language governing permissions and % 32% limitations under the License. % 33% % 34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35% 36% 37*/ 38 39/* 40 Include declarations. 41*/ 42#include "MagickCore/studio.h" 43#include "MagickCore/attribute.h" 44#include "MagickCore/blob.h" 45#include "MagickCore/blob-private.h" 46#include "MagickCore/cache.h" 47#include "MagickCore/color.h" 48#include "MagickCore/color-private.h" 49#include "MagickCore/colormap.h" 50#include "MagickCore/colormap-private.h" 51#include "MagickCore/colorspace.h" 52#include "MagickCore/colorspace-private.h" 53#include "MagickCore/exception.h" 54#include "MagickCore/exception-private.h" 55#include "MagickCore/image.h" 56#include "MagickCore/image-private.h" 57#include "MagickCore/list.h" 58#include "MagickCore/profile.h" 59#include "MagickCore/magick.h" 60#include "MagickCore/memory_.h" 61#include "MagickCore/monitor.h" 62#include "MagickCore/monitor-private.h" 63#include "MagickCore/option.h" 64#include "MagickCore/pixel.h" 65#include "MagickCore/pixel-accessor.h" 66#include "MagickCore/property.h" 67#include "MagickCore/quantize.h" 68#include "MagickCore/quantum-private.h" 69#include "MagickCore/static.h" 70#include "MagickCore/string_.h" 71#include "MagickCore/string-private.h" 72#include "MagickCore/module.h" 73 74/* 75 Define declarations. 76*/ 77#define MaximumLZWBits 12 78#define MaximumLZWCode (1UL << MaximumLZWBits) 79 80/* 81 Typdef declarations. 82*/ 83typedef struct _LZWCodeInfo 84{ 85 unsigned char 86 buffer[280]; 87 88 size_t 89 count, 90 bit; 91 92 MagickBooleanType 93 eof; 94} LZWCodeInfo; 95 96typedef struct _LZWStack 97{ 98 size_t 99 *codes, 100 *index, 101 *top; 102} LZWStack; 103 104typedef struct _LZWInfo 105{ 106 Image 107 *image; 108 109 LZWStack 110 *stack; 111 112 MagickBooleanType 113 genesis; 114 115 size_t 116 data_size, 117 maximum_data_value, 118 clear_code, 119 end_code, 120 bits, 121 first_code, 122 last_code, 123 maximum_code, 124 slot, 125 *table[2]; 126 127 LZWCodeInfo 128 code_info; 129} LZWInfo; 130 131/* 132 Forward declarations. 133*/ 134static inline int 135 GetNextLZWCode(LZWInfo *,const size_t); 136 137static MagickBooleanType 138 WriteGIFImage(const ImageInfo *,Image *,ExceptionInfo *); 139 140static ssize_t 141 ReadBlobBlock(Image *,unsigned char *); 142 143/* 144%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 145% % 146% % 147% % 148% D e c o d e I m a g e % 149% % 150% % 151% % 152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 153% 154% DecodeImage uncompresses an image via GIF-coding. 155% 156% The format of the DecodeImage method is: 157% 158% MagickBooleanType DecodeImage(Image *image,const ssize_t opacity) 159% 160% A description of each parameter follows: 161% 162% o image: the address of a structure of type Image. 163% 164% o opacity: The colormap index associated with the transparent color. 165% 166*/ 167 168static LZWInfo *RelinquishLZWInfo(LZWInfo *lzw_info) 169{ 170 if (lzw_info->table[0] != (size_t *) NULL) 171 lzw_info->table[0]=(size_t *) RelinquishMagickMemory( 172 lzw_info->table[0]); 173 if (lzw_info->table[1] != (size_t *) NULL) 174 lzw_info->table[1]=(size_t *) RelinquishMagickMemory( 175 lzw_info->table[1]); 176 if (lzw_info->stack != (LZWStack *) NULL) 177 { 178 if (lzw_info->stack->codes != (size_t *) NULL) 179 lzw_info->stack->codes=(size_t *) RelinquishMagickMemory( 180 lzw_info->stack->codes); 181 lzw_info->stack=(LZWStack *) RelinquishMagickMemory(lzw_info->stack); 182 } 183 lzw_info=(LZWInfo *) RelinquishMagickMemory(lzw_info); 184 return((LZWInfo *) NULL); 185} 186 187static inline void ResetLZWInfo(LZWInfo *lzw_info) 188{ 189 size_t 190 one; 191 192 lzw_info->bits=lzw_info->data_size+1; 193 one=1; 194 lzw_info->maximum_code=one << lzw_info->bits; 195 lzw_info->slot=lzw_info->maximum_data_value+3; 196 lzw_info->genesis=MagickTrue; 197} 198 199static LZWInfo *AcquireLZWInfo(Image *image,const size_t data_size) 200{ 201 LZWInfo 202 *lzw_info; 203 204 register ssize_t 205 i; 206 207 size_t 208 one; 209 210 lzw_info=(LZWInfo *) AcquireMagickMemory(sizeof(*lzw_info)); 211 if (lzw_info == (LZWInfo *) NULL) 212 return((LZWInfo *) NULL); 213 (void) ResetMagickMemory(lzw_info,0,sizeof(*lzw_info)); 214 lzw_info->image=image; 215 lzw_info->data_size=data_size; 216 one=1; 217 lzw_info->maximum_data_value=(one << data_size)-1; 218 lzw_info->clear_code=lzw_info->maximum_data_value+1; 219 lzw_info->end_code=lzw_info->maximum_data_value+2; 220 lzw_info->table[0]=(size_t *) AcquireQuantumMemory(MaximumLZWCode, 221 sizeof(**lzw_info->table)); 222 lzw_info->table[1]=(size_t *) AcquireQuantumMemory(MaximumLZWCode, 223 sizeof(**lzw_info->table)); 224 if ((lzw_info->table[0] == (size_t *) NULL) || 225 (lzw_info->table[1] == (size_t *) NULL)) 226 { 227 lzw_info=RelinquishLZWInfo(lzw_info); 228 return((LZWInfo *) NULL); 229 } 230 for (i=0; i <= (ssize_t) lzw_info->maximum_data_value; i++) 231 { 232 lzw_info->table[0][i]=0; 233 lzw_info->table[1][i]=(size_t) i; 234 } 235 ResetLZWInfo(lzw_info); 236 lzw_info->code_info.buffer[0]='\0'; 237 lzw_info->code_info.buffer[1]='\0'; 238 lzw_info->code_info.count=2; 239 lzw_info->code_info.bit=8*lzw_info->code_info.count; 240 lzw_info->code_info.eof=MagickFalse; 241 lzw_info->genesis=MagickTrue; 242 lzw_info->stack=(LZWStack *) AcquireMagickMemory(sizeof(*lzw_info->stack)); 243 if (lzw_info->stack == (LZWStack *) NULL) 244 { 245 lzw_info=RelinquishLZWInfo(lzw_info); 246 return((LZWInfo *) NULL); 247 } 248 lzw_info->stack->codes=(size_t *) AcquireQuantumMemory(2UL* 249 MaximumLZWCode,sizeof(*lzw_info->stack->codes)); 250 if (lzw_info->stack->codes == (size_t *) NULL) 251 { 252 lzw_info=RelinquishLZWInfo(lzw_info); 253 return((LZWInfo *) NULL); 254 } 255 lzw_info->stack->index=lzw_info->stack->codes; 256 lzw_info->stack->top=lzw_info->stack->codes+2*MaximumLZWCode; 257 return(lzw_info); 258} 259 260static inline int GetNextLZWCode(LZWInfo *lzw_info,const size_t bits) 261{ 262 int 263 code; 264 265 register ssize_t 266 i; 267 268 size_t 269 one; 270 271 while (((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) && 272 (lzw_info->code_info.eof == MagickFalse)) 273 { 274 ssize_t 275 count; 276 277 lzw_info->code_info.buffer[0]=lzw_info->code_info.buffer[ 278 lzw_info->code_info.count-2]; 279 lzw_info->code_info.buffer[1]=lzw_info->code_info.buffer[ 280 lzw_info->code_info.count-1]; 281 lzw_info->code_info.bit-=8*(lzw_info->code_info.count-2); 282 lzw_info->code_info.count=2; 283 count=ReadBlobBlock(lzw_info->image,&lzw_info->code_info.buffer[ 284 lzw_info->code_info.count]); 285 if (count > 0) 286 lzw_info->code_info.count+=count; 287 else 288 lzw_info->code_info.eof=MagickTrue; 289 } 290 if ((lzw_info->code_info.bit+bits) > (8*lzw_info->code_info.count)) 291 return(-1); 292 code=0; 293 one=1; 294 for (i=0; i < (ssize_t) bits; i++) 295 { 296 code|=((lzw_info->code_info.buffer[lzw_info->code_info.bit/8] & 297 (one << (lzw_info->code_info.bit % 8))) != 0) << i; 298 lzw_info->code_info.bit++; 299 } 300 return(code); 301} 302 303static inline int PopLZWStack(LZWStack *stack_info) 304{ 305 if (stack_info->index <= stack_info->codes) 306 return(-1); 307 stack_info->index--; 308 return((int) *stack_info->index); 309} 310 311static inline void PushLZWStack(LZWStack *stack_info,const size_t value) 312{ 313 if (stack_info->index >= stack_info->top) 314 return; 315 *stack_info->index=value; 316 stack_info->index++; 317} 318 319static int ReadBlobLZWByte(LZWInfo *lzw_info) 320{ 321 int 322 code; 323 324 size_t 325 one, 326 value; 327 328 ssize_t 329 count; 330 331 if (lzw_info->stack->index != lzw_info->stack->codes) 332 return(PopLZWStack(lzw_info->stack)); 333 if (lzw_info->genesis != MagickFalse) 334 { 335 lzw_info->genesis=MagickFalse; 336 do 337 { 338 lzw_info->first_code=(size_t) GetNextLZWCode(lzw_info,lzw_info->bits); 339 lzw_info->last_code=lzw_info->first_code; 340 } while (lzw_info->first_code == lzw_info->clear_code); 341 return((int) lzw_info->first_code); 342 } 343 code=GetNextLZWCode(lzw_info,lzw_info->bits); 344 if (code < 0) 345 return(code); 346 if ((size_t) code == lzw_info->clear_code) 347 { 348 ResetLZWInfo(lzw_info); 349 return(ReadBlobLZWByte(lzw_info)); 350 } 351 if ((size_t) code == lzw_info->end_code) 352 return(-1); 353 if ((size_t) code < lzw_info->slot) 354 value=(size_t) code; 355 else 356 { 357 PushLZWStack(lzw_info->stack,lzw_info->first_code); 358 value=lzw_info->last_code; 359 } 360 count=0; 361 while (value > lzw_info->maximum_data_value) 362 { 363 if ((size_t) count > MaximumLZWCode) 364 return(-1); 365 count++; 366 if ((size_t) value > MaximumLZWCode) 367 return(-1); 368 PushLZWStack(lzw_info->stack,lzw_info->table[1][value]); 369 value=lzw_info->table[0][value]; 370 } 371 lzw_info->first_code=lzw_info->table[1][value]; 372 PushLZWStack(lzw_info->stack,lzw_info->first_code); 373 one=1; 374 if (lzw_info->slot < MaximumLZWCode) 375 { 376 lzw_info->table[0][lzw_info->slot]=lzw_info->last_code; 377 lzw_info->table[1][lzw_info->slot]=lzw_info->first_code; 378 lzw_info->slot++; 379 if ((lzw_info->slot >= lzw_info->maximum_code) && 380 (lzw_info->bits < MaximumLZWBits)) 381 { 382 lzw_info->bits++; 383 lzw_info->maximum_code=one << lzw_info->bits; 384 } 385 } 386 lzw_info->last_code=(size_t) code; 387 return(PopLZWStack(lzw_info->stack)); 388} 389 390static MagickBooleanType DecodeImage(Image *image,const ssize_t opacity, 391 ExceptionInfo *exception) 392{ 393 int 394 c; 395 396 LZWInfo 397 *lzw_info; 398 399 size_t 400 pass; 401 402 ssize_t 403 index, 404 offset, 405 y; 406 407 unsigned char 408 data_size; 409 410 /* 411 Allocate decoder tables. 412 */ 413 assert(image != (Image *) NULL); 414 assert(image->signature == MagickCoreSignature); 415 if (image->debug != MagickFalse) 416 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 417 data_size=(unsigned char) ReadBlobByte(image); 418 if (data_size > MaximumLZWBits) 419 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename); 420 lzw_info=AcquireLZWInfo(image,data_size); 421 if (lzw_info == (LZWInfo *) NULL) 422 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 423 image->filename); 424 pass=0; 425 offset=0; 426 for (y=0; y < (ssize_t) image->rows; y++) 427 { 428 register ssize_t 429 x; 430 431 register Quantum 432 *magick_restrict q; 433 434 q=QueueAuthenticPixels(image,0,offset,image->columns,1,exception); 435 if (q == (Quantum *) NULL) 436 break; 437 for (x=0; x < (ssize_t) image->columns; ) 438 { 439 c=ReadBlobLZWByte(lzw_info); 440 if (c < 0) 441 break; 442 index=ConstrainColormapIndex(image,(ssize_t) c,exception); 443 SetPixelIndex(image,(Quantum) index,q); 444 SetPixelViaPixelInfo(image,image->colormap+index,q); 445 SetPixelAlpha(image,index == opacity ? TransparentAlpha : OpaqueAlpha,q); 446 x++; 447 q+=GetPixelChannels(image); 448 } 449 if (SyncAuthenticPixels(image,exception) == MagickFalse) 450 break; 451 if (x < (ssize_t) image->columns) 452 break; 453 if (image->interlace == NoInterlace) 454 offset++; 455 else 456 { 457 switch (pass) 458 { 459 case 0: 460 default: 461 { 462 offset+=8; 463 break; 464 } 465 case 1: 466 { 467 offset+=8; 468 break; 469 } 470 case 2: 471 { 472 offset+=4; 473 break; 474 } 475 case 3: 476 { 477 offset+=2; 478 break; 479 } 480 } 481 if ((pass == 0) && (offset >= (ssize_t) image->rows)) 482 { 483 pass++; 484 offset=4; 485 } 486 if ((pass == 1) && (offset >= (ssize_t) image->rows)) 487 { 488 pass++; 489 offset=2; 490 } 491 if ((pass == 2) && (offset >= (ssize_t) image->rows)) 492 { 493 pass++; 494 offset=1; 495 } 496 } 497 } 498 lzw_info=RelinquishLZWInfo(lzw_info); 499 if (y < (ssize_t) image->rows) 500 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename); 501 return(MagickTrue); 502} 503 504/* 505%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 506% % 507% % 508% % 509% E n c o d e I m a g e % 510% % 511% % 512% % 513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 514% 515% EncodeImage compresses an image via GIF-coding. 516% 517% The format of the EncodeImage method is: 518% 519% MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image, 520% const size_t data_size) 521% 522% A description of each parameter follows: 523% 524% o image_info: the image info. 525% 526% o image: the address of a structure of type Image. 527% 528% o data_size: The number of bits in the compressed packet. 529% 530*/ 531static MagickBooleanType EncodeImage(const ImageInfo *image_info,Image *image, 532 const size_t data_size,ExceptionInfo *exception) 533{ 534#define MaxCode(number_bits) ((one << (number_bits))-1) 535#define MaxHashTable 5003 536#define MaxGIFBits 12UL 537#define MaxGIFTable (1UL << MaxGIFBits) 538#define GIFOutputCode(code) \ 539{ \ 540 /* \ 541 Emit a code. \ 542 */ \ 543 if (bits > 0) \ 544 datum|=(code) << bits; \ 545 else \ 546 datum=code; \ 547 bits+=number_bits; \ 548 while (bits >= 8) \ 549 { \ 550 /* \ 551 Add a character to current packet. \ 552 */ \ 553 packet[length++]=(unsigned char) (datum & 0xff); \ 554 if (length >= 254) \ 555 { \ 556 (void) WriteBlobByte(image,(unsigned char) length); \ 557 (void) WriteBlob(image,length,packet); \ 558 length=0; \ 559 } \ 560 datum>>=8; \ 561 bits-=8; \ 562 } \ 563 if (free_code > max_code) \ 564 { \ 565 number_bits++; \ 566 if (number_bits == MaxGIFBits) \ 567 max_code=MaxGIFTable; \ 568 else \ 569 max_code=MaxCode(number_bits); \ 570 } \ 571} 572 573 Quantum 574 index; 575 576 short 577 *hash_code, 578 *hash_prefix, 579 waiting_code; 580 581 size_t 582 bits, 583 clear_code, 584 datum, 585 end_of_information_code, 586 free_code, 587 length, 588 max_code, 589 next_pixel, 590 number_bits, 591 one, 592 pass; 593 594 ssize_t 595 displacement, 596 offset, 597 k, 598 y; 599 600 unsigned char 601 *packet, 602 *hash_suffix; 603 604 /* 605 Allocate encoder tables. 606 */ 607 assert(image != (Image *) NULL); 608 one=1; 609 packet=(unsigned char *) AcquireQuantumMemory(256,sizeof(*packet)); 610 hash_code=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_code)); 611 hash_prefix=(short *) AcquireQuantumMemory(MaxHashTable,sizeof(*hash_prefix)); 612 hash_suffix=(unsigned char *) AcquireQuantumMemory(MaxHashTable, 613 sizeof(*hash_suffix)); 614 if ((packet == (unsigned char *) NULL) || (hash_code == (short *) NULL) || 615 (hash_prefix == (short *) NULL) || 616 (hash_suffix == (unsigned char *) NULL)) 617 { 618 if (packet != (unsigned char *) NULL) 619 packet=(unsigned char *) RelinquishMagickMemory(packet); 620 if (hash_code != (short *) NULL) 621 hash_code=(short *) RelinquishMagickMemory(hash_code); 622 if (hash_prefix != (short *) NULL) 623 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix); 624 if (hash_suffix != (unsigned char *) NULL) 625 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix); 626 return(MagickFalse); 627 } 628 /* 629 Initialize GIF encoder. 630 */ 631 (void) ResetMagickMemory(hash_code,0,MaxHashTable*sizeof(*hash_code)); 632 (void) ResetMagickMemory(hash_prefix,0,MaxHashTable*sizeof(*hash_prefix)); 633 (void) ResetMagickMemory(hash_suffix,0,MaxHashTable*sizeof(*hash_suffix)); 634 number_bits=data_size; 635 max_code=MaxCode(number_bits); 636 clear_code=((short) one << (data_size-1)); 637 end_of_information_code=clear_code+1; 638 free_code=clear_code+2; 639 length=0; 640 datum=0; 641 bits=0; 642 GIFOutputCode(clear_code); 643 /* 644 Encode pixels. 645 */ 646 offset=0; 647 pass=0; 648 waiting_code=0; 649 for (y=0; y < (ssize_t) image->rows; y++) 650 { 651 register const Quantum 652 *magick_restrict p; 653 654 register ssize_t 655 x; 656 657 p=GetVirtualPixels(image,0,offset,image->columns,1,exception); 658 if (p == (const Quantum *) NULL) 659 break; 660 if (y == 0) 661 { 662 waiting_code=(short) GetPixelIndex(image,p); 663 p+=GetPixelChannels(image); 664 } 665 for (x=(ssize_t) (y == 0 ? 1 : 0); x < (ssize_t) image->columns; x++) 666 { 667 /* 668 Probe hash table. 669 */ 670 index=(Quantum) ((size_t) GetPixelIndex(image,p) & 0xff); 671 p+=GetPixelChannels(image); 672 k=(ssize_t) (((size_t) index << (MaxGIFBits-8))+waiting_code); 673 if (k >= MaxHashTable) 674 k-=MaxHashTable; 675 next_pixel=MagickFalse; 676 displacement=1; 677 if (hash_code[k] > 0) 678 { 679 if ((hash_prefix[k] == waiting_code) && 680 (hash_suffix[k] == (unsigned char) index)) 681 { 682 waiting_code=hash_code[k]; 683 continue; 684 } 685 if (k != 0) 686 displacement=MaxHashTable-k; 687 for ( ; ; ) 688 { 689 k-=displacement; 690 if (k < 0) 691 k+=MaxHashTable; 692 if (hash_code[k] == 0) 693 break; 694 if ((hash_prefix[k] == waiting_code) && 695 (hash_suffix[k] == (unsigned char) index)) 696 { 697 waiting_code=hash_code[k]; 698 next_pixel=MagickTrue; 699 break; 700 } 701 } 702 if (next_pixel != MagickFalse) 703 continue; 704 } 705 GIFOutputCode((size_t) waiting_code); 706 if (free_code < MaxGIFTable) 707 { 708 hash_code[k]=(short) free_code++; 709 hash_prefix[k]=waiting_code; 710 hash_suffix[k]=(unsigned char) index; 711 } 712 else 713 { 714 /* 715 Fill the hash table with empty entries. 716 */ 717 for (k=0; k < MaxHashTable; k++) 718 hash_code[k]=0; 719 /* 720 Reset compressor and issue a clear code. 721 */ 722 free_code=clear_code+2; 723 GIFOutputCode(clear_code); 724 number_bits=data_size; 725 max_code=MaxCode(number_bits); 726 } 727 waiting_code=(short) index; 728 } 729 if (image_info->interlace == NoInterlace) 730 offset++; 731 else 732 switch (pass) 733 { 734 case 0: 735 default: 736 { 737 offset+=8; 738 if (offset >= (ssize_t) image->rows) 739 { 740 pass++; 741 offset=4; 742 } 743 break; 744 } 745 case 1: 746 { 747 offset+=8; 748 if (offset >= (ssize_t) image->rows) 749 { 750 pass++; 751 offset=2; 752 } 753 break; 754 } 755 case 2: 756 { 757 offset+=4; 758 if (offset >= (ssize_t) image->rows) 759 { 760 pass++; 761 offset=1; 762 } 763 break; 764 } 765 case 3: 766 { 767 offset+=2; 768 break; 769 } 770 } 771 } 772 /* 773 Flush out the buffered code. 774 */ 775 GIFOutputCode((size_t) waiting_code); 776 GIFOutputCode(end_of_information_code); 777 if (bits > 0) 778 { 779 /* 780 Add a character to current packet. 781 */ 782 packet[length++]=(unsigned char) (datum & 0xff); 783 if (length >= 254) 784 { 785 (void) WriteBlobByte(image,(unsigned char) length); 786 (void) WriteBlob(image,length,packet); 787 length=0; 788 } 789 } 790 /* 791 Flush accumulated data. 792 */ 793 if (length > 0) 794 { 795 (void) WriteBlobByte(image,(unsigned char) length); 796 (void) WriteBlob(image,length,packet); 797 } 798 /* 799 Free encoder memory. 800 */ 801 hash_suffix=(unsigned char *) RelinquishMagickMemory(hash_suffix); 802 hash_prefix=(short *) RelinquishMagickMemory(hash_prefix); 803 hash_code=(short *) RelinquishMagickMemory(hash_code); 804 packet=(unsigned char *) RelinquishMagickMemory(packet); 805 return(MagickTrue); 806} 807 808/* 809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 810% % 811% % 812% % 813% I s G I F % 814% % 815% % 816% % 817%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 818% 819% IsGIF() returns MagickTrue if the image format type, identified by the 820% magick string, is GIF. 821% 822% The format of the IsGIF method is: 823% 824% MagickBooleanType IsGIF(const unsigned char *magick,const size_t length) 825% 826% A description of each parameter follows: 827% 828% o magick: compare image format pattern against these bytes. 829% 830% o length: Specifies the length of the magick string. 831% 832*/ 833static MagickBooleanType IsGIF(const unsigned char *magick,const size_t length) 834{ 835 if (length < 4) 836 return(MagickFalse); 837 if (LocaleNCompare((char *) magick,"GIF8",4) == 0) 838 return(MagickTrue); 839 return(MagickFalse); 840} 841 842/* 843%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 844% % 845% % 846% % 847+ R e a d B l o b B l o c k % 848% % 849% % 850% % 851%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 852% 853% ReadBlobBlock() reads data from the image file and returns it. The 854% amount of data is determined by first reading a count byte. The number 855% of bytes read is returned. 856% 857% The format of the ReadBlobBlock method is: 858% 859% ssize_t ReadBlobBlock(Image *image,unsigned char *data) 860% 861% A description of each parameter follows: 862% 863% o image: the image. 864% 865% o data: Specifies an area to place the information requested from 866% the file. 867% 868*/ 869static ssize_t ReadBlobBlock(Image *image,unsigned char *data) 870{ 871 ssize_t 872 count; 873 874 unsigned char 875 block_count; 876 877 assert(image != (Image *) NULL); 878 assert(image->signature == MagickCoreSignature); 879 assert(data != (unsigned char *) NULL); 880 count=ReadBlob(image,1,&block_count); 881 if (count != 1) 882 return(0); 883 count=ReadBlob(image,(size_t) block_count,data); 884 if (count != (ssize_t) block_count) 885 return(0); 886 return(count); 887} 888 889/* 890%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 891% % 892% % 893% % 894% R e a d G I F I m a g e % 895% % 896% % 897% % 898%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 899% 900% ReadGIFImage() reads a Compuserve Graphics image file and returns it. 901% It allocates the memory necessary for the new Image structure and returns a 902% pointer to the new image. 903% 904% The format of the ReadGIFImage method is: 905% 906% Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception) 907% 908% A description of each parameter follows: 909% 910% o image_info: the image info. 911% 912% o exception: return any errors or warnings in this structure. 913% 914*/ 915static MagickBooleanType PingGIFImage(Image *image,ExceptionInfo *exception) 916{ 917 unsigned char 918 buffer[256], 919 length, 920 data_size; 921 922 assert(image != (Image *) NULL); 923 assert(image->signature == MagickCoreSignature); 924 if (image->debug != MagickFalse) 925 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 926 if (ReadBlob(image,1,&data_size) != 1) 927 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename); 928 if (data_size > MaximumLZWBits) 929 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename); 930 if (ReadBlob(image,1,&length) != 1) 931 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename); 932 while (length != 0) 933 { 934 if (ReadBlob(image,length,buffer) != (ssize_t) length) 935 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename); 936 if (ReadBlob(image,1,&length) != 1) 937 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename); 938 } 939 return(MagickTrue); 940} 941 942static Image *ReadGIFImage(const ImageInfo *image_info,ExceptionInfo *exception) 943{ 944#define BitSet(byte,bit) (((byte) & (bit)) == (bit)) 945#define LSBFirstOrder(x,y) (((y) << 8) | (x)) 946 947 Image 948 *image, 949 *meta_image; 950 951 int 952 number_extensionss=0; 953 954 MagickBooleanType 955 status; 956 957 RectangleInfo 958 page; 959 960 register ssize_t 961 i; 962 963 register unsigned char 964 *p; 965 966 size_t 967 delay, 968 dispose, 969 duration, 970 global_colors, 971 image_count, 972 iterations, 973 one; 974 975 ssize_t 976 count, 977 opacity; 978 979 unsigned char 980 background, 981 c, 982 flag, 983 *global_colormap, 984 buffer[257]; 985 986 /* 987 Open image file. 988 */ 989 assert(image_info != (const ImageInfo *) NULL); 990 assert(image_info->signature == MagickCoreSignature); 991 if (image_info->debug != MagickFalse) 992 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 993 image_info->filename); 994 assert(exception != (ExceptionInfo *) NULL); 995 assert(exception->signature == MagickCoreSignature); 996 image=AcquireImage(image_info,exception); 997 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 998 if (status == MagickFalse) 999 { 1000 image=DestroyImageList(image); 1001 return((Image *) NULL); 1002 } 1003 /* 1004 Determine if this a GIF file. 1005 */ 1006 count=ReadBlob(image,6,buffer); 1007 if ((count != 6) || ((LocaleNCompare((char *) buffer,"GIF87",5) != 0) && 1008 (LocaleNCompare((char *) buffer,"GIF89",5) != 0))) 1009 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 1010 page.width=ReadBlobLSBShort(image); 1011 page.height=ReadBlobLSBShort(image); 1012 flag=(unsigned char) ReadBlobByte(image); 1013 background=(unsigned char) ReadBlobByte(image); 1014 c=(unsigned char) ReadBlobByte(image); /* reserved */ 1015 one=1; 1016 global_colors=one << (((size_t) flag & 0x07)+1); 1017 global_colormap=(unsigned char *) AcquireQuantumMemory((size_t) 1018 MagickMax(global_colors,256),3UL*sizeof(*global_colormap)); 1019 if (global_colormap == (unsigned char *) NULL) 1020 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 1021 if (BitSet((int) flag,0x80) != 0) 1022 { 1023 count=ReadBlob(image,(size_t) (3*global_colors),global_colormap); 1024 if (count != (ssize_t) (3*global_colors)) 1025 { 1026 global_colormap=(unsigned char *) RelinquishMagickMemory( 1027 global_colormap); 1028 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); 1029 } 1030 } 1031 delay=0; 1032 dispose=0; 1033 duration=0; 1034 iterations=1; 1035 opacity=(-1); 1036 image_count=0; 1037 meta_image=AcquireImage(image_info,exception); /* metadata container */ 1038 for ( ; ; ) 1039 { 1040 count=ReadBlob(image,1,&c); 1041 if (count != 1) 1042 break; 1043 if (c == (unsigned char) ';') 1044 break; /* terminator */ 1045 if (c == (unsigned char) '!') 1046 { 1047 /* 1048 GIF Extension block. 1049 */ 1050 count=ReadBlob(image,1,&c); 1051 if (count != 1) 1052 { 1053 global_colormap=(unsigned char *) RelinquishMagickMemory( 1054 global_colormap); 1055 ThrowReaderException(CorruptImageError, 1056 "UnableToReadExtensionBlock"); 1057 } 1058 switch (c) 1059 { 1060 case 0xf9: 1061 { 1062 /* 1063 Read graphics control extension. 1064 */ 1065 while (ReadBlobBlock(image,buffer) != 0) ; 1066 dispose=(size_t) (buffer[0] >> 2); 1067 delay=(size_t) ((buffer[2] << 8) | buffer[1]); 1068 if ((ssize_t) (buffer[0] & 0x01) == 0x01) 1069 opacity=(ssize_t) buffer[3]; 1070 break; 1071 } 1072 case 0xfe: 1073 { 1074 char 1075 *comments; 1076 1077 size_t 1078 length; 1079 1080 /* 1081 Read comment extension. 1082 */ 1083 comments=AcquireString((char *) NULL); 1084 for (length=0; ; length+=count) 1085 { 1086 count=(ssize_t) ReadBlobBlock(image,buffer); 1087 if (count == 0) 1088 break; 1089 buffer[count]='\0'; 1090 (void) ConcatenateString(&comments,(const char *) buffer); 1091 } 1092 (void) SetImageProperty(meta_image,"comment",comments,exception); 1093 comments=DestroyString(comments); 1094 break; 1095 } 1096 case 0xff: 1097 { 1098 MagickBooleanType 1099 loop; 1100 1101 /* 1102 Read Netscape Loop extension. 1103 */ 1104 loop=MagickFalse; 1105 if (ReadBlobBlock(image,buffer) != 0) 1106 loop=LocaleNCompare((char *) buffer,"NETSCAPE2.0",11) == 0 ? 1107 MagickTrue : MagickFalse; 1108 if (loop != MagickFalse) 1109 { 1110 while (ReadBlobBlock(image,buffer) != 0) 1111 iterations=(size_t) ((buffer[2] << 8) | buffer[1]); 1112 break; 1113 } 1114 else 1115 { 1116 char 1117 name[MagickPathExtent]; 1118 1119 int 1120 block_length, 1121 info_length, 1122 reserved_length; 1123 1124 MagickBooleanType 1125 i8bim, 1126 icc, 1127 iptc, 1128 magick; 1129 1130 StringInfo 1131 *profile; 1132 1133 unsigned char 1134 *info; 1135 1136 /* 1137 Store GIF application extension as a generic profile. 1138 */ 1139 icc=LocaleNCompare((char *) buffer,"ICCRGBG1012",11) == 0 ? 1140 MagickTrue : MagickFalse; 1141 magick=LocaleNCompare((char *) buffer,"ImageMagick",11) == 0 ? 1142 MagickTrue : MagickFalse; 1143 i8bim=LocaleNCompare((char *) buffer,"MGK8BIM0000",11) == 0 ? 1144 MagickTrue : MagickFalse; 1145 iptc=LocaleNCompare((char *) buffer,"MGKIPTC0000",11) == 0 ? 1146 MagickTrue : MagickFalse; 1147 number_extensionss++; 1148 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1149 " Reading GIF application extension"); 1150 info=(unsigned char *) AcquireQuantumMemory(255UL, 1151 sizeof(*info)); 1152 if (info == (unsigned char *) NULL) 1153 ThrowReaderException(ResourceLimitError, 1154 "MemoryAllocationFailed"); 1155 reserved_length=255; 1156 for (info_length=0; ; ) 1157 { 1158 block_length=(int) ReadBlobBlock(image,&info[info_length]); 1159 if (block_length == 0) 1160 break; 1161 info_length+=block_length; 1162 if (info_length > (reserved_length-255)) 1163 { 1164 reserved_length+=4096; 1165 info=(unsigned char *) ResizeQuantumMemory(info,(size_t) 1166 reserved_length,sizeof(*info)); 1167 if (info == (unsigned char *) NULL) 1168 ThrowReaderException(ResourceLimitError, 1169 "MemoryAllocationFailed"); 1170 } 1171 } 1172 profile=BlobToStringInfo(info,(size_t) info_length); 1173 if (profile == (StringInfo *) NULL) 1174 ThrowReaderException(ResourceLimitError, 1175 "MemoryAllocationFailed"); 1176 if (i8bim != MagickFalse) 1177 (void) CopyMagickString(name,"8bim",sizeof(name)); 1178 else if (icc != MagickFalse) 1179 (void) CopyMagickString(name,"icc",sizeof(name)); 1180 else if (iptc != MagickFalse) 1181 (void) CopyMagickString(name,"iptc",sizeof(name)); 1182 else if (magick != MagickFalse) 1183 { 1184 (void) CopyMagickString(name,"magick",sizeof(name)); 1185 meta_image->gamma=StringToDouble((char *) info+6, 1186 (char **) NULL); 1187 } 1188 else 1189 (void) FormatLocaleString(name,sizeof(name),"gif:%.11s", 1190 buffer); 1191 info=(unsigned char *) RelinquishMagickMemory(info); 1192 if (magick == MagickFalse) 1193 (void) SetImageProfile(meta_image,name,profile,exception); 1194 profile=DestroyStringInfo(profile); 1195 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1196 " profile name=%s",name); 1197 } 1198 break; 1199 } 1200 default: 1201 { 1202 while (ReadBlobBlock(image,buffer) != 0) ; 1203 break; 1204 } 1205 } 1206 } 1207 if (c != (unsigned char) ',') 1208 continue; 1209 if (image_count != 0) 1210 { 1211 /* 1212 Allocate next image structure. 1213 */ 1214 AcquireNextImage(image_info,image,exception); 1215 if (GetNextImageInList(image) == (Image *) NULL) 1216 { 1217 image=DestroyImageList(image); 1218 global_colormap=(unsigned char *) RelinquishMagickMemory( 1219 global_colormap); 1220 return((Image *) NULL); 1221 } 1222 image=SyncNextImageInList(image); 1223 } 1224 image_count++; 1225 /* 1226 Read image attributes. 1227 */ 1228 meta_image->scene=image->scene; 1229 (void) CloneImageProperties(image,meta_image); 1230 DestroyImageProperties(meta_image); 1231 (void) CloneImageProfiles(image,meta_image); 1232 DestroyImageProfiles(meta_image); 1233 image->storage_class=PseudoClass; 1234 image->compression=LZWCompression; 1235 page.x=(ssize_t) ReadBlobLSBShort(image); 1236 page.y=(ssize_t) ReadBlobLSBShort(image); 1237 image->columns=ReadBlobLSBShort(image); 1238 image->rows=ReadBlobLSBShort(image); 1239 image->depth=8; 1240 flag=(unsigned char) ReadBlobByte(image); 1241 image->interlace=BitSet((int) flag,0x40) != 0 ? GIFInterlace : NoInterlace; 1242 image->colors=BitSet((int) flag,0x80) == 0 ? global_colors : one << 1243 ((size_t) (flag & 0x07)+1); 1244 if (opacity >= (ssize_t) image->colors) 1245 opacity=(-1); 1246 image->page.width=page.width; 1247 image->page.height=page.height; 1248 image->page.y=page.y; 1249 image->page.x=page.x; 1250 image->delay=delay; 1251 image->ticks_per_second=100; 1252 image->dispose=(DisposeType) dispose; 1253 image->iterations=iterations; 1254 image->alpha_trait=opacity >= 0 ? BlendPixelTrait : UndefinedPixelTrait; 1255 delay=0; 1256 dispose=0; 1257 if ((image->columns == 0) || (image->rows == 0)) 1258 { 1259 global_colormap=(unsigned char *) RelinquishMagickMemory( 1260 global_colormap); 1261 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize"); 1262 } 1263 /* 1264 Inititialize colormap. 1265 */ 1266 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) 1267 { 1268 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap); 1269 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 1270 } 1271 if (BitSet((int) flag,0x80) == 0) 1272 { 1273 /* 1274 Use global colormap. 1275 */ 1276 p=global_colormap; 1277 for (i=0; i < (ssize_t) image->colors; i++) 1278 { 1279 image->colormap[i].red=(double) ScaleCharToQuantum(*p++); 1280 image->colormap[i].green=(double) ScaleCharToQuantum(*p++); 1281 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++); 1282 if (i == opacity) 1283 { 1284 image->colormap[i].alpha=(double) TransparentAlpha; 1285 image->transparent_color=image->colormap[opacity]; 1286 } 1287 } 1288 image->background_color=image->colormap[MagickMin((ssize_t) background, 1289 (ssize_t) image->colors-1)]; 1290 } 1291 else 1292 { 1293 unsigned char 1294 *colormap; 1295 1296 /* 1297 Read local colormap. 1298 */ 1299 colormap=(unsigned char *) AcquireQuantumMemory(image->colors,3* 1300 sizeof(*colormap)); 1301 if (colormap == (unsigned char *) NULL) 1302 { 1303 global_colormap=(unsigned char *) RelinquishMagickMemory( 1304 global_colormap); 1305 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 1306 } 1307 count=ReadBlob(image,(3*image->colors)*sizeof(*colormap),colormap); 1308 if (count != (ssize_t) (3*image->colors)) 1309 { 1310 global_colormap=(unsigned char *) RelinquishMagickMemory( 1311 global_colormap); 1312 colormap=(unsigned char *) RelinquishMagickMemory(colormap); 1313 ThrowReaderException(CorruptImageError, 1314 "InsufficientImageDataInFile"); 1315 } 1316 p=colormap; 1317 for (i=0; i < (ssize_t) image->colors; i++) 1318 { 1319 image->colormap[i].red=(double) ScaleCharToQuantum(*p++); 1320 image->colormap[i].green=(double) ScaleCharToQuantum(*p++); 1321 image->colormap[i].blue=(double) ScaleCharToQuantum(*p++); 1322 if (i == opacity) 1323 image->colormap[i].alpha=(double) TransparentAlpha; 1324 } 1325 colormap=(unsigned char *) RelinquishMagickMemory(colormap); 1326 } 1327 if (image->gamma == 1.0) 1328 { 1329 for (i=0; i < (ssize_t) image->colors; i++) 1330 if (IsPixelInfoGray(image->colormap+i) == MagickFalse) 1331 break; 1332 (void) SetImageColorspace(image,i == (ssize_t) image->colors ? 1333 GRAYColorspace : RGBColorspace,exception); 1334 } 1335 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) 1336 if (image->scene >= (image_info->scene+image_info->number_scenes-1)) 1337 break; 1338 status=SetImageExtent(image,image->columns,image->rows,exception); 1339 if (status == MagickFalse) 1340 return(DestroyImageList(image)); 1341 /* 1342 Decode image. 1343 */ 1344 if (image_info->ping != MagickFalse) 1345 status=PingGIFImage(image,exception); 1346 else 1347 status=DecodeImage(image,opacity,exception); 1348 if ((image_info->ping == MagickFalse) && (status == MagickFalse)) 1349 { 1350 global_colormap=(unsigned char *) RelinquishMagickMemory( 1351 global_colormap); 1352 ThrowReaderException(CorruptImageError,"CorruptImage"); 1353 } 1354 duration+=image->delay*image->iterations; 1355 if (image_info->number_scenes != 0) 1356 if (image->scene >= (image_info->scene+image_info->number_scenes-1)) 1357 break; 1358 opacity=(-1); 1359 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) image->scene-1, 1360 image->scene); 1361 if (status == MagickFalse) 1362 break; 1363 } 1364 image->duration=duration; 1365 meta_image=DestroyImage(meta_image); 1366 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap); 1367 if ((image->columns == 0) || (image->rows == 0)) 1368 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize"); 1369 (void) CloseBlob(image); 1370 return(GetFirstImageInList(image)); 1371} 1372 1373/* 1374%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1375% % 1376% % 1377% % 1378% R e g i s t e r G I F I m a g e % 1379% % 1380% % 1381% % 1382%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1383% 1384% RegisterGIFImage() adds properties for the GIF image format to 1385% the list of supported formats. The properties include the image format 1386% tag, a method to read and/or write the format, whether the format 1387% supports the saving of more than one frame to the same file or blob, 1388% whether the format supports native in-memory I/O, and a brief 1389% description of the format. 1390% 1391% The format of the RegisterGIFImage method is: 1392% 1393% size_t RegisterGIFImage(void) 1394% 1395*/ 1396ModuleExport size_t RegisterGIFImage(void) 1397{ 1398 MagickInfo 1399 *entry; 1400 1401 entry=AcquireMagickInfo("GIF","GIF", 1402 "CompuServe graphics interchange format"); 1403 entry->decoder=(DecodeImageHandler *) ReadGIFImage; 1404 entry->encoder=(EncodeImageHandler *) WriteGIFImage; 1405 entry->magick=(IsImageFormatHandler *) IsGIF; 1406 entry->mime_type=ConstantString("image/gif"); 1407 (void) RegisterMagickInfo(entry); 1408 entry=AcquireMagickInfo("GIF","GIF87", 1409 "CompuServe graphics interchange format"); 1410 entry->decoder=(DecodeImageHandler *) ReadGIFImage; 1411 entry->encoder=(EncodeImageHandler *) WriteGIFImage; 1412 entry->magick=(IsImageFormatHandler *) IsGIF; 1413 entry->flags^=CoderAdjoinFlag; 1414 entry->version=ConstantString("version 87a"); 1415 entry->mime_type=ConstantString("image/gif"); 1416 (void) RegisterMagickInfo(entry); 1417 return(MagickImageCoderSignature); 1418} 1419 1420/* 1421%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1422% % 1423% % 1424% % 1425% U n r e g i s t e r G I F I m a g e % 1426% % 1427% % 1428% % 1429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1430% 1431% UnregisterGIFImage() removes format registrations made by the 1432% GIF module from the list of supported formats. 1433% 1434% The format of the UnregisterGIFImage method is: 1435% 1436% UnregisterGIFImage(void) 1437% 1438*/ 1439ModuleExport void UnregisterGIFImage(void) 1440{ 1441 (void) UnregisterMagickInfo("GIF"); 1442 (void) UnregisterMagickInfo("GIF87"); 1443} 1444 1445/* 1446%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1447% % 1448% % 1449% % 1450% W r i t e G I F I m a g e % 1451% % 1452% % 1453% % 1454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1455% 1456% WriteGIFImage() writes an image to a file in the Compuserve Graphics 1457% image format. 1458% 1459% The format of the WriteGIFImage method is: 1460% 1461% MagickBooleanType WriteGIFImage(const ImageInfo *image_info, 1462% Image *image,ExceptionInfo *exception) 1463% 1464% A description of each parameter follows. 1465% 1466% o image_info: the image info. 1467% 1468% o image: The image. 1469% 1470% o exception: return any errors or warnings in this structure. 1471% 1472*/ 1473static MagickBooleanType WriteGIFImage(const ImageInfo *image_info,Image *image, 1474 ExceptionInfo *exception) 1475{ 1476 int 1477 c; 1478 1479 ImageInfo 1480 *write_info; 1481 1482 MagickBooleanType 1483 status; 1484 1485 MagickOffsetType 1486 scene; 1487 1488 RectangleInfo 1489 page; 1490 1491 register ssize_t 1492 i; 1493 1494 register unsigned char 1495 *q; 1496 1497 size_t 1498 bits_per_pixel, 1499 delay, 1500 length, 1501 one; 1502 1503 ssize_t 1504 j, 1505 opacity; 1506 1507 unsigned char 1508 *colormap, 1509 *global_colormap; 1510 1511 /* 1512 Open output image file. 1513 */ 1514 assert(image_info != (const ImageInfo *) NULL); 1515 assert(image_info->signature == MagickCoreSignature); 1516 assert(image != (Image *) NULL); 1517 assert(image->signature == MagickCoreSignature); 1518 if (image->debug != MagickFalse) 1519 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1520 assert(exception != (ExceptionInfo *) NULL); 1521 assert(exception->signature == MagickCoreSignature); 1522 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 1523 if (status == MagickFalse) 1524 return(status); 1525 /* 1526 Allocate colormap. 1527 */ 1528 global_colormap=(unsigned char *) AcquireQuantumMemory(768UL, 1529 sizeof(*global_colormap)); 1530 colormap=(unsigned char *) AcquireQuantumMemory(768UL,sizeof(*colormap)); 1531 if ((global_colormap == (unsigned char *) NULL) || 1532 (colormap == (unsigned char *) NULL)) 1533 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 1534 for (i=0; i < 768; i++) 1535 colormap[i]=(unsigned char) 0; 1536 /* 1537 Write GIF header. 1538 */ 1539 write_info=CloneImageInfo(image_info); 1540 if (LocaleCompare(write_info->magick,"GIF87") != 0) 1541 (void) WriteBlob(image,6,(unsigned char *) "GIF89a"); 1542 else 1543 { 1544 (void) WriteBlob(image,6,(unsigned char *) "GIF87a"); 1545 write_info->adjoin=MagickFalse; 1546 } 1547 /* 1548 Determine image bounding box. 1549 */ 1550 page.width=image->columns; 1551 if (image->page.width > page.width) 1552 page.width=image->page.width; 1553 page.height=image->rows; 1554 if (image->page.height > page.height) 1555 page.height=image->page.height; 1556 page.x=image->page.x; 1557 page.y=image->page.y; 1558 (void) WriteBlobLSBShort(image,(unsigned short) page.width); 1559 (void) WriteBlobLSBShort(image,(unsigned short) page.height); 1560 /* 1561 Write images to file. 1562 */ 1563 if ((write_info->adjoin != MagickFalse) && 1564 (GetNextImageInList(image) != (Image *) NULL)) 1565 write_info->interlace=NoInterlace; 1566 scene=0; 1567 one=1; 1568 do 1569 { 1570 (void) TransformImageColorspace(image,sRGBColorspace,exception); 1571 opacity=(-1); 1572 if (IsImageOpaque(image,exception) != MagickFalse) 1573 { 1574 if ((image->storage_class == DirectClass) || (image->colors > 256)) 1575 (void) SetImageType(image,PaletteType,exception); 1576 } 1577 else 1578 { 1579 double 1580 alpha, 1581 beta; 1582 1583 /* 1584 Identify transparent colormap index. 1585 */ 1586 if ((image->storage_class == DirectClass) || (image->colors > 256)) 1587 (void) SetImageType(image,PaletteBilevelAlphaType,exception); 1588 for (i=0; i < (ssize_t) image->colors; i++) 1589 if (image->colormap[i].alpha != OpaqueAlpha) 1590 { 1591 if (opacity < 0) 1592 { 1593 opacity=i; 1594 continue; 1595 } 1596 alpha=fabs(image->colormap[i].alpha-TransparentAlpha); 1597 beta=fabs(image->colormap[opacity].alpha-TransparentAlpha); 1598 if (alpha < beta) 1599 opacity=i; 1600 } 1601 if (opacity == -1) 1602 { 1603 (void) SetImageType(image,PaletteBilevelAlphaType,exception); 1604 for (i=0; i < (ssize_t) image->colors; i++) 1605 if (image->colormap[i].alpha != OpaqueAlpha) 1606 { 1607 if (opacity < 0) 1608 { 1609 opacity=i; 1610 continue; 1611 } 1612 alpha=fabs(image->colormap[i].alpha-TransparentAlpha); 1613 beta=fabs(image->colormap[opacity].alpha-TransparentAlpha); 1614 if (alpha < beta) 1615 opacity=i; 1616 } 1617 } 1618 if (opacity >= 0) 1619 { 1620 image->colormap[opacity].red=image->transparent_color.red; 1621 image->colormap[opacity].green=image->transparent_color.green; 1622 image->colormap[opacity].blue=image->transparent_color.blue; 1623 } 1624 } 1625 if ((image->storage_class == DirectClass) || (image->colors > 256)) 1626 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 1627 for (bits_per_pixel=1; bits_per_pixel < 8; bits_per_pixel++) 1628 if ((one << bits_per_pixel) >= image->colors) 1629 break; 1630 q=colormap; 1631 for (i=0; i < (ssize_t) image->colors; i++) 1632 { 1633 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].red)); 1634 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].green)); 1635 *q++=ScaleQuantumToChar(ClampToQuantum(image->colormap[i].blue)); 1636 } 1637 for ( ; i < (ssize_t) (one << bits_per_pixel); i++) 1638 { 1639 *q++=(unsigned char) 0x0; 1640 *q++=(unsigned char) 0x0; 1641 *q++=(unsigned char) 0x0; 1642 } 1643 if ((GetPreviousImageInList(image) == (Image *) NULL) || 1644 (write_info->adjoin == MagickFalse)) 1645 { 1646 /* 1647 Write global colormap. 1648 */ 1649 c=0x80; 1650 c|=(8-1) << 4; /* color resolution */ 1651 c|=(bits_per_pixel-1); /* size of global colormap */ 1652 (void) WriteBlobByte(image,(unsigned char) c); 1653 for (j=0; j < (ssize_t) image->colors; j++) 1654 if (IsPixelInfoEquivalent(&image->background_color,image->colormap+j)) 1655 break; 1656 (void) WriteBlobByte(image,(unsigned char) 1657 (j == (ssize_t) image->colors ? 0 : j)); /* background color */ 1658 (void) WriteBlobByte(image,(unsigned char) 0x00); /* reserved */ 1659 length=(size_t) (3*(one << bits_per_pixel)); 1660 (void) WriteBlob(image,length,colormap); 1661 for (j=0; j < 768; j++) 1662 global_colormap[j]=colormap[j]; 1663 } 1664 if (LocaleCompare(write_info->magick,"GIF87") != 0) 1665 { 1666 const char 1667 *value; 1668 1669 /* 1670 Write graphics control extension. 1671 */ 1672 (void) WriteBlobByte(image,(unsigned char) 0x21); 1673 (void) WriteBlobByte(image,(unsigned char) 0xf9); 1674 (void) WriteBlobByte(image,(unsigned char) 0x04); 1675 c=image->dispose << 2; 1676 if (opacity >= 0) 1677 c|=0x01; 1678 (void) WriteBlobByte(image,(unsigned char) c); 1679 delay=(size_t) (100*image->delay/MagickMax((size_t) 1680 image->ticks_per_second,1)); 1681 (void) WriteBlobLSBShort(image,(unsigned short) delay); 1682 (void) WriteBlobByte(image,(unsigned char) (opacity >= 0 ? opacity : 1683 0)); 1684 (void) WriteBlobByte(image,(unsigned char) 0x00); 1685 value=GetImageProperty(image,"comment",exception); 1686 if ((LocaleCompare(write_info->magick,"GIF87") != 0) && 1687 (value != (const char *) NULL)) 1688 { 1689 register const char 1690 *p; 1691 1692 size_t 1693 count; 1694 1695 /* 1696 Write comment extension. 1697 */ 1698 (void) WriteBlobByte(image,(unsigned char) 0x21); 1699 (void) WriteBlobByte(image,(unsigned char) 0xfe); 1700 for (p=value; *p != '\0'; ) 1701 { 1702 count=MagickMin(strlen(p),255); 1703 (void) WriteBlobByte(image,(unsigned char) count); 1704 for (i=0; i < (ssize_t) count; i++) 1705 (void) WriteBlobByte(image,(unsigned char) *p++); 1706 } 1707 (void) WriteBlobByte(image,(unsigned char) 0x00); 1708 } 1709 if ((GetPreviousImageInList(image) == (Image *) NULL) && 1710 (GetNextImageInList(image) != (Image *) NULL) && 1711 (image->iterations != 1)) 1712 { 1713 /* 1714 Write Netscape Loop extension. 1715 */ 1716 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1717 " Writing GIF Extension %s","NETSCAPE2.0"); 1718 (void) WriteBlobByte(image,(unsigned char) 0x21); 1719 (void) WriteBlobByte(image,(unsigned char) 0xff); 1720 (void) WriteBlobByte(image,(unsigned char) 0x0b); 1721 (void) WriteBlob(image,11,(unsigned char *) "NETSCAPE2.0"); 1722 (void) WriteBlobByte(image,(unsigned char) 0x03); 1723 (void) WriteBlobByte(image,(unsigned char) 0x01); 1724 (void) WriteBlobLSBShort(image,(unsigned short) image->iterations); 1725 (void) WriteBlobByte(image,(unsigned char) 0x00); 1726 } 1727 if ((image->gamma != 1.0f/2.2f)) 1728 { 1729 char 1730 attributes[MagickPathExtent]; 1731 1732 ssize_t 1733 count; 1734 1735 /* 1736 Write ImageMagick extension. 1737 */ 1738 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1739 " Writing GIF Extension %s","ImageMagick"); 1740 (void) WriteBlobByte(image,(unsigned char) 0x21); 1741 (void) WriteBlobByte(image,(unsigned char) 0xff); 1742 (void) WriteBlobByte(image,(unsigned char) 0x0b); 1743 (void) WriteBlob(image,11,(unsigned char *) "ImageMagick"); 1744 count=FormatLocaleString(attributes,MagickPathExtent,"gamma=%g", 1745 image->gamma); 1746 (void) WriteBlobByte(image,(unsigned char) count); 1747 (void) WriteBlob(image,(size_t) count,(unsigned char *) attributes); 1748 (void) WriteBlobByte(image,(unsigned char) 0x00); 1749 } 1750 ResetImageProfileIterator(image); 1751 for ( ; ; ) 1752 { 1753 char 1754 *name; 1755 1756 const StringInfo 1757 *profile; 1758 1759 name=GetNextImageProfile(image); 1760 if (name == (const char *) NULL) 1761 break; 1762 profile=GetImageProfile(image,name); 1763 if (profile != (StringInfo *) NULL) 1764 { 1765 if ((LocaleCompare(name,"ICC") == 0) || 1766 (LocaleCompare(name,"ICM") == 0) || 1767 (LocaleCompare(name,"IPTC") == 0) || 1768 (LocaleCompare(name,"8BIM") == 0) || 1769 (LocaleNCompare(name,"gif:",4) == 0)) 1770 { 1771 ssize_t 1772 offset; 1773 1774 unsigned char 1775 *datum; 1776 1777 datum=GetStringInfoDatum(profile); 1778 length=GetStringInfoLength(profile); 1779 (void) WriteBlobByte(image,(unsigned char) 0x21); 1780 (void) WriteBlobByte(image,(unsigned char) 0xff); 1781 (void) WriteBlobByte(image,(unsigned char) 0x0b); 1782 if ((LocaleCompare(name,"ICC") == 0) || 1783 (LocaleCompare(name,"ICM") == 0)) 1784 { 1785 /* 1786 Write ICC extension. 1787 */ 1788 (void) WriteBlob(image,11,(unsigned char *) "ICCRGBG1012"); 1789 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1790 " Writing GIF Extension %s","ICCRGBG1012"); 1791 } 1792 else 1793 if ((LocaleCompare(name,"IPTC") == 0)) 1794 { 1795 /* 1796 Write IPTC extension. 1797 */ 1798 (void) WriteBlob(image,11,(unsigned char *) "MGKIPTC0000"); 1799 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1800 " Writing GIF Extension %s","MGKIPTC0000"); 1801 } 1802 else 1803 if ((LocaleCompare(name,"8BIM") == 0)) 1804 { 1805 /* 1806 Write 8BIM extension. 1807 */ 1808 (void) WriteBlob(image,11,(unsigned char *) 1809 "MGK8BIM0000"); 1810 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1811 " Writing GIF Extension %s","MGK8BIM0000"); 1812 } 1813 else 1814 { 1815 char 1816 extension[MagickPathExtent]; 1817 1818 /* 1819 Write generic extension. 1820 */ 1821 (void) CopyMagickString(extension,name+4, 1822 sizeof(extension)); 1823 (void) WriteBlob(image,11,(unsigned char *) extension); 1824 (void) LogMagickEvent(CoderEvent,GetMagickModule(), 1825 " Writing GIF Extension %s",name); 1826 } 1827 offset=0; 1828 while ((ssize_t) length > offset) 1829 { 1830 size_t 1831 block_length; 1832 1833 if ((length-offset) < 255) 1834 block_length=length-offset; 1835 else 1836 block_length=255; 1837 (void) WriteBlobByte(image,(unsigned char) block_length); 1838 (void) WriteBlob(image,(size_t) block_length,datum+offset); 1839 offset+=(ssize_t) block_length; 1840 } 1841 (void) WriteBlobByte(image,(unsigned char) 0x00); 1842 } 1843 } 1844 } 1845 } 1846 (void) WriteBlobByte(image,','); /* image separator */ 1847 /* 1848 Write the image header. 1849 */ 1850 page.x=image->page.x; 1851 page.y=image->page.y; 1852 if ((image->page.width != 0) && (image->page.height != 0)) 1853 page=image->page; 1854 (void) WriteBlobLSBShort(image,(unsigned short) (page.x < 0 ? 0 : page.x)); 1855 (void) WriteBlobLSBShort(image,(unsigned short) (page.y < 0 ? 0 : page.y)); 1856 (void) WriteBlobLSBShort(image,(unsigned short) image->columns); 1857 (void) WriteBlobLSBShort(image,(unsigned short) image->rows); 1858 c=0x00; 1859 if (write_info->interlace != NoInterlace) 1860 c|=0x40; /* pixel data is interlaced */ 1861 for (j=0; j < (ssize_t) (3*image->colors); j++) 1862 if (colormap[j] != global_colormap[j]) 1863 break; 1864 if (j == (ssize_t) (3*image->colors)) 1865 (void) WriteBlobByte(image,(unsigned char) c); 1866 else 1867 { 1868 c|=0x80; 1869 c|=(bits_per_pixel-1); /* size of local colormap */ 1870 (void) WriteBlobByte(image,(unsigned char) c); 1871 length=(size_t) (3*(one << bits_per_pixel)); 1872 (void) WriteBlob(image,length,colormap); 1873 } 1874 /* 1875 Write the image data. 1876 */ 1877 c=(int) MagickMax(bits_per_pixel,2); 1878 (void) WriteBlobByte(image,(unsigned char) c); 1879 status=EncodeImage(write_info,image,(size_t) MagickMax(bits_per_pixel,2)+1, 1880 exception); 1881 if (status == MagickFalse) 1882 { 1883 global_colormap=(unsigned char *) RelinquishMagickMemory( 1884 global_colormap); 1885 colormap=(unsigned char *) RelinquishMagickMemory(colormap); 1886 write_info=DestroyImageInfo(write_info); 1887 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 1888 } 1889 (void) WriteBlobByte(image,(unsigned char) 0x00); 1890 if (GetNextImageInList(image) == (Image *) NULL) 1891 break; 1892 image=SyncNextImageInList(image); 1893 scene++; 1894 status=SetImageProgress(image,SaveImagesTag,scene, 1895 GetImageListLength(image)); 1896 if (status == MagickFalse) 1897 break; 1898 } while (write_info->adjoin != MagickFalse); 1899 (void) WriteBlobByte(image,';'); /* terminator */ 1900 global_colormap=(unsigned char *) RelinquishMagickMemory(global_colormap); 1901 colormap=(unsigned char *) RelinquishMagickMemory(colormap); 1902 write_info=DestroyImageInfo(write_info); 1903 (void) CloseBlob(image); 1904 return(MagickTrue); 1905} 1906