1/* 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3% % 4% % 5% % 6% FFFFF L IIIII FFFFF % 7% F L I F % 8% FFF L I FFF % 9% F L I F % 10% F LLLLL IIIII F % 11% % 12% % 13% Read/Write Free Lossless Image Format % 14% % 15% Software Design % 16% Jon Sneyers % 17% April 2016 % 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/artifact.h" 44#include "MagickCore/blob.h" 45#include "MagickCore/blob-private.h" 46#include "MagickCore/client.h" 47#include "MagickCore/colorspace-private.h" 48#include "MagickCore/display.h" 49#include "MagickCore/exception.h" 50#include "MagickCore/exception-private.h" 51#include "MagickCore/image.h" 52#include "MagickCore/image-private.h" 53#include "MagickCore/list.h" 54#include "MagickCore/magick.h" 55#include "MagickCore/monitor.h" 56#include "MagickCore/monitor-private.h" 57#include "MagickCore/memory_.h" 58#include "MagickCore/option.h" 59#include "MagickCore/pixel-accessor.h" 60#include "MagickCore/quantum-private.h" 61#include "MagickCore/static.h" 62#include "MagickCore/string_.h" 63#include "MagickCore/string-private.h" 64#include "MagickCore/module.h" 65#include "MagickCore/utility.h" 66#include "MagickCore/xwindow.h" 67#include "MagickCore/xwindow-private.h" 68#if defined(MAGICKCORE_FLIF_DELEGATE) 69#include <flif.h> 70#endif 71 72/* 73 Forward declarations. 74*/ 75#if defined(MAGICKCORE_FLIF_DELEGATE) 76static MagickBooleanType 77 WriteFLIFImage(const ImageInfo *,Image *,ExceptionInfo *); 78#endif 79 80#if defined(MAGICKCORE_FLIF_DELEGATE) 81/* 82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 83% % 84% % 85% % 86% R e a d F L I F I m a g e % 87% % 88% % 89% % 90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 91% 92% ReadFLIFImage() reads an image in the FLIF image format. 93% 94% The format of the ReadFLIFImage method is: 95% 96% Image *ReadFLIFImage(const ImageInfo *image_info, 97% ExceptionInfo *exception) 98% 99% A description of each parameter follows: 100% 101% o image_info: the image info. 102% 103% o exception: return any errors or warnings in this structure. 104% 105*/ 106static Image *ReadFLIFImage(const ImageInfo *image_info, 107 ExceptionInfo *exception) 108{ 109 FLIF_DECODER 110 *flifdec; 111 112 FLIF_IMAGE 113 *flifimage; 114 115 Image 116 *image; 117 118 MagickBooleanType 119 status; 120 121 register Quantum 122 *q; 123 124 register ssize_t 125 x; 126 127 register unsigned short 128 *p; 129 130 size_t 131 count, 132 image_count, 133 length; 134 135 ssize_t 136 y; 137 138 unsigned char 139 *stream; 140 141 unsigned short 142 *pixels; 143 144 /* 145 Open image file. 146 */ 147 assert(image_info != (const ImageInfo *) NULL); 148 assert(image_info->signature == MagickCoreSignature); 149 if (image_info->debug != MagickFalse) 150 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 151 image_info->filename); 152 assert(exception != (ExceptionInfo *) NULL); 153 assert(exception->signature == MagickCoreSignature); 154 image=AcquireImage(image_info,exception); 155 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 156 if (status == MagickFalse) 157 { 158 image=DestroyImageList(image); 159 return((Image *) NULL); 160 } 161 length=(size_t) GetBlobSize(image); 162 stream=(unsigned char *) AcquireQuantumMemory(length,sizeof(*stream)); 163 if (stream == (unsigned char *) NULL) 164 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 165 count=ReadBlob(image,length,stream); 166 if (count != length) 167 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile"); 168 flifdec=flif_create_decoder(); 169 if (image_info->quality != UndefinedCompressionQuality) 170 flif_decoder_set_quality(flifdec,image_info->quality); 171 if (!flif_decoder_decode_memory(flifdec,stream,length)) 172 { 173 flif_destroy_decoder(flifdec); 174 ThrowReaderException(CorruptImageError,"CorruptImage"); 175 } 176 image_count=flif_decoder_num_images(flifdec); 177 flifimage=flif_decoder_get_image(flifdec,0); 178 length=sizeof(unsigned short)*4*flif_image_get_width(flifimage); 179 pixels=(unsigned short *) AcquireMagickMemory(length); 180 if (pixels == (unsigned short *) NULL) 181 { 182 flif_destroy_decoder(flifdec); 183 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 184 } 185 186 for (count=0; count < image_count; count++) 187 { 188 if (count > 0) 189 { 190 /* 191 Allocate next image structure. 192 */ 193 AcquireNextImage(image_info,image,exception); 194 if (GetNextImageInList(image) == (Image *) NULL) 195 { 196 image=DestroyImageList(image); 197 flif_destroy_decoder(flifdec); 198 pixels=(unsigned short *) RelinquishMagickMemory(pixels); 199 return((Image *) NULL); 200 } 201 image=SyncNextImageInList(image); 202 } 203 flifimage=flif_decoder_get_image(flifdec,count); 204 image->columns=(size_t) flif_image_get_width(flifimage); 205 image->rows=(size_t) flif_image_get_height(flifimage); 206 image->depth=flif_image_get_depth(flifimage); 207 image->alpha_trait=(flif_image_get_nb_channels(flifimage) > 3 ? 208 BlendPixelTrait : UndefinedPixelTrait); 209 image->delay=flif_image_get_frame_delay(flifimage); 210 image->ticks_per_second=1000; 211 image->scene=count; 212 image->dispose=BackgroundDispose; 213 for (y=0; y < (ssize_t) image->rows; y++) 214 { 215 flif_image_read_row_RGBA16(flifimage,y,pixels,length); 216 p=pixels; 217 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 218 if (q == (Quantum *) NULL) 219 break; 220 for (x=0; x < (ssize_t) image->columns; x++) 221 { 222 SetPixelRed(image,ScaleShortToQuantum(*p++),q); 223 SetPixelGreen(image,ScaleShortToQuantum(*p++),q); 224 SetPixelBlue(image,ScaleShortToQuantum(*p++),q); 225 SetPixelAlpha(image,ScaleShortToQuantum(*p++),q); 226 q+=GetPixelChannels(image); 227 } 228 if (SyncAuthenticPixels(image,exception) == MagickFalse) 229 break; 230 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 231 image->rows); 232 if (status == MagickFalse) 233 break; 234 } 235 } 236 flif_destroy_decoder(flifdec); 237 pixels=(unsigned short *) RelinquishMagickMemory(pixels); 238 return(image); 239} 240#endif 241 242/* 243%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 244% % 245% % 246% % 247% I s F L I F % 248% % 249% % 250% % 251%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 252% 253% IsFLIF() returns MagickTrue if the image format type, identified by the 254% magick string, is FLIF. 255% 256% The format of the IsFLIF method is: 257% 258% MagickBooleanType IsFLIF(const unsigned char *magick, 259% const size_t length) 260% 261% A description of each parameter follows: 262% 263% o magick: compare image format pattern against these bytes. 264% 265% o length: Specifies the length of the magick string. 266% 267*/ 268static MagickBooleanType IsFLIF(const unsigned char *magick, 269 const size_t length) 270{ 271 if (length < 4) 272 return(MagickFalse); 273 if (LocaleNCompare((char *) magick,"FLIF",4) == 0) 274 return(MagickTrue); 275 return(MagickFalse); 276} 277 278/* 279%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 280% % 281% % 282% % 283% R e g i s t e r F L I F I m a g e % 284% % 285% % 286% % 287%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 288% 289% RegisterFLIFImage() adds attributes for the FLIF image format to 290% the list of supported formats. The attributes include the image format 291% tag, a method to read and/or write the format, whether the format 292% supports the saving of more than one frame to the same file or blob, 293% whether the format supports native in-memory I/O, and a brief 294% description of the format. 295% 296% The format of the RegisterFLIFImage method is: 297% 298% size_t RegisterFLIFImage(void) 299% 300*/ 301ModuleExport size_t RegisterFLIFImage(void) 302{ 303 char 304 version[MagickPathExtent]; 305 306 MagickInfo 307 *entry; 308 309 *version='\0'; 310 entry=AcquireMagickInfo("FLIF","FLIF","Free Lossless Image Format"); 311#if defined(MAGICKCORE_FLIF_DELEGATE) 312 entry->decoder=(DecodeImageHandler *) ReadFLIFImage; 313 entry->encoder=(EncodeImageHandler *) WriteFLIFImage; 314 (void) FormatLocaleString(version,MagickPathExtent,"libflif %d.%d.%d [%04X]", 315 (FLIF_VERSION >> 16) & 0xff, 316 (FLIF_VERSION >> 8) & 0xff, 317 (FLIF_VERSION >> 0) & 0xff,FLIF_ABI_VERSION); 318#endif 319 entry->mime_type=ConstantString("image/flif"); 320 entry->magick=(IsImageFormatHandler *) IsFLIF; 321 if (*version != '\0') 322 entry->version=ConstantString(version); 323 (void) RegisterMagickInfo(entry); 324 return(MagickImageCoderSignature); 325} 326 327/* 328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 329% % 330% % 331% % 332% U n r e g i s t e r F L I F I m a g e % 333% % 334% % 335% % 336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 337% 338% UnregisterFLIFImage() removes format registrations made by the FLIF module 339% from the list of supported formats. 340% 341% The format of the UnregisterFLIFImage method is: 342% 343% UnregisterFLIFImage(void) 344% 345*/ 346ModuleExport void UnregisterFLIFImage(void) 347{ 348 (void) UnregisterMagickInfo("FLIF"); 349} 350 351#if defined(MAGICKCORE_FLIF_DELEGATE) 352/* 353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 354% % 355% % 356% % 357% W r i t e F L I F I m a g e % 358% % 359% % 360% % 361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 362% 363% WriteFLIFImage() writes an image in the FLIF image format. 364% 365% The format of the WriteFLIFImage method is: 366% 367% MagickBooleanType WriteFLIFImage(const ImageInfo *image_info, 368% Image *image) 369% 370% A description of each parameter follows. 371% 372% o image_info: the image info. 373% 374% o image: The image. 375% 376*/ 377static MagickBooleanType WriteFLIFImage(const ImageInfo *image_info, 378 Image *image, ExceptionInfo *exception) 379{ 380 FLIF_ENCODER 381 *flifenc; 382 383 FLIF_IMAGE 384 *flifimage; 385 386 int 387 flif_status; 388 389 MagickBooleanType 390 status; 391 392 MagickOffsetType 393 scene; 394 395 register const Quantum 396 *magick_restrict p; 397 398 register ssize_t 399 x; 400 401 register unsigned char 402 *magick_restrict qc; 403 404 register unsigned short 405 *magick_restrict qs; 406 407 size_t 408 columns, 409 length, 410 rows; 411 412 ssize_t 413 y; 414 415 void 416 *buffer; 417 418 void 419 *pixels; 420 421 assert(image_info != (const ImageInfo *) NULL); 422 assert(image_info->signature == MagickCoreSignature); 423 assert(image != (Image *) NULL); 424 assert(image->signature == MagickCoreSignature); 425 if (image->debug != MagickFalse) 426 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 427 if ((image->columns > 0xFFFF) || (image->rows > 0xFFFF)) 428 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit"); 429 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 430 if (status == MagickFalse) 431 return(status); 432 flifenc=flif_create_encoder(); 433 if (image_info->quality != UndefinedCompressionQuality) 434 flif_encoder_set_lossy(flifenc,3*(100-image_info->quality)); 435 436 /* relatively fast encoding */ 437 flif_encoder_set_learn_repeat(flifenc,1); 438 flif_encoder_set_split_threshold(flifenc,5461*8*5); 439 440 columns=image->columns; 441 rows=image->rows; 442 443 /* Convert image to FLIFIMAGE */ 444 if (image->depth > 8) 445 { 446 flifimage=flif_create_image_HDR(image->columns,image->rows); 447 length=sizeof(unsigned short)*4*image->columns; 448 } 449 else 450 { 451 flifimage=flif_create_image(image->columns,image->rows); 452 length=sizeof(unsigned char)*4*image->columns; 453 } 454 if (flifimage == (FLIF_IMAGE *) NULL) 455 { 456 flif_destroy_encoder(flifenc); 457 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 458 } 459 pixels=AcquireMagickMemory(length); 460 if (pixels == (void *) NULL) 461 { 462 flif_destroy_image(flifimage); 463 flif_destroy_encoder(flifenc); 464 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 465 } 466 scene=0; 467 468 do 469 { 470 for (y=0; y < (ssize_t) image->rows; y++) 471 { 472 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 473 if (p == (Quantum *) NULL) 474 break; 475 476 if (image->depth > 8) 477 { 478 qs=(unsigned short *) pixels; 479 for (x=0; x < (ssize_t) image->columns; x++) 480 { 481 *qs++=ScaleQuantumToShort(GetPixelRed(image,p)); 482 *qs++=ScaleQuantumToShort(GetPixelGreen(image,p)); 483 *qs++=ScaleQuantumToShort(GetPixelBlue(image,p)); 484 if (image->alpha_trait != UndefinedPixelTrait) 485 *qs++=ScaleQuantumToShort(GetPixelAlpha(image,p)); 486 else 487 *qs++=0xFFFF; 488 p+=GetPixelChannels(image); 489 } 490 flif_image_write_row_RGBA16(flifimage,y,pixels,length); 491 } 492 else 493 { 494 qc=pixels; 495 for (x=0; x < (ssize_t) image->columns; x++) 496 { 497 *qc++=ScaleQuantumToChar(GetPixelRed(image,p)); 498 *qc++=ScaleQuantumToChar(GetPixelGreen(image,p)); 499 *qc++=ScaleQuantumToChar(GetPixelBlue(image,p)); 500 if (image->alpha_trait != UndefinedPixelTrait) 501 *qc++=ScaleQuantumToChar(GetPixelAlpha(image,p)); 502 else 503 *qc++=0xFF; 504 p+=GetPixelChannels(image); 505 } 506 flif_image_write_row_RGBA8(flifimage,y,pixels,length); 507 } 508 } 509 flif_image_set_frame_delay(flifimage,image->delay*100/ 510 image->ticks_per_second); 511 flif_encoder_add_image(flifenc,flifimage); 512 if (GetNextImageInList(image) == (Image *) NULL) 513 break; 514 image=SyncNextImageInList(image); 515 if ((columns != image->columns) || (rows != image->rows)) 516 { 517 flif_destroy_image(flifimage); 518 flif_destroy_encoder(flifenc); 519 pixels=RelinquishMagickMemory(pixels); 520 ThrowWriterException(ImageError,"FramesNotSameDimensions"); 521 } 522 scene++; 523 status=SetImageProgress(image,SaveImagesTag,scene,GetImageListLength( 524 image)); 525 if (status == MagickFalse) 526 break; 527 } while (image_info->adjoin != MagickFalse); 528 529 flif_destroy_image(flifimage); 530 pixels=RelinquishMagickMemory(pixels); 531 flif_status=flif_encoder_encode_memory(flifenc,&buffer,&length); 532 if (flif_status) 533 WriteBlob(image,length,buffer); 534 CloseBlob(image); 535 flif_destroy_encoder(flifenc); 536 buffer=RelinquishMagickMemory(buffer); 537 return(flif_status == 0 ? MagickFalse : MagickTrue); 538} 539#endif 540