MagickCore 6.9.13
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 ResetTimer(&image->timer);
299 if (ping_info->verbose != MagickFalse)
300 (void) IdentifyImage(image,stdout,MagickFalse);
301 }
302 ping_info=DestroyImageInfo(ping_info);
303 return(image);
304}
305
306/*
307%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
308% %
309% %
310% %
311% P i n g I m a g e s %
312% %
313% %
314% %
315%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316%
317% PingImages() pings one or more images and returns them as an image list.
318%
319% The format of the PingImage method is:
320%
321% Image *PingImages(const ImageInfo *image_info,ExceptionInfo *exception)
322%
323% A description of each parameter follows:
324%
325% o image_info: the image info.
326%
327% o exception: return any errors or warnings in this structure.
328%
329*/
330MagickExport Image *PingImages(const ImageInfo *image_info,
331 ExceptionInfo *exception)
332{
333 char
334 filename[MaxTextExtent];
335
336 Image
337 *image,
338 *images;
339
340 ImageInfo
341 *read_info;
342
343 /*
344 Ping image list from a file.
345 */
346 assert(image_info != (ImageInfo *) NULL);
347 assert(image_info->signature == MagickCoreSignature);
348 assert(exception != (ExceptionInfo *) NULL);
349 if (IsEventLogging() != MagickFalse)
350 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
351 image_info->filename);
352 (void) InterpretImageFilename(image_info,(Image *) NULL,image_info->filename,
353 (int) image_info->scene,filename);
354 if (LocaleCompare(filename,image_info->filename) != 0)
355 {
356 ExceptionInfo
357 *sans;
358
359 ssize_t
360 extent,
361 scene;
362
363 /*
364 Images of the form image-%d.png[1-5].
365 */
366 read_info=CloneImageInfo(image_info);
367 sans=AcquireExceptionInfo();
368 (void) SetImageInfo(read_info,0,sans);
369 sans=DestroyExceptionInfo(sans);
370 if (read_info->number_scenes == 0)
371 {
372 read_info=DestroyImageInfo(read_info);
373 return(PingImage(image_info,exception));
374 }
375 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
376 images=NewImageList();
377 extent=(ssize_t) (read_info->scene+read_info->number_scenes);
378 for (scene=(ssize_t) read_info->scene; scene < (ssize_t) extent; scene++)
379 {
380 (void) InterpretImageFilename(image_info,(Image *) NULL,filename,(int)
381 scene,read_info->filename);
382 image=PingImage(read_info,exception);
383 if (image == (Image *) NULL)
384 continue;
385 AppendImageToList(&images,image);
386 }
387 read_info=DestroyImageInfo(read_info);
388 return(images);
389 }
390 return(PingImage(image_info,exception));
391}
392
393/*
394%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
395% %
396% %
397% %
398% R e a d I m a g e %
399% %
400% %
401% %
402%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
403%
404% ReadImage() reads an image or image sequence from a file or file handle.
405% The method returns a NULL if there is a memory shortage or if the image
406% cannot be read. On failure, a NULL image is returned and exception
407% describes the reason for the failure.
408%
409% The format of the ReadImage method is:
410%
411% Image *ReadImage(const ImageInfo *image_info,ExceptionInfo *exception)
412%
413% A description of each parameter follows:
414%
415% o image_info: Read the image defined by the file or filename members of
416% this structure.
417%
418% o exception: return any errors or warnings in this structure.
419%
420*/
421
422static MagickBooleanType IsCoderAuthorized(const char *module,
423 const char *coder,const PolicyRights rights,ExceptionInfo *exception)
424{
425 if (IsRightsAuthorized(CoderPolicyDomain,rights,coder) == MagickFalse)
426 {
427 errno=EPERM;
428 (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
429 "NotAuthorized","`%s'",coder);
430 return(MagickFalse);
431 }
432 if (IsRightsAuthorized(ModulePolicyDomain,rights,module) == MagickFalse)
433 {
434 errno=EPERM;
435 (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
436 "NotAuthorized","`%s'",module);
437 return(MagickFalse);
438 }
439 return(MagickTrue);
440}
441
442MagickExport Image *ReadImage(const ImageInfo *image_info,
443 ExceptionInfo *exception)
444{
445 char
446 filename[MaxTextExtent],
447 magick[MaxTextExtent],
448 magick_filename[MaxTextExtent];
449
450 const char
451 *value;
452
453 const DelegateInfo
454 *delegate_info;
455
456 const MagickInfo
457 *magick_info;
458
459 ExceptionInfo
460 *sans_exception;
461
462 GeometryInfo
463 geometry_info;
464
465 Image
466 *image,
467 *next;
468
469 ImageInfo
470 *read_info;
471
472 MagickBooleanType
473 status;
474
475 MagickStatusType
476 flags,
477 thread_support;
478
479 /*
480 Determine image type from filename prefix or suffix (e.g. image.jpg).
481 */
482 assert(image_info != (ImageInfo *) NULL);
483 assert(image_info->signature == MagickCoreSignature);
484 assert(image_info->filename != (char *) NULL);
485 assert(exception != (ExceptionInfo *) NULL);
486 if (IsEventLogging() != MagickFalse)
487 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
488 image_info->filename);
489 read_info=CloneImageInfo(image_info);
490 (void) CopyMagickString(magick_filename,read_info->filename,MaxTextExtent);
491 (void) SetImageInfo(read_info,0,exception);
492 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
493 (void) CopyMagickString(magick,read_info->magick,MaxTextExtent);
494 /*
495 Call appropriate image reader based on image type.
496 */
497 sans_exception=AcquireExceptionInfo();
498 magick_info=GetMagickInfo(read_info->magick,sans_exception);
499 if (sans_exception->severity == PolicyError)
500 magick_info=GetMagickInfo(read_info->magick,exception);
501 sans_exception=DestroyExceptionInfo(sans_exception);
502 if ((magick_info != (const MagickInfo *) NULL) &&
503 (GetMagickRawSupport(magick_info) != MagickFalse))
504 {
505 if (GetMagickEndianSupport(magick_info) == MagickFalse)
506 read_info->endian=UndefinedEndian;
507 else
508 if (image_info->endian == UndefinedEndian)
509 {
510 unsigned long
511 lsb_first;
512
513 lsb_first=1;
514 read_info->endian=(*(char *) &lsb_first) == 1 ? LSBEndian :
515 MSBEndian;
516 }
517 }
518 if ((magick_info != (const MagickInfo *) NULL) &&
519 (GetMagickSeekableStream(magick_info) != MagickFalse))
520 {
521 MagickBooleanType
522 status;
523
524 image=AcquireImage(read_info);
525 (void) CopyMagickString(image->filename,read_info->filename,
526 MaxTextExtent);
527 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
528 if (status == MagickFalse)
529 {
530 read_info=DestroyImageInfo(read_info);
531 image=DestroyImage(image);
532 return((Image *) NULL);
533 }
534 if (IsBlobSeekable(image) == MagickFalse)
535 {
536 /*
537 Coder requires a seekable stream.
538 */
539 *read_info->filename='\0';
540 status=ImageToFile(image,read_info->filename,exception);
541 if (status == MagickFalse)
542 {
543 (void) CloseBlob(image);
544 read_info=DestroyImageInfo(read_info);
545 image=DestroyImage(image);
546 return((Image *) NULL);
547 }
548 read_info->temporary=MagickTrue;
549 }
550 (void) CloseBlob(image);
551 image=DestroyImage(image);
552 }
553 image=NewImageList();
554 if ((magick_info == (const MagickInfo *) NULL) ||
555 (GetImageDecoder(magick_info) == (DecodeImageHandler *) NULL))
556 {
557 delegate_info=GetDelegateInfo(read_info->magick,(char *) NULL,exception);
558 if (delegate_info == (const DelegateInfo *) NULL)
559 {
560 (void) SetImageInfo(read_info,0,exception);
561 (void) CopyMagickString(read_info->filename,filename,MaxTextExtent);
562 magick_info=GetMagickInfo(read_info->magick,exception);
563 }
564 }
565 if ((magick_info != (const MagickInfo *) NULL) &&
566 (GetImageDecoder(magick_info) != (DecodeImageHandler *) NULL))
567 {
568 /*
569 Call appropriate image reader based on image type.
570 */
571 thread_support=GetMagickThreadSupport(magick_info);
572 if ((thread_support & DecoderThreadSupport) == 0)
573 LockSemaphoreInfo(magick_info->semaphore);
574 status=IsCoderAuthorized(magick_info->magick_module,read_info->magick,
575 ReadPolicyRights,exception);
576 image=(Image *) NULL;
577 if (status != MagickFalse)
578 image=GetImageDecoder(magick_info)(read_info,exception);
579 if ((thread_support & DecoderThreadSupport) == 0)
580 UnlockSemaphoreInfo(magick_info->semaphore);
581 }
582 else
583 {
584 MagickBooleanType
585 status;
586
587 delegate_info=GetDelegateInfo(read_info->magick,(char *) NULL,exception);
588 if (delegate_info == (const DelegateInfo *) NULL)
589 {
590 (void) ThrowMagickException(exception,GetMagickModule(),
591 MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
592 read_info->filename);
593 if (read_info->temporary != MagickFalse)
594 (void) RelinquishUniqueFileResource(read_info->filename);
595 read_info=DestroyImageInfo(read_info);
596 return((Image *) NULL);
597 }
598 /*
599 Let our decoding delegate process the image.
600 */
601 image=AcquireImage(read_info);
602 if (image == (Image *) NULL)
603 {
604 read_info=DestroyImageInfo(read_info);
605 return((Image *) NULL);
606 }
607 (void) CopyMagickString(image->filename,read_info->filename,
608 MaxTextExtent);
609 *read_info->filename='\0';
610 if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
611 LockSemaphoreInfo(delegate_info->semaphore);
612 status=InvokeDelegate(read_info,image,read_info->magick,(char *) NULL,
613 exception);
614 if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
615 UnlockSemaphoreInfo(delegate_info->semaphore);
616 image=DestroyImageList(image);
617 read_info->temporary=MagickTrue;
618 if (status != MagickFalse)
619 (void) SetImageInfo(read_info,0,exception);
620 magick_info=GetMagickInfo(read_info->magick,exception);
621 if ((magick_info == (const MagickInfo *) NULL) ||
622 (GetImageDecoder(magick_info) == (DecodeImageHandler *) NULL))
623 {
624 if (IsPathAccessible(read_info->filename) != MagickFalse)
625 (void) ThrowMagickException(exception,GetMagickModule(),
626 MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
627 read_info->magick);
628 else
629 ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
630 read_info->filename);
631 read_info=DestroyImageInfo(read_info);
632 return((Image *) NULL);
633 }
634 /*
635 Call appropriate image reader based on image type.
636 */
637 thread_support=GetMagickThreadSupport(magick_info);
638 if ((thread_support & DecoderThreadSupport) == 0)
639 LockSemaphoreInfo(magick_info->semaphore);
640 status=IsCoderAuthorized(magick_info->magick_module,read_info->magick,
641 ReadPolicyRights,exception);
642 image=(Image *) NULL;
643 if (status != MagickFalse)
644 image=(Image *) (GetImageDecoder(magick_info))(read_info,exception);
645 if ((thread_support & DecoderThreadSupport) == 0)
646 UnlockSemaphoreInfo(magick_info->semaphore);
647 }
648 if (read_info->temporary != MagickFalse)
649 {
650 (void) RelinquishUniqueFileResource(read_info->filename);
651 read_info->temporary=MagickFalse;
652 if (image != (Image *) NULL)
653 (void) CopyMagickString(image->filename,filename,MaxTextExtent);
654 }
655 if (image == (Image *) NULL)
656 {
657 read_info=DestroyImageInfo(read_info);
658 return(image);
659 }
660 if (exception->severity >= ErrorException)
661 (void) LogMagickEvent(ExceptionEvent,GetMagickModule(),
662 "Coder (%s) generated an image despite an error (%d), "
663 "notify the developers",image->magick,exception->severity);
664 if (IsBlobTemporary(image) != MagickFalse)
665 (void) RelinquishUniqueFileResource(read_info->filename);
666 if ((IsSceneGeometry(read_info->scenes,MagickFalse) != MagickFalse) &&
667 (GetImageListLength(image) != 1))
668 {
669 Image
670 *clones;
671
672 clones=CloneImages(image,read_info->scenes,exception);
673 image=DestroyImageList(image);
674 if (clones != (Image *) NULL)
675 image=GetFirstImageInList(clones);
676 if (image == (Image *) NULL)
677 {
678 read_info=DestroyImageInfo(read_info);
679 return(image);
680 }
681 }
682 for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
683 {
684 char
685 magick_path[MaxTextExtent],
686 *property,
687 timestamp[MaxTextExtent];
688
689 const char
690 *option;
691
692 const StringInfo
693 *profile;
694
695 ssize_t
696 option_type;
697
698 static const char
699 *source_date_epoch = (const char *) NULL;
700
701 static MagickBooleanType
702 epoch_initialized = MagickFalse;
703
704 next->taint=MagickFalse;
705 GetPathComponent(magick_filename,MagickPath,magick_path);
706 if ((*magick_path == '\0') && (*next->magick == '\0'))
707 (void) CopyMagickString(next->magick,magick,MaxTextExtent);
708 (void) CopyMagickString(next->magick_filename,magick_filename,
709 MaxTextExtent);
710 if (IsBlobTemporary(image) != MagickFalse)
711 (void) CopyMagickString(next->filename,filename,MaxTextExtent);
712 if (next->magick_columns == 0)
713 next->magick_columns=next->columns;
714 if (next->magick_rows == 0)
715 next->magick_rows=next->rows;
716 (void) GetImageProperty(next,"exif:*");
717 (void) GetImageProperty(next,"icc:*");
718 (void) GetImageProperty(next,"iptc:*");
719 (void) GetImageProperty(next,"xmp:*");
720 value=GetImageProperty(next,"exif:Orientation");
721 if (value == (char *) NULL)
722 value=GetImageProperty(next,"tiff:Orientation");
723 if (value != (char *) NULL)
724 {
725 next->orientation=(OrientationType) StringToLong(value);
726 (void) DeleteImageProperty(next,"tiff:Orientation");
727 (void) DeleteImageProperty(next,"exif:Orientation");
728 }
729 value=GetImageProperty(next,"exif:XResolution");
730 if (value != (char *) NULL)
731 {
732 geometry_info.rho=next->x_resolution;
733 geometry_info.sigma=1.0;
734 (void) ParseGeometry(value,&geometry_info);
735 if (geometry_info.sigma != 0)
736 next->x_resolution=geometry_info.rho/geometry_info.sigma;
737 if (strchr(value,',') != (char *) NULL)
738 next->x_resolution=geometry_info.rho+geometry_info.sigma/1000.0;
739 (void) DeleteImageProperty(next,"exif:XResolution");
740 }
741 value=GetImageProperty(next,"exif:YResolution");
742 if (value != (char *) NULL)
743 {
744 geometry_info.rho=next->y_resolution;
745 geometry_info.sigma=1.0;
746 (void) ParseGeometry(value,&geometry_info);
747 if (geometry_info.sigma != 0)
748 next->y_resolution=geometry_info.rho/geometry_info.sigma;
749 if (strchr(value,',') != (char *) NULL)
750 next->y_resolution=geometry_info.rho+geometry_info.sigma/1000.0;
751 (void) DeleteImageProperty(next,"exif:YResolution");
752 }
753 value=GetImageProperty(next,"exif:ResolutionUnit");
754 if (value == (char *) NULL)
755 value=GetImageProperty(next,"tiff:ResolutionUnit");
756 if (value != (char *) NULL)
757 {
758 option_type=ParseCommandOption(MagickResolutionOptions,MagickFalse,
759 value);
760 if (option_type >= 0)
761 next->units=(ResolutionType) option_type;
762 (void) DeleteImageProperty(next,"exif:ResolutionUnit");
763 (void) DeleteImageProperty(next,"tiff:ResolutionUnit");
764 }
765 if (next->page.width == 0)
766 next->page.width=next->columns;
767 if (next->page.height == 0)
768 next->page.height=next->rows;
769 option=GetImageOption(read_info,"caption");
770 if (option != (const char *) NULL)
771 {
772 property=InterpretImageProperties(read_info,next,option);
773 (void) SetImageProperty(next,"caption",property);
774 property=DestroyString(property);
775 }
776 option=GetImageOption(read_info,"comment");
777 if (option != (const char *) NULL)
778 {
779 property=InterpretImageProperties(read_info,next,option);
780 (void) SetImageProperty(next,"comment",property);
781 property=DestroyString(property);
782 }
783 option=GetImageOption(read_info,"label");
784 if (option != (const char *) NULL)
785 {
786 property=InterpretImageProperties(read_info,next,option);
787 (void) SetImageProperty(next,"label",property);
788 property=DestroyString(property);
789 }
790 if (LocaleCompare(next->magick,"TEXT") == 0)
791 (void) ParseAbsoluteGeometry("0x0+0+0",&next->page);
792 if ((read_info->extract != (char *) NULL) &&
793 (read_info->stream == (StreamHandler) NULL))
794 {
795 RectangleInfo
796 geometry;
797
798 SetGeometry(next,&geometry);
799 flags=ParseAbsoluteGeometry(read_info->extract,&geometry);
800 if ((next->columns != geometry.width) ||
801 (next->rows != geometry.height))
802 {
803 if (((flags & XValue) != 0) || ((flags & YValue) != 0))
804 {
805 Image
806 *crop_image;
807
808 crop_image=CropImage(next,&geometry,exception);
809 if (crop_image != (Image *) NULL)
810 ReplaceImageInList(&next,crop_image);
811 }
812 else
813 if (((flags & WidthValue) != 0) || ((flags & HeightValue) != 0))
814 {
815 (void) ParseRegionGeometry(next,read_info->extract,&geometry,
816 exception);
817 if ((geometry.width != 0) && (geometry.height != 0))
818 {
819 Image *resize_image=ResizeImage(next,geometry.width,
820 geometry.height,next->filter,next->blur,exception);
821 if (resize_image != (Image *) NULL)
822 ReplaceImageInList(&next,resize_image);
823 }
824 }
825 }
826 }
827 profile=GetImageProfile(next,"icc");
828 if (profile == (const StringInfo *) NULL)
829 profile=GetImageProfile(next,"icm");
830 if (profile != (const StringInfo *) NULL)
831 {
832 next->color_profile.length=GetStringInfoLength(profile);
833 next->color_profile.info=GetStringInfoDatum(profile);
834 }
835 profile=GetImageProfile(next,"iptc");
836 if (profile == (const StringInfo *) NULL)
837 profile=GetImageProfile(next,"8bim");
838 if (profile != (const StringInfo *) NULL)
839 {
840 next->iptc_profile.length=GetStringInfoLength(profile);
841 next->iptc_profile.info=GetStringInfoDatum(profile);
842 }
843 if (epoch_initialized == MagickFalse)
844 {
845 source_date_epoch=getenv("SOURCE_DATE_EPOCH");
846 epoch_initialized=MagickTrue;
847 }
848 if (source_date_epoch == (const char *) NULL)
849 {
850 (void) FormatMagickTime(image->timestamp,MaxTextExtent,timestamp);
851 (void) SetImageProperty(next,"date:timestamp",timestamp);
852 (void) FormatMagickTime((time_t) GetBlobProperties(next)->st_mtime,
853 MaxTextExtent,timestamp);
854 (void) SetImageProperty(next,"date:modify",timestamp);
855 (void) FormatMagickTime((time_t) GetBlobProperties(next)->st_ctime,
856 MaxTextExtent,timestamp);
857 (void) SetImageProperty(next,"date:create",timestamp);
858 }
859 option=GetImageOption(image_info,"delay");
860 if (option != (const char *) NULL)
861 {
862 GeometryInfo
863 geometry_info;
864
865 flags=ParseGeometry(option,&geometry_info);
866 if ((flags & GreaterValue) != 0)
867 {
868 if (next->delay > (size_t) floor(geometry_info.rho+0.5))
869 next->delay=(size_t) floor(geometry_info.rho+0.5);
870 }
871 else
872 if ((flags & LessValue) != 0)
873 {
874 if (next->delay < (size_t) floor(geometry_info.rho+0.5))
875 next->delay=(size_t) floor(geometry_info.rho+0.5);
876 }
877 else
878 next->delay=(size_t) floor(geometry_info.rho+0.5);
879 if ((flags & SigmaValue) != 0)
880 next->ticks_per_second=CastDoubleToLong(floor(
881 geometry_info.sigma+0.5));
882 }
883 option=GetImageOption(image_info,"dispose");
884 if (option != (const char *) NULL)
885 {
886 option_type=ParseCommandOption(MagickDisposeOptions,MagickFalse,
887 option);
888 if (option_type >= 0)
889 next->dispose=(DisposeType) option_type;
890 }
891 if (read_info->verbose != MagickFalse)
892 (void) IdentifyImage(next,stderr,MagickFalse);
893 image=next;
894 }
895 read_info=DestroyImageInfo(read_info);
896 if (GetBlobError(image) != MagickFalse)
897 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
898 return(GetFirstImageInList(image));
899}
900
901/*
902%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
903% %
904% %
905% %
906% R e a d I m a g e s %
907% %
908% %
909% %
910%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
911%
912% ReadImages() reads one or more images and returns them as an image list.
913%
914% The format of the ReadImage method is:
915%
916% Image *ReadImages(const ImageInfo *image_info,ExceptionInfo *exception)
917%
918% A description of each parameter follows:
919%
920% o image_info: the image info.
921%
922% o exception: return any errors or warnings in this structure.
923%
924*/
925MagickExport Image *ReadImages(const ImageInfo *image_info,
926 ExceptionInfo *exception)
927{
928 char
929 filename[MaxTextExtent];
930
931 Image
932 *image,
933 *images;
934
935 ImageInfo
936 *read_info;
937
938 /*
939 Read image list from a file.
940 */
941 assert(image_info != (ImageInfo *) NULL);
942 assert(image_info->signature == MagickCoreSignature);
943 assert(exception != (ExceptionInfo *) NULL);
944 if (IsEventLogging() != MagickFalse)
945 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
946 image_info->filename);
947 read_info=CloneImageInfo(image_info);
948 *read_info->magick='\0';
949 (void) InterpretImageFilename(read_info,(Image *) NULL,read_info->filename,
950 (int) read_info->scene,filename);
951 if (LocaleCompare(filename,read_info->filename) != 0)
952 {
953 ExceptionInfo
954 *sans;
955
956 ssize_t
957 extent,
958 scene;
959
960 /*
961 Images of the form image-%d.png[1-5].
962 */
963 sans=AcquireExceptionInfo();
964 (void) SetImageInfo(read_info,0,sans);
965 sans=DestroyExceptionInfo(sans);
966 if (read_info->number_scenes == 0)
967 {
968 read_info=DestroyImageInfo(read_info);
969 return(ReadImage(image_info,exception));
970 }
971 (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
972 images=NewImageList();
973 extent=(ssize_t) (read_info->scene+read_info->number_scenes);
974 for (scene=(ssize_t) read_info->scene; scene < (ssize_t) extent; scene++)
975 {
976 (void) InterpretImageFilename(image_info,(Image *) NULL,filename,(int)
977 scene,read_info->filename);
978 image=ReadImage(read_info,exception);
979 if (image == (Image *) NULL)
980 continue;
981 AppendImageToList(&images,image);
982 }
983 read_info=DestroyImageInfo(read_info);
984 return(images);
985 }
986 image=ReadImage(read_info,exception);
987 read_info=DestroyImageInfo(read_info);
988 return(image);
989}
990
991/*
992%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
993% %
994% %
995% %
996+ R e a d I n l i n e I m a g e %
997% %
998% %
999% %
1000%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1001%
1002% ReadInlineImage() reads a Base64-encoded inline image or image sequence.
1003% The method returns a NULL if there is a memory shortage or if the image
1004% cannot be read. On failure, a NULL image is returned and exception
1005% describes the reason for the failure.
1006%
1007% The format of the ReadInlineImage method is:
1008%
1009% Image *ReadInlineImage(const ImageInfo *image_info,const char *content,
1010% ExceptionInfo *exception)
1011%
1012% A description of each parameter follows:
1013%
1014% o image_info: the image info.
1015%
1016% o content: the image encoded in Base64.
1017%
1018% o exception: return any errors or warnings in this structure.
1019%
1020*/
1021MagickExport Image *ReadInlineImage(const ImageInfo *image_info,
1022 const char *content,ExceptionInfo *exception)
1023{
1024 Image
1025 *image;
1026
1027 ImageInfo
1028 *read_info;
1029
1030 unsigned char
1031 *blob;
1032
1033 size_t
1034 length;
1035
1036 const char
1037 *p;
1038
1039 /*
1040 Skip over header (e.g. data:image/gif;base64,).
1041 */
1042 image=NewImageList();
1043 for (p=content; (*p != ',') && (*p != '\0'); p++) ;
1044 if (*p == '\0')
1045 ThrowReaderException(CorruptImageError,"CorruptImage");
1046 blob=Base64Decode(++p,&length);
1047 if (length == 0)
1048 {
1049 blob=(unsigned char *) RelinquishMagickMemory(blob);
1050 ThrowReaderException(CorruptImageError,"CorruptImage");
1051 }
1052 read_info=CloneImageInfo(image_info);
1053 (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
1054 (void *) NULL);
1055 *read_info->filename='\0';
1056 *read_info->magick='\0';
1057 for (p=content; (*p != '/') && (*p != '\0'); p++) ;
1058 if (*p != '\0')
1059 {
1060 char
1061 *q;
1062
1063 ssize_t
1064 i;
1065
1066 /*
1067 Extract media type.
1068 */
1069 if (LocaleNCompare(++p,"x-",2) == 0)
1070 p+=(ptrdiff_t) 2;
1071 (void) CopyMagickString(read_info->filename,"data.",MagickPathExtent);
1072 q=read_info->filename+5;
1073 for (i=0; (*p != ';') && (*p != '\0') && (i < (MagickPathExtent-6)); i++)
1074 *q++=(*p++);
1075 *q++='\0';
1076 }
1077 image=BlobToImage(read_info,blob,length,exception);
1078 blob=(unsigned char *) RelinquishMagickMemory(blob);
1079 read_info=DestroyImageInfo(read_info);
1080 return(image);
1081}
1082
1083/*
1084%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1085% %
1086% %
1087% %
1088% W r i t e I m a g e %
1089% %
1090% %
1091% %
1092%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093%
1094% WriteImage() writes an image or an image sequence to a file or file handle.
1095% If writing to a file is on disk, the name is defined by the filename member
1096% of the image structure. WriteImage() returns MagickFalse is there is a
1097% memory shortage or if the image cannot be written. Check the exception
1098% member of image to determine the cause for any failure.
1099%
1100% The format of the WriteImage method is:
1101%
1102% MagickBooleanType WriteImage(const ImageInfo *image_info,Image *image)
1103%
1104% A description of each parameter follows:
1105%
1106% o image_info: the image info.
1107%
1108% o image: the image.
1109%
1110*/
1111MagickExport MagickBooleanType WriteImage(const ImageInfo *image_info,
1112 Image *image)
1113{
1114 char
1115 filename[MaxTextExtent];
1116
1117 const char
1118 *option;
1119
1120 const DelegateInfo
1121 *delegate_info;
1122
1123 const MagickInfo
1124 *magick_info;
1125
1126 ExceptionInfo
1127 *exception,
1128 *sans_exception;
1129
1130 ImageInfo
1131 *write_info;
1132
1133 MagickBooleanType
1134 status,
1135 temporary;
1136
1137 MagickStatusType
1138 thread_support;
1139
1140 /*
1141 Determine image type from filename prefix or suffix (e.g. image.jpg).
1142 */
1143 assert(image_info != (ImageInfo *) NULL);
1144 assert(image_info->signature == MagickCoreSignature);
1145 assert(image != (Image *) NULL);
1146 assert(image->signature == MagickCoreSignature);
1147 if (IsEventLogging() != MagickFalse)
1148 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1149 image_info->filename);
1150 exception=(&image->exception);
1151 sans_exception=AcquireExceptionInfo();
1152 write_info=CloneImageInfo(image_info);
1153 (void) CopyMagickString(write_info->filename,image->filename,MaxTextExtent);
1154 (void) SetImageInfo(write_info,1,sans_exception);
1155 if (*write_info->magick == '\0')
1156 (void) CopyMagickString(write_info->magick,image->magick,MaxTextExtent);
1157 if (LocaleCompare(write_info->magick,"clipmask") == 0)
1158 {
1159 if (image->clip_mask == (Image *) NULL)
1160 {
1161 (void) ThrowMagickException(exception,GetMagickModule(),
1162 OptionError,"NoClipPathDefined","`%s'",image->filename);
1163 write_info=DestroyImageInfo(write_info);
1164 return(MagickFalse);
1165 }
1166 image=image->clip_mask;
1167 (void) SetImageInfo(write_info,1,sans_exception);
1168 }
1169 (void) CopyMagickString(filename,image->filename,MaxTextExtent);
1170 (void) CopyMagickString(image->filename,write_info->filename,MaxTextExtent);
1171 /*
1172 Call appropriate image writer based on image type.
1173 */
1174 magick_info=GetMagickInfo(write_info->magick,sans_exception);
1175 if (sans_exception->severity == PolicyError)
1176 magick_info=GetMagickInfo(write_info->magick,exception);
1177 sans_exception=DestroyExceptionInfo(sans_exception);
1178 if (magick_info != (const MagickInfo *) NULL)
1179 {
1180 if (GetMagickEndianSupport(magick_info) == MagickFalse)
1181 image->endian=UndefinedEndian;
1182 else
1183 if ((image_info->endian == UndefinedEndian) &&
1184 (GetMagickRawSupport(magick_info) != MagickFalse))
1185 {
1186 unsigned long
1187 lsb_first;
1188
1189 lsb_first=1;
1190 image->endian=(*(char *) &lsb_first) == 1 ? LSBEndian : MSBEndian;
1191 }
1192 }
1193 if (SyncImagePixelCache(image,exception) == MagickFalse)
1194 return(MagickFalse);
1195 if (SyncImageProfiles(image) == MagickFalse)
1196 return(MagickFalse);
1197 DisassociateImageStream(image);
1198 option=GetImageOption(image_info,"delegate:bimodal");
1199 if ((option != (const char *) NULL) &&
1200 (IsMagickTrue(option) != MagickFalse) &&
1201 (write_info->page == (char *) NULL) &&
1202 (GetPreviousImageInList(image) == (Image *) NULL) &&
1203 (GetNextImageInList(image) == (Image *) NULL) &&
1204 (IsTaintImage(image) == MagickFalse))
1205 {
1206 delegate_info=GetDelegateInfo(image->magick,write_info->magick,
1207 exception);
1208 if ((delegate_info != (const DelegateInfo *) NULL) &&
1209 (GetDelegateMode(delegate_info) == 0) &&
1210 (IsPathAccessible(image->magick_filename) != MagickFalse))
1211 {
1212 /*
1213 Process image with bi-modal delegate.
1214 */
1215 (void) CopyMagickString(image->filename,image->magick_filename,
1216 MaxTextExtent);
1217 status=InvokeDelegate(write_info,image,image->magick,
1218 write_info->magick,exception);
1219 write_info=DestroyImageInfo(write_info);
1220 (void) CopyMagickString(image->filename,filename,MaxTextExtent);
1221 return(status);
1222 }
1223 }
1224 status=MagickFalse;
1225 temporary=MagickFalse;
1226 if ((magick_info != (const MagickInfo *) NULL) &&
1227 (GetMagickSeekableStream(magick_info) != MagickFalse))
1228 {
1229 char
1230 filename[MaxTextExtent];
1231
1232 (void) CopyMagickString(filename,image->filename,MaxTextExtent);
1233 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1234 (void) CopyMagickString(image->filename,filename,MaxTextExtent);
1235 if (status != MagickFalse)
1236 {
1237 if (IsBlobSeekable(image) == MagickFalse)
1238 {
1239 /*
1240 A seekable stream is required by the encoder.
1241 */
1242 write_info->adjoin=MagickTrue;
1243 (void) CopyMagickString(write_info->filename,image->filename,
1244 MaxTextExtent);
1245 (void) AcquireUniqueFilename(image->filename);
1246 temporary=MagickTrue;
1247 }
1248 if (CloseBlob(image) == MagickFalse)
1249 status=MagickFalse;
1250 }
1251 }
1252 if ((magick_info != (const MagickInfo *) NULL) &&
1253 (GetImageEncoder(magick_info) != (EncodeImageHandler *) NULL))
1254 {
1255 /*
1256 Call appropriate image writer based on image type.
1257 */
1258 thread_support=GetMagickThreadSupport(magick_info);
1259 if ((thread_support & EncoderThreadSupport) == 0)
1260 LockSemaphoreInfo(magick_info->semaphore);
1261 status=IsCoderAuthorized(magick_info->magick_module,write_info->magick,
1262 WritePolicyRights,exception);
1263 if (status != MagickFalse)
1264 status=GetImageEncoder(magick_info)(write_info,image);
1265 if ((thread_support & EncoderThreadSupport) == 0)
1266 UnlockSemaphoreInfo(magick_info->semaphore);
1267 }
1268 else
1269 {
1270 delegate_info=GetDelegateInfo((char *) NULL,write_info->magick,
1271 exception);
1272 if (delegate_info != (DelegateInfo *) NULL)
1273 {
1274 /*
1275 Process the image with delegate.
1276 */
1277 *write_info->filename='\0';
1278 if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
1279 LockSemaphoreInfo(delegate_info->semaphore);
1280 status=InvokeDelegate(write_info,image,(char *) NULL,
1281 write_info->magick,exception);
1282 if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
1283 UnlockSemaphoreInfo(delegate_info->semaphore);
1284 (void) CopyMagickString(image->filename,filename,MaxTextExtent);
1285 }
1286 else
1287 {
1288 sans_exception=AcquireExceptionInfo();
1289 magick_info=GetMagickInfo(write_info->magick,sans_exception);
1290 if (sans_exception->severity == PolicyError)
1291 magick_info=GetMagickInfo(write_info->magick,exception);
1292 sans_exception=DestroyExceptionInfo(sans_exception);
1293 if ((write_info->affirm == MagickFalse) &&
1294 (magick_info == (const MagickInfo *) NULL))
1295 {
1296 (void) CopyMagickString(write_info->magick,image->magick,
1297 MaxTextExtent);
1298 magick_info=GetMagickInfo(write_info->magick,exception);
1299 }
1300 if ((magick_info == (const MagickInfo *) NULL) ||
1301 (GetImageEncoder(magick_info) == (EncodeImageHandler *) NULL))
1302 {
1303 char
1304 extension[MaxTextExtent];
1305
1306 GetPathComponent(image->filename,ExtensionPath,extension);
1307 if (*extension != '\0')
1308 magick_info=GetMagickInfo(extension,exception);
1309 else
1310 magick_info=GetMagickInfo(image->magick,exception);
1311 (void) CopyMagickString(image->filename,filename,MaxTextExtent);
1312 }
1313 if ((magick_info == (const MagickInfo *) NULL) ||
1314 (GetImageEncoder(magick_info) == (EncodeImageHandler *) NULL))
1315 {
1316 magick_info=GetMagickInfo(image->magick,exception);
1317 if ((magick_info == (const MagickInfo *) NULL) ||
1318 (GetImageEncoder(magick_info) == (EncodeImageHandler *) NULL))
1319 (void) ThrowMagickException(exception,GetMagickModule(),
1320 MissingDelegateError,"NoEncodeDelegateForThisImageFormat",
1321 "`%s'",write_info->magick);
1322 else
1323 (void) ThrowMagickException(exception,GetMagickModule(),
1324 MissingDelegateWarning,"NoEncodeDelegateForThisImageFormat",
1325 "`%s'",write_info->magick);
1326 }
1327 if ((magick_info != (const MagickInfo *) NULL) &&
1328 (GetImageEncoder(magick_info) != (EncodeImageHandler *) NULL))
1329 {
1330 /*
1331 Call appropriate image writer based on image type.
1332 */
1333 thread_support=GetMagickThreadSupport(magick_info);
1334 if ((thread_support & EncoderThreadSupport) == 0)
1335 LockSemaphoreInfo(magick_info->semaphore);
1336 status=IsCoderAuthorized(magick_info->magick_module,write_info->magick,
1337 WritePolicyRights,exception);
1338 if (status != MagickFalse)
1339 status=GetImageEncoder(magick_info)(write_info,image);
1340 if ((thread_support & EncoderThreadSupport) == 0)
1341 UnlockSemaphoreInfo(magick_info->semaphore);
1342 }
1343 }
1344 }
1345 if (temporary != MagickFalse)
1346 {
1347 /*
1348 Copy temporary image file to permanent.
1349 */
1350 status=OpenBlob(write_info,image,ReadBinaryBlobMode,exception);
1351 if (status != MagickFalse)
1352 {
1353 (void) RelinquishUniqueFileResource(write_info->filename);
1354 status=ImageToFile(image,write_info->filename,exception);
1355 }
1356 if (CloseBlob(image) == MagickFalse)
1357 status=MagickFalse;
1358 (void) RelinquishUniqueFileResource(image->filename);
1359 (void) CopyMagickString(image->filename,write_info->filename,
1360 MaxTextExtent);
1361 }
1362 if ((LocaleCompare(write_info->magick,"info") != 0) &&
1363 (write_info->verbose != MagickFalse))
1364 (void) IdentifyImage(image,stderr,MagickFalse);
1365 write_info=DestroyImageInfo(write_info);
1366 if (GetBlobError(image) != MagickFalse)
1367 ThrowWriterException(FileOpenError,"UnableToWriteFile");
1368 return(status);
1369}
1370
1371/*
1372%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1373% %
1374% %
1375% %
1376% W r i t e I m a g e s %
1377% %
1378% %
1379% %
1380%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1381%
1382% WriteImages() writes an image sequence into one or more files. While
1383% WriteImage() can write an image sequence, it is limited to writing
1384% the sequence into a single file using a format which supports multiple
1385% frames. WriteImages(), however, does not have this limitation, instead it
1386% generates multiple output files if necessary (or when requested). When
1387% ImageInfo's adjoin flag is set to MagickFalse, the file name is expected
1388% to include a printf-style formatting string for the frame number (e.g.
1389% "image%02d.png").
1390%
1391% The format of the WriteImages method is:
1392%
1393% MagickBooleanType WriteImages(const ImageInfo *image_info,Image *images,
1394% const char *filename,ExceptionInfo *exception)
1395%
1396% A description of each parameter follows:
1397%
1398% o image_info: the image info.
1399%
1400% o images: the image list.
1401%
1402% o filename: the image filename.
1403%
1404% o exception: return any errors or warnings in this structure.
1405%
1406*/
1407MagickExport MagickBooleanType WriteImages(const ImageInfo *image_info,
1408 Image *images,const char *filename,ExceptionInfo *exception)
1409{
1410#define WriteImageTag "Write/Image"
1411
1412 ExceptionInfo
1413 *sans_exception;
1414
1415 ImageInfo
1416 *write_info;
1417
1418 MagickBooleanType
1419 proceed;
1420
1421 MagickOffsetType
1422 i;
1423
1424 MagickProgressMonitor
1425 progress_monitor;
1426
1427 MagickSizeType
1428 number_images;
1429
1430 MagickStatusType
1431 status;
1432
1433 Image
1434 *p;
1435
1436 assert(image_info != (const ImageInfo *) NULL);
1437 assert(image_info->signature == MagickCoreSignature);
1438 assert(images != (Image *) NULL);
1439 assert(images->signature == MagickCoreSignature);
1440 assert(exception != (ExceptionInfo *) NULL);
1441 if (IsEventLogging() != MagickFalse)
1442 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1443 write_info=CloneImageInfo(image_info);
1444 *write_info->magick='\0';
1445 images=GetFirstImageInList(images);
1446 if (images == (Image *) NULL)
1447 return(MagickFalse);
1448 if (filename != (const char *) NULL)
1449 for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1450 (void) CopyMagickString(p->filename,filename,MaxTextExtent);
1451 (void) CopyMagickString(write_info->filename,images->filename,MaxTextExtent);
1452 sans_exception=AcquireExceptionInfo();
1453 (void) SetImageInfo(write_info,(unsigned int) GetImageListLength(images),
1454 sans_exception);
1455 sans_exception=DestroyExceptionInfo(sans_exception);
1456 if (*write_info->magick == '\0')
1457 (void) CopyMagickString(write_info->magick,images->magick,MaxTextExtent);
1458 p=images;
1459 for ( ; GetNextImageInList(p) != (Image *) NULL; p=GetNextImageInList(p))
1460 {
1461 if (p->scene >= GetNextImageInList(p)->scene)
1462 {
1463 ssize_t
1464 i;
1465
1466 /*
1467 Generate consistent scene numbers.
1468 */
1469 i=(ssize_t) images->scene;
1470 for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1471 p->scene=(size_t) i++;
1472 break;
1473 }
1474 }
1475 /*
1476 Write images.
1477 */
1478 status=MagickTrue;
1479 progress_monitor=(MagickProgressMonitor) NULL;
1480 i=0;
1481 number_images=GetImageListLength(images);
1482 for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1483 {
1484 if (number_images != 1)
1485 progress_monitor=SetImageProgressMonitor(p,(MagickProgressMonitor) NULL,
1486 p->client_data);
1487 status&=WriteImage(write_info,p);
1488 GetImageException(p,exception);
1489 if (number_images != 1)
1490 (void) SetImageProgressMonitor(p,progress_monitor,p->client_data);
1491 if (write_info->adjoin != MagickFalse)
1492 break;
1493 if (number_images != 1)
1494 {
1495 proceed=SetImageProgress(p,WriteImageTag,i++,number_images);
1496 if (proceed == MagickFalse)
1497 break;
1498 }
1499 }
1500 write_info=DestroyImageInfo(write_info);
1501 return(status != 0 ? MagickTrue : MagickFalse);
1502}