Coverage Report

Created: 2026-10-28 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/coders/mpc.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2026 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% SPDX-License-Identifier: MIT
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%                            M   M  PPPP    CCCC                              %
15
%                            MM MM  P   P  C                                  %
16
%                            M M M  PPPP   C                                  %
17
%                            M   M  P      C                                  %
18
%                            M   M  P       CCCC                              %
19
%                                                                             %
20
%                                                                             %
21
%              Read/Write Magick Persistent Cache Image Format.               %
22
%                                                                             %
23
%                                                                             %
24
%                              Software Design                                %
25
%                                John Cristy                                  %
26
%                                 March 2000                                  %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
%
33
%
34
*/
35

36
/*
37
  Include declarations.
38
*/
39
#include "magick/studio.h"
40
#include "magick/attribute.h"
41
#include "magick/blob.h"
42
#include "magick/color.h"
43
#include "magick/color_lookup.h"
44
#include "magick/colormap.h"
45
#include "magick/enum_strings.h"
46
#include "magick/magick.h"
47
#include "magick/monitor.h"
48
#include "magick/pixel_cache.h"
49
#include "magick/profile.h"
50
#include "magick/utility.h"
51
#include "magick/static.h"
52

53
/*
54
  Forward declarations.
55
*/
56
static unsigned int
57
  WriteMPCImage(const ImageInfo *,Image *);
58

59
/*
60
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61
%                                                                             %
62
%                                                                             %
63
%                                                                             %
64
%   I s M P C                                                                 %
65
%                                                                             %
66
%                                                                             %
67
%                                                                             %
68
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69
%
70
%  Method IsMPC returns MagickTrue if the image format type, identified by the
71
%  magick string, is an Magick Persistent Cache image.
72
%
73
%  The format of the IsMPC method is:
74
%
75
%      unsigned int IsMPC(const unsigned char *magick,const size_t length)
76
%
77
%  A description of each parameter follows:
78
%
79
%    o status:  Method IsMPC returns MagickTrue if the image format type is MPC.
80
%
81
%    o magick: This string is generally the first few bytes of an image file
82
%      or blob.
83
%
84
%    o length: Specifies the length of the magick string.
85
%
86
%
87
*/
88
static MagickBool IsMPC(const unsigned char *magick,const size_t length)
89
0
{
90
0
  if (length < 14)
91
0
    return(MagickFalse);
92
0
  if (LocaleNCompare((char *) magick,"id=MagickCache",14) == 0)
93
0
    return(MagickTrue);
94
0
  return(MagickFalse);
95
0
}
96

97
/*
98
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99
%                                                                             %
100
%                                                                             %
101
%                                                                             %
102
%   R e a d C A C H E I m a g e                                               %
103
%                                                                             %
104
%                                                                             %
105
%                                                                             %
106
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
107
%
108
%  Method ReadMPCImage reads an Magick Persistent Cache image file and returns
109
%  it.  It allocates the memory necessary for the new Image structure and
110
%  returns a pointer to the new image.
111
%
112
%  The format of the ReadMPCImage method is:
113
%
114
%      Image *ReadMPCImage(const ImageInfo *image_info,ExceptionInfo *exception)
115
%
116
%  Decompression code contributed by Kyle Shorter.
117
%
118
%  A description of each parameter follows:
119
%
120
%    o image: Method ReadMPCImage returns a pointer to the image after
121
%      reading.  A null image is returned if there is a memory shortage or
122
%      if the image cannot be read.
123
%
124
%    o image_info: Specifies a pointer to a ImageInfo structure.
125
%
126
%    o exception: return any errors or warnings in this structure.
127
%
128
%
129
*/
130
131
#define ThrowMPCReaderException(code_,reason_,image_)                   \
132
66.5k
  do {                                                                  \
133
66.5k
    MagickFreeResourceLimitedMemory(char *,comment);                    \
134
66.5k
    MagickFreeResourceLimitedMemory(char *,values);                     \
135
66.5k
    if (number_of_profiles > 0)                                         \
136
66.5k
      {                                                                 \
137
11.0k
        unsigned long _index;                                           \
138
51.3k
        for (_index=0; _index < number_of_profiles; _index++)           \
139
40.3k
          {                                                             \
140
40.3k
            MagickFreeMemory(profiles[_index].name);                    \
141
40.3k
            MagickFreeResourceLimitedMemory(unsigned char *,profiles[_index].info); \
142
40.3k
          }                                                             \
143
11.0k
        MagickFreeResourceLimitedMemory(ProfileInfo *,profiles);        \
144
11.0k
        number_of_profiles=0;                                           \
145
11.0k
      }                                                                 \
146
66.5k
    ThrowReaderException(code_,reason_,image_);                         \
147
0
  } while (0);
