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