Coverage Report

Created: 2025-11-16 07:22

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