148
149
460k
#define ReadMPCMaxKeyWordCount 256 /* Arbitrary limit on number of keywords in MPC frame */
150
151
/*
152
  Ignore attempts to set the same attribute multiple times.
153
*/
154
static MagickPassFail
155
SetNewImageAttribute(Image *image,const char *key,const char *value)
156
260k
{
157
260k
  MagickPassFail
158
260k
    status;
159
160
260k
  if (GetImageAttribute(image,key) == (const ImageAttribute *) NULL)
161
192k
    status = SetImageAttribute(image,key,value);
162
68.6k
  else
163
68.6k
    status = MagickFail;
164
165
260k
  return status;
166
260k
};
167
168
static Image *ReadMPCImage(const ImageInfo *image_info,ExceptionInfo *exception)
169
67.1k
{
170
67.1k
  char
171
67.1k
    cache_filename[MaxTextExtent],
172
67.1k
    id[MaxTextExtent],
173
67.1k
    keyword[MaxTextExtent];
174
175
67.1k
  ExtendedSignedIntegralType
176
67.1k
    offset;
177
178
67.1k
  Image
179
67.1k
    *image;
180
181
67.1k
  int
182
67.1k
    c;
183
184
67.1k
  register unsigned long
185
67.1k
    i;
186
187
67.1k
  register unsigned char
188
67.1k
    *p;
189
190
67.1k
  unsigned int
191
67.1k
    comment_count,
192
67.1k
    keyword_count,
193
67.1k
    status;
194
195
67.1k
  unsigned long
196
67.1k
    quantum_depth;
197
198
67.1k
  ProfileInfo
199
67.1k
    *profiles=0;
200
201
67.1k
  unsigned long
202
67.1k
    number_of_profiles=0;
203
204
67.1k
  char
205
67.1k
    *comment = NULL,
206
67.1k
    *values = NULL;
207
208
  /*
209
    Open image file.
210
  */
211
67.1k
  assert(image_info != (const ImageInfo *) NULL);
212
67.1k
  assert(image_info->signature == MagickSignature);
213
67.1k
  assert(exception != (ExceptionInfo *) NULL);
214
67.1k
  assert(exception->signature == MagickSignature);
215
67.1k
  image=AllocateImage(image_info);
216
  /*
217
    We must unset the grayscale and monochrome flags by default since
218
    the MPC format does not necessarily update the pixel cache while
219
    it is read.
220
  */
221
67.1k
  image->is_grayscale=MagickFalse;
222
67.1k
  image->is_monochrome=MagickFalse;
223
  /*
224
    Open blob
225
  */
226
67.1k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
227
67.1k
  if (status == MagickFail)
228
67.1k
    ThrowReaderException(FileOpenError,UnableToOpenFile,image);
229
67.1k
  (void) strlcpy(cache_filename,image->filename,MaxTextExtent);
230
67.1k
  AppendImageFormat("cache",cache_filename);
231
67.1k
  c=ReadBlobByte(image);
232
67.1k
  if (c == EOF)
233
0
    {
234
0
      DestroyImage(image);
235
0
      return((Image *) NULL);
236
0
    }
237
67.1k
  *id='\0';
238
67.1k
  offset=0;
239
67.1k
  do
240
67.1k
  {
241
    /*
242
      Decode image header;  header terminates one character beyond a ':'.
243
    */
244
67.1k
    quantum_depth=QuantumDepth;
245
67.1k
    image->depth=8;
246
67.1k
    image->compression=NoCompression;
247
67.1k
    image->storage_class=DirectClass;
248
67.1k
    comment_count=0;
249
67.1k
    keyword_count=0;
250
671k
    while (isgraph(c) && (c != ':'))
251
632k
    {
252
632k
      register char
253
632k
        *p;
254
255
632k
      if (c == '{')
256
15.6k
        {
257
15.6k
          size_t
258
15.6k
            comment_length;
259
260
          /*
261
            Insist that format is identified prior to any comments.
262
          */
263
15.6k
          if (id[0] == '\0')
264
709
            {
265
709
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
266
709
                                    "Comment precedes format identifier (id=MagickCache)");
267
709
              ThrowMPCReaderException(CorruptImageError,ImproperImageHeader,image);
268
0
            }
269
270
          /*
271
            Insist that only one comment is provided
272
          */
273
14.9k
          if (comment_count > 0)
274
2.96k
            {
275
2.96k
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
276
2.96k
                                    "Too many comments!");
277
2.96k
              ThrowMPCReaderException(CorruptImageError,ImproperImageHeader,image);
278
0
            }
279
280
          /*
281
            Read comment-- any text between { }.
282
          */
283
12.0k
          comment_length=MaxTextExtent;
284
12.0k
          comment=MagickAllocateResourceLimitedMemory(char *,comment_length);
285
12.0k
          if (comment == (char *) NULL)
286
0
            ThrowMPCReaderException(ResourceLimitError,MemoryAllocationFailed,
287
12.0k
              image);
288
12.0k
          p=comment;
289
8.15M
          for ( ; comment != (char *) NULL; p++)
290
8.15M
          {
291
8.15M
            c=ReadBlobByte(image);
292
8.15M
            if ((c == EOF) || (c == '}'))
293
12.0k
              break;
294
8.14M
            if ((size_t) (p-comment+1) >= comment_length)
295
3.43k
              {
296
3.43k
                char
297
3.43k
                  *new_comment;
298
299
3.43k
                *p='\0';
300
3.43k
                comment_length += MaxTextExtent;
301
3.43k
                new_comment=MagickReallocateResourceLimitedMemory(char *,comment,comment_length);
302
3.43k
                if (new_comment == (char *) NULL)
303
0
                  {
304
0
                    MagickFreeResourceLimitedMemory(char *,comment);
305
0
                    break;
306
0
                  }
307
3.43k
                comment=new_comment;
308
3.43k
                p=comment+strlen(comment);
309
3.43k
              }
310
8.14M
            *p=c;
311
8.14M
          }
312
12.0k
          if (comment == (char *) NULL)
313
0
            ThrowMPCReaderException(ResourceLimitError,MemoryAllocationFailed,
314
12.0k
              image);
315
12.0k
          *p='\0';
316
12.0k
          (void) SetNewImageAttribute(image,"comment",comment);
317
12.0k
          comment_count++;
318
12.0k
          MagickFreeResourceLimitedMemory(char *,comment);
319
12.0k
          c=ReadBlobByte(image);
320
12.0k
        }
321
617k
      else
322
617k
        if (isalnum(c))
323
484k
          {
324
484k
            size_t
325
484k
              values_length;
326
327
484k
            MagickBool
328
484k
              in_brace=MagickFalse;
329
330
            /*
331
              Get keyword.
332
            */
333
484k
            keyword[0]='\0';
334
484k
            p=keyword;
335
484k
            do
336
12.5M
            {
337
12.5M
              if ((p-keyword) < (MaxTextExtent-1))
338
6.63M
                *p++=c;
339
12.5M
              c=ReadBlobByte(image);
340
12.5M
            } while ((c != '=') && (c != EOF));
341
484k
            *p='\0';
342
484k
            if (c == EOF)
343
468k
              ThrowMPCReaderException(CorruptImageWarning,ImproperImageHeader,image);
344
345
            /*
346
              Insist that the first keyword must be 'id' (id=MagickCache)
347
            */
348
468k
            if ((keyword_count == 0) && (LocaleCompare(keyword,"id") != 0))
349
21
              {
350
21
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
351
21
                                      "First keyword must be 'id' (have '%s')",
352
21
                                      keyword);
353
21
                ThrowMPCReaderException(CorruptImageError,ImproperImageHeader,image);
354
0
              }
355
356
            /*
357
              Get values.
358
359
              Values not containing spaces are terminated by the first
360
              white-space (or new-line) encountered.  Values containing
361
              spaces and/or new-lines must be surrounded by braces.
362
            */
363
468k
            values_length=MaxTextExtent;
364
468k
            values=MagickAllocateResourceLimitedMemory(char *,values_length);
365
468k
            if (values == (char *) NULL)
366
468k
              ThrowMPCReaderException(ResourceLimitError,MemoryAllocationFailed,image);
367
468k
            values[0]='\0';
368
468k
            c=ReadBlobByte(image);
369
468k
            in_brace=(c == '{');
370
468k
            if (in_brace)
371
52.7k
              c=ReadBlobByte(image);
372
468k
            p=values;
373
72.4M
            while ((((!in_brace) && (c != '\n')) ||
374
15.4M
                     ((in_brace) && (c != '}'))) &&
375
72.3M
                   (c != EOF))
376
72.3M
              {
377
72.3M
                if ((size_t) (p-values+1) >= values_length)
378
29.0k
                  {
379
29.0k
                    char
380
29.0k
                      *new_values;
381
382
29.0k
                    *p='\0';
383
29.0k
                    values_length += MaxTextExtent;
384
29.0k
                    new_values=MagickReallocateResourceLimitedMemory(char *,values,values_length);
385
29.0k
                    if (new_values == (char *) NULL)
386
0
                      {
387
0
                        MagickFreeResourceLimitedMemory(char *,values);
388
0
                        break;
389
0
                      }
390
29.0k
                    values=new_values;
391
29.0k
                    p=values+strlen(values);
392
29.0k
                  }
393
72.3M
                if (values == (char *) NULL)
394
72.3M
                  ThrowMPCReaderException(ResourceLimitError,MemoryAllocationFailed,image);
395
72.3M
                *p++=c;
396
72.3M
                c=ReadBlobByte(image);
397
72.3M
                if (!in_brace)
398
56.9M
                  if (isspace(c))
399
341k
                    break;
400
72.3M
              }
401
468k
            *p='\0';
402
468k
            keyword_count++;
403
468k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
404
468k
                                  "keyword[%u]=\"%s\" values=\"%s\"",keyword_count,keyword,values);
405
            /*
406
              Insist that the first keyword value must be 'MagickCache' (id=MagickCache)
407
            */
408
468k
            if ((keyword_count == 1) && (LocaleCompare(values,"MagickCache") != 0))
409
8.45k
              {
410
8.45k
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
411
8.45k
                                      "First keyword/value must be 'id=MagickCache'");
412
8.45k
                ThrowMPCReaderException(CorruptImageError,ImproperImageHeader,image);
413
0
              }
414
            /*
415
              Arbitrarily limit the number of header keywords to avoid DOS attempts.
416
            */
417
460k
            if (keyword_count > ReadMPCMaxKeyWordCount)
418
56
              {
419
56
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
420
56
                                      "Excessive key word count %u"
421
56
                                      " (Denial of service attempt?)",keyword_count);
422
56
                ThrowMPCReaderException(CorruptImageError,ImproperImageHeader,image);
423
0
              }
424
            /*
425
              Assign a value to the specified keyword.
426
            */
427
459k
            switch (*keyword)
