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