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