428
459k
            {
429
5.53k
              case 'b':
430
14.9k
              case 'B':
431
14.9k
              {
432
14.9k
                if (LocaleCompare(keyword,"background-color") == 0)
433
532
                  {
434
532
                    (void) QueryColorDatabase(values,&image->background_color,
435
532
                      exception);
436
532
                    break;
437
532
                  }
438
14.4k
                if (LocaleCompare(keyword,"blue-primary") == 0)
439
418
                  {
440
418
                    (void) sscanf(values,"%lf,%lf",
441
418
                      &image->chromaticity.blue_primary.x,
442
418
                      &image->chromaticity.blue_primary.y);
443
418
                    break;
444
418
                  }
445
14.0k
                if (LocaleCompare(keyword,"border-color") == 0)
446
5.90k
                  {
447
5.90k
                    (void) QueryColorDatabase(values,&image->border_color,
448
5.90k
                      exception);
449
5.90k
                    break;
450
5.90k
                  }
451
8.11k
                (void) SetNewImageAttribute(image,keyword,
452
8.11k
                  *values == '{' ? values+1 : values);
453
8.11k
                break;
454
14.0k
              }
455
31.5k
              case 'c':
456
36.8k
              case 'C':
457
36.8k
              {
458
36.8k
                if (LocaleCompare(keyword,"class") == 0)
459
498
                  {
460
498
                    image->storage_class=UndefinedClass;
461
498
                    if (LocaleCompare(values,"PseudoClass") == 0)
462
0
                      image->storage_class=PseudoClass;
463
498
                    else
464
498
                      if (LocaleCompare(values,"DirectClass") == 0)
465
0
                        image->storage_class=DirectClass;
466
498
                    break;
467
498
                  }
468
36.3k
                if (LocaleCompare(keyword,"colors") == 0)
469
295
                  {
470
295
                    image->colors=MagickAtoL(values);
471
295
                    break;
472
295
                  }
473
36.0k
                if (LocaleCompare(keyword,"colorspace") == 0)
474
1.59k
                  {
475
1.59k
                    image->colorspace=UndefinedColorspace;
476
1.59k
                    if (LocaleCompare(values,"CMYK") == 0)
477
0
                      image->colorspace=CMYKColorspace;
478
1.59k
                    else
479
1.59k
                      if (LocaleCompare(values,"RGB") == 0)
480
550
                        image->colorspace=RGBColorspace;
481
1.59k
                    break;
482
1.59k
                  }
483
34.4k
                if (LocaleCompare(keyword,"compression") == 0)
484
1.96k
                  {
485
1.96k
                    CompressionType
486
1.96k
                      compression;
487
488
1.96k
                    compression=UndefinedCompression;
489
1.96k
                    if (LocaleCompare("None",values) == 0)
490
230
                      compression=NoCompression;
491
1.96k
                    if (LocaleCompare("BZip",values) == 0)
492
213
                      compression=BZipCompression;
493
1.96k
                    if (LocaleCompare("Fax",values) == 0)
494
262
                      compression=FaxCompression;
495
1.96k
                    if (LocaleCompare("Group4",values) == 0)
496
0
                      compression=Group4Compression;
497
1.96k
                    if (LocaleCompare("JPEG",values) == 0)
498
0
                      compression=JPEGCompression;
499
1.96k
                    if (LocaleCompare("Lossless",values) == 0)
500
0
                      compression=LosslessJPEGCompression;
501
1.96k
                    if (LocaleCompare("LZW",values) == 0)
502
0
                      compression=LZWCompression;
503
1.96k
                    if (LocaleCompare("RLE",values) == 0)
504
194
                      compression=RLECompression;
505
1.96k
                    if (LocaleCompare("Zip",values) == 0)
506
474
                      compression=ZipCompression;
507
1.96k
                    image->compression=compression;
508
1.96k
                    break;
509
1.96k
                  }
510
32.5k
                if (LocaleCompare(keyword,"columns") == 0)
511
21.2k
                  {
512
21.2k
                    image->columns= MagickAtoL(values);
513
21.2k
                    break;
514
21.2k
                  }
515
11.2k
                (void) SetNewImageAttribute(image,keyword,
516
11.2k
                  *values == '{' ? values+1 : values);
517
11.2k
                break;
518
32.5k
              }
519
7.66k
              case 'd':
520
8.51k
              case 'D':
521
8.51k
              {
522
8.51k
                if (LocaleCompare(keyword,"delay") == 0)
523
204
                  {
524
204
                    image->delay=MagickAtoL(values);
525
204
                    break;
526
204
                  }
527
8.30k
                if (LocaleCompare(keyword,"depth") == 0)
528
362
                  {
529
362
                    image->depth=MagickAtoL(values);
530
362
                    break;
531
362
                  }
532
7.94k
                if (LocaleCompare(keyword,"dispose") == 0)
533
1.75k
                  {
534
1.75k
                    image->dispose=(DisposeType) MagickAtoL(values);
535
1.75k
                    if (LocaleCompare(values,"Background") == 0)
536
274
                      image->dispose=BackgroundDispose;
537
1.48k
                    else
538
1.48k
                      if (LocaleCompare(values,"None") == 0)
539
987
                        image->dispose=NoneDispose;
540
495
                      else
541
495
                        if (LocaleCompare(values,"Previous") == 0)
542
0
                          image->dispose=PreviousDispose;
543
1.75k
                    break;
544
1.75k
                  }
545
6.18k
                (void) SetNewImageAttribute(image,keyword,
546
6.18k
                  *values == '{' ? values+1 : values);
547
6.18k
                break;
548
7.94k
              }
549
4.23k
              case 'e':
550
5.36k
              case 'E':
551
5.36k
              {
552
5.36k
                if (LocaleCompare(keyword,"error") == 0)
553
342
                  {
554
342
                    image->error.mean_error_per_pixel=MagickAtoF(values);
555
342
                    break;
556
342
                  }
557
5.02k
                (void) SetNewImageAttribute(image,keyword,
558
5.02k
                  *values == '{' ? values+1 : values);
559
5.02k
                break;
560
5.36k
              }
561
12.5k
              case 'g':
562
27.3k
              case 'G':
563
27.3k
              {
564
27.3k
                if (LocaleCompare(keyword,"gamma") == 0)
565
210
                  {
566
210
                    image->gamma=MagickAtoF(values);
567
210
                    break;
568
210
                  }
569
27.1k
                if (LocaleCompare(keyword,"grayscale") == 0)
570
1.10k
                  {
571
1.10k
                    if (LocaleCompare(values,"True") == 0)
572
281
                      image->is_grayscale=MagickTrue;
573
1.10k
                    break;
574
1.10k
                  }
575
26.0k
                if (LocaleCompare(keyword,"green-primary") == 0)
576
310
                  {
577
310
                    (void) sscanf(values,"%lf,%lf",
578
310
                      &image->chromaticity.green_primary.x,
579
310
                      &image->chromaticity.green_primary.y);
580
310
                    break;
581
310
                  }
582
25.7k
                (void) SetNewImageAttribute(image,keyword,
583
25.7k
                  *values == '{' ? values+1 : values);
584
25.7k
                break;
585
26.0k
              }
586
68.6k
              case 'i':
587
81.0k
              case 'I':
588
81.0k
              {
589
81.0k
                if (LocaleCompare(keyword,"id") == 0)
590
63.4k
                  {
591
63.4k
                    (void) strlcpy(id,values,MaxTextExtent);
592
63.4k
                    break;
593
63.4k
                  }
594
17.5k
                if (LocaleCompare(keyword,"iterations") == 0)
595
409
                  {
596
409
                    image->iterations=MagickAtoL(values);
597
409
                    break;
598
409
                  }
599
17.0k
                (void) SetNewImageAttribute(image,keyword,
600
17.0k
                  *values == '{' ? values+1 : values);
601
17.0k
                break;
602
17.5k
              }
603
16.1k
              case 'm':
604
17.8k
              case 'M':
605
17.8k
              {
606
17.8k
                if (LocaleCompare(keyword,"matte") == 0)
607
510
                  {
608
510
                    image->matte=(LocaleCompare(values,"True") == 0) ||
609
308
                      (LocaleCompare(values,"true") == 0);
610
510
                    break;
611
510
                  }
612
17.3k
                if (LocaleCompare(keyword,"matte-color") == 0)
613
8.75k
                  {
614
8.75k
                    (void) QueryColorDatabase(values,&image->matte_color,
615
8.75k
                      exception);
616
8.75k
                    break;
617
8.75k
                  }
618
8.58k
                if (LocaleCompare(keyword,"maximum-error") == 0)
619
199
                  {
620
199
                    image->error.normalized_maximum_error=MagickAtoF(values);
621
199
                    break;
622
199
                  }
623
8.38k
                if (LocaleCompare(keyword,"mean-error") == 0)
624
624
                  {
625
624
                    image->error.normalized_mean_error=MagickAtoF(values);
626
624
                    break;
627
624
                  }
628
7.76k
                if (LocaleCompare(keyword,"monochrome") == 0)
629
0
                  {
630
0
                    if (LocaleCompare(values,"True") == 0)
631
0
                      image->is_monochrome=MagickTrue;
632
0
                    break;
633
0
                  }
634
7.76k
                if (LocaleCompare(keyword,"montage") == 0)
635
1.48k
                  {
636
1.48k
                    (void) CloneString(&image->montage,values);
637
1.48k
                    break;
638
1.48k
                  }
639
6.27k
                (void) SetNewImageAttribute(image,keyword,
640
6.27k
                  *values == '{' ? values+1 : values);
641
6.27k
                break;
642
7.76k
              }
643
12.6k
              case 'o':
644
13.8k
              case 'O':
645
13.8k
              {
646
13.8k
                if (LocaleCompare(keyword,"opaque") == 0)
647
658
                  {
648
658
                    image->matte=(LocaleCompare(values,"True") == 0) ||
649
450
                      (LocaleCompare(values,"true") == 0);
650
658
                    break;
651
658
                  }
652
13.2k
                if (LocaleCompare(keyword,"orientation") == 0)
653
2.71k
                  {
654
2.71k
                    image->orientation=StringToOrientationType(values);
655
2.71k
                    break;
656
2.71k
                  }
657
10.5k
                (void) SetNewImageAttribute(image,keyword,
658
10.5k
                  *values == '{' ? values+1 : values);
659
10.5k
                break;
660
13.2k
              }
661
7.41k
              case 'p':
662
147k
              case 'P':
663
147k
              {
664
147k
                if (LocaleCompare(keyword,"page") == 0)
665
27.4k
                  {
666
27.4k
                    char
667
27.4k
                      *geometry;
668
669
27.4k
                    geometry=GetPageGeometry(values);
670
27.4k
                    (void) GetGeometry(geometry,&image->page.x,&image->page.y,
671
27.4k
                      &image->page.width,&image->page.height);
672
27.4k
                    MagickFreeMemory(geometry);
673
27.4k
                    break;
674
27.4k
                  }
675
120k
                if (LocaleNCompare(keyword,"profile-",8) == 0)
676
50.7k
                  {
677
50.7k
                    ProfileInfo
678
50.7k
                      *new_profiles;
679
680
50.7k
                    i=number_of_profiles;
681
682
50.7k
                    new_profiles=MagickReallocateResourceLimitedArray(ProfileInfo *,profiles,
683
50.7k
                        (size_t)number_of_profiles+1,sizeof(ProfileInfo));
684
50.7k
                    if (new_profiles == (ProfileInfo *) NULL)
685
50.7k
                      ThrowMPCReaderException(ResourceLimitError,MemoryAllocationFailed,image);
686
50.7k
                    profiles=new_profiles;
687
50.7k
                    profiles[i].name=AllocateString(keyword+8);
688
50.7k
                    profiles[i].length=MagickAtoL(values);
689
50.7k
                    profiles[i].info=(unsigned char *) NULL;
690
50.7k
                    number_of_profiles++;
691
50.7k
                    break;
692
50.7k
                  }
693
69.5k
                (void) SetNewImageAttribute(image,keyword,
694
69.5k
                  *values == '{' ? values+1 : values);
695
69.5k
                break;
696
120k
              }
697
1.61k
              case 'q':
698
4.36k
              case 'Q':
699
4.36k
              {
700
4.36k
                if (LocaleCompare(keyword,"quantum-depth") == 0)
701
0
                  {
702
0
                    quantum_depth=MagickAtoL(values);
703
0
                    break;
704
0
                  }
705
4.36k
                (void) SetNewImageAttribute(image,keyword,
706
4.36k
                  *values == '{' ? values+1 : values);
707
4.36k
                break;
708
4.36k
              }
709
32.5k
              case 'r':
710
39.1k
              case 'R':
711
39.1k
              {
712
39.1k
                if (LocaleCompare(keyword,"red-primary") == 0)
713
303
                  {
714
303
                    (void) sscanf(values,"%lf,%lf",
715
303
                      &image->chromaticity.red_primary.x,
716
303
                      &image->chromaticity.red_primary.y);
717
303
                    break;
718
303
                  }
719
38.8k
                if (LocaleCompare(keyword,"rendering-intent") == 0)
720
296
                  {
721
296
                    image->rendering_intent=UndefinedIntent;
722
296
                    if (LocaleCompare(values,"Saturation") == 0)
723
0
                      image->rendering_intent=SaturationIntent;
724
296
                    else
725
296
                      if (LocaleCompare(values,"perceptual") == 0)
726
0
                        image->rendering_intent=PerceptualIntent;
727
296
                      else
728
296
                        if (LocaleCompare(values,"absolute") == 0)
729
0
                          image->rendering_intent=AbsoluteIntent;
730
296
                        else
731
296
                          if (LocaleCompare(values,"relative") == 0)
732
0
                            image->rendering_intent=RelativeIntent;
733
296
                    break;
734
296
                  }
735
38.5k
                if (LocaleCompare(keyword,"resolution") == 0)
736
4.87k
                  {
737
4.87k
                    (void) GetMagickDimension(values,&image->x_resolution,
738
4.87k
                                              &image->y_resolution,NULL,NULL);
739
4.87k
                    break;
740
4.87k
                  }
741
33.6k
                if (LocaleCompare(keyword,"rows") == 0)
742
11.1k
                  {
743
11.1k
                    image->rows=MagickAtoL(values);
744
11.1k
                    break;
745
11.1k
                  }
746
22.5k
                (void) SetNewImageAttribute(image,keyword,
747
22.5k
                  *values == '{' ? values+1 : values);
748
22.5k
                break;
749
33.6k
              }
750
5.30k
              case 's':
751
13.7k
              case 'S':
752
13.7k
              {
753
13.7k
                if (LocaleCompare(keyword,"scene") == 0)
754
544
                  {
755
544
                    image->scene=MagickAtoL(values);
756
544
                    break;
757
544
                  }
758
13.2k
                (void) SetNewImageAttribute(image,keyword,
759
13.2k
                  *values == '{' ? values+1 : values);
760
13.2k
                break;
761
13.7k
              }
762
5.49k
              case 'u':
763
8.31k
              case 'U':
764
8.31k
              {
765
8.31k
                if (LocaleCompare(keyword,"units") == 0)
766
371
                  {
767
371
                    image->units=UndefinedResolution;
768
371
                    if (LocaleCompare(values,"pixels-per-inch") == 0)
769
0
                      image->units=PixelsPerInchResolution;
770
371
                    else
771
371
                      if (LocaleCompare(values,"pixels-per-centimeter") == 0)
772
0
                        image->units=PixelsPerCentimeterResolution;
773
371
                    break;
774
371
                  }
775
7.94k
                (void) SetNewImageAttribute(image,keyword,
776
7.94k
                  *values == '{' ? values+1 : values);
777
7.94k
                break;
778
8.31k
              }
779
16.3k
              case 'w':
780
17.0k
              case 'W':
781
17.0k
              {
782
17.0k
                if (LocaleCompare(keyword,"white-point") == 0)
783
0
                  {
784
0
                    (void) sscanf(values,"%lf,%lf",
785
0
                      &image->chromaticity.white_point.x,
786
0
                      &image->chromaticity.white_point.y);
787
0
                    break;
788
0
                  }
789
17.0k
                (void) SetNewImageAttribute(image,keyword,
790
17.0k
                  *values == '{' ? values+1 : values);
791
17.0k
                break;
792
17.0k
              }
793
23.8k
              default:
794
23.8k
              {
795
23.8k
                (void) SetNewImageAttribute(image,keyword,
796
23.8k
                  *values == '{' ? values+1 : values);
797
23.8k
                break;
798
17.0k
              }
799
459k
            }
