--- /dev/null
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% FFFFF L IIIII FFFFF %
+% F L I F %
+% FFF L I FFF %
+% F L I F %
+% F LLLLL IIIII F %
+% %
+% %
+% Read/Write Free Lossless Image Format %
+% %
+% Software Design %
+% Jon Sneyers %
+% April 2016 %
+% %
+% %
+% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization %
+% dedicated to making software imaging solutions freely available. %
+% %
+% You may not use this file except in compliance with the License. You may %
+% obtain a copy of the License at %
+% %
+% http://www.imagemagick.org/script/license.php %
+% %
+% Unless required by applicable law or agreed to in writing, software %
+% distributed under the License is distributed on an "AS IS" BASIS, %
+% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
+% See the License for the specific language governing permissions and %
+% limitations under the License. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+\f
+/*
+ Include declarations.
+*/
+#include "MagickCore/studio.h"
+#include "MagickCore/artifact.h"
+#include "MagickCore/blob.h"
+#include "MagickCore/blob-private.h"
+#include "MagickCore/client.h"
+#include "MagickCore/colorspace-private.h"
+#include "MagickCore/display.h"
+#include "MagickCore/exception.h"
+#include "MagickCore/exception-private.h"
+#include "MagickCore/image.h"
+#include "MagickCore/image-private.h"
+#include "MagickCore/list.h"
+#include "MagickCore/magick.h"
+#include "MagickCore/monitor.h"
+#include "MagickCore/monitor-private.h"
+#include "MagickCore/memory_.h"
+#include "MagickCore/option.h"
+#include "MagickCore/pixel-accessor.h"
+#include "MagickCore/quantum-private.h"
+#include "MagickCore/static.h"
+#include "MagickCore/string_.h"
+#include "MagickCore/string-private.h"
+#include "MagickCore/module.h"
+#include "MagickCore/utility.h"
+#include "MagickCore/xwindow.h"
+#include "MagickCore/xwindow-private.h"
+#if defined(MAGICKCORE_FLIF_DELEGATE)
+#include <flif.h>
+#endif
+\f
+/*
+ Forward declarations.
+*/
+#if defined(MAGICKCORE_FLIF_DELEGATE)
+static MagickBooleanType
+ WriteFLIFImage(const ImageInfo *,Image *,ExceptionInfo *);
+#endif
+\f
+#if defined(MAGICKCORE_FLIF_DELEGATE)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e a d F L I F I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadFLIFImage() reads an image in the FLIF image format.
+%
+% The format of the ReadFLIFImage method is:
+%
+% Image *ReadFLIFImage(const ImageInfo *image_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static Image *ReadFLIFImage(const ImageInfo *image_info,
+ ExceptionInfo *exception)
+{
+ FLIF_DECODER
+ *flifdec;
+
+ FLIF_IMAGE
+ *flifimage;
+
+ Image
+ *image;
+
+ MagickBooleanType
+ status;
+
+ register Quantum
+ *q;
+
+ register ssize_t
+ x;
+
+ register unsigned short
+ *p;
+
+ size_t
+ count,
+ image_count,
+ length;
+
+ ssize_t
+ y;
+
+ unsigned char
+ *stream;
+
+ unsigned short
+ *pixels;
+
+ /*
+ Open image file.
+ */
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickCoreSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickCoreSignature);
+ image=AcquireImage(image_info,exception);
+ status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
+ if (status == MagickFalse)
+ {
+ image=DestroyImageList(image);
+ return((Image *) NULL);
+ }
+ length=(size_t) GetBlobSize(image);
+ stream=(unsigned char *) AcquireQuantumMemory(length,sizeof(*stream));
+ if (stream == (unsigned char *) NULL)
+ ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
+ count=ReadBlob(image,length,stream);
+ if (count != length)
+ ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
+ flifdec=flif_create_decoder();
+ if (image_info->quality != UndefinedCompressionQuality)
+ flif_decoder_set_quality(flifdec,image_info->quality);
+ if (!flif_decoder_decode_memory(flifdec,stream,length))
+ {
+ flif_destroy_decoder(flifdec);
+ ThrowReaderException(CorruptImageError,"CorruptImage");
+ }
+ image_count=flif_decoder_num_images(flifdec);
+ flifimage=flif_decoder_get_image(flifdec,0);
+ length=sizeof(unsigned short)*4*flif_image_get_height(flifimage);
+ pixels=(unsigned short *) AcquireMagickMemory(length);
+ if (pixels == (unsigned short *) NULL)
+ {
+ flif_destroy_decoder(flifdec);
+ ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+
+ for (count=0; count < image_count; count++)
+ {
+ if (count > 0)
+ {
+ /*
+ Allocate next image structure.
+ */
+ AcquireNextImage(image_info,image,exception);
+ if (GetNextImageInList(image) == (Image *) NULL)
+ {
+ image=DestroyImageList(image);
+ flif_destroy_decoder(flifdec);
+ pixels=(unsigned short *) RelinquishMagickMemory(pixels);
+ return((Image *) NULL);
+ }
+ image=SyncNextImageInList(image);
+ }
+ flifimage=flif_decoder_get_image(flifdec,count);
+ image->columns=(size_t) flif_image_get_width(flifimage);
+ image->rows=(size_t) flif_image_get_height(flifimage);
+ image->depth=flif_image_get_depth(flifimage);
+ image->alpha_trait=(flif_image_get_nb_channels(flifimage) > 3 ?
+ BlendPixelTrait : UndefinedPixelTrait);
+ image->delay=flif_image_get_frame_delay(flifimage);
+ image->ticks_per_second=1000;
+ image->scene=count;
+ image->dispose=BackgroundDispose;
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ flif_image_read_row_RGBA16(flifimage,y,pixels,length);
+ p=pixels;
+ q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
+ if (q == (Quantum *) NULL)
+ break;
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ SetPixelRed(image,ScaleShortToQuantum(*p++),q);
+ SetPixelGreen(image,ScaleShortToQuantum(*p++),q);
+ SetPixelBlue(image,ScaleShortToQuantum(*p++),q);
+ SetPixelAlpha(image,ScaleShortToQuantum(*p++),q);
+ q+=GetPixelChannels(image);
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
+ image->rows);
+ if (status == MagickFalse)
+ break;
+ }
+ }
+ flif_destroy_decoder(flifdec);
+ pixels=(unsigned short *) RelinquishMagickMemory(pixels);
+ return(image);
+}
+#endif
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s F L I F %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsFLIF() returns MagickTrue if the image format type, identified by the
+% magick string, is FLIF.
+%
+% The format of the IsFLIF method is:
+%
+% MagickBooleanType IsFLIF(const unsigned char *magick,
+% const size_t length)
+%
+% A description of each parameter follows:
+%
+% o magick: compare image format pattern against these bytes.
+%
+% o length: Specifies the length of the magick string.
+%
+*/
+static MagickBooleanType IsFLIF(const unsigned char *magick,
+ const size_t length)
+{
+ if (length < 4)
+ return(MagickFalse);
+ if (LocaleNCompare((char *) magick,"FLIF",4) == 0)
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e g i s t e r F L I F I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RegisterFLIFImage() adds attributes for the FLIF image format to
+% the list of supported formats. The attributes include the image format
+% tag, a method to read and/or write the format, whether the format
+% supports the saving of more than one frame to the same file or blob,
+% whether the format supports native in-memory I/O, and a brief
+% description of the format.
+%
+% The format of the RegisterFLIFImage method is:
+%
+% size_t RegisterFLIFImage(void)
+%
+*/
+ModuleExport size_t RegisterFLIFImage(void)
+{
+ char
+ version[MagickPathExtent];
+
+ MagickInfo
+ *entry;
+
+ *version='\0';
+ entry=AcquireMagickInfo("FLIF","FLIF","Free Lossless Image Format");
+#if defined(MAGICKCORE_FLIF_DELEGATE)
+ entry->decoder=(DecodeImageHandler *) ReadFLIFImage;
+ entry->encoder=(EncodeImageHandler *) WriteFLIFImage;
+ (void) FormatLocaleString(version,MagickPathExtent,"libflif %d.%d.%d [%04X]",
+ (FLIF_VERSION >> 16) & 0xff,
+ (FLIF_VERSION >> 8) & 0xff,
+ (FLIF_VERSION >> 0) & 0xff,FLIF_ABI_VERSION);
+#endif
+ entry->mime_type=ConstantString("image/flif");
+ entry->magick=(IsImageFormatHandler *) IsFLIF;
+ if (*version != '\0')
+ entry->version=ConstantString(version);
+ (void) RegisterMagickInfo(entry);
+ return(MagickImageCoderSignature);
+}
+\f
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% U n r e g i s t e r F L I F I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UnregisterFLIFImage() removes format registrations made by the FLIF module
+% from the list of supported formats.
+%
+% The format of the UnregisterFLIFImage method is:
+%
+% UnregisterFLIFImage(void)
+%
+*/
+ModuleExport void UnregisterFLIFImage(void)
+{
+ (void) UnregisterMagickInfo("FLIF");
+}
+
+#if defined(MAGICKCORE_FLIF_DELEGATE)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% W r i t e F L I F I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteFLIFImage() writes an image in the FLIF image format.
+%
+% The format of the WriteFLIFImage method is:
+%
+% MagickBooleanType WriteFLIFImage(const ImageInfo *image_info,
+% Image *image)
+%
+% A description of each parameter follows.
+%
+% o image_info: the image info.
+%
+% o image: The image.
+%
+*/
+static MagickBooleanType WriteFLIFImage(const ImageInfo *image_info,
+ Image *image, ExceptionInfo *exception)
+{
+ FLIF_ENCODER
+ *flifenc;
+
+ FLIF_IMAGE
+ *flifimage;
+
+ int
+ flif_status;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ scene;
+
+ register const Quantum
+ *magick_restrict p;
+
+ register ssize_t
+ x;
+
+ register unsigned char
+ *magick_restrict qc;
+
+ register unsigned short
+ *magick_restrict qs;
+
+ size_t
+ columns,
+ length,
+ rows;
+
+ ssize_t
+ y;
+
+ void
+ *buffer;
+
+ void
+ *pixels;
+
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickCoreSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickCoreSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((image->columns > 0xFFFF) || (image->rows > 0xFFFF))
+ ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
+ status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
+ if (status == MagickFalse)
+ return(status);
+ flifenc=flif_create_encoder();
+ if (image_info->quality != UndefinedCompressionQuality)
+ flif_encoder_set_lossy(flifenc,3*(100-image_info->quality));
+
+ /* relatively fast encoding */
+ flif_encoder_set_learn_repeat(flifenc,1);
+ flif_encoder_set_split_threshold(flifenc,5461*8*5);
+
+ columns=image->columns;
+ rows=image->rows;
+
+ /* Convert image to FLIFIMAGE */
+ if (image->depth > 8)
+ {
+ flifimage=flif_create_image_HDR(image->columns,image->rows);
+ length=sizeof(unsigned short)*4*image->columns;
+ }
+ else
+ {
+ flifimage=flif_create_image(image->columns,image->rows);
+ length=sizeof(unsigned char)*4*image->columns;
+ }
+ if (flifimage == (FLIF_IMAGE *) NULL)
+ {
+ flif_destroy_encoder(flifenc);
+ ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ pixels=AcquireMagickMemory(length);
+ if (pixels == (void *) NULL)
+ {
+ flif_destroy_image(flifimage);
+ flif_destroy_encoder(flifenc);
+ ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ scene=0;
+
+ do
+ {
+ for (y=0; y < (ssize_t) image->rows; y++)
+ {
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (Quantum *) NULL)
+ break;
+
+ if (image->depth > 8)
+ {
+ qs=(unsigned short *) pixels;
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ *qs++=ScaleQuantumToShort(GetPixelRed(image,p));
+ *qs++=ScaleQuantumToShort(GetPixelGreen(image,p));
+ *qs++=ScaleQuantumToShort(GetPixelBlue(image,p));
+ if (image->alpha_trait != UndefinedPixelTrait)
+ *qs++=ScaleQuantumToShort(GetPixelAlpha(image,p));
+ else
+ *qs++=0xFFFF;
+ p+=GetPixelChannels(image);
+ }
+ flif_image_write_row_RGBA16(flifimage,y,pixels,length);
+ }
+ else
+ {
+ qc=pixels;
+ for (x=0; x < (ssize_t) image->columns; x++)
+ {
+ *qc++=ScaleQuantumToChar(GetPixelRed(image,p));
+ *qc++=ScaleQuantumToChar(GetPixelGreen(image,p));
+ *qc++=ScaleQuantumToChar(GetPixelBlue(image,p));
+ if (image->alpha_trait != UndefinedPixelTrait)
+ *qc++=ScaleQuantumToChar(GetPixelAlpha(image,p));
+ else
+ *qc++=0xFF;
+ p+=GetPixelChannels(image);
+ }
+ flif_image_write_row_RGBA8(flifimage,y,pixels,length);
+ }
+ }
+ flif_image_set_frame_delay(flifimage,image->delay*100/
+ image->ticks_per_second);
+ flif_encoder_add_image(flifenc,flifimage);
+ if (GetNextImageInList(image) == (Image *) NULL)
+ break;
+ image=SyncNextImageInList(image);
+ if ((columns != image->columns) || (rows != image->rows))
+ {
+ flif_destroy_image(flifimage);
+ flif_destroy_encoder(flifenc);
+ pixels=RelinquishMagickMemory(pixels);
+ ThrowWriterException(ImageError,"FramesNotSameDimensions");
+ }
+ scene++;
+ status=SetImageProgress(image,SaveImagesTag,scene,GetImageListLength(
+ image));
+ if (status == MagickFalse)
+ break;
+ } while (image_info->adjoin != MagickFalse);
+
+ flif_destroy_image(flifimage);
+ pixels=RelinquishMagickMemory(pixels);
+ flif_status=flif_encoder_encode_memory(flifenc,&buffer,&length);
+ if (flif_status)
+ WriteBlob(image,length,buffer);
+ CloseBlob(image);
+ flif_destroy_encoder(flifenc);
+ buffer=RelinquishMagickMemory(buffer);
+ return(flif_status == 0 ? MagickFalse : MagickTrue);
+}
+#endif