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