800
459k
            MagickFreeResourceLimitedMemory(char *,values);
801
459k
          }
802
132k
        else
803
132k
          {
804
132k
            c=ReadBlobByte(image);
805
132k
          }
806
604k
      while (isspace(c))
807
483k
        c=ReadBlobByte(image);
808
604k
    }
809
38.7k
    (void) ReadBlobByte(image);
810
811
38.7k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
812
38.7k
                          "id=\"%s\" class=%s compression=%s matte=%s "
813
38.7k
                          "columns=%lu rows=%lu depth=%u",
814
38.7k
                          id,ClassTypeToString(image->storage_class),
815
38.7k
                          CompressionTypeToString(image->compression),
816
38.7k
                          MagickBoolToString(image->matte),
817
38.7k
                          image->columns, image->rows, image->depth);
818
819
    /*
820
      Verify that required image information is defined.
821
    */
822
38.7k
    if ((LocaleCompare(id,"MagickCache") != 0) ||
823
36.9k
        (image->storage_class == UndefinedClass) ||
824
36.8k
        (image->compression == UndefinedCompression) || (image->columns == 0) ||
825
15.1k
        (image->rows == 0))
826
30.2k
      ThrowMPCReaderException(CorruptImageError,ImproperImageHeader,image);
827
8.52k
    if (quantum_depth != QuantumDepth)
