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