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