828
8.52k
      ThrowMPCReaderException(CacheError,InconsistentPersistentCacheDepth,image);
829
8.52k
    if (image->montage != (char *) NULL)
830
106
      {
831
106
        register char
832
106
          *p;
833
834
106
        size_t
835
106
          directory_length;
836
837
        /*
838
          Image directory.
839
        */
840
106
        image->directory=MagickAllocateMemory(char *,MaxTextExtent);
841
106
        if (image->directory == (char *) NULL)
842
106
          ThrowMPCReaderException(CorruptImageError,UnableToReadImageData,image);
843
106
        p=image->directory;
844
106
        directory_length=0;
845
106
        do
846
751k
        {
847
751k
          *p='\0';
848
751k
          if (((directory_length+1) % MaxTextExtent) == 0)
849
356
            {
850
              /*
851
                Allocate more memory for the image directory.
852
              */
853
356
              MagickReallocMemory(char *,image->directory,
854
356
                (directory_length+MaxTextExtent+1));
855
356
              if (image->directory == (char *) NULL)
856
0
                ThrowMPCReaderException(CorruptImageError,UnableToReadImageData,
857
356
                  image);
858
356
              p=image->directory+directory_length;
859
356
            }
860
751k
          c=ReadBlobByte(image);
861
751k
          if (c == EOF)
862
86
            break;
863
751k
          *p++=c;
864
751k
          ++directory_length;
865
751k
        } while (c != '\0');
866
106
      }
867
868
    /*
869
      Attached profiles.
870
    */
871
8.52k
    if (number_of_profiles > 0)
872
7.88k
      {
873
22.1k
        for (i=0; i < number_of_profiles; i++)
874
18.9k
        {
875
18.9k
          if (profiles[i].length > 0)
876
15.3k
            {
877
15.3k
              if ((((magick_off_t) profiles[i].length) > 0) &&
878
14.1k
                  ((BlobIsSeekable(image)
879
14.1k
                    && (GetBlobSize(image) - TellBlob(image)) >
880
14.1k
                    (magick_off_t) profiles[i].length) ||
881
4.35k
                   (profiles[i].length < 15*1024*1024)))
882
12.2k
                {
883
12.2k
                  profiles[i].info=MagickAllocateResourceLimitedMemory(unsigned char *,profiles[i].length);
884
12.2k
                  if (profiles[i].info == (unsigned char *) NULL)
885
0
                    ThrowMPCReaderException(ResourceLimitError,MemoryAllocationFailed,
886
12.2k
                                             image);
887
12.2k
                  if (ReadBlob(image,profiles[i].length,profiles[i].info)
888
12.2k
                      != profiles[i].length)
889
1.51k
                    ThrowMPCReaderException(CorruptImageError,
890
12.2k
                                            UnexpectedEndOfFile,
891
12.2k
                                            image);
892
10.7k
                  (void) SetImageProfile(image,profiles[i].name,profiles[i].info,profiles[i].length);
893
10.7k
                }
894
3.15k
              else
895
3.15k
                {
896
3.15k
                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
897
3.15k
                                        "Profile size %" MAGICK_SIZE_T_F "u is excessively large",
898
3.15k
                                        (MAGICK_SIZE_T ) profiles[i].length);
899
3.15k
                  ThrowMPCReaderException(CorruptImageError,ImproperImageHeader,
900
3.15k
                                           image);
901
0
                }
902
15.3k
            }
903
14.3k
          MagickFreeMemory(profiles[i].name);
904
14.3k
          MagickFreeResourceLimitedMemory(unsigned char *,profiles[i].info);
905
14.3k
        }
906
3.22k
        MagickFreeResourceLimitedMemory(ProfileInfo *,profiles);
907
3.22k
        number_of_profiles=0;
908
3.22k
      }
909
910
3.86k
    if (image->storage_class == PseudoClass)
911
0
      {
912
        /*
913
          Create image colormap.
914
        */
915
0
        if (!AllocateImageColormap(image,image->colors))
916
0
          ThrowMPCReaderException(ResourceLimitError,MemoryAllocationFailed,
917
0
            image);
918
0
        if (image->colors == 0)
919
0
          for (i=0; i < 256U; i++)
920
0
          {
921
0
            image->colormap[i].red=ScaleCharToQuantum(i);
922
0
            image->colormap[i].green=ScaleCharToQuantum(i);
923
0
            image->colormap[i].blue=ScaleCharToQuantum(i);
924
0
            image->colors++;
925
0
          }
926
0
        else
927
0
          {
928
0
            unsigned char
929
0
              *colormap;
930
931
0
            unsigned int
932
0
              packet_size;
933
934
            /*
935
              Read image colormap from file.
936
            */
937
0
            packet_size=image->depth > 8 ? 6 : 3;
938
0
            colormap=MagickAllocateResourceLimitedArray(unsigned char *,packet_size,image->colors);
939
0
            if (colormap == (unsigned char *) NULL)
940
0
              ThrowMPCReaderException(ResourceLimitError,MemoryAllocationFailed,
941
0
                image);
942
0
            (void) ReadBlob(image, (size_t) packet_size*image->colors,colormap);
943
0
            p=colormap;
944
0
            if (image->depth <= 8)
945
0
              for (i=0; i < image->colors; i++)
946
0
              {
947
0
                image->colormap[i].red=ScaleCharToQuantum(*p++);
948
0
                image->colormap[i].green=ScaleCharToQuantum(*p++);
949
0
                image->colormap[i].blue=ScaleCharToQuantum(*p++);
950
0
              }
951
0
            else
952
0
              for (i=0; i < image->colors; i++)
953
0
              {
954
0
                image->colormap[i].red=(*p++ << 8);
955
0
                image->colormap[i].red|=(*p++);
956
0
                image->colormap[i].green=(*p++ << 8);
957
0
                image->colormap[i].green|=(*p++);
958
0
                image->colormap[i].blue=(*p++ << 8);
959
0
                image->colormap[i].blue|=(*p++);
960
0
              }
961
0
            MagickFreeResourceLimitedMemory(unsigned char *,colormap);
962
0
          }
963
0
      }
