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