• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %     CCCC   OOO   N   N  SSSSS  TTTTT  IIIII  TTTTT  U   U  TTTTT  EEEEE     %
7 %    C      O   O  NN  N  SS       T      I      T    U   U    T    E         %
8 %    C      O   O  N N N  ESSS     T      I      T    U   U    T    EEE       %
9 %    C      O   O  N  NN     SS    T      I      T    U   U    T    E         %
10 %     CCCC   OOO   N   N  SSSSS    T    IIIII    T     UUU     T    EEEEE     %
11 %                                                                             %
12 %                                                                             %
13 %                  MagickCore Methods to Consitute an Image                   %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                                  Cristy                                     %
17 %                               October 1998                                  %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2019 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/attribute.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/blob-private.h"
46 #include "MagickCore/exception.h"
47 #include "MagickCore/exception-private.h"
48 #include "MagickCore/cache.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/coder-private.h"
51 #include "MagickCore/colorspace-private.h"
52 #include "MagickCore/constitute.h"
53 #include "MagickCore/constitute-private.h"
54 #include "MagickCore/delegate.h"
55 #include "MagickCore/geometry.h"
56 #include "MagickCore/identify.h"
57 #include "MagickCore/image-private.h"
58 #include "MagickCore/list.h"
59 #include "MagickCore/magick.h"
60 #include "MagickCore/memory_.h"
61 #include "MagickCore/monitor.h"
62 #include "MagickCore/monitor-private.h"
63 #include "MagickCore/option.h"
64 #include "MagickCore/pixel.h"
65 #include "MagickCore/pixel-accessor.h"
66 #include "MagickCore/policy.h"
67 #include "MagickCore/profile.h"
68 #include "MagickCore/profile-private.h"
69 #include "MagickCore/property.h"
70 #include "MagickCore/quantum.h"
71 #include "MagickCore/resize.h"
72 #include "MagickCore/resource_.h"
73 #include "MagickCore/semaphore.h"
74 #include "MagickCore/statistic.h"
75 #include "MagickCore/stream.h"
76 #include "MagickCore/string_.h"
77 #include "MagickCore/string-private.h"
78 #include "MagickCore/timer.h"
79 #include "MagickCore/token.h"
80 #include "MagickCore/transform.h"
81 #include "MagickCore/utility.h"
82 #include "MagickCore/utility-private.h"
83 
84 /*
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 %                                                                             %
87 %                                                                             %
88 %                                                                             %
89 %   C o n s t i t u t e I m a g e                                             %
90 %                                                                             %
91 %                                                                             %
92 %                                                                             %
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94 %
95 %  ConstituteImage() returns an image from the pixel data you supply.
96 %  The pixel data must be in scanline order top-to-bottom.  The data can be
97 %  char, short int, int, float, or double.  Float and double require the
98 %  pixels to be normalized [0..1], otherwise [0..QuantumRange].  For example, to
99 %  create a 640x480 image from unsigned red-green-blue character data, use:
100 %
101 %      image = ConstituteImage(640,480,"RGB",CharPixel,pixels,&exception);
102 %
103 %  The format of the ConstituteImage method is:
104 %
105 %      Image *ConstituteImage(const size_t columns,const size_t rows,
106 %        const char *map,const StorageType storage,const void *pixels,
107 %        ExceptionInfo *exception)
108 %
109 %  A description of each parameter follows:
110 %
111 %    o columns: width in pixels of the image.
112 %
113 %    o rows: height in pixels of the image.
114 %
115 %    o map:  This string reflects the expected ordering of the pixel array.
116 %      It can be any combination or order of R = red, G = green, B = blue,
117 %      A = alpha (0 is transparent), O = opacity (0 is opaque), C = cyan,
118 %      Y = yellow, M = magenta, K = black, I = intensity (for grayscale),
119 %      P = pad.
120 %
121 %    o storage: Define the data type of the pixels.  Float and double types are
122 %      expected to be normalized [0..1] otherwise [0..QuantumRange].  Choose
123 %      from these types: CharPixel, DoublePixel, FloatPixel, IntegerPixel,
124 %      LongPixel, QuantumPixel, or ShortPixel.
125 %
126 %    o pixels: This array of values contain the pixel components as defined by
127 %      map and type.  You must preallocate this array where the expected
128 %      length varies depending on the values of width, height, map, and type.
129 %
130 %    o exception: return any errors or warnings in this structure.
131 %
132 */
ConstituteImage(const size_t columns,const size_t rows,const char * map,const StorageType storage,const void * pixels,ExceptionInfo * exception)133 MagickExport Image *ConstituteImage(const size_t columns,const size_t rows,
134   const char *map,const StorageType storage,const void *pixels,
135   ExceptionInfo *exception)
136 {
137   Image
138     *image;
139 
140   MagickBooleanType
141     status;
142 
143   register ssize_t
144     i;
145 
146   /*
147     Allocate image structure.
148   */
149   assert(map != (const char *) NULL);
150   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",map);
151   assert(pixels != (void *) NULL);
152   assert(exception != (ExceptionInfo *) NULL);
153   assert(exception->signature == MagickCoreSignature);
154   image=AcquireImage((ImageInfo *) NULL,exception);
155   if (image == (Image *) NULL)
156     return((Image *) NULL);
157   for (i=0; i < (ssize_t) strlen(map); i++)
158   {
159     switch (map[i])
160     {
161       case 'a':
162       case 'A':
163       case 'O':
164       case 'o':
165       {
166         image->alpha_trait=BlendPixelTrait;
167         break;
168       }
169       case 'C':
170       case 'c':
171       case 'm':
172       case 'M':
173       case 'Y':
174       case 'y':
175       case 'K':
176       case 'k':
177       {
178         image->colorspace=CMYKColorspace;
179         break;
180       }
181       case 'I':
182       case 'i':
183       {
184         image->colorspace=GRAYColorspace;
185         break;
186       }
187       default:
188       {
189         if (strlen(map) == 1)
190           image->colorspace=GRAYColorspace;
191         break;
192       }
193     }
194   }
195   status=SetImageExtent(image,columns,rows,exception);
196   if (status == MagickFalse)
197     return(DestroyImageList(image));
198   status=ResetImagePixels(image,exception);
199   if (status == MagickFalse)
200     return(DestroyImageList(image));
201   status=ImportImagePixels(image,0,0,columns,rows,map,storage,pixels,exception);
202   if (status == MagickFalse)
203     image=DestroyImage(image);
204   return(image);
205 }
206 
207 /*
208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
209 %                                                                             %
210 %                                                                             %
211 %                                                                             %
212 %   P i n g I m a g e                                                         %
213 %                                                                             %
214 %                                                                             %
215 %                                                                             %
216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
217 %
218 %  PingImage() returns all the properties of an image or image sequence
219 %  except for the pixels.  It is much faster and consumes far less memory
220 %  than ReadImage().  On failure, a NULL image is returned and exception
221 %  describes the reason for the failure.
222 %
223 %  The format of the PingImage method is:
224 %
225 %      Image *PingImage(const ImageInfo *image_info,ExceptionInfo *exception)
226 %
227 %  A description of each parameter follows:
228 %
229 %    o image_info: Ping the image defined by the file or filename members of
230 %      this structure.
231 %
232 %    o exception: return any errors or warnings in this structure.
233 %
234 */
235 
236 #if defined(__cplusplus) || defined(c_plusplus)
237 extern "C" {
238 #endif
239 
PingStream(const Image * magick_unused (image),const void * magick_unused (pixels),const size_t columns)240 static size_t PingStream(const Image *magick_unused(image),
241   const void *magick_unused(pixels),const size_t columns)
242 {
243   magick_unreferenced(image);
244   magick_unreferenced(pixels);
245   return(columns);
246 }
247 
248 #if defined(__cplusplus) || defined(c_plusplus)
249 }
250 #endif
251 
PingImage(const ImageInfo * image_info,ExceptionInfo * exception)252 MagickExport Image *PingImage(const ImageInfo *image_info,
253   ExceptionInfo *exception)
254 {
255   Image
256     *image;
257 
258   ImageInfo
259     *ping_info;
260 
261   assert(image_info != (ImageInfo *) NULL);
262   assert(image_info->signature == MagickCoreSignature);
263   if (image_info->debug != MagickFalse)
264     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
265       image_info->filename);
266   assert(exception != (ExceptionInfo *) NULL);
267   ping_info=CloneImageInfo(image_info);
268   ping_info->ping=MagickTrue;
269   image=ReadStream(ping_info,&PingStream,exception);
270   if (image != (Image *) NULL)
271     {
272       ResetTimer(&image->timer);
273       if (ping_info->verbose != MagickFalse)
274         (void) IdentifyImage(image,stdout,MagickFalse,exception);
275     }
276   ping_info=DestroyImageInfo(ping_info);
277   return(image);
278 }
279 
280 /*
281 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
282 %                                                                             %
283 %                                                                             %
284 %                                                                             %
285 %   P i n g I m a g e s                                                       %
286 %                                                                             %
287 %                                                                             %
288 %                                                                             %
289 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
290 %
291 %  PingImages() pings one or more images and returns them as an image list.
292 %
293 %  The format of the PingImage method is:
294 %
295 %      Image *PingImages(ImageInfo *image_info,const char *filename,
296 %        ExceptionInfo *exception)
297 %
298 %  A description of each parameter follows:
299 %
300 %    o image_info: the image info.
301 %
302 %    o filename: the image filename.
303 %
304 %    o exception: return any errors or warnings in this structure.
305 %
306 */
PingImages(ImageInfo * image_info,const char * filename,ExceptionInfo * exception)307 MagickExport Image *PingImages(ImageInfo *image_info,const char *filename,
308   ExceptionInfo *exception)
309 {
310   char
311     ping_filename[MagickPathExtent];
312 
313   Image
314     *image,
315     *images;
316 
317   ImageInfo
318     *read_info;
319 
320   /*
321     Ping image list from a file.
322   */
323   assert(image_info != (ImageInfo *) NULL);
324   assert(image_info->signature == MagickCoreSignature);
325   if (image_info->debug != MagickFalse)
326     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
327       image_info->filename);
328   assert(exception != (ExceptionInfo *) NULL);
329   (void) SetImageOption(image_info,"filename",filename);
330   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
331   (void) InterpretImageFilename(image_info,(Image *) NULL,image_info->filename,
332     (int) image_info->scene,ping_filename,exception);
333   if (LocaleCompare(ping_filename,image_info->filename) != 0)
334     {
335       ExceptionInfo
336         *sans;
337 
338       ssize_t
339         extent,
340         scene;
341 
342       /*
343         Images of the form image-%d.png[1-5].
344       */
345       read_info=CloneImageInfo(image_info);
346       sans=AcquireExceptionInfo();
347       (void) SetImageInfo(read_info,0,sans);
348       sans=DestroyExceptionInfo(sans);
349       if (read_info->number_scenes == 0)
350         {
351           read_info=DestroyImageInfo(read_info);
352           return(PingImage(image_info,exception));
353         }
354       (void) CopyMagickString(ping_filename,read_info->filename,
355         MagickPathExtent);
356       images=NewImageList();
357       extent=(ssize_t) (read_info->scene+read_info->number_scenes);
358       for (scene=(ssize_t) read_info->scene; scene < (ssize_t) extent; scene++)
359       {
360         (void) InterpretImageFilename(image_info,(Image *) NULL,ping_filename,
361           (int) scene,read_info->filename,exception);
362         image=PingImage(read_info,exception);
363         if (image == (Image *) NULL)
364           continue;
365         AppendImageToList(&images,image);
366       }
367       read_info=DestroyImageInfo(read_info);
368       return(images);
369     }
370   return(PingImage(image_info,exception));
371 }
372 
373 /*
374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 %                                                                             %
376 %                                                                             %
377 %                                                                             %
378 %   R e a d I m a g e                                                         %
379 %                                                                             %
380 %                                                                             %
381 %                                                                             %
382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
383 %
384 %  ReadImage() reads an image or image sequence from a file or file handle.
385 %  The method returns a NULL if there is a memory shortage or if the image
386 %  cannot be read.  On failure, a NULL image is returned and exception
387 %  describes the reason for the failure.
388 %
389 %  The format of the ReadImage method is:
390 %
391 %      Image *ReadImage(const ImageInfo *image_info,ExceptionInfo *exception)
392 %
393 %  A description of each parameter follows:
394 %
395 %    o image_info: Read the image defined by the file or filename members of
396 %      this structure.
397 %
398 %    o exception: return any errors or warnings in this structure.
399 %
400 */
401 
IsCoderAuthorized(const char * coder,const PolicyRights rights,ExceptionInfo * exception)402 static MagickBooleanType IsCoderAuthorized(const char *coder,
403   const PolicyRights rights,ExceptionInfo *exception)
404 {
405   if (IsRightsAuthorized(CoderPolicyDomain,rights,coder) == MagickFalse)
406     {
407       errno=EPERM;
408       (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
409         "NotAuthorized","`%s'",coder);
410       return(MagickFalse);
411     }
412   return(MagickTrue);
413 }
414 
ReadImage(const ImageInfo * image_info,ExceptionInfo * exception)415 MagickExport Image *ReadImage(const ImageInfo *image_info,
416   ExceptionInfo *exception)
417 {
418   char
419     filename[MagickPathExtent],
420     magick[MagickPathExtent],
421     magick_filename[MagickPathExtent];
422 
423   const char
424     *value;
425 
426   const DelegateInfo
427     *delegate_info;
428 
429   const MagickInfo
430     *magick_info;
431 
432   DecodeImageHandler
433     *decoder;
434 
435   ExceptionInfo
436     *sans_exception;
437 
438   GeometryInfo
439     geometry_info;
440 
441   Image
442     *image,
443     *next;
444 
445   ImageInfo
446     *read_info;
447 
448   MagickBooleanType
449     status;
450 
451   MagickStatusType
452     flags;
453 
454   /*
455     Determine image type from filename prefix or suffix (e.g. image.jpg).
456   */
457   assert(image_info != (ImageInfo *) NULL);
458   assert(image_info->signature == MagickCoreSignature);
459   assert(image_info->filename != (char *) NULL);
460   if (image_info->debug != MagickFalse)
461     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
462       image_info->filename);
463   assert(exception != (ExceptionInfo *) NULL);
464   read_info=CloneImageInfo(image_info);
465   (void) CopyMagickString(magick_filename,read_info->filename,MagickPathExtent);
466   (void) SetImageInfo(read_info,0,exception);
467   (void) CopyMagickString(filename,read_info->filename,MagickPathExtent);
468   (void) CopyMagickString(magick,read_info->magick,MagickPathExtent);
469   /*
470     Call appropriate image reader based on image type.
471   */
472   sans_exception=AcquireExceptionInfo();
473   magick_info=GetMagickInfo(read_info->magick,sans_exception);
474   sans_exception=DestroyExceptionInfo(sans_exception);
475   if (magick_info != (const MagickInfo *) NULL)
476     {
477       if (GetMagickEndianSupport(magick_info) == MagickFalse)
478         read_info->endian=UndefinedEndian;
479       else
480         if ((image_info->endian == UndefinedEndian) &&
481             (GetMagickRawSupport(magick_info) != MagickFalse))
482           {
483             unsigned long
484               lsb_first;
485 
486             lsb_first=1;
487             read_info->endian=(*(char *) &lsb_first) == 1 ? LSBEndian :
488               MSBEndian;
489          }
490     }
491   if ((magick_info != (const MagickInfo *) NULL) &&
492       (GetMagickDecoderSeekableStream(magick_info) != MagickFalse))
493     {
494       image=AcquireImage(read_info,exception);
495       (void) CopyMagickString(image->filename,read_info->filename,
496         MagickPathExtent);
497       status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
498       if (status == MagickFalse)
499         {
500           read_info=DestroyImageInfo(read_info);
501           image=DestroyImage(image);
502           return((Image *) NULL);
503         }
504       if (IsBlobSeekable(image) == MagickFalse)
505         {
506           /*
507             Coder requires a seekable stream.
508           */
509           *read_info->filename='\0';
510           status=ImageToFile(image,read_info->filename,exception);
511           if (status == MagickFalse)
512             {
513               (void) CloseBlob(image);
514               read_info=DestroyImageInfo(read_info);
515               image=DestroyImage(image);
516               return((Image *) NULL);
517             }
518           read_info->temporary=MagickTrue;
519         }
520       (void) CloseBlob(image);
521       image=DestroyImage(image);
522     }
523   image=NewImageList();
524   decoder=GetImageDecoder(magick_info);
525   if (decoder == (DecodeImageHandler *) NULL)
526     {
527       delegate_info=GetDelegateInfo(read_info->magick,(char *) NULL,exception);
528       if (delegate_info == (const DelegateInfo *) NULL)
529         {
530           (void) SetImageInfo(read_info,0,exception);
531           (void) CopyMagickString(read_info->filename,filename,
532             MagickPathExtent);
533           magick_info=GetMagickInfo(read_info->magick,exception);
534           decoder=GetImageDecoder(magick_info);
535         }
536     }
537   if (decoder != (DecodeImageHandler *) NULL)
538     {
539       /*
540         Call appropriate image reader based on image type.
541       */
542       if (GetMagickDecoderThreadSupport(magick_info) == MagickFalse)
543         LockSemaphoreInfo(magick_info->semaphore);
544       status=IsCoderAuthorized(read_info->magick,ReadPolicyRights,exception);
545       image=(Image *) NULL;
546       if (status != MagickFalse)
547         image=decoder(read_info,exception);
548       if (GetMagickDecoderThreadSupport(magick_info) == MagickFalse)
549         UnlockSemaphoreInfo(magick_info->semaphore);
550     }
551   else
552     {
553       delegate_info=GetDelegateInfo(read_info->magick,(char *) NULL,exception);
554       if (delegate_info == (const DelegateInfo *) NULL)
555         {
556           (void) ThrowMagickException(exception,GetMagickModule(),
557             MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
558             read_info->magick);
559           if (read_info->temporary != MagickFalse)
560             (void) RelinquishUniqueFileResource(read_info->filename);
561           read_info=DestroyImageInfo(read_info);
562           return((Image *) NULL);
563         }
564       /*
565         Let our decoding delegate process the image.
566       */
567       image=AcquireImage(read_info,exception);
568       if (image == (Image *) NULL)
569         {
570           read_info=DestroyImageInfo(read_info);
571           return((Image *) NULL);
572         }
573       (void) CopyMagickString(image->filename,read_info->filename,
574         MagickPathExtent);
575       *read_info->filename='\0';
576       if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
577         LockSemaphoreInfo(delegate_info->semaphore);
578       status=InvokeDelegate(read_info,image,read_info->magick,(char *) NULL,
579         exception);
580       if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
581         UnlockSemaphoreInfo(delegate_info->semaphore);
582       image=DestroyImageList(image);
583       read_info->temporary=MagickTrue;
584       if (status != MagickFalse)
585         (void) SetImageInfo(read_info,0,exception);
586       magick_info=GetMagickInfo(read_info->magick,exception);
587       decoder=GetImageDecoder(magick_info);
588       if (decoder == (DecodeImageHandler *) NULL)
589         {
590           if (IsPathAccessible(read_info->filename) != MagickFalse)
591             (void) ThrowMagickException(exception,GetMagickModule(),
592               MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
593               read_info->magick);
594           else
595             ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
596               read_info->filename);
597           read_info=DestroyImageInfo(read_info);
598           return((Image *) NULL);
599         }
600       /*
601         Call appropriate image reader based on image type.
602       */
603       if (GetMagickDecoderThreadSupport(magick_info) == MagickFalse)
604         LockSemaphoreInfo(magick_info->semaphore);
605       status=IsCoderAuthorized(read_info->magick,ReadPolicyRights,exception);
606       image=(Image *) NULL;
607       if (status != MagickFalse)
608         image=(decoder)(read_info,exception);
609       if (GetMagickDecoderThreadSupport(magick_info) == MagickFalse)
610         UnlockSemaphoreInfo(magick_info->semaphore);
611     }
612   if (read_info->temporary != MagickFalse)
613     {
614       (void) RelinquishUniqueFileResource(read_info->filename);
615       read_info->temporary=MagickFalse;
616       if (image != (Image *) NULL)
617         (void) CopyMagickString(image->filename,filename,MagickPathExtent);
618     }
619   if (image == (Image *) NULL)
620     {
621       read_info=DestroyImageInfo(read_info);
622       return(image);
623     }
624   if (exception->severity >= ErrorException)
625     (void) LogMagickEvent(ExceptionEvent,GetMagickModule(),
626       "Coder (%s) generated an image despite an error (%d), "
627       "notify the developers",image->magick,exception->severity);
628   if (IsBlobTemporary(image) != MagickFalse)
629     (void) RelinquishUniqueFileResource(read_info->filename);
630   if ((IsSceneGeometry(read_info->scenes,MagickFalse) != MagickFalse) &&
631       (GetImageListLength(image) != 1))
632     {
633       Image
634         *clones;
635 
636       clones=CloneImages(image,read_info->scenes,exception);
637       if (clones != (Image *) NULL)
638         {
639           image=DestroyImageList(image);
640           image=GetFirstImageInList(clones);
641         }
642     }
643   for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
644   {
645     char
646       magick_path[MagickPathExtent],
647       *property,
648       timestamp[MagickPathExtent];
649 
650     const char
651       *option;
652 
653     const StringInfo
654       *profile;
655 
656     ssize_t
657       option_type;
658 
659     next->taint=MagickFalse;
660     GetPathComponent(magick_filename,MagickPath,magick_path);
661     if (*magick_path == '\0' && *next->magick == '\0')
662       (void) CopyMagickString(next->magick,magick,MagickPathExtent);
663     (void) CopyMagickString(next->magick_filename,magick_filename,
664       MagickPathExtent);
665     if (IsBlobTemporary(image) != MagickFalse)
666       (void) CopyMagickString(next->filename,filename,MagickPathExtent);
667     if (next->magick_columns == 0)
668       next->magick_columns=next->columns;
669     if (next->magick_rows == 0)
670       next->magick_rows=next->rows;
671     value=GetImageProperty(next,"exif:Orientation",exception);
672     if (value == (char *) NULL)
673       value=GetImageProperty(next,"tiff:Orientation",exception);
674     if (value != (char *) NULL)
675       {
676         next->orientation=(OrientationType) StringToLong(value);
677         (void) DeleteImageProperty(next,"tiff:Orientation");
678         (void) DeleteImageProperty(next,"exif:Orientation");
679       }
680     value=GetImageProperty(next,"exif:XResolution",exception);
681     if (value != (char *) NULL)
682       {
683         geometry_info.rho=next->resolution.x;
684         geometry_info.sigma=1.0;
685         flags=ParseGeometry(value,&geometry_info);
686         if (geometry_info.sigma != 0)
687           next->resolution.x=geometry_info.rho/geometry_info.sigma;
688         if (strchr(value,',') != (char *) NULL)
689           next->resolution.x=geometry_info.rho+geometry_info.sigma/1000.0;
690         (void) DeleteImageProperty(next,"exif:XResolution");
691       }
692     value=GetImageProperty(next,"exif:YResolution",exception);
693     if (value != (char *) NULL)
694       {
695         geometry_info.rho=next->resolution.y;
696         geometry_info.sigma=1.0;
697         flags=ParseGeometry(value,&geometry_info);
698         if (geometry_info.sigma != 0)
699           next->resolution.y=geometry_info.rho/geometry_info.sigma;
700         if (strchr(value,',') != (char *) NULL)
701           next->resolution.y=geometry_info.rho+geometry_info.sigma/1000.0;
702         (void) DeleteImageProperty(next,"exif:YResolution");
703       }
704     value=GetImageProperty(next,"exif:ResolutionUnit",exception);
705     if (value == (char *) NULL)
706       value=GetImageProperty(next,"tiff:ResolutionUnit",exception);
707     if (value != (char *) NULL)
708       {
709         option_type=ParseCommandOption(MagickResolutionOptions,MagickFalse,
710           value);
711         if (option_type >= 0)
712           next->units=(ResolutionType) option_type;
713         (void) DeleteImageProperty(next,"exif:ResolutionUnit");
714         (void) DeleteImageProperty(next,"tiff:ResolutionUnit");
715       }
716     if (next->page.width == 0)
717       next->page.width=next->columns;
718     if (next->page.height == 0)
719       next->page.height=next->rows;
720     option=GetImageOption(read_info,"caption");
721     if (option != (const char *) NULL)
722       {
723         property=InterpretImageProperties(read_info,next,option,exception);
724         (void) SetImageProperty(next,"caption",property,exception);
725         property=DestroyString(property);
726       }
727     option=GetImageOption(read_info,"comment");
728     if (option != (const char *) NULL)
729       {
730         property=InterpretImageProperties(read_info,next,option,exception);
731         (void) SetImageProperty(next,"comment",property,exception);
732         property=DestroyString(property);
733       }
734     option=GetImageOption(read_info,"label");
735     if (option != (const char *) NULL)
736       {
737         property=InterpretImageProperties(read_info,next,option,exception);
738         (void) SetImageProperty(next,"label",property,exception);
739         property=DestroyString(property);
740       }
741     if (LocaleCompare(next->magick,"TEXT") == 0)
742       (void) ParseAbsoluteGeometry("0x0+0+0",&next->page);
743     if ((read_info->extract != (char *) NULL) &&
744         (read_info->stream == (StreamHandler) NULL))
745       {
746         RectangleInfo
747           geometry;
748 
749         SetGeometry(next,&geometry);
750         flags=ParseAbsoluteGeometry(read_info->extract,&geometry);
751         if ((next->columns != geometry.width) ||
752             (next->rows != geometry.height))
753           {
754             if (((flags & XValue) != 0) || ((flags & YValue) != 0))
755               {
756                 Image
757                   *crop_image;
758 
759                 crop_image=CropImage(next,&geometry,exception);
760                 if (crop_image != (Image *) NULL)
761                   ReplaceImageInList(&next,crop_image);
762               }
763             else
764               if (((flags & WidthValue) != 0) || ((flags & HeightValue) != 0))
765                 {
766                   Image
767                     *size_image;
768 
769                   flags=ParseRegionGeometry(next,read_info->extract,&geometry,
770                     exception);
771                   size_image=ResizeImage(next,geometry.width,geometry.height,
772                     next->filter,exception);
773                   if (size_image != (Image *) NULL)
774                     ReplaceImageInList(&next,size_image);
775                 }
776           }
777       }
778     profile=GetImageProfile(next,"icc");
779     if (profile == (const StringInfo *) NULL)
780       profile=GetImageProfile(next,"icm");
781     profile=GetImageProfile(next,"iptc");
782     if (profile == (const StringInfo *) NULL)
783       profile=GetImageProfile(next,"8bim");
784     (void) FormatMagickTime((time_t) GetBlobProperties(next)->st_mtime,
785       MagickPathExtent,timestamp);
786     (void) SetImageProperty(next,"date:modify",timestamp,exception);
787     (void) FormatMagickTime((time_t) GetBlobProperties(next)->st_ctime,
788       MagickPathExtent,timestamp);
789     (void) SetImageProperty(next,"date:create",timestamp,exception);
790     option=GetImageOption(image_info,"delay");
791     if (option != (const char *) NULL)
792       {
793         flags=ParseGeometry(option,&geometry_info);
794         if ((flags & GreaterValue) != 0)
795           {
796             if (next->delay > (size_t) floor(geometry_info.rho+0.5))
797               next->delay=(size_t) floor(geometry_info.rho+0.5);
798           }
799         else
800           if ((flags & LessValue) != 0)
801             {
802               if (next->delay < (size_t) floor(geometry_info.rho+0.5))
803                 next->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
804             }
805           else
806             next->delay=(size_t) floor(geometry_info.rho+0.5);
807         if ((flags & SigmaValue) != 0)
808           next->ticks_per_second=(ssize_t) floor(geometry_info.sigma+0.5);
809       }
810     option=GetImageOption(image_info,"dispose");
811     if (option != (const char *) NULL)
812       {
813         option_type=ParseCommandOption(MagickDisposeOptions,MagickFalse,
814           option);
815         if (option_type >= 0)
816           next->dispose=(DisposeType) option_type;
817       }
818     if (read_info->verbose != MagickFalse)
819       (void) IdentifyImage(next,stderr,MagickFalse,exception);
820     image=next;
821   }
822   read_info=DestroyImageInfo(read_info);
823   return(GetFirstImageInList(image));
824 }
825 
826 /*
827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
828 %                                                                             %
829 %                                                                             %
830 %                                                                             %
831 %   R e a d I m a g e s                                                       %
832 %                                                                             %
833 %                                                                             %
834 %                                                                             %
835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
836 %
837 %  ReadImages() reads one or more images and returns them as an image list.
838 %
839 %  The format of the ReadImage method is:
840 %
841 %      Image *ReadImages(ImageInfo *image_info,const char *filename,
842 %        ExceptionInfo *exception)
843 %
844 %  A description of each parameter follows:
845 %
846 %    o image_info: the image info.
847 %
848 %    o filename: the image filename.
849 %
850 %    o exception: return any errors or warnings in this structure.
851 %
852 */
ReadImages(ImageInfo * image_info,const char * filename,ExceptionInfo * exception)853 MagickExport Image *ReadImages(ImageInfo *image_info,const char *filename,
854   ExceptionInfo *exception)
855 {
856   char
857     read_filename[MagickPathExtent];
858 
859   Image
860     *image,
861     *images;
862 
863   ImageInfo
864     *read_info;
865 
866   /*
867     Read image list from a file.
868   */
869   assert(image_info != (ImageInfo *) NULL);
870   assert(image_info->signature == MagickCoreSignature);
871   if (image_info->debug != MagickFalse)
872     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
873       image_info->filename);
874   assert(exception != (ExceptionInfo *) NULL);
875   read_info=CloneImageInfo(image_info);
876   *read_info->magick='\0';
877   (void) SetImageOption(read_info,"filename",filename);
878   (void) CopyMagickString(read_info->filename,filename,MagickPathExtent);
879   (void) InterpretImageFilename(read_info,(Image *) NULL,filename,
880     (int) read_info->scene,read_filename,exception);
881   if (LocaleCompare(read_filename,read_info->filename) != 0)
882     {
883       ExceptionInfo
884         *sans;
885 
886       ssize_t
887         extent,
888         scene;
889 
890       /*
891         Images of the form image-%d.png[1-5].
892       */
893       sans=AcquireExceptionInfo();
894       (void) SetImageInfo(read_info,0,sans);
895       sans=DestroyExceptionInfo(sans);
896       if (read_info->number_scenes != 0)
897         {
898           (void) CopyMagickString(read_filename,read_info->filename,
899             MagickPathExtent);
900           images=NewImageList();
901           extent=(ssize_t) (read_info->scene+read_info->number_scenes);
902           scene=(ssize_t) read_info->scene;
903           for ( ; scene < (ssize_t) extent; scene++)
904           {
905             (void) InterpretImageFilename(image_info,(Image *) NULL,
906               read_filename,(int) scene,read_info->filename,exception);
907             image=ReadImage(read_info,exception);
908             if (image == (Image *) NULL)
909               continue;
910             AppendImageToList(&images,image);
911           }
912           read_info=DestroyImageInfo(read_info);
913           return(images);
914         }
915     }
916   (void) CopyMagickString(read_info->filename,filename,MagickPathExtent);
917   image=ReadImage(read_info,exception);
918   read_info=DestroyImageInfo(read_info);
919   return(image);
920 }
921 
922 /*
923 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
924 %                                                                             %
925 %                                                                             %
926 %                                                                             %
927 +   R e a d I n l i n e I m a g e                                             %
928 %                                                                             %
929 %                                                                             %
930 %                                                                             %
931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
932 %
933 %  ReadInlineImage() reads a Base64-encoded inline image or image sequence.
934 %  The method returns a NULL if there is a memory shortage or if the image
935 %  cannot be read.  On failure, a NULL image is returned and exception
936 %  describes the reason for the failure.
937 %
938 %  The format of the ReadInlineImage method is:
939 %
940 %      Image *ReadInlineImage(const ImageInfo *image_info,const char *content,
941 %        ExceptionInfo *exception)
942 %
943 %  A description of each parameter follows:
944 %
945 %    o image_info: the image info.
946 %
947 %    o content: the image encoded in Base64.
948 %
949 %    o exception: return any errors or warnings in this structure.
950 %
951 */
ReadInlineImage(const ImageInfo * image_info,const char * content,ExceptionInfo * exception)952 MagickExport Image *ReadInlineImage(const ImageInfo *image_info,
953   const char *content,ExceptionInfo *exception)
954 {
955   Image
956     *image;
957 
958   ImageInfo
959     *read_info;
960 
961   unsigned char
962     *blob;
963 
964   size_t
965     length;
966 
967   register const char
968     *p;
969 
970   /*
971     Skip over header (e.g. data:image/gif;base64,).
972   */
973   image=NewImageList();
974   for (p=content; (*p != ',') && (*p != '\0'); p++) ;
975   if (*p == '\0')
976     ThrowReaderException(CorruptImageError,"CorruptImage");
977   p++;
978   length=0;
979   blob=Base64Decode(p,&length);
980   if (length == 0)
981     {
982       blob=(unsigned char *) RelinquishMagickMemory(blob);
983       ThrowReaderException(CorruptImageError,"CorruptImage");
984     }
985   read_info=CloneImageInfo(image_info);
986   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
987     (void *) NULL);
988   *read_info->filename='\0';
989   *read_info->magick='\0';
990   image=BlobToImage(read_info,blob,length,exception);
991   blob=(unsigned char *) RelinquishMagickMemory(blob);
992   read_info=DestroyImageInfo(read_info);
993   return(image);
994 }
995 
996 /*
997 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
998 %                                                                             %
999 %                                                                             %
1000 %                                                                             %
1001 %   W r i t e I m a g e                                                       %
1002 %                                                                             %
1003 %                                                                             %
1004 %                                                                             %
1005 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1006 %
1007 %  WriteImage() writes an image or an image sequence to a file or file handle.
1008 %  If writing to a file is on disk, the name is defined by the filename member
1009 %  of the image structure.  WriteImage() returns MagickFalse is there is a
1010 %  memory shortage or if the image cannot be written.  Check the exception
1011 %  member of image to determine the cause for any failure.
1012 %
1013 %  The format of the WriteImage method is:
1014 %
1015 %      MagickBooleanType WriteImage(const ImageInfo *image_info,Image *image,
1016 %        ExceptionInfo *exception)
1017 %
1018 %  A description of each parameter follows:
1019 %
1020 %    o image_info: the image info.
1021 %
1022 %    o image: the image.
1023 %
1024 %    o exception: return any errors or warnings in this structure.
1025 %
1026 */
WriteImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)1027 MagickExport MagickBooleanType WriteImage(const ImageInfo *image_info,
1028   Image *image,ExceptionInfo *exception)
1029 {
1030   char
1031     filename[MagickPathExtent];
1032 
1033   const char
1034     *option;
1035 
1036   const DelegateInfo
1037     *delegate_info;
1038 
1039   const MagickInfo
1040     *magick_info;
1041 
1042   EncodeImageHandler
1043     *encoder;
1044 
1045   ExceptionInfo
1046     *sans_exception;
1047 
1048   ImageInfo
1049     *write_info;
1050 
1051   MagickBooleanType
1052     status,
1053     temporary;
1054 
1055   /*
1056     Determine image type from filename prefix or suffix (e.g. image.jpg).
1057   */
1058   assert(image_info != (ImageInfo *) NULL);
1059   assert(image_info->signature == MagickCoreSignature);
1060   assert(image != (Image *) NULL);
1061   if (image->debug != MagickFalse)
1062     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1063       image_info->filename);
1064   assert(image->signature == MagickCoreSignature);
1065   assert(exception != (ExceptionInfo *) NULL);
1066   sans_exception=AcquireExceptionInfo();
1067   write_info=CloneImageInfo(image_info);
1068   (void) CopyMagickString(write_info->filename,image->filename,
1069     MagickPathExtent);
1070   (void) SetImageInfo(write_info,1,sans_exception);
1071   if (*write_info->magick == '\0')
1072     (void) CopyMagickString(write_info->magick,image->magick,MagickPathExtent);
1073   (void) CopyMagickString(filename,image->filename,MagickPathExtent);
1074   (void) CopyMagickString(image->filename,write_info->filename,
1075     MagickPathExtent);
1076   /*
1077     Call appropriate image writer based on image type.
1078   */
1079   magick_info=GetMagickInfo(write_info->magick,sans_exception);
1080   sans_exception=DestroyExceptionInfo(sans_exception);
1081   if (magick_info != (const MagickInfo *) NULL)
1082     {
1083       if (GetMagickEndianSupport(magick_info) == MagickFalse)
1084         image->endian=UndefinedEndian;
1085       else
1086         if ((image_info->endian == UndefinedEndian) &&
1087             (GetMagickRawSupport(magick_info) != MagickFalse))
1088           {
1089             unsigned long
1090               lsb_first;
1091 
1092             lsb_first=1;
1093             image->endian=(*(char *) &lsb_first) == 1 ? LSBEndian : MSBEndian;
1094          }
1095     }
1096   (void) SyncImageProfiles(image);
1097   DisassociateImageStream(image);
1098   option=GetImageOption(image_info,"delegate:bimodal");
1099   if ((IsStringTrue(option) != MagickFalse) &&
1100       (write_info->page == (char *) NULL) &&
1101       (GetPreviousImageInList(image) == (Image *) NULL) &&
1102       (GetNextImageInList(image) == (Image *) NULL) &&
1103       (IsTaintImage(image) == MagickFalse) )
1104     {
1105       delegate_info=GetDelegateInfo(image->magick,write_info->magick,exception);
1106       if ((delegate_info != (const DelegateInfo *) NULL) &&
1107           (GetDelegateMode(delegate_info) == 0) &&
1108           (IsPathAccessible(image->magick_filename) != MagickFalse))
1109         {
1110           /*
1111             Process image with bi-modal delegate.
1112           */
1113           (void) CopyMagickString(image->filename,image->magick_filename,
1114             MagickPathExtent);
1115           status=InvokeDelegate(write_info,image,image->magick,
1116             write_info->magick,exception);
1117           write_info=DestroyImageInfo(write_info);
1118           (void) CopyMagickString(image->filename,filename,MagickPathExtent);
1119           return(status);
1120         }
1121     }
1122   status=MagickFalse;
1123   temporary=MagickFalse;
1124   if ((magick_info != (const MagickInfo *) NULL) &&
1125       (GetMagickEncoderSeekableStream(magick_info) != MagickFalse))
1126     {
1127       char
1128         image_filename[MagickPathExtent];
1129 
1130       (void) CopyMagickString(image_filename,image->filename,MagickPathExtent);
1131       status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1132       (void) CopyMagickString(image->filename, image_filename,MagickPathExtent);
1133       if (status != MagickFalse)
1134         {
1135           if (IsBlobSeekable(image) == MagickFalse)
1136             {
1137               /*
1138                 A seekable stream is required by the encoder.
1139               */
1140               write_info->adjoin=MagickTrue;
1141               (void) CopyMagickString(write_info->filename,image->filename,
1142                 MagickPathExtent);
1143               (void) AcquireUniqueFilename(image->filename);
1144               temporary=MagickTrue;
1145             }
1146           (void) CloseBlob(image);
1147         }
1148     }
1149   encoder=GetImageEncoder(magick_info);
1150   if (encoder != (EncodeImageHandler *) NULL)
1151     {
1152       /*
1153         Call appropriate image writer based on image type.
1154       */
1155       if (GetMagickEncoderThreadSupport(magick_info) == MagickFalse)
1156         LockSemaphoreInfo(magick_info->semaphore);
1157       status=IsCoderAuthorized(write_info->magick,WritePolicyRights,exception);
1158       if (status != MagickFalse)
1159         status=encoder(write_info,image,exception);
1160       if (GetMagickEncoderThreadSupport(magick_info) == MagickFalse)
1161         UnlockSemaphoreInfo(magick_info->semaphore);
1162     }
1163   else
1164     {
1165       delegate_info=GetDelegateInfo((char *) NULL,write_info->magick,exception);
1166       if (delegate_info != (DelegateInfo *) NULL)
1167         {
1168           /*
1169             Process the image with delegate.
1170           */
1171           *write_info->filename='\0';
1172           if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
1173             LockSemaphoreInfo(delegate_info->semaphore);
1174           status=InvokeDelegate(write_info,image,(char *) NULL,
1175             write_info->magick,exception);
1176           if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
1177             UnlockSemaphoreInfo(delegate_info->semaphore);
1178           (void) CopyMagickString(image->filename,filename,MagickPathExtent);
1179         }
1180       else
1181         {
1182           sans_exception=AcquireExceptionInfo();
1183           magick_info=GetMagickInfo(write_info->magick,sans_exception);
1184           sans_exception=DestroyExceptionInfo(sans_exception);
1185           if ((write_info->affirm == MagickFalse) &&
1186               (magick_info == (const MagickInfo *) NULL))
1187             {
1188               (void) CopyMagickString(write_info->magick,image->magick,
1189                 MagickPathExtent);
1190               magick_info=GetMagickInfo(write_info->magick,exception);
1191             }
1192           encoder=GetImageEncoder(magick_info);
1193           if (encoder == (EncodeImageHandler *) NULL)
1194             {
1195               char
1196                 extension[MagickPathExtent];
1197 
1198               GetPathComponent(image->filename,ExtensionPath,extension);
1199               if (*extension != '\0')
1200                 magick_info=GetMagickInfo(extension,exception);
1201               else
1202                 magick_info=GetMagickInfo(image->magick,exception);
1203               (void) CopyMagickString(image->filename,filename,
1204                 MagickPathExtent);
1205               encoder=GetImageEncoder(magick_info);
1206             }
1207           if (encoder == (EncodeImageHandler *) NULL)
1208             {
1209               magick_info=GetMagickInfo(image->magick,exception);
1210               encoder=GetImageEncoder(magick_info);
1211               if (encoder == (EncodeImageHandler *) NULL)
1212                 (void) ThrowMagickException(exception,GetMagickModule(),
1213                   MissingDelegateError,"NoEncodeDelegateForThisImageFormat",
1214                   "`%s'",write_info->magick);
1215             }
1216           if (encoder != (EncodeImageHandler *) NULL)
1217             {
1218               /*
1219                 Call appropriate image writer based on image type.
1220               */
1221               if (GetMagickEncoderThreadSupport(magick_info) == MagickFalse)
1222                 LockSemaphoreInfo(magick_info->semaphore);
1223               status=IsCoderAuthorized(write_info->magick,WritePolicyRights,
1224                 exception);
1225               if (status != MagickFalse)
1226                 status=encoder(write_info,image,exception);
1227               if (GetMagickEncoderThreadSupport(magick_info) == MagickFalse)
1228                 UnlockSemaphoreInfo(magick_info->semaphore);
1229             }
1230         }
1231     }
1232   if (temporary != MagickFalse)
1233     {
1234       /*
1235         Copy temporary image file to permanent.
1236       */
1237       status=OpenBlob(write_info,image,ReadBinaryBlobMode,exception);
1238       if (status != MagickFalse)
1239         {
1240           (void) RelinquishUniqueFileResource(write_info->filename);
1241           status=ImageToFile(image,write_info->filename,exception);
1242         }
1243       (void) CloseBlob(image);
1244       (void) RelinquishUniqueFileResource(image->filename);
1245       (void) CopyMagickString(image->filename,write_info->filename,
1246         MagickPathExtent);
1247     }
1248   if ((LocaleCompare(write_info->magick,"info") != 0) &&
1249       (write_info->verbose != MagickFalse))
1250     (void) IdentifyImage(image,stdout,MagickFalse,exception);
1251   write_info=DestroyImageInfo(write_info);
1252   return(status);
1253 }
1254 
1255 /*
1256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1257 %                                                                             %
1258 %                                                                             %
1259 %                                                                             %
1260 %   W r i t e I m a g e s                                                     %
1261 %                                                                             %
1262 %                                                                             %
1263 %                                                                             %
1264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1265 %
1266 %  WriteImages() writes an image sequence into one or more files.  While
1267 %  WriteImage() can write an image sequence, it is limited to writing
1268 %  the sequence into a single file using a format which supports multiple
1269 %  frames.   WriteImages(), however, does not have this limitation, instead it
1270 %  generates multiple output files if necessary (or when requested).  When
1271 %  ImageInfo's adjoin flag is set to MagickFalse, the file name is expected
1272 %  to include a printf-style formatting string for the frame number (e.g.
1273 %  "image%02d.png").
1274 %
1275 %  The format of the WriteImages method is:
1276 %
1277 %      MagickBooleanType WriteImages(const ImageInfo *image_info,Image *images,
1278 %        const char *filename,ExceptionInfo *exception)
1279 %
1280 %  A description of each parameter follows:
1281 %
1282 %    o image_info: the image info.
1283 %
1284 %    o images: the image list.
1285 %
1286 %    o filename: the image filename.
1287 %
1288 %    o exception: return any errors or warnings in this structure.
1289 %
1290 */
WriteImages(const ImageInfo * image_info,Image * images,const char * filename,ExceptionInfo * exception)1291 MagickExport MagickBooleanType WriteImages(const ImageInfo *image_info,
1292   Image *images,const char *filename,ExceptionInfo *exception)
1293 {
1294 #define WriteImageTag  "Write/Image"
1295 
1296   ExceptionInfo
1297     *sans_exception;
1298 
1299   ImageInfo
1300     *write_info;
1301 
1302   MagickBooleanType
1303     proceed;
1304 
1305   MagickOffsetType
1306     progress;
1307 
1308   MagickProgressMonitor
1309     progress_monitor;
1310 
1311   MagickSizeType
1312     number_images;
1313 
1314   MagickStatusType
1315     status;
1316 
1317   register Image
1318     *p;
1319 
1320   assert(image_info != (const ImageInfo *) NULL);
1321   assert(image_info->signature == MagickCoreSignature);
1322   assert(images != (Image *) NULL);
1323   assert(images->signature == MagickCoreSignature);
1324   if (images->debug != MagickFalse)
1325     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1326   assert(exception != (ExceptionInfo *) NULL);
1327   write_info=CloneImageInfo(image_info);
1328   *write_info->magick='\0';
1329   images=GetFirstImageInList(images);
1330   if (filename != (const char *) NULL)
1331     for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1332       (void) CopyMagickString(p->filename,filename,MagickPathExtent);
1333   (void) CopyMagickString(write_info->filename,images->filename,
1334     MagickPathExtent);
1335   sans_exception=AcquireExceptionInfo();
1336   (void) SetImageInfo(write_info,(unsigned int) GetImageListLength(images),
1337     sans_exception);
1338   sans_exception=DestroyExceptionInfo(sans_exception);
1339   if (*write_info->magick == '\0')
1340     (void) CopyMagickString(write_info->magick,images->magick,MagickPathExtent);
1341   p=images;
1342   for ( ; GetNextImageInList(p) != (Image *) NULL; p=GetNextImageInList(p))
1343   {
1344     register Image
1345       *next;
1346 
1347     next=GetNextImageInList(p);
1348     if (next == (Image *) NULL)
1349       break;
1350     if (p->scene >= next->scene)
1351       {
1352         register ssize_t
1353           i;
1354 
1355         /*
1356           Generate consistent scene numbers.
1357         */
1358         i=(ssize_t) images->scene;
1359         for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1360           p->scene=(size_t) i++;
1361         break;
1362       }
1363   }
1364   /*
1365     Write images.
1366   */
1367   status=MagickTrue;
1368   progress_monitor=(MagickProgressMonitor) NULL;
1369   progress=0;
1370   number_images=GetImageListLength(images);
1371   for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1372   {
1373     if (number_images != 1)
1374       progress_monitor=SetImageProgressMonitor(p,(MagickProgressMonitor) NULL,
1375         p->client_data);
1376     status&=WriteImage(write_info,p,exception);
1377     if (number_images != 1)
1378       (void) SetImageProgressMonitor(p,progress_monitor,p->client_data);
1379     if (write_info->adjoin != MagickFalse)
1380       break;
1381     if (number_images != 1)
1382       {
1383 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1384         #pragma omp atomic
1385 #endif
1386         progress++;
1387         proceed=SetImageProgress(p,WriteImageTag,progress,number_images);
1388         if (proceed == MagickFalse)
1389           break;
1390       }
1391   }
1392   write_info=DestroyImageInfo(write_info);
1393   return(status != 0 ? MagickTrue : MagickFalse);
1394 }
1395