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 "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)
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   Quantum
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,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     {
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,(int32_t) 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,exception);
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->alpha_trait=(flif_image_get_nb_channels(flifimage) > 3 ?
209       BlendPixelTrait : UndefinedPixelTrait);
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 == (Quantum *) NULL)
220         break;
221       for (x=0; x < (ssize_t) image->columns; x++)
222       {
223         SetPixelRed(image,ScaleShortToQuantum(*p++),q);
224         SetPixelGreen(image,ScaleShortToQuantum(*p++),q);
225         SetPixelBlue(image,ScaleShortToQuantum(*p++),q);
226         SetPixelAlpha(image,ScaleShortToQuantum(*p++),q);
227         q+=GetPixelChannels(image);
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[MagickPathExtent];
308 
309   MagickInfo
310     *entry;
311 
312   *version='\0';
313   entry=AcquireMagickInfo("FLIF","FLIF","Free Lossless Image Format");
314 #if defined(MAGICKCORE_FLIF_DELEGATE)
315   entry->decoder=(DecodeImageHandler *) ReadFLIFImage;
316   entry->encoder=(EncodeImageHandler *) WriteFLIFImage;
317   (void) FormatLocaleString(version,MagickPathExtent,"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->mime_type=ConstantString("image/flif");
323   entry->magick=(IsImageFormatHandler *) IsFLIF;
324   if (*version != '\0')
325     entry->version=ConstantString(version);
326   (void) RegisterMagickInfo(entry);
327   return(MagickImageCoderSignature);
328 }
329 
330 /*
331 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332 %                                                                             %
333 %                                                                             %
334 %                                                                             %
335 %   U n r e g i s t e r F L I F I m a g e                                     %
336 %                                                                             %
337 %                                                                             %
338 %                                                                             %
339 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
340 %
341 %  UnregisterFLIFImage() removes format registrations made by the FLIF module
342 %  from the list of supported formats.
343 %
344 %  The format of the UnregisterFLIFImage method is:
345 %
346 %      UnregisterFLIFImage(void)
347 %
348 */
UnregisterFLIFImage(void)349 ModuleExport void UnregisterFLIFImage(void)
350 {
351   (void) UnregisterMagickInfo("FLIF");
352 }
353 
354 #if defined(MAGICKCORE_FLIF_DELEGATE)
355 /*
356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
357 %                                                                             %
358 %                                                                             %
359 %                                                                             %
360 %   W r i t e F L I F I m a g e                                               %
361 %                                                                             %
362 %                                                                             %
363 %                                                                             %
364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
365 %
366 %  WriteFLIFImage() writes an image in the FLIF image format.
367 %
368 %  The format of the WriteFLIFImage method is:
369 %
370 %      MagickBooleanType WriteFLIFImage(const ImageInfo *image_info,
371 %        Image *image)
372 %
373 %  A description of each parameter follows.
374 %
375 %    o image_info: the image info.
376 %
377 %    o image:  The image.
378 %
379 */
WriteFLIFImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)380 static MagickBooleanType WriteFLIFImage(const ImageInfo *image_info,
381   Image *image, ExceptionInfo *exception)
382 {
383   FLIF_ENCODER
384     *flifenc;
385 
386   FLIF_IMAGE
387     *flifimage;
388 
389   int
390     flif_status;
391 
392   MagickBooleanType
393     status;
394 
395   MagickOffsetType
396     scene;
397 
398   const Quantum
399     *magick_restrict p;
400 
401   ssize_t
402     x;
403 
404   unsigned char
405     *magick_restrict qc;
406 
407   unsigned short
408     *magick_restrict qs;
409 
410   size_t
411     columns,
412     imageListLength,
413     length,
414     rows;
415 
416   ssize_t
417     y;
418 
419   void
420     *buffer;
421 
422   void
423     *pixels;
424 
425   assert(image_info != (const ImageInfo *) NULL);
426   assert(image_info->signature == MagickCoreSignature);
427   assert(image != (Image *) NULL);
428   assert(image->signature == MagickCoreSignature);
429   if (image->debug != MagickFalse)
430     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
431   if ((image->columns > 0xFFFF) || (image->rows > 0xFFFF))
432     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
433   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
434   if (status == MagickFalse)
435     return(status);
436   flifenc=flif_create_encoder();
437   if (image_info->quality != UndefinedCompressionQuality)
438     flif_encoder_set_lossy(flifenc,3*(100-(int32_t) image_info->quality));
439 
440   /* relatively fast encoding */
441   flif_encoder_set_learn_repeat(flifenc,1);
442   flif_encoder_set_split_threshold(flifenc,5461*8*5);
443 
444   columns=image->columns;
445   rows=image->rows;
446 
447   /* Convert image to FLIFIMAGE */
448   if (image->depth > 8)
449     {
450       flifimage=flif_create_image_HDR((uint32_t) image->columns,
451         (uint32_t) image->rows);
452       length=sizeof(unsigned short)*4*image->columns;
453     }
454   else
455     {
456       flifimage=flif_create_image((uint32_t) image->columns,
457         (uint32_t) image->rows);
458       length=sizeof(unsigned char)*4*image->columns;
459     }
460   if (flifimage == (FLIF_IMAGE *) NULL)
461     {
462       flif_destroy_encoder(flifenc);
463       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
464     }
465   pixels=AcquireMagickMemory(length);
466   if (pixels == (void *) NULL)
467     {
468       flif_destroy_image(flifimage);
469       flif_destroy_encoder(flifenc);
470       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
471     }
472   scene=0;
473   imageListLength=GetImageListLength(image);
474   do
475   {
476     for (y=0; y < (ssize_t) image->rows; y++)
477     {
478       p=GetVirtualPixels(image,0,y,image->columns,1,exception);
479       if (p == (Quantum *) NULL)
480         break;
481 
482       if (image->depth > 8)
483         {
484           qs=(unsigned short *) pixels;
485           for (x=0; x < (ssize_t) image->columns; x++)
486           {
487             *qs++=ScaleQuantumToShort(GetPixelRed(image,p));
488             *qs++=ScaleQuantumToShort(GetPixelGreen(image,p));
489             *qs++=ScaleQuantumToShort(GetPixelBlue(image,p));
490             if (image->alpha_trait != UndefinedPixelTrait)
491               *qs++=ScaleQuantumToShort(GetPixelAlpha(image,p));
492             else
493               *qs++=0xFFFF;
494             p+=GetPixelChannels(image);
495           }
496           flif_image_write_row_RGBA16(flifimage,y,pixels,length);
497         }
498       else
499         {
500           qc=pixels;
501           for (x=0; x < (ssize_t) image->columns; x++)
502           {
503             *qc++=ScaleQuantumToChar(GetPixelRed(image,p));
504             *qc++=ScaleQuantumToChar(GetPixelGreen(image,p));
505             *qc++=ScaleQuantumToChar(GetPixelBlue(image,p));
506             if (image->alpha_trait != UndefinedPixelTrait)
507               *qc++=ScaleQuantumToChar(GetPixelAlpha(image,p));
508             else
509               *qc++=0xFF;
510             p+=GetPixelChannels(image);
511           }
512           flif_image_write_row_RGBA8(flifimage,y,pixels,length);
513         }
514     }
515     flif_image_set_frame_delay(flifimage,(uint32_t) image->delay*100/
516       image->ticks_per_second);
517     flif_encoder_add_image(flifenc,flifimage);
518     if (GetNextImageInList(image) == (Image *) NULL)
519       break;
520     image=SyncNextImageInList(image);
521     if ((columns != image->columns) || (rows != image->rows))
522       {
523         flif_destroy_image(flifimage);
524         flif_destroy_encoder(flifenc);
525         pixels=RelinquishMagickMemory(pixels);
526         ThrowWriterException(ImageError,"FramesNotSameDimensions");
527       }
528     scene++;
529     status=SetImageProgress(image,SaveImagesTag,scene,imageListLength);
530     if (status == MagickFalse)
531        break;
532   } while (image_info->adjoin != MagickFalse);
533   flif_destroy_image(flifimage);
534   pixels=RelinquishMagickMemory(pixels);
535   flif_status=flif_encoder_encode_memory(flifenc,&buffer,&length);
536   if (flif_status)
537     WriteBlob(image,length,buffer);
538   CloseBlob(image);
539   flif_destroy_encoder(flifenc);
540   buffer=RelinquishMagickMemory(buffer);
541   return(flif_status == 0 ? MagickFalse : MagickTrue);
542 }
543 #endif
544