]> 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/delegate.h"
51 #include "MagickCore/geometry.h"
52 #include "MagickCore/identify.h"
53 #include "MagickCore/image-private.h"
54 #include "MagickCore/list.h"
55 #include "MagickCore/magick.h"
56 #include "MagickCore/memory_.h"
57 #include "MagickCore/monitor.h"
58 #include "MagickCore/monitor-private.h"
59 #include "MagickCore/option.h"
60 #include "MagickCore/pixel.h"
61 #include "MagickCore/pixel-accessor.h"
62 #include "MagickCore/policy.h"
63 #include "MagickCore/profile.h"
64 #include "MagickCore/property.h"
65 #include "MagickCore/quantum.h"
66 #include "MagickCore/resize.h"
67 #include "MagickCore/resource_.h"
68 #include "MagickCore/semaphore.h"
69 #include "MagickCore/statistic.h"
70 #include "MagickCore/stream.h"
71 #include "MagickCore/string_.h"
72 #include "MagickCore/string-private.h"
73 #include "MagickCore/timer.h"
74 #include "MagickCore/transform.h"
75 #include "MagickCore/utility.h"
76 \f
77 static SemaphoreInfo
78   *constitute_semaphore = (SemaphoreInfo *) NULL;
79 \f
80 /*
81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82 %                                                                             %
83 %                                                                             %
84 %                                                                             %
85 +   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                       %
86 %                                                                             %
87 %                                                                             %
88 %                                                                             %
89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 %
91 %  ConstituteComponentGenesis() instantiates the constitute component.
92 %
93 %  The format of the ConstituteComponentGenesis method is:
94 %
95 %      MagickBooleanType ConstituteComponentGenesis(void)
96 %
97 */
98 MagickExport MagickBooleanType ConstituteComponentGenesis(void)
99 {
100   AcquireSemaphoreInfo(&constitute_semaphore);
101   return(MagickTrue);
102 }
103 \f
104 /*
105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 %                                                                             %
107 %                                                                             %
108 %                                                                             %
109 +   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                     %
110 %                                                                             %
111 %                                                                             %
112 %                                                                             %
113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 %
115 %  ConstituteComponentTerminus() destroys the constitute component.
116 %
117 %  The format of the ConstituteComponentTerminus method is:
118 %
119 %      ConstituteComponentTerminus(void)
120 %
121 */
122 MagickExport void ConstituteComponentTerminus(void)
123 {
124   if (constitute_semaphore == (SemaphoreInfo *) NULL)
125     AcquireSemaphoreInfo(&constitute_semaphore);
126   DestroySemaphoreInfo(&constitute_semaphore);
127 }
128 \f
129 /*
130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131 %                                                                             %
132 %                                                                             %
133 %                                                                             %
134 %   C o n s t i t u t e I m a g e                                             %
135 %                                                                             %
136 %                                                                             %
137 %                                                                             %
138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139 %
140 %  ConstituteImage() returns an image from the pixel data you supply.
141 %  The pixel data must be in scanline order top-to-bottom.  The data can be
142 %  char, short int, int, float, or double.  Float and double require the
143 %  pixels to be normalized [0..1], otherwise [0..QuantumRange].  For example, to
144 %  create a 640x480 image from unsigned red-green-blue character data, use:
145 %
146 %      image = ConstituteImage(640,480,"RGB",CharPixel,pixels,&exception);
147 %
148 %  The format of the ConstituteImage method is:
149 %
150 %      Image *ConstituteImage(const size_t columns,const size_t rows,
151 %        const char *map,const StorageType storage,const void *pixels,
152 %        ExceptionInfo *exception)
153 %
154 %  A description of each parameter follows:
155 %
156 %    o columns: width in pixels of the image.
157 %
158 %    o rows: height in pixels of the image.
159 %
160 %    o map:  This string reflects the expected ordering of the pixel array.
161 %      It can be any combination or order of R = red, G = green, B = blue,
162 %      A = alpha (0 is transparent), O = opacity (0 is opaque), C = cyan,
163 %      Y = yellow, M = magenta, K = black, I = intensity (for grayscale),
164 %      P = pad.
165 %
166 %    o storage: Define the data type of the pixels.  Float and double types are
167 %      expected to be normalized [0..1] otherwise [0..QuantumRange].  Choose
168 %      from these types: CharPixel, DoublePixel, FloatPixel, IntegerPixel,
169 %      LongPixel, QuantumPixel, or ShortPixel.
170 %
171 %    o pixels: This array of values contain the pixel components as defined by
172 %      map and type.  You must preallocate this array where the expected
173 %      length varies depending on the values of width, height, map, and type.
174 %
175 %    o exception: return any errors or warnings in this structure.
176 %
177 */
178 MagickExport Image *ConstituteImage(const size_t columns,
179   const size_t rows,const char *map,const StorageType storage,
180   const void *pixels,ExceptionInfo *exception)
181 {
182   Image
183     *image;
184
185   MagickBooleanType
186     status;
187
188   /*
189     Allocate image structure.
190   */
191   assert(map != (const char *) NULL);
192   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",map);
193   assert(pixels != (void *) NULL);
194   assert(exception != (ExceptionInfo *) NULL);
195   assert(exception->signature == MagickSignature);
196   image=AcquireImage((ImageInfo *) NULL);
197   if (image == (Image *) NULL)
198     return((Image *) NULL);
199   if ((columns == 0) || (rows == 0))
200     ThrowImageException(OptionError,"NonZeroWidthAndHeightRequired");
201   image->columns=columns;
202   image->rows=rows;
203   (void) SetImageBackgroundColor(image);
204   status=ImportImagePixels(image,0,0,columns,rows,map,storage,pixels);
205   if (status == MagickFalse)
206     {
207       InheritException(exception,&image->exception);
208       image=DestroyImage(image);
209     }
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);
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);
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);
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);
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);
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);
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);
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 %
975 %  A description of each parameter follows:
976 %
977 %    o image_info: the image info.
978 %
979 %    o image: the image.
980 %
981 */
982 MagickExport MagickBooleanType WriteImage(const ImageInfo *image_info,
983   Image *image)
984 {
985   char
986     filename[MaxTextExtent];
987
988   const char
989     *option;
990
991   const DelegateInfo
992     *delegate_info;
993
994   const MagickInfo
995     *magick_info;
996
997   ExceptionInfo
998     *sans_exception;
999
1000   ImageInfo
1001     *write_info;
1002
1003   MagickBooleanType
1004     status,
1005     temporary;
1006
1007   MagickStatusType
1008     thread_support;
1009
1010   PolicyDomain
1011     domain;
1012
1013   PolicyRights
1014     rights;
1015
1016   /*
1017     Determine image type from filename prefix or suffix (e.g. image.jpg).
1018   */
1019   assert(image_info != (ImageInfo *) NULL);
1020   assert(image_info->signature == MagickSignature);
1021   if (image->debug != MagickFalse)
1022     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1023       image_info->filename);
1024   assert(image != (Image *) NULL);
1025   assert(image->signature == MagickSignature);
1026   sans_exception=AcquireExceptionInfo();
1027   write_info=CloneImageInfo(image_info);
1028   (void) CopyMagickString(write_info->filename,image->filename,MaxTextExtent);
1029   if (*write_info->magick == '\0')
1030     (void) CopyMagickString(write_info->magick,image->magick,MaxTextExtent);
1031   (void) SetImageInfo(write_info,1,sans_exception);
1032   if (LocaleCompare(write_info->magick,"clipmask") == 0)
1033     {
1034       if (image->clip_mask == (Image *) NULL)
1035         {
1036           (void) ThrowMagickException(&image->exception,GetMagickModule(),
1037             OptionError,"NoClipPathDefined","`%s'",image->filename);
1038           return(MagickFalse);
1039         }
1040       image=image->clip_mask;
1041       (void) SetImageInfo(write_info,1,sans_exception);
1042     }
1043   (void) CopyMagickString(filename,image->filename,MaxTextExtent);
1044   (void) CopyMagickString(image->filename,write_info->filename,MaxTextExtent);
1045   domain=CoderPolicyDomain;
1046   rights=WritePolicyRights;
1047   if (IsRightsAuthorized(domain,rights,write_info->magick) == MagickFalse)
1048     {
1049       sans_exception=DestroyExceptionInfo(sans_exception);
1050       errno=EPERM;
1051       ThrowBinaryException(PolicyError,"NotAuthorized",filename);
1052     }
1053   magick_info=GetMagickInfo(write_info->magick,sans_exception);
1054   sans_exception=DestroyExceptionInfo(sans_exception);
1055   if (magick_info != (const MagickInfo *) NULL)
1056     {
1057       if (GetMagickEndianSupport(magick_info) == MagickFalse)
1058         image->endian=UndefinedEndian;
1059       else
1060         if ((image_info->endian == UndefinedEndian) &&
1061             (GetMagickRawSupport(magick_info) != MagickFalse))
1062           {
1063             size_t
1064               lsb_first;
1065
1066             lsb_first=1;
1067             image->endian=(*(char *) &lsb_first) == 1 ? LSBEndian : MSBEndian;
1068          }
1069     }
1070   (void) SyncImageProfiles(image);
1071   option=GetImageOption(image_info,"delegate:bimodal");
1072   if ((option != (const char *) NULL) &&
1073       (IsMagickTrue(option) != MagickFalse) &&
1074       (write_info->page == (char *) NULL) &&
1075       (GetPreviousImageInList(image) == (Image *) NULL) &&
1076       (GetNextImageInList(image) == (Image *) NULL) &&
1077       (IsTaintImage(image) == MagickFalse))
1078     {
1079       delegate_info=GetDelegateInfo(image->magick,write_info->magick,
1080         &image->exception);
1081       if ((delegate_info != (const DelegateInfo *) NULL) &&
1082           (GetDelegateMode(delegate_info) == 0) &&
1083           (IsPathAccessible(image->magick_filename) != MagickFalse))
1084         {
1085           /*
1086             Process image with bi-modal delegate.
1087           */
1088           (void) CopyMagickString(image->filename,image->magick_filename,
1089             MaxTextExtent);
1090           status=InvokeDelegate(write_info,image,image->magick,
1091             write_info->magick,&image->exception);
1092           write_info=DestroyImageInfo(write_info);
1093           (void) CopyMagickString(image->filename,filename,MaxTextExtent);
1094           return(status);
1095         }
1096     }
1097   status=MagickFalse;
1098   temporary=MagickFalse;
1099   if ((magick_info != (const MagickInfo *) NULL) &&
1100       (GetMagickSeekableStream(magick_info) != MagickFalse))
1101     {
1102       char
1103         filename[MaxTextExtent];
1104
1105       (void) CopyMagickString(filename,image->filename,MaxTextExtent);
1106       status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1107       (void) CopyMagickString(image->filename,filename,MaxTextExtent);
1108       if (status != MagickFalse)
1109         {
1110           if (IsBlobSeekable(image) == MagickFalse)
1111             {
1112               /*
1113                 A seekable stream is required by the encoder.
1114               */
1115               write_info->adjoin=MagickTrue;
1116               (void) CopyMagickString(write_info->filename,image->filename,
1117                 MaxTextExtent);
1118               (void) AcquireUniqueFilename(image->filename);
1119               temporary=MagickTrue;
1120             }
1121           (void) CloseBlob(image);
1122         }
1123     }
1124   if (constitute_semaphore == (SemaphoreInfo *) NULL)
1125     AcquireSemaphoreInfo(&constitute_semaphore);
1126   if ((magick_info != (const MagickInfo *) NULL) &&
1127       (GetImageEncoder(magick_info) != (EncodeImageHandler *) NULL))
1128     {
1129       /*
1130         Call appropriate image writer based on image type.
1131       */
1132       thread_support=GetMagickThreadSupport(magick_info);
1133       if ((thread_support & EncoderThreadSupport) == 0)
1134         LockSemaphoreInfo(constitute_semaphore);
1135       status=GetImageEncoder(magick_info)(write_info,image);
1136       if ((thread_support & EncoderThreadSupport) == 0)
1137         UnlockSemaphoreInfo(constitute_semaphore);
1138     }
1139   else
1140     {
1141       delegate_info=GetDelegateInfo((char *) NULL,write_info->magick,
1142         &image->exception);
1143       if (delegate_info != (DelegateInfo *) NULL)
1144         {
1145           /*
1146             Process the image with delegate.
1147           */
1148           *write_info->filename='\0';
1149           if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
1150             LockSemaphoreInfo(constitute_semaphore);
1151           status=InvokeDelegate(write_info,image,(char *) NULL,
1152             write_info->magick,&image->exception);
1153           if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
1154             UnlockSemaphoreInfo(constitute_semaphore);
1155           (void) CopyMagickString(image->filename,filename,MaxTextExtent);
1156         }
1157       else
1158         {
1159           sans_exception=AcquireExceptionInfo();
1160           magick_info=GetMagickInfo(write_info->magick,sans_exception);
1161           sans_exception=DestroyExceptionInfo(sans_exception);
1162           if ((write_info->affirm == MagickFalse) &&
1163               (magick_info == (const MagickInfo *) NULL))
1164             {
1165               (void) CopyMagickString(write_info->magick,image->magick,
1166                 MaxTextExtent);
1167               magick_info=GetMagickInfo(write_info->magick,&image->exception);
1168             }
1169           if ((magick_info == (const MagickInfo *) NULL) ||
1170               (GetImageEncoder(magick_info) == (EncodeImageHandler *) NULL))
1171             {
1172               char
1173                 extension[MaxTextExtent];
1174
1175               GetPathComponent(image->filename,ExtensionPath,extension);
1176               if (*extension != '\0')
1177                 magick_info=GetMagickInfo(extension,&image->exception);
1178               else
1179                 magick_info=GetMagickInfo(image->magick,&image->exception);
1180               (void) CopyMagickString(image->filename,filename,MaxTextExtent);
1181             }
1182           if ((magick_info == (const MagickInfo *) NULL) ||
1183               (GetImageEncoder(magick_info) == (EncodeImageHandler *) NULL))
1184             (void) ThrowMagickException(&image->exception,GetMagickModule(),
1185               MissingDelegateError,"NoEncodeDelegateForThisImageFormat","`%s'",
1186               image->filename);
1187           else
1188             {
1189               /*
1190                 Call appropriate image writer based on image type.
1191               */
1192               thread_support=GetMagickThreadSupport(magick_info);
1193               if ((thread_support & EncoderThreadSupport) == 0)
1194                 LockSemaphoreInfo(constitute_semaphore);
1195               status=GetImageEncoder(magick_info)(write_info,image);
1196               if ((thread_support & EncoderThreadSupport) == 0)
1197                 UnlockSemaphoreInfo(constitute_semaphore);
1198             }
1199         }
1200     }
1201   if (GetBlobError(image) != MagickFalse)
1202     ThrowFileException(&image->exception,FileOpenError,
1203       "AnErrorHasOccurredWritingToFile",image->filename);
1204   if (temporary == MagickTrue)
1205     {
1206       /*
1207         Copy temporary image file to permanent.
1208       */
1209       status=OpenBlob(write_info,image,ReadBinaryBlobMode,&image->exception);
1210       if (status != MagickFalse)
1211         status=ImageToFile(image,write_info->filename,&image->exception);
1212       (void) CloseBlob(image);
1213       (void) RelinquishUniqueFileResource(image->filename);
1214       (void) CopyMagickString(image->filename,write_info->filename,
1215         MaxTextExtent);
1216     }
1217   if ((LocaleCompare(write_info->magick,"info") != 0) &&
1218       (write_info->verbose != MagickFalse))
1219     (void) IdentifyImage(image,stdout,MagickFalse);
1220   write_info=DestroyImageInfo(write_info);
1221   return(status);
1222 }
1223 \f
1224 /*
1225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1226 %                                                                             %
1227 %                                                                             %
1228 %                                                                             %
1229 %   W r i t e I m a g e s                                                     %
1230 %                                                                             %
1231 %                                                                             %
1232 %                                                                             %
1233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1234 %
1235 %  WriteImages() writes an image sequence into one or more files.  While
1236 %  WriteImage() can write an image sequence, it is limited to writing
1237 %  the sequence into a single file using a format which supports multiple
1238 %  frames.   WriteImages(), however, does not have this limitation, instead it
1239 %  generates multiple output files if necessary (or when requested).  When
1240 %  ImageInfo's adjoin flag is set to MagickFalse, the file name is expected
1241 %  to include a printf-style formatting string for the frame number (e.g.
1242 %  "image%02d.png").
1243 %
1244 %  The format of the WriteImages method is:
1245 %
1246 %      MagickBooleanType WriteImages(const ImageInfo *image_info,Image *images,
1247 %        const char *filename,ExceptionInfo *exception)
1248 %
1249 %  A description of each parameter follows:
1250 %
1251 %    o image_info: the image info.
1252 %
1253 %    o images: the image list.
1254 %
1255 %    o filename: the image filename.
1256 %
1257 %    o exception: return any errors or warnings in this structure.
1258 %
1259 */
1260 MagickExport MagickBooleanType WriteImages(const ImageInfo *image_info,
1261   Image *images,const char *filename,ExceptionInfo *exception)
1262 {
1263 #define WriteImageTag  "Write/Image"
1264
1265   BlobInfo
1266     *blob;
1267
1268   ExceptionInfo
1269     *sans_exception;
1270
1271   ImageInfo
1272     *write_info;
1273
1274   MagickBooleanType
1275     proceed;
1276
1277   MagickOffsetType
1278     i;
1279
1280   MagickProgressMonitor
1281     progress_monitor;
1282
1283   MagickSizeType
1284     number_images;
1285
1286   MagickStatusType
1287     status;
1288
1289   register Image
1290     *p;
1291
1292   assert(image_info != (const ImageInfo *) NULL);
1293   assert(image_info->signature == MagickSignature);
1294   assert(images != (Image *) NULL);
1295   assert(images->signature == MagickSignature);
1296   if (images->debug != MagickFalse)
1297     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1298   assert(exception != (ExceptionInfo *) NULL);
1299   write_info=CloneImageInfo(image_info);
1300   images=GetFirstImageInList(images);
1301   blob=CloneBlobInfo(images->blob);  /* thread specific I/O handler */
1302   DestroyBlob(images);
1303   images->blob=blob;
1304   if (filename != (const char *) NULL)
1305     for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1306       (void) CopyMagickString(p->filename,filename,MaxTextExtent);
1307   (void) CopyMagickString(write_info->filename,images->filename,MaxTextExtent);
1308   if (*write_info->magick == '\0')
1309     (void) CopyMagickString(write_info->magick,images->magick,MaxTextExtent);
1310   sans_exception=AcquireExceptionInfo();
1311   (void) SetImageInfo(write_info,(unsigned int) GetImageListLength(images),
1312     sans_exception);
1313   sans_exception=DestroyExceptionInfo(sans_exception);
1314   p=images;
1315   for ( ; GetNextImageInList(p) != (Image *) NULL; p=GetNextImageInList(p))
1316     if (p->scene >= GetNextImageInList(p)->scene)
1317       {
1318         register ssize_t
1319           i;
1320
1321         /*
1322           Generate consistent scene numbers.
1323         */
1324         i=(ssize_t) images->scene;
1325         for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1326           p->scene=(size_t) i++;
1327         break;
1328       }
1329   /*
1330     Write images.
1331   */
1332   status=MagickTrue;
1333   progress_monitor=(MagickProgressMonitor) NULL;
1334   i=0;
1335   number_images=GetImageListLength(images);
1336   for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1337   {
1338     if (number_images != 1)
1339       progress_monitor=SetImageProgressMonitor(p,(MagickProgressMonitor) NULL,
1340         p->client_data);
1341     status&=WriteImage(write_info,p);
1342     GetImageException(p,exception);
1343     if (number_images != 1)
1344       (void) SetImageProgressMonitor(p,progress_monitor,p->client_data);
1345     if (write_info->adjoin != MagickFalse)
1346       break;
1347     if (number_images != 1)
1348       {
1349         proceed=SetImageProgress(p,WriteImageTag,i++,number_images);
1350         if (proceed == MagickFalse)
1351           break;
1352       }
1353   }
1354   write_info=DestroyImageInfo(write_info);
1355   return(status != 0 ? MagickTrue : MagickFalse);
1356 }