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-2021 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 %    https://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 "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/blob.h"
45 #include "magick/blob-private.h"
46 #include "magick/client.h"
47 #include "magick/colorspace-private.h"
48 #include "magick/display.h"
49 #include "magick/exception.h"
50 #include "magick/exception-private.h"
51 #include "magick/image.h"
52 #include "magick/image-private.h"
53 #include "magick/list.h"
54 #include "magick/magick.h"
55 #include "magick/monitor.h"
56 #include "magick/monitor-private.h"
57 #include "magick/memory_.h"
58 #include "magick/option.h"
59 #include "magick/pixel-accessor.h"
60 #include "magick/quantum-private.h"
61 #include "magick/static.h"
62 #include "magick/string_.h"
63 #include "magick/string-private.h"
64 #include "magick/module.h"
65 #include "magick/utility.h"
66 #include "magick/xwindow.h"
67 #include "magick/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)
76 static 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 */
ReadFLIFImage(const ImageInfo * image_info,ExceptionInfo * exception)106 static 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   PixelPacket
122     *q;
123 
124   ssize_t
125     x;
126 
127   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);
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     {
168       stream=(unsigned char *) RelinquishMagickMemory(stream);
169       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
170     }
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 *) AcquireQuantumMemory(1,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);
197         if (GetNextImageInList(image) == (Image *) NULL)
198           {
199             status=MagickFalse;
200             break;
201           }
202         image=SyncNextImageInList(image);
203       }
204     flifimage=flif_decoder_get_image(flifdec,count);
205     image->columns=(size_t) flif_image_get_width(flifimage);
206     image->rows=(size_t) flif_image_get_height(flifimage);
207     image->depth=flif_image_get_depth(flifimage);
208     image->matte=(flif_image_get_nb_channels(flifimage) > 3 ?
209       MagickTrue : MagickFalse);
210     image->delay=flif_image_get_frame_delay(flifimage);
211     image->ticks_per_second=1000;
212     image->scene=count;
213     image->dispose=BackgroundDispose;
214     for (y=0; y < (ssize_t) image->rows; y++)
215     {
216       flif_image_read_row_RGBA16(flifimage,y,pixels,length);
217       p=pixels;
218       q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
219       if (q == (PixelPacket *) NULL)
220         break;
221       for (x=0; x < (ssize_t) image->columns; x++)
222       {
223         SetPixelRed(q,ScaleShortToQuantum(*p++));
224         SetPixelGreen(q,ScaleShortToQuantum(*p++));
225         SetPixelBlue(q,ScaleShortToQuantum(*p++));
226         SetPixelAlpha(q,ScaleShortToQuantum(*p++));
227         q++;
228       }
229       if (SyncAuthenticPixels(image,exception) == MagickFalse)
230         break;
231       status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
232         image->rows);
233       if (status == MagickFalse)
234         break;
235     }
236   }
237   flif_destroy_decoder(flifdec);
238   pixels=(unsigned short *) RelinquishMagickMemory(pixels);
239   if (status == MagickFalse)
240     return(DestroyImageList(image));
241   return(image);
242 }
243 #endif
244 
245 /*
246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247 %                                                                             %
248 %                                                                             %
249 %                                                                             %
250 %   I s F L I F                                                               %
251 %                                                                             %
252 %                                                                             %
253 %                                                                             %
254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255 %
256 %  IsFLIF() returns MagickTrue if the image format type, identified by the
257 %  magick string, is FLIF.
258 %
259 %  The format of the IsFLIF method is:
260 %
261 %      MagickBooleanType IsFLIF(const unsigned char *magick,
262 %        const size_t length)
263 %
264 %  A description of each parameter follows:
265 %
266 %    o magick: compare image format pattern against these bytes.
267 %
268 %    o length: Specifies the length of the magick string.
269 %
270 */
IsFLIF(const unsigned char * magick,const size_t length)271 static MagickBooleanType IsFLIF(const unsigned char *magick,
272   const size_t length)
273 {
274   if (length < 4)
275     return(MagickFalse);
276   if (LocaleNCompare((char *) magick,"FLIF",4) == 0)
277     return(MagickTrue);
278   return(MagickFalse);
279 }
280 
281 /*
282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
283 %                                                                             %
284 %                                                                             %
285 %                                                                             %
286 %   R e g i s t e r F L I F I m a g e                                         %
287 %                                                                             %
288 %                                                                             %
289 %                                                                             %
290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
291 %
292 %  RegisterFLIFImage() adds attributes for the FLIF image format to
293 %  the list of supported formats.  The attributes include the image format
294 %  tag, a method to read and/or write the format, whether the format
295 %  supports the saving of more than one frame to the same file or blob,
296 %  whether the format supports native in-memory I/O, and a brief
297 %  description of the format.
298 %
299 %  The format of the RegisterFLIFImage method is:
300 %
301 %      size_t RegisterFLIFImage(void)
302 %
303 */
RegisterFLIFImage(void)304 ModuleExport size_t RegisterFLIFImage(void)
305 {
306   char
307     version[MaxTextExtent];
308 
309   MagickInfo
310     *entry;
311 
312   *version='\0';
313   entry=SetMagickInfo("FLIF");
314 #if defined(MAGICKCORE_FLIF_DELEGATE)
315   entry->decoder=(DecodeImageHandler *) ReadFLIFImage;
316   entry->encoder=(EncodeImageHandler *) WriteFLIFImage;
317   (void) FormatLocaleString(version,MaxTextExtent,"libflif %d.%d.%d [%04X]",
318     (FLIF_VERSION >> 16) & 0xff,
319     (FLIF_VERSION  >> 8) & 0xff,
320     (FLIF_VERSION  >> 0) & 0xff,FLIF_ABI_VERSION);
321 #endif
322   entry->description=ConstantString("Free Lossless Image Format");
323   entry->adjoin=MagickTrue;
324   entry->magick_module=ConstantString("FLIF");
325   entry->mime_type=ConstantString("image/flif");
326   entry->magick=(IsImageFormatHandler *) IsFLIF;
327   if (*version != '\0')
328     entry->version=ConstantString(version);
329   (void) RegisterMagickInfo(entry);
330   return(MagickImageCoderSignature);
331 }
332 
333 /*
334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335 %                                                                             %
336 %                                                                             %
337 %                                                                             %
338 %   U n r e g i s t e r F L I F I m a g e                                     %
339 %                                                                             %
340 %                                                                             %
341 %                                                                             %
342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
343 %
344 %  UnregisterFLIFImage() removes format registrations made by the FLIF module
345 %  from the list of supported formats.
346 %
347 %  The format of the UnregisterFLIFImage method is:
348 %
349 %      UnregisterFLIFImage(void)
350 %
351 */
UnregisterFLIFImage(void)352 ModuleExport void UnregisterFLIFImage(void)
353 {
354   (void) UnregisterMagickInfo("FLIF");
355 }
356 
357 #if defined(MAGICKCORE_FLIF_DELEGATE)
358 /*
359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
360 %                                                                             %
361 %                                                                             %
362 %                                                                             %
363 %   W r i t e F L I F I m a g e                                               %
364 %                                                                             %
365 %                                                                             %
366 %                                                                             %
367 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
368 %
369 %  WriteFLIFImage() writes an image in the FLIF image format.
370 %
371 %  The format of the WriteFLIFImage method is:
372 %
373 %      MagickBooleanType WriteFLIFImage(const ImageInfo *image_info,
374 %        Image *image)
375 %
376 %  A description of each parameter follows.
377 %
378 %    o image_info: the image info.
379 %
380 %    o image:  The image.
381 %
382 */
WriteFLIFImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)383 static MagickBooleanType WriteFLIFImage(const ImageInfo *image_info,
384   Image *image, ExceptionInfo *exception)
385 {
386   FLIF_ENCODER
387     *flifenc;
388 
389   FLIF_IMAGE
390     *flifimage;
391 
392   int
393     flif_status;
394 
395   MagickBooleanType
396     status;
397 
398   MagickOffsetType
399     scene;
400 
401   const PixelPacket
402     *magick_restrict p;
403 
404   ssize_t
405     x;
406 
407   unsigned char
408     *magick_restrict qc;
409 
410   unsigned short
411     *magick_restrict qs;
412 
413   size_t
414     columns,
415     imageListLength,
416     length,
417     rows;
418 
419   ssize_t
420     y;
421 
422   void
423     *buffer;
424 
425   void
426     *pixels;
427 
428   assert(image_info != (const ImageInfo *) NULL);
429   assert(image_info->signature == MagickCoreSignature);
430   assert(image != (Image *) NULL);
431   assert(image->signature == MagickCoreSignature);
432   if (image->debug != MagickFalse)
433     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
434   if ((image->columns > 0xFFFF) || (image->rows > 0xFFFF))
435     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
436   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
437   if (status == MagickFalse)
438     return(status);
439   flifenc=flif_create_encoder();
440   if (image_info->quality != UndefinedCompressionQuality)
441     flif_encoder_set_lossy(flifenc,3*(100-image_info->quality));
442 
443   /* relatively fast encoding */
444   flif_encoder_set_learn_repeat(flifenc,1);
445   flif_encoder_set_split_threshold(flifenc,5461*8*5);
446 
447   columns=image->columns;
448   rows=image->rows;
449 
450   /* Convert image to FLIFIMAGE */
451   if (image->depth > 8)
452     {
453       flifimage=flif_create_image_HDR(image->columns,image->rows);
454       length=sizeof(unsigned short)*4*image->columns;
455     }
456   else
457     {
458       flifimage=flif_create_image(image->columns,image->rows);
459       length=sizeof(unsigned char)*4*image->columns;
460     }
461   if (flifimage == (FLIF_IMAGE *) NULL)
462     {
463       flif_destroy_encoder(flifenc);
464       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
465     }
466   pixels=AcquireMagickMemory(length);
467   if (pixels == (void *) NULL)
468     {
469       flif_destroy_image(flifimage);
470       flif_destroy_encoder(flifenc);
471       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
472     }
473   scene=0;
474   imageListLength=GetImageListLength(image);
475   do
476   {
477     for (y=0; y < (ssize_t) image->rows; y++)
478     {
479       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
480       if (p == (PixelPacket *) NULL)
481         break;
482 
483       if (image->depth > 8)
484         {
485           qs=(unsigned short *) pixels;
486           for (x=0; x < (ssize_t) image->columns; x++)
487           {
488             *qs++=ScaleQuantumToShort(GetPixelRed(p));
489             *qs++=ScaleQuantumToShort(GetPixelGreen(p));
490             *qs++=ScaleQuantumToShort(GetPixelBlue(p));
491             if (image->matte != MagickFalse)
492               *qs++=ScaleQuantumToShort(GetPixelAlpha(p));
493             else
494               *qs++=0xFFFF;
495             p++;
496           }
497           flif_image_write_row_RGBA16(flifimage,y,pixels,length);
498         }
499       else
500         {
501           qc=pixels;
502           for (x=0; x < (ssize_t) image->columns; x++)
503           {
504             *qc++=ScaleQuantumToChar(GetPixelRed(p));
505             *qc++=ScaleQuantumToChar(GetPixelGreen(p));
506             *qc++=ScaleQuantumToChar(GetPixelBlue(p));
507             if (image->matte != MagickFalse)
508               *qc++=ScaleQuantumToChar(GetPixelAlpha(p));
509             else
510               *qc++=0xFF;
511             p++;
512           }
513           flif_image_write_row_RGBA8(flifimage,y,pixels,length);
514         }
515     }
516     flif_image_set_frame_delay(flifimage,image->delay*100/
517       image->ticks_per_second);
518     flif_encoder_add_image(flifenc,flifimage);
519     if (GetNextImageInList(image) == (Image *) NULL)
520       break;
521     image=SyncNextImageInList(image);
522     if ((columns != image->columns) || (rows != image->rows))
523       {
524         flif_destroy_image(flifimage);
525         flif_destroy_encoder(flifenc);
526         pixels=RelinquishMagickMemory(pixels);
527         ThrowWriterException(ImageError,"FramesNotSameDimensions");
528       }
529     scene++;
530     status=SetImageProgress(image,SaveImagesTag,scene,imageListLength);
531     if (status == MagickFalse)
532        break;
533   } while (image_info->adjoin != MagickFalse);
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