blob: f02cf2021d4263bc1b80d6fa8163fc9127ed005e [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% M M PPPP CCCC %
7% MM MM P P C %
8% M M M PPPP C %
9% M M P C %
10% M M P CCCC %
11% %
12% %
13% Read/Write Magick Persistant Cache Image Format %
14% %
15% Software Design %
16% John Cristy %
17% March 2000 %
18% %
19% %
cristy45ef08f2012-12-07 13:13:34 +000020% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
cristy4c08aed2011-07-01 19:47:50 +000043#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/attribute.h"
46#include "MagickCore/blob.h"
47#include "MagickCore/blob-private.h"
48#include "MagickCore/cache.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colormap.h"
52#include "MagickCore/constitute.h"
53#include "MagickCore/exception.h"
54#include "MagickCore/exception-private.h"
55#include "MagickCore/geometry.h"
56#include "MagickCore/hashmap.h"
57#include "MagickCore/image.h"
58#include "MagickCore/image-private.h"
59#include "MagickCore/list.h"
60#include "MagickCore/magick.h"
61#include "MagickCore/memory_.h"
62#include "MagickCore/module.h"
63#include "MagickCore/monitor.h"
64#include "MagickCore/monitor-private.h"
65#include "MagickCore/option.h"
66#include "MagickCore/profile.h"
67#include "MagickCore/property.h"
68#include "MagickCore/quantum-private.h"
69#include "MagickCore/static.h"
70#include "MagickCore/statistic.h"
71#include "MagickCore/string_.h"
72#include "MagickCore/string-private.h"
73#include "MagickCore/utility.h"
cristya371edf2013-02-06 13:42:17 +000074#include "MagickCore/version-private.h"
cristy3ed852e2009-09-05 21:47:34 +000075
76/*
77 Forward declarations.
78*/
79static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +000080 WriteMPCImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000081
82/*
83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84% %
85% %
86% %
87% I s M P C %
88% %
89% %
90% %
91%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92%
93% IsMPC() returns MagickTrue if the image format type, identified by the
94% magick string, is an Magick Persistent Cache image.
95%
96% The format of the IsMPC method is:
97%
98% MagickBooleanType IsMPC(const unsigned char *magick,const size_t length)
99%
100% A description of each parameter follows:
101%
102% o magick: compare image format pattern against these bytes.
103%
104% o length: Specifies the length of the magick string.
105%
106*/
107static MagickBooleanType IsMPC(const unsigned char *magick,const size_t length)
108{
109 if (length < 14)
110 return(MagickFalse);
111 if (LocaleNCompare((const char *) magick,"id=MagickCache",14) == 0)
112 return(MagickTrue);
113 return(MagickFalse);
114}
115
116/*
117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
118% %
119% %
120% %
121% R e a d C A C H E I m a g e %
122% %
123% %
124% %
125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126%
127% ReadMPCImage() reads an Magick Persistent Cache image file and returns
128% it. It allocates the memory necessary for the new Image structure and
129% returns a pointer to the new image.
130%
131% The format of the ReadMPCImage method is:
132%
133% Image *ReadMPCImage(const ImageInfo *image_info,ExceptionInfo *exception)
134%
135% Decompression code contributed by Kyle Shorter.
136%
137% A description of each parameter follows:
138%
139% o image_info: the image info.
140%
141% o exception: return any errors or warnings in this structure.
142%
143*/
144static Image *ReadMPCImage(const ImageInfo *image_info,ExceptionInfo *exception)
145{
146 char
147 cache_filename[MaxTextExtent],
148 id[MaxTextExtent],
149 keyword[MaxTextExtent],
150 *options;
151
152 const unsigned char
153 *p;
154
155 GeometryInfo
156 geometry_info;
157
158 Image
159 *image;
160
161 int
162 c;
163
164 LinkedListInfo
165 *profiles;
166
167 MagickBooleanType
168 status;
169
170 MagickOffsetType
171 offset;
172
173 MagickStatusType
174 flags;
175
cristybb503372010-05-27 20:51:26 +0000176 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000177 i;
178
179 size_t
cristyaff6d802011-04-26 01:46:31 +0000180 depth,
cristy6e323672013-02-05 18:44:04 +0000181 length;
cristy3ed852e2009-09-05 21:47:34 +0000182
183 ssize_t
184 count;
185
186 StringInfo
187 *profile;
188
cristy6e323672013-02-05 18:44:04 +0000189 unsigned int
190 signature;
191
cristy3ed852e2009-09-05 21:47:34 +0000192 /*
193 Open image file.
194 */
195 assert(image_info != (const ImageInfo *) NULL);
196 assert(image_info->signature == MagickSignature);
197 if (image_info->debug != MagickFalse)
198 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
199 image_info->filename);
200 assert(exception != (ExceptionInfo *) NULL);
201 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000202 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000203 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
204 if (status == MagickFalse)
205 {
206 image=DestroyImageList(image);
207 return((Image *) NULL);
208 }
209 (void) CopyMagickString(cache_filename,image->filename,MaxTextExtent);
210 AppendImageFormat("cache",cache_filename);
211 c=ReadBlobByte(image);
212 if (c == EOF)
213 {
214 image=DestroyImage(image);
215 return((Image *) NULL);
216 }
217 *id='\0';
218 (void) ResetMagickMemory(keyword,0,sizeof(keyword));
219 offset=0;
220 do
221 {
222 /*
223 Decode image header; header terminates one character beyond a ':'.
224 */
225 profiles=(LinkedListInfo *) NULL;
226 length=MaxTextExtent;
227 options=AcquireString((char *) NULL);
cristy6e323672013-02-05 18:44:04 +0000228 signature=GetMagickSignature((const StringInfo *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000229 image->depth=8;
230 image->compression=NoCompression;
231 while ((isgraph(c) != MagickFalse) && (c != (int) ':'))
232 {
233 register char
234 *p;
235
236 if (c == (int) '{')
237 {
238 char
239 *comment;
240
241 /*
242 Read comment-- any text between { }.
243 */
244 length=MaxTextExtent;
245 comment=AcquireString((char *) NULL);
246 for (p=comment; comment != (char *) NULL; p++)
247 {
248 c=ReadBlobByte(image);
cristyb880ee82012-04-07 15:08:14 +0000249 if (c == (int) '\\')
250 c=ReadBlobByte(image);
251 else
252 if ((c == EOF) || (c == (int) '}'))
253 break;
cristy3ed852e2009-09-05 21:47:34 +0000254 if ((size_t) (p-comment+1) >= length)
255 {
256 *p='\0';
257 length<<=1;
258 comment=(char *) ResizeQuantumMemory(comment,length+
259 MaxTextExtent,sizeof(*comment));
260 if (comment == (char *) NULL)
261 break;
262 p=comment+strlen(comment);
263 }
264 *p=(char) c;
265 }
266 if (comment == (char *) NULL)
267 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
268 *p='\0';
cristyd15e6592011-10-15 00:13:06 +0000269 (void) SetImageProperty(image,"comment",comment,exception);
cristy3ed852e2009-09-05 21:47:34 +0000270 comment=DestroyString(comment);
271 c=ReadBlobByte(image);
272 }
273 else
274 if (isalnum(c) != MagickFalse)
275 {
276 /*
277 Get the keyword.
278 */
279 p=keyword;
280 do
281 {
cristy3ed852e2009-09-05 21:47:34 +0000282 if (c == (int) '=')
283 break;
284 if ((size_t) (p-keyword) < (MaxTextExtent-1))
285 *p++=(char) c;
286 c=ReadBlobByte(image);
287 } while (c != EOF);
288 *p='\0';
289 p=options;
290 while (isspace((int) ((unsigned char) c)) != 0)
291 c=ReadBlobByte(image);
292 if (c == (int) '=')
293 {
294 /*
295 Get the keyword value.
296 */
297 c=ReadBlobByte(image);
298 while ((c != (int) '}') && (c != EOF))
299 {
300 if ((size_t) (p-options+1) >= length)
301 {
302 *p='\0';
303 length<<=1;
304 options=(char *) ResizeQuantumMemory(options,length+
305 MaxTextExtent,sizeof(*options));
306 if (options == (char *) NULL)
307 break;
308 p=options+strlen(options);
309 }
310 if (options == (char *) NULL)
311 ThrowReaderException(ResourceLimitError,
312 "MemoryAllocationFailed");
313 *p++=(char) c;
314 c=ReadBlobByte(image);
cristyb880ee82012-04-07 15:08:14 +0000315 if (c == '\\')
316 {
317 c=ReadBlobByte(image);
318 if (c == (int) '}')
319 {
320 *p++=(char) c;
321 c=ReadBlobByte(image);
322 }
323 }
cristy3ed852e2009-09-05 21:47:34 +0000324 if (*options != '{')
325 if (isspace((int) ((unsigned char) c)) != 0)
326 break;
327 }
328 }
329 *p='\0';
330 if (*options == '{')
cristy01cd7302012-10-05 16:02:03 +0000331 (void) CopyMagickString(options,options+1,strlen(options));
cristy3ed852e2009-09-05 21:47:34 +0000332 /*
333 Assign a value to the specified keyword.
334 */
335 switch (*keyword)
336 {
cristy8a46d822012-08-28 23:32:39 +0000337 case 'a':
338 case 'A':
339 {
340 if (LocaleCompare(keyword,"alpha-trait") == 0)
341 {
342 ssize_t
343 alpha_trait;
344
345 alpha_trait=ParseCommandOption(MagickPixelTraitOptions,
346 MagickFalse,options);
347 if (alpha_trait < 0)
348 break;
349 image->alpha_trait=(PixelTrait) alpha_trait;
350 break;
351 }
352 (void) SetImageProperty(image,keyword,options,exception);
353 break;
354 }
cristy3ed852e2009-09-05 21:47:34 +0000355 case 'b':
356 case 'B':
357 {
358 if (LocaleCompare(keyword,"background-color") == 0)
359 {
cristy9950d572011-10-01 18:22:35 +0000360 (void) QueryColorCompliance(options,AllCompliance,
361 &image->background_color,exception);
cristy3ed852e2009-09-05 21:47:34 +0000362 break;
363 }
364 if (LocaleCompare(keyword,"blue-primary") == 0)
365 {
366 flags=ParseGeometry(options,&geometry_info);
367 image->chromaticity.blue_primary.x=geometry_info.rho;
368 image->chromaticity.blue_primary.y=geometry_info.sigma;
369 if ((flags & SigmaValue) == 0)
370 image->chromaticity.blue_primary.y=
371 image->chromaticity.blue_primary.x;
372 break;
373 }
374 if (LocaleCompare(keyword,"border-color") == 0)
375 {
cristy9950d572011-10-01 18:22:35 +0000376 (void) QueryColorCompliance(options,AllCompliance,
377 &image->border_color,exception);
cristy3ed852e2009-09-05 21:47:34 +0000378 break;
379 }
cristyd15e6592011-10-15 00:13:06 +0000380 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000381 break;
382 }
383 case 'c':
384 case 'C':
385 {
386 if (LocaleCompare(keyword,"class") == 0)
387 {
cristybb503372010-05-27 20:51:26 +0000388 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000389 storage_class;
390
cristy042ee782011-04-22 18:48:30 +0000391 storage_class=ParseCommandOption(MagickClassOptions,
cristy3ed852e2009-09-05 21:47:34 +0000392 MagickFalse,options);
393 if (storage_class < 0)
394 break;
395 image->storage_class=(ClassType) storage_class;
396 break;
397 }
398 if (LocaleCompare(keyword,"colors") == 0)
399 {
cristye27293e2009-12-18 02:53:20 +0000400 image->colors=StringToUnsignedLong(options);
cristy3ed852e2009-09-05 21:47:34 +0000401 break;
402 }
403 if (LocaleCompare(keyword,"colorspace") == 0)
404 {
cristybb503372010-05-27 20:51:26 +0000405 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000406 colorspace;
407
cristy042ee782011-04-22 18:48:30 +0000408 colorspace=ParseCommandOption(MagickColorspaceOptions,
cristy3ed852e2009-09-05 21:47:34 +0000409 MagickFalse,options);
410 if (colorspace < 0)
411 break;
cristy176b29a2012-06-21 13:35:15 +0000412 (void) SetImageColorspace(image,(ColorspaceType) colorspace, exception);
cristy3ed852e2009-09-05 21:47:34 +0000413 break;
414 }
415 if (LocaleCompare(keyword,"compression") == 0)
416 {
cristybb503372010-05-27 20:51:26 +0000417 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000418 compression;
419
cristy042ee782011-04-22 18:48:30 +0000420 compression=ParseCommandOption(MagickCompressOptions,
cristy3ed852e2009-09-05 21:47:34 +0000421 MagickFalse,options);
422 if (compression < 0)
423 break;
424 image->compression=(CompressionType) compression;
425 break;
426 }
427 if (LocaleCompare(keyword,"columns") == 0)
428 {
cristye27293e2009-12-18 02:53:20 +0000429 image->columns=StringToUnsignedLong(options);
cristy3ed852e2009-09-05 21:47:34 +0000430 break;
431 }
cristyd15e6592011-10-15 00:13:06 +0000432 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000433 break;
434 }
435 case 'd':
436 case 'D':
437 {
438 if (LocaleCompare(keyword,"delay") == 0)
439 {
cristye27293e2009-12-18 02:53:20 +0000440 image->delay=StringToUnsignedLong(options);
cristy3ed852e2009-09-05 21:47:34 +0000441 break;
442 }
443 if (LocaleCompare(keyword,"depth") == 0)
444 {
cristye27293e2009-12-18 02:53:20 +0000445 image->depth=StringToUnsignedLong(options);
cristy3ed852e2009-09-05 21:47:34 +0000446 break;
447 }
448 if (LocaleCompare(keyword,"dispose") == 0)
449 {
cristybb503372010-05-27 20:51:26 +0000450 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000451 dispose;
452
cristy042ee782011-04-22 18:48:30 +0000453 dispose=ParseCommandOption(MagickDisposeOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +0000454 options);
455 if (dispose < 0)
456 break;
457 image->dispose=(DisposeType) dispose;
458 break;
459 }
cristyd15e6592011-10-15 00:13:06 +0000460 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000461 break;
462 }
463 case 'e':
464 case 'E':
465 {
466 if (LocaleCompare(keyword,"endian") == 0)
467 {
cristybb503372010-05-27 20:51:26 +0000468 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000469 endian;
470
cristy042ee782011-04-22 18:48:30 +0000471 endian=ParseCommandOption(MagickEndianOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +0000472 options);
473 if (endian < 0)
474 break;
475 image->endian=(EndianType) endian;
476 break;
477 }
478 if (LocaleCompare(keyword,"error") == 0)
479 {
cristy9b34e302011-11-05 02:15:45 +0000480 image->error.mean_error_per_pixel=StringToDouble(options,
481 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000482 break;
483 }
cristyd15e6592011-10-15 00:13:06 +0000484 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000485 break;
486 }
487 case 'g':
488 case 'G':
489 {
490 if (LocaleCompare(keyword,"gamma") == 0)
491 {
cristydbdd0e32011-11-04 23:29:40 +0000492 image->gamma=StringToDouble(options,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000493 break;
494 }
495 if (LocaleCompare(keyword,"green-primary") == 0)
496 {
497 flags=ParseGeometry(options,&geometry_info);
498 image->chromaticity.green_primary.x=geometry_info.rho;
499 image->chromaticity.green_primary.y=geometry_info.sigma;
500 if ((flags & SigmaValue) == 0)
501 image->chromaticity.green_primary.y=
502 image->chromaticity.green_primary.x;
503 break;
504 }
cristyd15e6592011-10-15 00:13:06 +0000505 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000506 break;
507 }
508 case 'i':
509 case 'I':
510 {
511 if (LocaleCompare(keyword,"id") == 0)
512 {
513 (void) CopyMagickString(id,options,MaxTextExtent);
514 break;
515 }
516 if (LocaleCompare(keyword,"iterations") == 0)
517 {
cristye27293e2009-12-18 02:53:20 +0000518 image->iterations=StringToUnsignedLong(options);
cristy3ed852e2009-09-05 21:47:34 +0000519 break;
520 }
cristyd15e6592011-10-15 00:13:06 +0000521 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000522 break;
523 }
524 case 'm':
525 case 'M':
526 {
cristy240e8252013-02-06 15:00:04 +0000527 if (LocaleCompare(keyword,"magick-signature") == 0)
528 {
529 signature=(unsigned int) StringToUnsignedLong(options);
530 break;
531 }
cristy3ed852e2009-09-05 21:47:34 +0000532 if (LocaleCompare(keyword,"matte-color") == 0)
533 {
cristy9950d572011-10-01 18:22:35 +0000534 (void) QueryColorCompliance(options,AllCompliance,
535 &image->matte_color,exception);
cristy3ed852e2009-09-05 21:47:34 +0000536 break;
537 }
538 if (LocaleCompare(keyword,"maximum-error") == 0)
539 {
cristy9b34e302011-11-05 02:15:45 +0000540 image->error.normalized_maximum_error=StringToDouble(
541 options,(char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000542 break;
543 }
544 if (LocaleCompare(keyword,"mean-error") == 0)
545 {
cristy9b34e302011-11-05 02:15:45 +0000546 image->error.normalized_mean_error=StringToDouble(options,
547 (char **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000548 break;
549 }
550 if (LocaleCompare(keyword,"montage") == 0)
551 {
552 (void) CloneString(&image->montage,options);
553 break;
554 }
cristyd15e6592011-10-15 00:13:06 +0000555 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000556 break;
557 }
558 case 'o':
559 case 'O':
560 {
cristy3ed852e2009-09-05 21:47:34 +0000561 if (LocaleCompare(keyword,"orientation") == 0)
562 {
cristybb503372010-05-27 20:51:26 +0000563 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000564 orientation;
565
cristy042ee782011-04-22 18:48:30 +0000566 orientation=ParseCommandOption(MagickOrientationOptions,
cristy3ed852e2009-09-05 21:47:34 +0000567 MagickFalse,options);
568 if (orientation < 0)
569 break;
570 image->orientation=(OrientationType) orientation;
571 break;
572 }
cristyd15e6592011-10-15 00:13:06 +0000573 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000574 break;
575 }
576 case 'p':
577 case 'P':
578 {
579 if (LocaleCompare(keyword,"page") == 0)
580 {
581 char
582 *geometry;
583
584 geometry=GetPageGeometry(options);
585 (void) ParseAbsoluteGeometry(geometry,&image->page);
586 geometry=DestroyString(geometry);
587 break;
588 }
589 if ((LocaleNCompare(keyword,"profile:",8) == 0) ||
590 (LocaleNCompare(keyword,"profile-",8) == 0))
591 {
592 if (profiles == (LinkedListInfo *) NULL)
593 profiles=NewLinkedList(0);
594 (void) AppendValueToLinkedList(profiles,
595 AcquireString(keyword+8));
cristy07c9bd62011-09-01 13:41:23 +0000596 profile=BlobToStringInfo((const void *) NULL,(size_t)
597 StringToLong(options));
cristy0381c632011-09-01 18:02:13 +0000598 if (profile == (StringInfo *) NULL)
cristy07c9bd62011-09-01 13:41:23 +0000599 ThrowReaderException(ResourceLimitError,
600 "MemoryAllocationFailed");
cristyd15e6592011-10-15 00:13:06 +0000601 (void) SetImageProfile(image,keyword+8,profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000602 profile=DestroyStringInfo(profile);
603 break;
604 }
cristyd15e6592011-10-15 00:13:06 +0000605 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000606 break;
607 }
608 case 'q':
609 case 'Q':
610 {
611 if (LocaleCompare(keyword,"quality") == 0)
612 {
cristye27293e2009-12-18 02:53:20 +0000613 image->quality=StringToUnsignedLong(options);
cristy3ed852e2009-09-05 21:47:34 +0000614 break;
615 }
cristyd15e6592011-10-15 00:13:06 +0000616 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000617 break;
618 }
619 case 'r':
620 case 'R':
621 {
622 if (LocaleCompare(keyword,"red-primary") == 0)
623 {
624 flags=ParseGeometry(options,&geometry_info);
625 image->chromaticity.red_primary.x=geometry_info.rho;
626 if ((flags & SigmaValue) != 0)
627 image->chromaticity.red_primary.y=geometry_info.sigma;
628 break;
629 }
630 if (LocaleCompare(keyword,"rendering-intent") == 0)
631 {
cristybb503372010-05-27 20:51:26 +0000632 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000633 rendering_intent;
634
cristy042ee782011-04-22 18:48:30 +0000635 rendering_intent=ParseCommandOption(MagickIntentOptions,
cristy3ed852e2009-09-05 21:47:34 +0000636 MagickFalse,options);
637 if (rendering_intent < 0)
638 break;
639 image->rendering_intent=(RenderingIntent) rendering_intent;
640 break;
641 }
642 if (LocaleCompare(keyword,"resolution") == 0)
643 {
644 flags=ParseGeometry(options,&geometry_info);
cristy2a11bef2011-10-28 18:33:11 +0000645 image->resolution.x=geometry_info.rho;
646 image->resolution.y=geometry_info.sigma;
cristy3ed852e2009-09-05 21:47:34 +0000647 if ((flags & SigmaValue) == 0)
cristy2a11bef2011-10-28 18:33:11 +0000648 image->resolution.y=image->resolution.x;
cristy3ed852e2009-09-05 21:47:34 +0000649 break;
650 }
651 if (LocaleCompare(keyword,"rows") == 0)
652 {
cristye27293e2009-12-18 02:53:20 +0000653 image->rows=StringToUnsignedLong(options);
cristy3ed852e2009-09-05 21:47:34 +0000654 break;
655 }
cristyd15e6592011-10-15 00:13:06 +0000656 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000657 break;
658 }
659 case 's':
660 case 'S':
661 {
662 if (LocaleCompare(keyword,"scene") == 0)
663 {
cristye27293e2009-12-18 02:53:20 +0000664 image->scene=StringToUnsignedLong(options);
cristy3ed852e2009-09-05 21:47:34 +0000665 break;
666 }
cristyd15e6592011-10-15 00:13:06 +0000667 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000668 break;
669 }
670 case 't':
671 case 'T':
672 {
673 if (LocaleCompare(keyword,"ticks-per-second") == 0)
674 {
cristybb503372010-05-27 20:51:26 +0000675 image->ticks_per_second=(ssize_t) StringToLong(options);
cristy3ed852e2009-09-05 21:47:34 +0000676 break;
677 }
678 if (LocaleCompare(keyword,"tile-offset") == 0)
679 {
680 char
681 *geometry;
682
683 geometry=GetPageGeometry(options);
684 (void) ParseAbsoluteGeometry(geometry,&image->tile_offset);
685 geometry=DestroyString(geometry);
686 }
687 if (LocaleCompare(keyword,"type") == 0)
688 {
cristybb503372010-05-27 20:51:26 +0000689 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000690 type;
691
cristy042ee782011-04-22 18:48:30 +0000692 type=ParseCommandOption(MagickTypeOptions,MagickFalse,
cristy3ed852e2009-09-05 21:47:34 +0000693 options);
694 if (type < 0)
695 break;
696 image->type=(ImageType) type;
697 break;
698 }
cristyd15e6592011-10-15 00:13:06 +0000699 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000700 break;
701 }
702 case 'u':
703 case 'U':
704 {
705 if (LocaleCompare(keyword,"units") == 0)
706 {
cristybb503372010-05-27 20:51:26 +0000707 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000708 units;
709
cristyd15e6592011-10-15 00:13:06 +0000710 units=ParseCommandOption(MagickResolutionOptions,
711 MagickFalse,options);
cristy3ed852e2009-09-05 21:47:34 +0000712 if (units < 0)
713 break;
714 image->units=(ResolutionType) units;
715 break;
716 }
cristyd15e6592011-10-15 00:13:06 +0000717 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000718 break;
719 }
720 case 'w':
721 case 'W':
722 {
723 if (LocaleCompare(keyword,"white-point") == 0)
724 {
725 flags=ParseGeometry(options,&geometry_info);
726 image->chromaticity.white_point.x=geometry_info.rho;
727 image->chromaticity.white_point.y=geometry_info.sigma;
728 if ((flags & SigmaValue) == 0)
729 image->chromaticity.white_point.y=
730 image->chromaticity.white_point.x;
731 break;
732 }
cristyd15e6592011-10-15 00:13:06 +0000733 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000734 break;
735 }
736 default:
737 {
cristyd15e6592011-10-15 00:13:06 +0000738 (void) SetImageProperty(image,keyword,options,exception);
cristy3ed852e2009-09-05 21:47:34 +0000739 break;
740 }
741 }
742 }
743 else
744 c=ReadBlobByte(image);
745 while (isspace((int) ((unsigned char) c)) != 0)
746 c=ReadBlobByte(image);
747 }
748 options=DestroyString(options);
749 (void) ReadBlobByte(image);
750 /*
751 Verify that required image information is defined.
752 */
753 if ((LocaleCompare(id,"MagickCache") != 0) ||
754 (image->storage_class == UndefinedClass) ||
755 (image->compression == UndefinedCompression) || (image->columns == 0) ||
756 (image->rows == 0))
757 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy6e323672013-02-05 18:44:04 +0000758 if (signature != GetMagickSignature((const StringInfo *) NULL))
759 ThrowReaderException(CacheError,"IncompatibleAPI");
cristy3ed852e2009-09-05 21:47:34 +0000760 if (image->montage != (char *) NULL)
761 {
762 register char
763 *p;
764
765 /*
766 Image directory.
767 */
768 length=MaxTextExtent;
769 image->directory=AcquireString((char *) NULL);
770 p=image->directory;
771 do
772 {
773 *p='\0';
774 if ((strlen(image->directory)+MaxTextExtent) >= length)
775 {
776 /*
777 Allocate more memory for the image directory.
778 */
779 length<<=1;
780 image->directory=(char *) ResizeQuantumMemory(image->directory,
781 length+MaxTextExtent,sizeof(*image->directory));
782 if (image->directory == (char *) NULL)
783 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
784 p=image->directory+strlen(image->directory);
785 }
786 c=ReadBlobByte(image);
787 *p++=(char) c;
788 } while (c != (int) '\0');
789 }
790 if (profiles != (LinkedListInfo *) NULL)
791 {
792 const char
793 *name;
794
795 const StringInfo
796 *profile;
797
798 register unsigned char
799 *p;
800
801 /*
802 Read image profiles.
803 */
804 ResetLinkedListIterator(profiles);
805 name=(const char *) GetNextValueInLinkedList(profiles);
806 while (name != (const char *) NULL)
807 {
808 profile=GetImageProfile(image,name);
809 if (profile != (StringInfo *) NULL)
810 {
811 p=GetStringInfoDatum(profile);
812 count=ReadBlob(image,GetStringInfoLength(profile),p);
813 }
814 name=(const char *) GetNextValueInLinkedList(profiles);
815 }
816 profiles=DestroyLinkedList(profiles,RelinquishMagickMemory);
817 }
818 depth=GetImageQuantumDepth(image,MagickFalse);
819 if (image->storage_class == PseudoClass)
820 {
821 /*
822 Create image colormap.
823 */
cristy018f07f2011-09-04 21:15:19 +0000824 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000825 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
826 if (image->colors != 0)
827 {
828 size_t
829 packet_size;
830
831 unsigned char
832 *colormap;
833
834 /*
835 Read image colormap from file.
836 */
837 packet_size=(size_t) (3UL*depth/8UL);
838 colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
839 packet_size*sizeof(*colormap));
840 if (colormap == (unsigned char *) NULL)
841 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
842 count=ReadBlob(image,packet_size*image->colors,colormap);
843 if (count != (ssize_t) (packet_size*image->colors))
844 ThrowReaderException(CorruptImageError,
845 "InsufficientImageDataInFile");
846 p=colormap;
847 switch (depth)
848 {
849 default:
850 ThrowReaderException(CorruptImageError,
851 "ImageDepthNotSupported");
852 case 8:
853 {
854 unsigned char
855 pixel;
856
cristybb503372010-05-27 20:51:26 +0000857 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000858 {
859 p=PushCharPixel(p,&pixel);
860 image->colormap[i].red=ScaleCharToQuantum(pixel);
861 p=PushCharPixel(p,&pixel);
862 image->colormap[i].green=ScaleCharToQuantum(pixel);
863 p=PushCharPixel(p,&pixel);
864 image->colormap[i].blue=ScaleCharToQuantum(pixel);
865 }
866 break;
867 }
868 case 16:
869 {
870 unsigned short
871 pixel;
872
cristybb503372010-05-27 20:51:26 +0000873 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000874 {
875 p=PushShortPixel(MSBEndian,p,&pixel);
876 image->colormap[i].red=ScaleShortToQuantum(pixel);
877 p=PushShortPixel(MSBEndian,p,&pixel);
878 image->colormap[i].green=ScaleShortToQuantum(pixel);
879 p=PushShortPixel(MSBEndian,p,&pixel);
880 image->colormap[i].blue=ScaleShortToQuantum(pixel);
881 }
882 break;
883 }
884 case 32:
885 {
cristy4cb162a2010-05-30 03:04:47 +0000886 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000887 pixel;
888
cristybb503372010-05-27 20:51:26 +0000889 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000890 {
891 p=PushLongPixel(MSBEndian,p,&pixel);
892 image->colormap[i].red=ScaleLongToQuantum(pixel);
893 p=PushLongPixel(MSBEndian,p,&pixel);
894 image->colormap[i].green=ScaleLongToQuantum(pixel);
895 p=PushLongPixel(MSBEndian,p,&pixel);
896 image->colormap[i].blue=ScaleLongToQuantum(pixel);
897 }
898 break;
899 }
900 }
901 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
902 }
903 }
904 if (EOFBlob(image) != MagickFalse)
905 {
906 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
907 image->filename);
908 break;
909 }
910 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
911 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
912 break;
913 /*
914 Attach persistent pixel cache.
915 */
916 status=PersistPixelCache(image,cache_filename,MagickTrue,&offset,exception);
917 if (status == MagickFalse)
918 ThrowReaderException(CacheError,"UnableToPersistPixelCache");
919 /*
920 Proceed to next image.
921 */
922 do
923 {
924 c=ReadBlobByte(image);
925 } while ((isgraph(c) == MagickFalse) && (c != EOF));
926 if (c != EOF)
927 {
928 /*
929 Allocate next image structure.
930 */
cristy9950d572011-10-01 18:22:35 +0000931 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000932 if (GetNextImageInList(image) == (Image *) NULL)
933 {
934 image=DestroyImageList(image);
935 return((Image *) NULL);
936 }
937 image=SyncNextImageInList(image);
938 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
939 GetBlobSize(image));
940 if (status == MagickFalse)
941 break;
942 }
943 } while (c != EOF);
944 (void) CloseBlob(image);
945 return(GetFirstImageInList(image));
946}
947
948/*
949%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
950% %
951% %
952% %
953% R e g i s t e r M P C I m a g e %
954% %
955% %
956% %
957%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
958%
959% RegisterMPCImage() adds properties for the Cache image format to
960% the list of supported formats. The properties include the image format
961% tag, a method to read and/or write the format, whether the format
962% supports the saving of more than one frame to the same file or blob,
963% whether the format supports native in-memory I/O, and a brief
964% description of the format.
965%
966% The format of the RegisterMPCImage method is:
967%
cristybb503372010-05-27 20:51:26 +0000968% size_t RegisterMPCImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000969%
970*/
cristybb503372010-05-27 20:51:26 +0000971ModuleExport size_t RegisterMPCImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000972{
973 MagickInfo
974 *entry;
975
976 entry=SetMagickInfo("CACHE");
977 entry->description=ConstantString("Magick Persistent Cache image format");
978 entry->module=ConstantString("CACHE");
979 entry->stealth=MagickTrue;
980 (void) RegisterMagickInfo(entry);
981 entry=SetMagickInfo("MPC");
982 entry->decoder=(DecodeImageHandler *) ReadMPCImage;
983 entry->encoder=(EncodeImageHandler *) WriteMPCImage;
984 entry->magick=(IsImageFormatHandler *) IsMPC;
985 entry->description=ConstantString("Magick Persistent Cache image format");
986 entry->module=ConstantString("MPC");
987 (void) RegisterMagickInfo(entry);
988 return(MagickImageCoderSignature);
989}
990
991/*
992%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
993% %
994% %
995% %
996% U n r e g i s t e r M P C I m a g e %
997% %
998% %
999% %
1000%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1001%
1002% UnregisterMPCImage() removes format registrations made by the
1003% MPC module from the list of supported formats.
1004%
1005% The format of the UnregisterMPCImage method is:
1006%
1007% UnregisterMPCImage(void)
1008%
1009*/
1010ModuleExport void UnregisterMPCImage(void)
1011{
1012 (void) UnregisterMagickInfo("CACHE");
1013 (void) UnregisterMagickInfo("MPC");
1014}
1015
1016/*
1017%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1018% %
1019% %
1020% %
1021% W r i t e M P C I m a g e %
1022% %
1023% %
1024% %
1025%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1026%
1027% WriteMPCImage() writes an Magick Persistent Cache image to a file.
1028%
1029% The format of the WriteMPCImage method is:
1030%
cristy1e178e72011-08-28 19:44:34 +00001031% MagickBooleanType WriteMPCImage(const ImageInfo *image_info,
1032% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001033%
1034% A description of each parameter follows:
1035%
1036% o image_info: the image info.
1037%
1038% o image: the image.
1039%
cristy1e178e72011-08-28 19:44:34 +00001040% o exception: return any errors or warnings in this structure.
1041%
cristy3ed852e2009-09-05 21:47:34 +00001042*/
cristy1e178e72011-08-28 19:44:34 +00001043static MagickBooleanType WriteMPCImage(const ImageInfo *image_info,Image *image,
1044 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001045{
1046 char
1047 buffer[MaxTextExtent],
1048 cache_filename[MaxTextExtent];
1049
1050 const char
1051 *property,
1052 *value;
1053
1054 MagickBooleanType
1055 status;
1056
1057 MagickOffsetType
1058 offset,
1059 scene;
1060
cristybb503372010-05-27 20:51:26 +00001061 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001062 i;
1063
cristybb503372010-05-27 20:51:26 +00001064 size_t
cristyeaedf062010-05-29 22:36:02 +00001065 depth,
1066 one;
cristy3ed852e2009-09-05 21:47:34 +00001067
1068 /*
1069 Open persistent cache.
1070 */
1071 assert(image_info != (const ImageInfo *) NULL);
1072 assert(image_info->signature == MagickSignature);
1073 assert(image != (Image *) NULL);
1074 assert(image->signature == MagickSignature);
1075 if (image->debug != MagickFalse)
1076 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +00001077 assert(exception != (ExceptionInfo *) NULL);
1078 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +00001079 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +00001080 if (status == MagickFalse)
1081 return(status);
1082 (void) CopyMagickString(cache_filename,image->filename,MaxTextExtent);
1083 AppendImageFormat("cache",cache_filename);
1084 scene=0;
1085 offset=0;
cristyeaedf062010-05-29 22:36:02 +00001086 one=1;
cristy3ed852e2009-09-05 21:47:34 +00001087 do
1088 {
1089 /*
1090 Write persistent cache meta-information.
1091 */
1092 depth=GetImageQuantumDepth(image,MagickTrue);
1093 if ((image->storage_class == PseudoClass) &&
cristyeaedf062010-05-29 22:36:02 +00001094 (image->colors > (one << depth)))
cristy3ed852e2009-09-05 21:47:34 +00001095 image->storage_class=DirectClass;
1096 (void) WriteBlobString(image,"id=MagickCache\n");
cristy240e8252013-02-06 15:00:04 +00001097 (void) FormatLocaleString(buffer,MaxTextExtent,"magick-signature=%u\n",
cristy6e323672013-02-05 18:44:04 +00001098 GetMagickSignature((const StringInfo *) NULL));
cristy3ed852e2009-09-05 21:47:34 +00001099 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001100 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy8a46d822012-08-28 23:32:39 +00001101 "class=%s colors=%.20g alpha-trait=%s\n",CommandOptionToMnemonic(
cristye8c25f92010-06-03 00:53:06 +00001102 MagickClassOptions,image->storage_class),(double) image->colors,
cristy8a46d822012-08-28 23:32:39 +00001103 CommandOptionToMnemonic(MagickPixelTraitOptions,(ssize_t)
1104 image->alpha_trait));
cristy3ed852e2009-09-05 21:47:34 +00001105 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001106 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001107 "columns=%.20g rows=%.20g depth=%.20g\n",(double) image->columns,
1108 (double) image->rows,(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00001109 (void) WriteBlobString(image,buffer);
cristy5f1c1ff2010-12-23 21:38:06 +00001110 if (image->type != UndefinedType)
cristy3ed852e2009-09-05 21:47:34 +00001111 {
cristyb51dff52011-05-19 16:55:47 +00001112 (void) FormatLocaleString(buffer,MaxTextExtent,"type=%s\n",
cristy042ee782011-04-22 18:48:30 +00001113 CommandOptionToMnemonic(MagickTypeOptions,image->type));
cristy3ed852e2009-09-05 21:47:34 +00001114 (void) WriteBlobString(image,buffer);
1115 }
1116 if (image->colorspace != UndefinedColorspace)
1117 {
cristyb51dff52011-05-19 16:55:47 +00001118 (void) FormatLocaleString(buffer,MaxTextExtent,"colorspace=%s\n",
cristy042ee782011-04-22 18:48:30 +00001119 CommandOptionToMnemonic(MagickColorspaceOptions,image->colorspace));
cristy3ed852e2009-09-05 21:47:34 +00001120 (void) WriteBlobString(image,buffer);
1121 }
1122 if (image->endian != UndefinedEndian)
1123 {
cristyb51dff52011-05-19 16:55:47 +00001124 (void) FormatLocaleString(buffer,MaxTextExtent,"endian=%s\n",
cristy042ee782011-04-22 18:48:30 +00001125 CommandOptionToMnemonic(MagickEndianOptions,image->endian));
cristy3ed852e2009-09-05 21:47:34 +00001126 (void) WriteBlobString(image,buffer);
1127 }
1128 if (image->compression != UndefinedCompression)
1129 {
cristyb51dff52011-05-19 16:55:47 +00001130 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy042ee782011-04-22 18:48:30 +00001131 "compression=%s quality=%.20g\n",CommandOptionToMnemonic(
cristye8c25f92010-06-03 00:53:06 +00001132 MagickCompressOptions,image->compression),(double) image->quality);
cristy3ed852e2009-09-05 21:47:34 +00001133 (void) WriteBlobString(image,buffer);
1134 }
1135 if (image->units != UndefinedResolution)
1136 {
cristyb51dff52011-05-19 16:55:47 +00001137 (void) FormatLocaleString(buffer,MaxTextExtent,"units=%s\n",
cristy042ee782011-04-22 18:48:30 +00001138 CommandOptionToMnemonic(MagickResolutionOptions,image->units));
cristy3ed852e2009-09-05 21:47:34 +00001139 (void) WriteBlobString(image,buffer);
1140 }
cristy2a11bef2011-10-28 18:33:11 +00001141 if ((image->resolution.x != 0) || (image->resolution.y != 0))
cristy3ed852e2009-09-05 21:47:34 +00001142 {
cristyb51dff52011-05-19 16:55:47 +00001143 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy2a11bef2011-10-28 18:33:11 +00001144 "resolution=%gx%g\n",image->resolution.x,image->resolution.y);
cristy3ed852e2009-09-05 21:47:34 +00001145 (void) WriteBlobString(image,buffer);
1146 }
1147 if ((image->page.width != 0) || (image->page.height != 0))
1148 {
cristyb51dff52011-05-19 16:55:47 +00001149 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001150 "page=%.20gx%.20g%+.20g%+.20g\n",(double) image->page.width,(double)
1151 image->page.height,(double) image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001152 (void) WriteBlobString(image,buffer);
1153 }
1154 else
1155 if ((image->page.x != 0) || (image->page.y != 0))
1156 {
cristyb51dff52011-05-19 16:55:47 +00001157 (void) FormatLocaleString(buffer,MaxTextExtent,"page=%+ld%+ld\n",
cristyf2faecf2010-05-28 19:19:36 +00001158 (long) image->page.x,(long) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001159 (void) WriteBlobString(image,buffer);
1160 }
1161 if ((image->page.x != 0) || (image->page.y != 0))
1162 {
cristyb51dff52011-05-19 16:55:47 +00001163 (void) FormatLocaleString(buffer,MaxTextExtent,"tile-offset=%+ld%+ld\n",
cristyf2faecf2010-05-28 19:19:36 +00001164 (long) image->tile_offset.x,(long) image->tile_offset.y);
cristy3ed852e2009-09-05 21:47:34 +00001165 (void) WriteBlobString(image,buffer);
1166 }
1167 if ((GetNextImageInList(image) != (Image *) NULL) ||
1168 (GetPreviousImageInList(image) != (Image *) NULL))
1169 {
1170 if (image->scene == 0)
cristyb51dff52011-05-19 16:55:47 +00001171 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001172 "iterations=%.20g delay=%.20g ticks-per-second=%.20g\n",(double)
1173 image->iterations,(double) image->delay,(double)
cristyf2faecf2010-05-28 19:19:36 +00001174 image->ticks_per_second);
cristy3ed852e2009-09-05 21:47:34 +00001175 else
cristyb51dff52011-05-19 16:55:47 +00001176 (void) FormatLocaleString(buffer,MaxTextExtent,"scene=%.20g "
cristyaff6d802011-04-26 01:46:31 +00001177 "iterations=%.20g delay=%.20g ticks-per-second=%.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001178 (double) image->scene,(double) image->iterations,(double)
1179 image->delay,(double) image->ticks_per_second);
cristy3ed852e2009-09-05 21:47:34 +00001180 (void) WriteBlobString(image,buffer);
1181 }
1182 else
1183 {
1184 if (image->scene != 0)
1185 {
cristyb51dff52011-05-19 16:55:47 +00001186 (void) FormatLocaleString(buffer,MaxTextExtent,"scene=%.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001187 (double) image->scene);
cristy3ed852e2009-09-05 21:47:34 +00001188 (void) WriteBlobString(image,buffer);
1189 }
1190 if (image->iterations != 0)
1191 {
cristyb51dff52011-05-19 16:55:47 +00001192 (void) FormatLocaleString(buffer,MaxTextExtent,"iterations=%.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001193 (double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00001194 (void) WriteBlobString(image,buffer);
1195 }
1196 if (image->delay != 0)
1197 {
cristyb51dff52011-05-19 16:55:47 +00001198 (void) FormatLocaleString(buffer,MaxTextExtent,"delay=%.20g\n",
cristye8c25f92010-06-03 00:53:06 +00001199 (double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00001200 (void) WriteBlobString(image,buffer);
1201 }
1202 if (image->ticks_per_second != UndefinedTicksPerSecond)
1203 {
cristyb51dff52011-05-19 16:55:47 +00001204 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001205 "ticks-per-second=%.20g\n",(double) image->ticks_per_second);
cristy3ed852e2009-09-05 21:47:34 +00001206 (void) WriteBlobString(image,buffer);
1207 }
1208 }
1209 if (image->gravity != UndefinedGravity)
1210 {
cristyb51dff52011-05-19 16:55:47 +00001211 (void) FormatLocaleString(buffer,MaxTextExtent,"gravity=%s\n",
cristy042ee782011-04-22 18:48:30 +00001212 CommandOptionToMnemonic(MagickGravityOptions,image->gravity));
cristy3ed852e2009-09-05 21:47:34 +00001213 (void) WriteBlobString(image,buffer);
1214 }
1215 if (image->dispose != UndefinedDispose)
1216 {
cristyb51dff52011-05-19 16:55:47 +00001217 (void) FormatLocaleString(buffer,MaxTextExtent,"dispose=%s\n",
cristy042ee782011-04-22 18:48:30 +00001218 CommandOptionToMnemonic(MagickDisposeOptions,image->dispose));
cristy3ed852e2009-09-05 21:47:34 +00001219 (void) WriteBlobString(image,buffer);
1220 }
1221 if (image->rendering_intent != UndefinedIntent)
1222 {
cristyb51dff52011-05-19 16:55:47 +00001223 (void) FormatLocaleString(buffer,MaxTextExtent,
cristyaff6d802011-04-26 01:46:31 +00001224 "rendering-intent=%s\n",CommandOptionToMnemonic(MagickIntentOptions,
1225 image->rendering_intent));
cristy3ed852e2009-09-05 21:47:34 +00001226 (void) WriteBlobString(image,buffer);
1227 }
1228 if (image->gamma != 0.0)
1229 {
cristyb51dff52011-05-19 16:55:47 +00001230 (void) FormatLocaleString(buffer,MaxTextExtent,"gamma=%g\n",
cristy3ed852e2009-09-05 21:47:34 +00001231 image->gamma);
1232 (void) WriteBlobString(image,buffer);
1233 }
1234 if (image->chromaticity.white_point.x != 0.0)
1235 {
1236 /*
1237 Note chomaticity points.
1238 */
cristyb51dff52011-05-19 16:55:47 +00001239 (void) FormatLocaleString(buffer,MaxTextExtent,"red-primary="
cristye7f51092010-01-17 00:39:37 +00001240 "%g,%g green-primary=%g,%g blue-primary=%g,%g\n",
cristy3ed852e2009-09-05 21:47:34 +00001241 image->chromaticity.red_primary.x,image->chromaticity.red_primary.y,
1242 image->chromaticity.green_primary.x,
1243 image->chromaticity.green_primary.y,
1244 image->chromaticity.blue_primary.x,
1245 image->chromaticity.blue_primary.y);
1246 (void) WriteBlobString(image,buffer);
cristyb51dff52011-05-19 16:55:47 +00001247 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye7f51092010-01-17 00:39:37 +00001248 "white-point=%g,%g\n",image->chromaticity.white_point.x,
cristy8cd5b312010-01-07 01:10:24 +00001249 image->chromaticity.white_point.y);
cristy3ed852e2009-09-05 21:47:34 +00001250 (void) WriteBlobString(image,buffer);
1251 }
1252 if (image->orientation != UndefinedOrientation)
1253 {
cristyb51dff52011-05-19 16:55:47 +00001254 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy042ee782011-04-22 18:48:30 +00001255 "orientation=%s\n",CommandOptionToMnemonic(MagickOrientationOptions,
cristy3ed852e2009-09-05 21:47:34 +00001256 image->orientation));
1257 (void) WriteBlobString(image,buffer);
1258 }
1259 if (image->profiles != (void *) NULL)
1260 {
1261 const char
1262 *name;
1263
1264 const StringInfo
1265 *profile;
1266
1267 /*
1268 Generic profile.
1269 */
1270 ResetImageProfileIterator(image);
1271 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
1272 {
1273 profile=GetImageProfile(image,name);
1274 if (profile != (StringInfo *) NULL)
1275 {
cristyb51dff52011-05-19 16:55:47 +00001276 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001277 "profile:%s=%.20g\n",name,(double)
1278 GetStringInfoLength(profile));
cristy3ed852e2009-09-05 21:47:34 +00001279 (void) WriteBlobString(image,buffer);
1280 }
1281 name=GetNextImageProfile(image);
1282 }
1283 }
1284 if (image->montage != (char *) NULL)
1285 {
cristyb51dff52011-05-19 16:55:47 +00001286 (void) FormatLocaleString(buffer,MaxTextExtent,"montage=%s\n",
cristy3ed852e2009-09-05 21:47:34 +00001287 image->montage);
1288 (void) WriteBlobString(image,buffer);
1289 }
1290 ResetImagePropertyIterator(image);
1291 property=GetNextImageProperty(image);
1292 while (property != (const char *) NULL)
1293 {
cristyb51dff52011-05-19 16:55:47 +00001294 (void) FormatLocaleString(buffer,MaxTextExtent,"%s=",property);
cristy3ed852e2009-09-05 21:47:34 +00001295 (void) WriteBlobString(image,buffer);
cristyd15e6592011-10-15 00:13:06 +00001296 value=GetImageProperty(image,property,exception);
cristy3ed852e2009-09-05 21:47:34 +00001297 if (value != (const char *) NULL)
1298 {
cristyd76ed5d2012-05-20 16:15:57 +00001299 size_t
1300 length;
1301
cristy181c99c2012-05-20 16:14:14 +00001302 length=strlen(value);
1303 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00001304 if (isspace((int) ((unsigned char) value[i])) != 0)
1305 break;
cristy181c99c2012-05-20 16:14:14 +00001306 if (i == (ssize_t) length)
1307 (void) WriteBlob(image,length,(const unsigned char *) value);
cristyb880ee82012-04-07 15:08:14 +00001308 else
1309 {
1310 (void) WriteBlobByte(image,'{');
cristy181c99c2012-05-20 16:14:14 +00001311 if (strchr(value,'}') == (char *) NULL)
1312 (void) WriteBlob(image,length,(const unsigned char *) value);
1313 else
1314 for (i=0; i < (ssize_t) length; i++)
1315 {
1316 if (value[i] == (int) '}')
1317 (void) WriteBlobByte(image,'\\');
1318 (void) WriteBlobByte(image,value[i]);
1319 }
cristyb880ee82012-04-07 15:08:14 +00001320 (void) WriteBlobByte(image,'}');
1321 }
cristy3ed852e2009-09-05 21:47:34 +00001322 }
1323 (void) WriteBlobByte(image,'\n');
1324 property=GetNextImageProperty(image);
1325 }
1326 ResetImageArtifactIterator(image);
1327 (void) WriteBlobString(image,"\f\n:\032");
1328 if (image->montage != (char *) NULL)
1329 {
1330 /*
1331 Write montage tile directory.
1332 */
1333 if (image->directory != (char *) NULL)
1334 (void) WriteBlobString(image,image->directory);
1335 (void) WriteBlobByte(image,'\0');
1336 }
1337 if (image->profiles != 0)
1338 {
1339 const char
1340 *name;
1341
1342 const StringInfo
1343 *profile;
1344
1345 /*
1346 Write image profiles.
1347 */
1348 ResetImageProfileIterator(image);
1349 name=GetNextImageProfile(image);
1350 while (name != (const char *) NULL)
1351 {
1352 profile=GetImageProfile(image,name);
1353 (void) WriteBlob(image,GetStringInfoLength(profile),
1354 GetStringInfoDatum(profile));
1355 name=GetNextImageProfile(image);
1356 }
1357 }
1358 if (image->storage_class == PseudoClass)
1359 {
1360 size_t
1361 packet_size;
1362
1363 unsigned char
1364 *colormap,
1365 *q;
1366
1367 /*
1368 Allocate colormap.
1369 */
1370 packet_size=(size_t) (3UL*depth/8UL);
1371 colormap=(unsigned char *) AcquireQuantumMemory(image->colors,
1372 packet_size*sizeof(*colormap));
1373 if (colormap == (unsigned char *) NULL)
1374 return(MagickFalse);
1375 /*
1376 Write colormap to file.
1377 */
1378 q=colormap;
cristybb503372010-05-27 20:51:26 +00001379 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001380 {
1381 switch (depth)
1382 {
1383 default:
1384 ThrowWriterException(CorruptImageError,"ImageDepthNotSupported");
1385 case 32:
1386 {
cristy4cb162a2010-05-30 03:04:47 +00001387 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00001388 pixel;
1389
1390 pixel=ScaleQuantumToLong(image->colormap[i].red);
1391 q=PopLongPixel(MSBEndian,pixel,q);
1392 pixel=ScaleQuantumToLong(image->colormap[i].green);
1393 q=PopLongPixel(MSBEndian,pixel,q);
1394 pixel=ScaleQuantumToLong(image->colormap[i].blue);
1395 q=PopLongPixel(MSBEndian,pixel,q);
1396 }
1397 case 16:
1398 {
1399 unsigned short
1400 pixel;
1401
1402 pixel=ScaleQuantumToShort(image->colormap[i].red);
1403 q=PopShortPixel(MSBEndian,pixel,q);
1404 pixel=ScaleQuantumToShort(image->colormap[i].green);
1405 q=PopShortPixel(MSBEndian,pixel,q);
1406 pixel=ScaleQuantumToShort(image->colormap[i].blue);
1407 q=PopShortPixel(MSBEndian,pixel,q);
1408 break;
1409 }
1410 case 8:
1411 {
1412 unsigned char
1413 pixel;
1414
1415 pixel=(unsigned char) ScaleQuantumToChar(image->colormap[i].red);
1416 q=PopCharPixel(pixel,q);
1417 pixel=(unsigned char) ScaleQuantumToChar(
1418 image->colormap[i].green);
1419 q=PopCharPixel(pixel,q);
1420 pixel=(unsigned char) ScaleQuantumToChar(image->colormap[i].blue);
1421 q=PopCharPixel(pixel,q);
1422 break;
1423 }
1424 }
1425 }
1426 (void) WriteBlob(image,packet_size*image->colors,colormap);
1427 colormap=(unsigned char *) RelinquishMagickMemory(colormap);
1428 }
1429 /*
1430 Initialize persistent pixel cache.
1431 */
1432 status=PersistPixelCache(image,cache_filename,MagickFalse,&offset,
cristy1e178e72011-08-28 19:44:34 +00001433 exception);
cristy3ed852e2009-09-05 21:47:34 +00001434 if (status == MagickFalse)
1435 ThrowWriterException(CacheError,"UnableToPersistPixelCache");
1436 if (GetNextImageInList(image) == (Image *) NULL)
1437 break;
1438 image=SyncNextImageInList(image);
1439 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1440 {
1441 status=image->progress_monitor(SaveImagesTag,scene,
1442 GetImageListLength(image),image->client_data);
1443 if (status == MagickFalse)
1444 break;
1445 }
1446 scene++;
1447 } while (image_info->adjoin != MagickFalse);
1448 (void) CloseBlob(image);
1449 return(status);
1450}