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