Coverage Report

Created: 2026-01-20 07:37

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