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)
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 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 */
IsFLIF(const unsigned char * magick,const size_t length)268 static 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 */
RegisterFLIFImage(void)301 ModuleExport 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 */
UnregisterFLIFImage(void)346 ModuleExport 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 */
WriteFLIFImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)377 static 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