964
3.86k
    if (EOFBlob(image))
965
560
      {
966
560
        ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,
967
560
          image->filename);
968
560
        break;
969
560
      }
970
3.30k
    if (image_info->ping && (image_info->subrange != 0))
971
0
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
972
0
        break;
973
974
3.30k
    if (CheckImagePixelLimits(image, exception) != MagickPass)
975
3.14k
      ThrowMPCReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
976
977
    /*
978
      Attach persistent pixel cache.
979
    */
980
153
    status=PersistCache(image,cache_filename,MagickTrue,&offset,exception);
981
153
    if (status == MagickFail)
982
153
      ThrowMPCReaderException(CacheError,UnableToPeristPixelCache,image);
983
0
    StopTimer(&image->timer);
984
    /*
985
      Proceed to next image.
986
    */
987
0
    do
988
0
    {
989
0
      c=ReadBlobByte(image);
990
0
    } while (!isgraph(c) && (c != EOF));
991
0
    if (c != EOF)
992
0
      {
993
        /*
994
          Allocate next image structure.
995
        */
996
0
        AllocateNextImage(image_info,image);
997
0
        if (image->next == (Image *) NULL)
998
0
          {
999
0
            DestroyImageList(image);
1000
0
            return((Image *) NULL);
1001
0
          }
1002
0
        image=SyncNextImageInList(image);
1003
0
        status=MagickMonitorFormatted(TellBlob(image),GetBlobSize(image),
1004
0
                                      exception,LoadImagesText,
1005
0
                                      image->filename);
1006
0
        if (status == MagickFail)
1007
0
          break;
1008
0
      }
1009
0
  } while (c != EOF);
1010
560
  while (image->previous != (Image *) NULL)
1011
0
    image=image->previous;
1012
560
  CloseBlob(image);
1013
560
  return(image);
1014
67.1k
}
1015

1016
/*
1017
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1018
%                                                                             %
1019
%                                                                             %
1020
%                                                                             %
1021
%   R e g i s t e r M P C I m a g e                                           %
1022
%                                                                             %
1023
%                                                                             %
1024
%                                                                             %
1025
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1026
%
1027
%  Method RegisterMPCImage adds attributes for the Cache image format to
1028
%  the list of supported formats.  The attributes include the image format
1029
%  tag, a method to read and/or write the format, whether the format
1030
%  supports the saving of more than one frame to the same file or blob,
1031
%  whether the format supports native in-memory I/O, and a brief
1032
%  description of the format.
1033
%
1034
%  The format of the RegisterMPCImage method is:
1035
%
1036
%      RegisterMPCImage(void)
1037
%
1038
*/
1039
ModuleExport void RegisterMPCImage(void)
1040
5
{
1041
5
  MagickInfo
1042
5
    *entry;
1043
1044
5
  entry=SetMagickInfo("CACHE");
1045
5
  entry->description="Magick Persistent Cache image format";
1046
5
  entry->module="CACHE";
1047
5
  entry->coder_class=UnstableCoderClass;
1048
5
  (void) RegisterMagickInfo(entry);
1049
1050
5
  entry=SetMagickInfo("MPC");
1051
5
  entry->decoder=(DecoderHandler) ReadMPCImage;
1052
5
  entry->encoder=(EncoderHandler) WriteMPCImage;
1053
5
  entry->magick=(MagickHandler) IsMPC;
1054
5
  entry->description="Magick Persistent Cache image format";
1055
5
  entry->adjoin=MagickFalse;
1056
5
  entry->seekable_stream=MagickTrue;
1057
5
  entry->blob_support=MagickFalse;
1058
5
  entry->module="MPC";
1059
5
  entry->coder_class=UnstableCoderClass;
1060
5
  (void) RegisterMagickInfo(entry);
1061
5
}
1062

1063
/*
1064
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1065
%                                                                             %
1066
%                                                                             %
1067
%                                                                             %
1068
%   U n r e g i s t e r M P C I m a g e                                       %
1069
%                                                                             %
1070
%                                                                             %
1071
%                                                                             %
1072
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1073
%
1074
%  Method UnregisterMPCImage removes format registrations made by the
1075
%  MPC module from the list of supported formats.
1076
%
1077
%  The format of the UnregisterMPCImage method is:
1078
%
1079
%      UnregisterMPCImage(void)
1080
%
1081
*/
1082
ModuleExport void UnregisterMPCImage(void)
1083
0
{
1084
0
  (void) UnregisterMagickInfo("CACHE");
1085
0
  (void) UnregisterMagickInfo("MPC");
1086
0
}
1087

1088
/*
1089
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1090
%                                                                             %
1091
%                                                                             %
1092
%                                                                             %
1093
%   W r i t e M P C I m a g e                                                 %
1094
%                                                                             %
1095
%                                                                             %
1096
%                                                                             %
1097
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1098
%
1099
%  Method WriteMPCImage writes an Magick Persistent Cache image to a file.
1100
%
1101
%  The format of the WriteMPCImage method is:
1102
%
1103
%      MagickPassFail WriteMPCImage(const ImageInfo *image_info,Image *image)
1104
%
1105
%  A description of each parameter follows:
1106
%
1107
%    o status: Method WriteMPCImage return MagickPass if the image is written.
1108
%      MagickFail is returned if there is a memory shortage or if the image file
1109
%      fails to write.
1110
%
1111
%    o image_info: Specifies a pointer to a ImageInfo structure.
1112
%
1113
%    o image: A pointer to an Image structure.
1114
%
1115
%
1116
*/
1117
static MagickPassFail WriteMPCImage(const ImageInfo *image_info,Image *image)
1118
0
{
1119
0
  char
1120
0
    buffer[MaxTextExtent],
1121
0
    cache_filename[MaxTextExtent];
1122
1123
0
  const ImageAttribute
1124
0
    *attribute;
1125
1126
0
  ExtendedSignedIntegralType
1127
0
    offset;
1128
1129
0
  register unsigned long
1130
0
    i;
1131
1132
0
  unsigned int
1133
0
    status;
1134
1135
0
  unsigned long
1136
0
    scene;
1137
1138
0
  ImageProfileIterator
1139
0
    profile_iterator;
1140
1141
0
  const char
1142
0
    *profile_name;
1143
1144
0
  const unsigned char
1145
0
    *profile_info;
1146
1147
0
  size_t
1148
0
    profile_length=0;
1149
1150
0
  size_t
1151
0
    image_list_length;
1152
1153
  /*
1154
    Open persistent cache.
1155
  */
1156
0
  assert(image_info != (const ImageInfo *) NULL);
1157
0
  assert(image_info->signature == MagickSignature);
1158
0
  assert(image != (Image *) NULL);
1159
0
  assert(image->signature == MagickSignature);
1160
0
  image_list_length=GetImageListLength(image);
1161
0
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1162
0
  if (status == MagickFail)
1163
0
    ThrowWriterException(FileOpenError,UnableToOpenFile,image);
1164
0
  (void) strlcpy(cache_filename,image->filename,MaxTextExtent);
1165
0
  AppendImageFormat("cache",cache_filename);
1166
0
  scene=0;
1167
0
  offset=0;
1168
0
  do
1169
0
    {
1170
      /*
1171
        Write persistent cache meta-information.
1172
      */
1173
0
      (void) WriteBlobString(image,"id=MagickCache\n");
1174
0
      MagickFormatString(buffer,sizeof(buffer),"quantum-depth=%d\n",QuantumDepth);
1175
0
      (void) WriteBlobString(image,buffer);
1176
0
      if (image->storage_class == PseudoClass)
1177
0
        MagickFormatString(buffer,sizeof(buffer),"class=PseudoClass  colors=%u  matte=%s\n",
1178
0
                           image->colors,MagickBoolToString(image->matte));
1179
0
      else
1180
0
        if (image->colorspace == CMYKColorspace)
1181
0
          MagickFormatString(buffer,sizeof(buffer),"class=DirectClass  colorspace=CMYK  matte=%s\n",
1182
0
                             MagickBoolToString(image->matte));
1183
0
        else
1184
0
          MagickFormatString(buffer,sizeof(buffer),"class=DirectClass  matte=%s\n",
1185
0
                             MagickBoolToString(image->matte));
1186
0
      (void) WriteBlobString(image,buffer);
1187
0
      switch (image->compression)
1188
0
        {
1189
0
        default:
1190
0
        case NoCompression:
1191
0
          {
1192
0
            (void) WriteBlobString(image,"compression=None\n");
1193
0
            break;
1194
0
          }
1195
0
        case BZipCompression:
1196
0
          {
1197
0
            (void) WriteBlobString(image,"compression=BZip\n");
1198
0
            break;
1199
0
          }
1200
0
        case FaxCompression:
1201
0
          {
1202
0
            (void) WriteBlobString(image,"compression=Fax\n");
1203
0
            break;
1204
0
          }
1205
0
        case Group4Compression:
1206
0
          {
1207
0
            (void) WriteBlobString(image,"compression=Group4\n");
1208
0
            break;
1209
0
          }
1210
0
        case JPEGCompression:
1211
0
          {
1212
0
            (void) WriteBlobString(image,"compression=JPEG\n");
1213
0
            break;
1214
0
          }
1215
0
        case LosslessJPEGCompression:
1216
0
          {
1217
0
            (void) WriteBlobString(image,"compression=Lossless\n");
1218
0
            break;
1219
0
          }
1220
0
        case LZWCompression:
1221
0
          {
1222
0
            (void) WriteBlobString(image,"compression=LZW\n");
1223
0
            break;
1224
0
          }
1225
0
        case RLECompression:
1226
0
          {
1227
0
            (void) WriteBlobString(image,"compression=RLE\n");
1228
0
            break;
1229
0
          }
1230
0
        case ZipCompression:
1231
0
          {
1232
0
            (void) WriteBlobString(image,"compression=Zip\n");
1233
0
            break;
1234
0
          }
1235
0
        }
1236
0
      MagickFormatString(buffer,sizeof(buffer),"columns=%lu  rows=%lu  depth=%u\n",image->columns,
1237
0
                         image->rows,image->depth);
1238
0
      (void) WriteBlobString(image,buffer);
1239
0
      if (image->is_monochrome != MagickFalse)
1240
0
        {
1241
0
          MagickFormatString(buffer,sizeof(buffer),"monochrome=True\n");
1242
0
          (void) WriteBlobString(image,buffer);
1243
0
        }
1244
0
      if (image->is_grayscale != MagickFalse)
1245
0
        {
1246
0
          MagickFormatString(buffer,sizeof(buffer),"grayscale=True\n");
1247
0
          (void) WriteBlobString(image,buffer);
1248
0
        }
1249
0
      if ((image->x_resolution != 0) && (image->y_resolution != 0))
1250
0
        {
1251
0
          char
1252
0
            units[MaxTextExtent];
1253
1254
          /*
1255
            Set image resolution.
1256
          */
1257
0
          (void) strlcpy(units,"undefined",sizeof(units));
1258
0
          if (image->units == PixelsPerInchResolution)
1259
0
            (void) strlcpy(units,"pixels-per-inch",sizeof(units));
1260
0
          if (image->units == PixelsPerCentimeterResolution)
1261
0
            (void) strlcpy(units,"pixels-per-centimeter",sizeof(units));
1262
0
          MagickFormatString(buffer,sizeof(buffer),"Resolution=%gx%g  units=%.1024s\n",
1263
0
                             image->x_resolution,image->y_resolution,units);
1264
0
          (void) WriteBlobString(image,buffer);
1265
0
        }
1266
0
      if ((image->page.width != 0) && (image->page.height != 0))
1267
0
        {
1268
0
          MagickFormatString(buffer,sizeof(buffer),"page=%lux%lu%+ld%+ld\n",image->page.width,
1269
0
                             image->page.height,image->page.x,image->page.y);
1270
0
          (void) WriteBlobString(image,buffer);
1271
0
        }
1272
0
      if ((image->next != (Image *) NULL) || (image->previous != (Image *) NULL))
1273
0
        {
1274
0
          if (image->scene == 0)
1275
0
            MagickFormatString(buffer,sizeof(buffer),"iterations=%lu  delay=%lu\n",image->iterations,
1276
0
                               image->delay);
1277
0
          else
1278
0
            MagickFormatString(buffer,sizeof(buffer),"scene=%lu  iterations=%lu  delay=%lu\n",
1279
0
                               image->scene,image->iterations,image->delay);
1280
0
          (void) WriteBlobString(image,buffer);
1281
0
        }
1282
0
      else
1283
0
        {
1284
0
          if (image->scene != 0)
1285
0
            {
1286
0
              MagickFormatString(buffer,sizeof(buffer),"scene=%lu\n",image->scene);
1287
0
              (void) WriteBlobString(image,buffer);
1288
0
            }
1289
0
          if (image->iterations != 0)
1290
0
            {
1291
0
              MagickFormatString(buffer,sizeof(buffer),"iterations=%lu\n",image->iterations);
1292
0
              (void) WriteBlobString(image,buffer);
1293
0
            }
1294
0
          if (image->delay != 0)
1295
0
            {
1296
0
              MagickFormatString(buffer,sizeof(buffer),"delay=%lu\n",image->delay);
1297
0
              (void) WriteBlobString(image,buffer);
1298
0
            }
1299
0
        }
1300
0
      if (image->dispose != UndefinedDispose)
1301
0
        {
1302
0
          if (image->dispose == BackgroundDispose)
1303
0
            (void) strlcpy(buffer,"dispose=background\n",sizeof(buffer));
1304
0
          else
1305
0
            if (image->dispose == NoneDispose)
1306
0
              (void) strlcpy(buffer,"dispose=none\n",sizeof(buffer));
1307
0
            else
1308
0
              (void) strlcpy(buffer,"dispose=previous\n",sizeof(buffer));
1309
0
          (void) WriteBlobString(image,buffer);
1310
0
        }
1311
0
      if (image->error.mean_error_per_pixel != 0.0)
1312
0
        {
1313
0
          MagickFormatString(buffer,sizeof(buffer),"error=%g  mean-error=%g  maximum-error=%g\n",
1314
0
                             image->error.mean_error_per_pixel,image->error.normalized_mean_error,
1315
0
                             image->error.normalized_maximum_error);
1316
0
          (void) WriteBlobString(image,buffer);
1317
0
        }
1318
0
      if (image->rendering_intent != UndefinedIntent)
1319
0
        {
1320
0
          if (image->rendering_intent == SaturationIntent)
1321
0
            (void) WriteBlobString(image,"rendering-intent=saturation\n");
1322
0
          else
1323
0
            if (image->rendering_intent == PerceptualIntent)
1324
0
              (void) WriteBlobString(image,"rendering-intent=perceptual\n");
1325
0
            else
1326
0
              if (image->rendering_intent == AbsoluteIntent)
1327
0
                (void) WriteBlobString(image,"rendering-intent=absolute\n");
1328
0
              else
1329
0
                (void) WriteBlobString(image,"rendering-intent=relative\n");
1330
0
        }
1331
0
      if (image->gamma != 0.0)
1332
0
        {
1333
0
          MagickFormatString(buffer,sizeof(buffer),"gamma=%g\n",image->gamma);
1334
0
          (void) WriteBlobString(image,buffer);
1335
0
        }
1336
0
      if (image->chromaticity.white_point.x != 0.0)
1337
0
        {
1338
          /*
1339
            Note chomaticity points.
1340
          */
1341
0
          MagickFormatString(buffer,sizeof(buffer),
1342
0
                             "red-primary=%g,%g  green-primary=%g,%g  blue-primary=%g,%g\n",
1343
0
                             image->chromaticity.red_primary.x,image->chromaticity.red_primary.y,
1344
0
                             image->chromaticity.green_primary.x,
1345
0
                             image->chromaticity.green_primary.y,
1346
0
                             image->chromaticity.blue_primary.x,
1347
0
                             image->chromaticity.blue_primary.y);
1348
0
          (void) WriteBlobString(image,buffer);
1349
0
          MagickFormatString(buffer,sizeof(buffer),"white-point=%g,%g\n",
1350
0
                             image->chromaticity.white_point.x,image->chromaticity.white_point.y);
1351
0
          (void) WriteBlobString(image,buffer);
1352
0
        }
1353
0
      if (image->orientation != UndefinedOrientation)
1354
0
        {
1355
0
          MagickFormatString(buffer,sizeof(buffer),"orientation=%s\n",
1356
0
                             OrientationTypeToString(image->orientation));
1357
0
          (void) WriteBlobString(image,buffer);
1358
0
        }
1359
      /*
1360
        Attached profiles.
1361
      */
1362
0
      profile_iterator=AllocateImageProfileIterator(image);
1363
0
      if (profile_iterator)
1364
0
        {
1365
0
          while(NextImageProfile(profile_iterator,&profile_name,&profile_info,
1366
0
                                 &profile_length)
1367
0
                != MagickFail)
1368
0
            {
1369
0
              MagickFormatString(buffer,sizeof(buffer),"profile-%.1024s=%lu\n",
1370
0
                                 profile_name == (char *) NULL ? "generic" :
1371
0
                                 profile_name,(unsigned long)
1372
0
                                 profile_length);
1373
0
              (void) WriteBlobString(image,buffer);
1374
0
            }
1375
0
          DeallocateImageProfileIterator(profile_iterator);
1376
0
        }
1377
1378
0
      if (image->montage != (char *) NULL)
1379
0
        {
1380
0
          MagickFormatString(buffer,sizeof(buffer),"montage=%.1024s\n",image->montage);
1381
0
          (void) WriteBlobString(image,buffer);
1382
0
        }
1383
0
      attribute=GetImageAttribute(image,(char *) NULL);
1384
0
      for ( ; attribute != (const ImageAttribute *) NULL; attribute=attribute->next)
1385
0
        {
1386
0
          if (attribute->value != NULL)
1387
0
            {
1388
0
              long
1389
0
                j;
1390
1391
0
              MagickFormatString(buffer,sizeof(buffer),"%.1024s=",attribute->key);
1392
0
              (void) WriteBlobString(image,buffer);
1393
0
              for (j=0; j < (long) strlen(attribute->value); j++)
1394
0
                if (isspace((int) attribute->value[j]))
1395
0
                  break;
1396
0
              if (j < (long) strlen(attribute->value))
1397
0
                (void) WriteBlobByte(image,'{');
1398
0
              (void) WriteBlobString(image,attribute->value);
1399
0
              if (j < (long) strlen(attribute->value))
1400
0
                (void) WriteBlobByte(image,'}');
1401
0
              (void) WriteBlobByte(image,'\n');
1402
0
            }
1403
0
        }
1404
0
      (void) WriteBlobString(image,"\f\n:\032");
1405
0
      if (image->montage != (char *) NULL)
1406
0
        {
1407
          /*
1408
            Write montage tile directory.
1409
          */
1410
0
          if (image->directory != (char *) NULL)
1411
0
            (void) WriteBlobString(image,image->directory);
1412
0
          (void) WriteBlobByte(image,'\0');
1413
0
        }
1414
1415
      /*
1416
        Attached profiles.
1417
      */
1418
0
      profile_iterator=AllocateImageProfileIterator(image);
1419
0
      if (profile_iterator)
1420
0
        {
1421
0
          while(NextImageProfile(profile_iterator,&profile_name,&profile_info,
1422
0
                                 &profile_length) != MagickFail)
1423
0
            {
1424
0
              if (profile_length != 0)
1425
0
                (void) WriteBlob(image,profile_length,profile_info);
1426
0
            }
1427
0
          DeallocateImageProfileIterator(profile_iterator);
1428
0
        }
1429
1430
0
      if (image->storage_class == PseudoClass)
1431
0
        {
1432
0
          register unsigned char
1433
0
            *q;
1434
1435
0
          unsigned char
1436
0
            *colormap;
1437
1438
0
          unsigned int
1439
0
            packet_size;
1440
1441
          /*
1442
            Allocate colormap.
1443
          */
1444
0
          packet_size=image->depth > 8 ? 6 : 3;
1445
0
          colormap=MagickAllocateResourceLimitedArray(unsigned char *,packet_size,image->colors);
1446
0
          if (colormap == (unsigned char *) NULL)
1447
0
            return(MagickFail);
1448
          /*
1449
            Write colormap to file.
1450
          */
1451
0
          q=colormap;
1452
0
          if (image->depth <= 8)
1453
0
            for (i=0; i < image->colors; i++)
1454
0
              {
1455
0
                *q++=image->colormap[i].red;
1456
0
                *q++=image->colormap[i].green;
1457
0
                *q++=image->colormap[i].blue;
1458
0
              }
1459
0
#if QuantumDepth > 8
1460
0
          else
1461
0
            for (i=0; i < image->colors; i++)
1462
0
              {
1463
0
                *q++=image->colormap[i].red >> 8;
1464
0
                *q++=image->colormap[i].red & 0xff;
1465
0
                *q++=image->colormap[i].green >> 8;
1466
0
                *q++=image->colormap[i].green & 0xff;
1467
0
                *q++=image->colormap[i].blue >> 8;
1468
0
                *q++=image->colormap[i].blue & 0xff;
1469
0
              }
1470
0
#endif /* QuantumDepth > 8 */
1471
1472
0
          (void) WriteBlob(image, (size_t) packet_size*image->colors,colormap);
1473
0
          MagickFreeResourceLimitedMemory(unsigned char *,colormap);
1474
0
        }
1475
      /*
1476
        Initialize persistent pixel cache.
1477
      */
1478
0
      status=PersistCache(image,cache_filename,MagickFalse,&offset,&image->exception);
1479
0
      if (status == MagickFail)
1480
0
        ThrowWriterException(CacheError,UnableToPeristPixelCache,image);
1481
0
      if (image->next == (Image *) NULL)
1482
0
        break;
1483
0
      image=SyncNextImageInList(image);
1484
0
      status=MagickMonitorFormatted(scene++,image_list_length,
1485
0
                                    &image->exception,SaveImagesText,
1486
0
                                    image->filename);
1487
0
      if (status == False)
1488
0
        break;
1489
0
    } while (image_info->adjoin);
1490
0
  if (image_info->adjoin)
1491
0
    while (image->previous != (Image *) NULL)
1492
0
      image=image->previous;
1493
0
  status &= CloseBlob(image);
1494
0
  return(status);
1495
0
}