Coverage Report

Created: 2026-10-28 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/coders/xpm.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2026 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
% SPDX-License-Identifier: MIT
6
%
7
% This program is covered by multiple licenses, which are described in
8
% Copyright.txt. You should have received a copy of Copyright.txt with this
9
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
10
%
11
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12
%                                                                             %
13
%                                                                             %
14
%                                                                             %
15
%                            X   X  PPPP   M   M                              %
16
%                             X X   P   P  MM MM                              %
17
%                              X    PPPP   M M M                              %
18
%                             X X   P      M   M                              %
19
%                            X   X  P      M   M                              %
20
%                                                                             %
21
%                                                                             %
22
%                 Read/Write X Windows system Pixmap Format.                  %
23
%                                                                             %
24
%                                                                             %
25
%                              Software Design                                %
26
%                                John Cristy                                  %
27
%                                 July 1992                                   %
28
%                                                                             %
29
%                                                                             %
30
%                                                                             %
31
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
32
%
33
%
34
*/
35

36
/*
37
  Include declarations.
38
*/
39
#include "magick/studio.h"
40
#include "magick/analyze.h"
41
#include "magick/blob.h"
42
#include "magick/color.h"
43
#include "magick/color_lookup.h"
44
#include "magick/colormap.h"
45
#include "magick/log.h"
46
#include "magick/magick.h"
47
#include "magick/monitor.h"
48
#include "magick/pixel_cache.h"
49
#include "magick/quantize.h"
50
#include "magick/resize.h"
51
#include "magick/utility.h"
52
#include "magick/static.h"
53

54
55
19.2k
#define MAX_XPM_SUPPORTED_COLORS        Max(0x20000,MaxColormapSize)
56
57
/*
58
  Forward declarations.
59
*/
60
static unsigned int
61
  WritePICONImage(const ImageInfo *,Image *),
62
  WriteXPMImage(const ImageInfo *,Image *);
63
64
typedef magick_uint32_t xpmkeyval_t;
65
66
typedef struct xpmkey
67
{
68
  magick_uint32_t index;        /* Colormap index */
69
  xpmkeyval_t keyval;           /* Encoded XPM key value */
70
} xpmkey_t;
71
72

73
static int XPMKeyCompare(const void *l, const void *r)
74
70.7M
{
75
70.7M
  const xpmkey_t * restrict lp = (const xpmkey_t *) l;
76
70.7M
  const xpmkey_t * restrict rp = (const xpmkey_t *) r;
77
70.7M
  int sense;
78
79
70.7M
  if (lp->keyval > rp->keyval)
80
25.0M
    sense = 1;
81
45.6M
  else if (lp->keyval < rp->keyval)
82
18.4M
    sense = -1;
83
27.1M
  else
84
27.1M
    sense = 0;
85
86
70.7M
  return sense;
87
70.7M
}
88

89
/*
90
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91
%                                                                             %
92
%                                                                             %
93
%                                                                             %
94
%   I s X P M                                                                 %
95
%                                                                             %
96
%                                                                             %
97
%                                                                             %
98
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
99
%
100
%  Method IsXPM returns True if the image format type, identified by the
101
%  magick string, is XPM.
102
%
103
%  The format of the IsXPM method is:
104
%
105
%      unsigned int IsXPM(const unsigned char *magick,const size_t length)
106
%
107
%  A description of each parameter follows:
108
%
109
%    o status:  Method IsXPM returns True if the image format type is XPM.
110
%
111
%    o magick: This string is generally the first few bytes of an image file
112
%      or blob.
113
%
114
%    o length: Specifies the length of the magick string.
115
%
116
%
117
*/
118
static unsigned int IsXPM(const unsigned char *magick,const size_t length)
119
0
{
120
0
  if (length < 9)
121
0
    return(False);
122
0
  if (LocaleNCompare((char *) magick,"/* XPM */",9) == 0)
123
0
    return(True);
124
0
  return(False);
125
0
}
126

127
/*
128
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129
%                                                                             %
130
%                                                                             %
131
%                                                                             %
132
%   R e a d X P M I m a g e                                                   %
133
%                                                                             %
134
%                                                                             %
135
%                                                                             %
136
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
137
%
138
%  Method ReadXPMImage reads an X11 pixmap image file and returns it.  It
139
%  allocates the memory necessary for the new Image structure and returns a
140
%  pointer to the new image.
141
%
142
%  The format of the ReadXPMImage method is:
143
%
144
%      Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
145
%
146
%  A description of each parameter follows:
147
%
148
%    o image:  Method ReadXPMImage returns a pointer to the image after
149
%      creating it. A null image is returned if there is a memory shortage
150
%      or if the image cannot be read.
151
%
152
%    o image_info: Specifies a pointer to a ImageInfo structure.
153
%
154
%    o exception: return any errors or warnings in this structure.
155
%
156
%
157
*/
158
159
static char **StringToListMod(char *text)
160
13.9k
{
161
13.9k
  char
162
13.9k
    **textlist;
163
164
13.9k
  register char
165
13.9k
    *p;
166
167
13.9k
  register size_t
168
13.9k
    i;
169
170
13.9k
  size_t
171
13.9k
    lines;
172
173
13.9k
  if (text == (char *) NULL)
174
0
    return((char **) NULL);
175
176
  /*
177
    Convert string to an ASCII list, modifying the input string.
178
  */
179
13.9k
  lines=1;
180
62.0M
  for (p=text; *p != '\0'; p++)
181
62.0M
    if (*p == '\n')
182
7.21M
      lines++;
183
13.9k
  textlist=MagickAllocateResourceLimitedMemory(char **,(lines+1)*sizeof(char *));
184
13.9k
  if (textlist == (char **) NULL)
185
0
    MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed,
186
13.9k
                      UnableToConvertText);
187
13.9k
  i=0;
188
13.9k
  textlist[i]=text;
189
62.0M
  for (p=text; *p != '\0'; p++)
190
62.0M
    {
191
62.0M
      if (*p == '\r')
192
10.3k
        {
193
10.3k
          *p='\0';
194
10.3k
          continue;
195
10.3k
        }
196
62.0M
      if (*p == '\n')
197
7.21M
        {
198
7.21M
          *p='\0';
199
7.21M
          if (*(p+1) != '\0')
200
7.20M
            {
201
7.20M
              i++;
202
7.20M
              textlist[i]=p+1;
203
7.20M
            }
204
7.21M
        }
205
62.0M
    }
206
13.9k
  i++;
207
13.9k
  textlist[i]=(char *) NULL;
208
13.9k
  return(textlist);
209
13.9k
}
210
211
static char *ParseColor(char * restrict data) MAGICK_FUNC_PURE;
212
213
static char *ParseColor(char * restrict data)
214
7.00M
{
215
38.9M
#define NumberTargets 6U
216
7.00M
#define TargetsDimension 4U
217
218
7.00M
  static const char
219
7.00M
    targets[NumberTargets][TargetsDimension] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
220
221
7.00M
  register char
222
7.00M
    * restrict p, /* points into 'data' */
223
7.00M
    * restrict r; /* points into 'data' */
224
225
7.00M
  register const char
226
7.00M
    *q; /* points into targets */
227
228
7.00M
  register unsigned long
229
7.00M
    i;
230
231
38.9M
  for (i=0; i < NumberTargets; i++)
232
33.6M
    {
233
33.6M
      r=data;
234
177M
      for (q=targets[i]; *r != '\0'; r++)
235
146M
        {
236
146M
          if (*r != *q)
237
144M
            continue;
238
1.72M
          if (!isspace((int) (*(r-1))))
239
20.4k
            continue;
240
1.70M
          p=r;
241
1.70M
          for ( ; ; )
242
5.10M
            {
243
5.10M
              if ((size_t) (q-targets[i]) >= TargetsDimension)
244
0
                return((char *) NULL);
245
5.10M
              if (*q == '\0')
246
1.69M
                return(r);
247
3.40M
              if (*p++ != *q++)
248
10.4k
                break;
249
3.40M
            }
250
10.4k
          q=targets[i];
251
10.4k
        }
252
33.6M
    }
253
5.31M
  return((char *) NULL);
254
7.00M
}
255
#define ThrowXPMReaderException(code_,reason_,image_)   \
256
33.9k
  do {                                                  \
257
33.9k
    MagickFreeResourceLimitedMemory(xpmkey_t *,keys);   \
258
33.9k
    MagickFreeResourceLimitedMemory(char **,textlist);  \
259
33.9k
    MagickFreeResourceLimitedMemory(char *,xpm_buffer); \
260
33.9k
    ThrowReaderException(code_,reason_,image_);         \
261
0
  } while (0);
262
263
static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
264
36.2k
{
265
36.2k
  xpmkey_t
266
36.2k
    *keys = (xpmkey_t *) NULL;
267
268
36.2k
  char
269
36.2k
    target[MaxTextExtent],
270
36.2k
    **textlist = (char **) NULL,
271
36.2k
    *xpm_buffer = (char *) NULL;
272
273
36.2k
  Image
274
36.2k
    *image;
275
276
36.2k
  int
277
36.2k
    count;
278
279
36.2k
  unsigned int
280
36.2k
    i,
281
36.2k
    j,
282
36.2k
    k,
283
36.2k
    none;
284
285
36.2k
  long
286
36.2k
    y;
287
288
36.2k
  register char
289
36.2k
    *p,
290
36.2k
    *q;
291
292
36.2k
  register IndexPacket
293
36.2k
    *indexes;
294
295
36.2k
  register long
296
36.2k
    x;
297
298
36.2k
  register PixelPacket
299
36.2k
    *r;
300
301
36.2k
  MagickPassFail
302
36.2k
    status;
303
304
36.2k
  MagickBool
305
36.2k
    colormap_initialized;
306
307
36.2k
  size_t
308
36.2k
    length;
309
310
36.2k
  unsigned int
311
36.2k
    width; /* characters per pixel */
312
313
  /*
314
    Open image file.
315
  */
316
36.2k
  assert(image_info != (const ImageInfo *) NULL);
317
36.2k
  assert(image_info->signature == MagickSignature);
318
36.2k
  assert(exception != (ExceptionInfo *) NULL);
319
36.2k
  assert(exception->signature == MagickSignature);
320
36.2k
  image=AllocateImage(image_info);
321
36.2k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
322
36.2k
  if (status == MagickFail)
323
36.2k
    ThrowXPMReaderException(FileOpenError,UnableToOpenFile,image);
324
  /*
325
    Read XPM file.
326
  */
327
36.2k
  length=MaxTextExtent*4;
328
36.2k
  xpm_buffer=MagickAllocateResourceLimitedMemory(char *,length);
329
36.2k
  if (xpm_buffer != (char *) NULL)
330
36.2k
    {
331
36.2k
      xpm_buffer[0]='\0';
332
36.2k
      p=xpm_buffer;
333
2.42M
      while (ReadBlobString(image,p) != (char *) NULL)
334
2.39M
        {
335
2.39M
          char *new_xpm_buffer;
336
2.39M
          if (*p == '#')
337
9.66k
            if ((p == xpm_buffer) || (*(p-1) == '\n'))
338
7.27k
              continue;
339
2.38M
          if ((*p == '}') && (*(p+1) == ';'))
340
4
            break;
341
2.38M
          p+=strlen(p);
342
2.38M
          if (((size_t) (p-xpm_buffer)+MaxTextExtent+1) < length)
343
2.37M
            continue;
344
10.8k
          length += MaxTextExtent*4;
345
10.8k
          new_xpm_buffer=MagickReallocateResourceLimitedMemory(char *,xpm_buffer,length);
346
10.8k
          if (new_xpm_buffer == (char *) NULL)
347
0
            {
348
0
              MagickFreeResourceLimitedMemory(char *,xpm_buffer);
349
0
              break;
350
0
            }
351
10.8k
          if (xpm_buffer != new_xpm_buffer)
352
1.43k
            {
353
1.43k
              p=(p-xpm_buffer)+new_xpm_buffer;
354
1.43k
              xpm_buffer=new_xpm_buffer;
355
1.43k
            }
356
10.8k
        }
357
36.2k
    }
358
36.2k
  if (xpm_buffer == (char *) NULL)
359
36.2k
    ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image);
360
361
  /*
362
    Parse image properties from file header while skipping over comment markers.
363
  */
364
36.2k
  count=0;
365
709k
  for (p=xpm_buffer; ((p - xpm_buffer) < 512) && (*p != '\0'); p++)
366
696k
    {
367
696k
      if ((*p != '"') || !isdigit((int) *(p+1)))
368
668k
        continue;
369
28.0k
      count=sscanf(p+1,"%lu %lu %u %u",&image->columns,&image->rows,
370
28.0k
                   &image->colors,&width);
371
28.0k
      if (count == 4)
372
23.0k
        {
373
23.0k
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
374
23.0k
                                "Columns: %lu, Rows: %lu, Colors: %u, Char Per Pixel: %u",
375
23.0k
                                image->columns, image->rows, image->colors, width);
376
23.0k
          break;
377
23.0k
        }
378
28.0k
    }
379
36.2k
  if ((count != 4) || (width == 0) || (width > 3) ||
380
22.1k
      (image->columns == 0) || (image->rows == 0) ||
381
19.4k
      (image->colors == 0))
382
19.2k
    ThrowXPMReaderException(CorruptImageError,ImproperImageHeader,image);
383
19.2k
  if (image->colors > MAX_XPM_SUPPORTED_COLORS)
384
18.2k
    ThrowXPMReaderException(CoderError,ColormapTooLarge,image);
385
18.2k
  image->depth=16;
386
387
  /*
388
    Remove unquoted characters.
389
  */
390
18.2k
  {
391
18.2k
    MagickBool inquote = MagickFalse;
392
18.2k
    q=xpm_buffer;
393
79.8M
    while (*p != '\0')
394
79.8M
      {
395
79.8M
        if (*p++ == '"')
396
15.6M
          {
397
15.6M
            if (inquote)
398
7.83M
              *q++='\n';
399
15.6M
            inquote = !inquote;
400
15.6M
          }
401
79.8M
        if (inquote)
402
58.7M
          *q++=*p;
403
79.8M
      }
404
18.2k
    *q='\0';
405
18.2k
    if (inquote)
406
3.28k
      {
407
3.28k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
408
3.28k
                              "Imbalanced quotes error");
409
3.28k
        ThrowXPMReaderException(CorruptImageError,CorruptImage,image);
410
0
      }
411
18.2k
  }
412
413
  /*
414
    Scan for non-white space binary control codes and reject file if
415
    they are present.
416
  */
417
62.4M
  for (p=xpm_buffer; *p != '\0'; p++)
418
62.4M
    if (((unsigned char) *p < 32) && !isspace((int)(unsigned char) (*p)))
419
1.00k
      break;
420
14.9k
  if (*p != '\0')
421
1.00k
    {
422
1.00k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
423
1.00k
                            "Binary control codes error");
424
1.00k
      ThrowXPMReaderException(CorruptImageError,CorruptImage,image);
425
0
    }
426
13.9k
  textlist=StringToListMod(xpm_buffer);
427
13.9k
  if (textlist == (char **) NULL)
428
13.9k
    ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image);
429
#if 0
430
  if (image->logging)
431
    {
432
      for (i=0; textlist[i] != (char *) NULL; i++)
433
        { };
434
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
435
                            "TextList has %u entries", i);
436
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
437
                            "TextList");
438
      for (i=0; textlist[i] != (char *) NULL; i++)
439
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
440
                              "    %u: \"%s\"", i, textlist[i]);
441
    }
442
#endif
443
444
  /*
445
    Initialize image structure.
446
  */
447
13.9k
  keys=MagickAllocateResourceLimitedArray(xpmkey_t *,image->colors,sizeof(xpmkey_t));
448
13.9k
  if (keys == (xpmkey_t *) NULL)
449
13.9k
    ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image);
450
114M
  for (i=0; i < image->colors; i++)
451
114M
    {
452
114M
      keys[i].index=0;
453
114M
      keys[i].keyval=0;
454
114M
    }
455
13.9k
  if (image->colors <= MaxColormapSize)
456
13.8k
    {
457
13.8k
      if (!AllocateImageColormap(image,image->colors))
458
13.8k
        ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image);
459
13.8k
    }
460
109
  else
461
109
    {
462
      /* Allocate temporary palette. */
463
109
      if (image->colormap != (PixelPacket *)NULL)
464
0
        MagickFreeMemory(image->colormap);
465
109
      image->colormap=MagickAllocateMemory(PixelPacket *,
466
109
                                           MagickArraySize((size_t) image->colors,
467
109
                                                           sizeof(PixelPacket)));
468
109
      if (image->colormap==NULL)
469
109
        ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image);
470
109
    }
471
472
  /*
473
    Read image colormap.
474
  */
475
13.9k
  i=1;
476
13.9k
  none=(~0U);
477
13.9k
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
478
13.9k
                        "Parsing colormap...");
479
13.9k
  colormap_initialized=MagickFalse;
480
5.33M
  for (j=0; j < image->colors; j++)
481
5.32M
    {
482
5.32M
      p=textlist[i++];
483
5.32M
      if ((p == (char *) NULL) || (p[0] == '\0'))
484
2.41k
        break;
485
5.31M
      if (image->logging)
486
5.31M
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
487
5.31M
                              "    %u: %s", i-1, textlist[i-1]);
488
5.31M
      if (strlen(p) < width)
489
2.11k
        break;
490
491
5.31M
      keys[j].index = j;
492
5.31M
      keys[j].keyval = 0;
493
13.2M
      for (k=0; k < width; k++)
494
7.92M
        keys[j].keyval |= ((xpmkeyval_t) p[k]) << (k * 8);
495
496
      /* printf("Key[%03u] =\"%s\" (0x%04X)\n", keys[j].index, p, (unsigned int) keys[j].keyval); */
497
      /*
498
        Parse color.
499
      */
500
5.31M
      (void) strlcpy(target,"gray",sizeof(target));
501
5.31M
      q=ParseColor(p+width);
502
5.31M
      if (q != (char *) NULL)
503
1.69M
        {
504
3.38M
          while (!isspace((int) (*q)) && (*q != '\0'))
505
1.69M
            q++;
506
1.69M
          (void) strlcpy(target,q,MaxTextExtent);
507
1.69M
          q=ParseColor(target);
508
1.69M
          if (q != (char *) NULL)
509
1.38k
            *q='\0';
510
1.69M
        }
511
5.31M
      (void) MagickStripString(target);
512
5.31M
      if (LocaleCompare(target,"none") == 0)
513
4.32k
        {
514
4.32k
          image->storage_class=DirectClass;
515
4.32k
          image->matte=True;
516
4.32k
          none=j;
517
4.32k
          (void) strlcpy(target,"black",sizeof(target));
518
4.32k
        }
519
5.31M
      if (!QueryColorDatabase(target,&image->colormap[j],exception))
520
720
        {
521
          /* Promote warning to error */
522
720
          exception->severity = CorruptImageError;
523
720
          break;
524
720
        }
525
      /* We are going to be done now */
526
5.31M
      if (j+1 == image->colors)
527
8.74k
        colormap_initialized=MagickTrue;
528
5.31M
    }
529
13.9k
  if (!colormap_initialized)
530
8.74k
    ThrowXPMReaderException(CorruptImageError,CorruptImage,image);
531
532
  /*
533
    Log the XPM Colormap if logging is enabled.
534
  */
535
8.74k
  if (image->logging)
536
8.74k
    {
537
8.74k
      char
538
8.74k
        name[MaxTextExtent];
539
540
8.74k
      unsigned int
541
8.74k
        ii;
542
543
8.74k
      register PixelPacket
544
8.74k
        *p;
545
546
      /*
547
        Display image colormap.
548
      */
549
8.74k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
550
8.74k
                            "XPM Colormap (Depth %u):", image->depth);
551
8.74k
      p=image->colormap;
552
5.08M
      for (ii=0; ii < image->colors; ii++)
553
5.07M
        {
554
5.07M
          char
555
5.07M
            tuple[MaxTextExtent];
556
557
5.07M
          GetColorTuple(p,image->depth,image->matte,False,tuple);
558
5.07M
          (void) QueryColorname(image,p,X11Compliance,name,&image->exception);
559
5.07M
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
560
5.07M
                                "    %" MAGICK_SIZE_T_F "u: %.1024s\t  %.1024s",
561
5.07M
                                (MAGICK_SIZE_T) ii,tuple,name);
562
5.07M
          p++;
563
5.07M
        }
564
8.74k
    }
565
566
8.74k
  image->depth=GetImageDepth(image,&image->exception);
567
8.74k
  image->depth=NormalizeDepthToOctet(image->depth);
568
569
8.74k
  if (!image_info->ping)
570
8.74k
    {
571
8.74k
      size_t
572
8.74k
        expected_pixels=(size_t) image->rows * (size_t) image->columns,
573
8.74k
        pixels=0;
574
575
8.74k
      xpmkey_t
576
8.74k
        key = { 0, 0 },
577
8.74k
        *keyp;
578
579
8.74k
      xpmkeyval_t
580
8.74k
        keyval;
581
582
8.74k
      j = 0;
583
584
      /*
585
        Sort keys by kval
586
      */
587
8.74k
      qsort((void *) keys,image->colors, sizeof(keys[0]), XPMKeyCompare);
588
589
#if 0
590
      for (j=0; j < image->colors; j++)
591
        printf("Key[%03u] = (0x%04X)\n", keys[j].index, (unsigned int) keys[j].keyval);
592
#endif
593
594
      /*
595
        Read image pixels.
596
      */
597
8.74k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
598
8.74k
                            "Parsing pixels...");
599
102k
      for (y=0; y < (long) image->rows; y++)
600
100k
        {
601
100k
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
602
100k
                                "  y=%ld", y);
603
100k
          p=textlist[i++];
604
100k
          if ((p == (char *) NULL) || (p[0] == '\0'))
605
2.64k
            break;
606
97.4k
          if (image->logging)
607
97.4k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
608
97.4k
                                  "    %u: %s", i-1, textlist[i-1]);
609
97.4k
          r=SetImagePixels(image,0,y,image->columns,1);
610
97.4k
          if (r == (PixelPacket *) NULL)
611
3.30k
            break;
612
94.1k
          indexes=AccessMutableIndexes(image);
613
2.22M
          for (x=0; x < (long) image->columns; x++)
614
2.13M
            {
615
2.13M
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
616
2.13M
                                    "  x=%ld", y);
617
2.13M
              keyval=0;
618
7.45M
              for (k=0; k < width; k++)
619
5.31M
                {
620
5.31M
                  if (p[k] == '\0')
621
130
                    {
622
130
                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
623
130
                                            "Unexpected end of row %ld! (k=%u)", y, k);
624
130
                      ThrowXPMReaderException(CorruptImageError,UnexpectedEndOfFile,
625
130
                                              image);
626
0
                      break;
627
130
                    }
628
5.31M
                  keyval |= ((xpmkeyval_t) p[k]) << (k * 8);
629
5.31M
                }
630
2.13M
              if (keyval != key.keyval)
631
1.26M
                {
632
1.26M
                  key.keyval = keyval;
633
1.26M
                  keyp=(xpmkey_t *) bsearch((const void *) &key,(void *) keys,image->colors,
634
1.26M
                                            sizeof(keys[0]),XPMKeyCompare);
635
1.26M
                  if (keyp != (xpmkey_t *) NULL)
636
1.26M
                    {
637
1.26M
                      key.index = keyp->index;
638
1.26M
                    }
639
219
                  else
640
219
                    {
641
219
                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
642
219
                                            "Failed to find key! (0x%04X)",
643
219
                                            key.keyval);
644
219
                      ThrowXPMReaderException(CorruptImageError,CorruptImage,
645
219
                                              image);
646
0
                      break;
647
219
                    }
648
1.26M
                }
649
650
2.13M
              VerifyColormapIndex(status,image,key.index);
651
2.13M
              if (status == MagickFail)
652
0
                break;
653
2.13M
              if (image->storage_class == PseudoClass)
654
137k
                indexes[x]=(IndexPacket) key.index;
655
2.13M
              *r=image->colormap[key.index];
656
2.13M
              r->opacity=(Quantum)
657
2.13M
                (key.index == none ? TransparentOpacity : OpaqueOpacity);
658
2.13M
              pixels++;
659
2.13M
              r++;
660
2.13M
              p+=width;
661
2.13M
            } /* for (x= */
662
93.8k
          if (status == MagickFail)
663
0
            break;
664
93.8k
          if (!SyncImagePixels(image))
665
0
            break;
666
93.8k
        } /* for (y= */
667
8.39k
      if (pixels != expected_pixels)
668
6.12k
        {
669
6.12k
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
670
6.12k
                                "Failed to transfer required"
671
6.12k
                                " number of pixels (%zu vs %zu)",pixels,expected_pixels);
672
6.12k
          ThrowXPMReaderException(CorruptImageError,CorruptImage,
673
6.12k
                                  image);
674
0
        }
675
8.39k
    } /* if (!image_info->ping) */
676
2.26k
  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
677
2.26k
                        "status=%u", status);
678
  /*
679
    Free resources.
680
  */
681
2.26k
  MagickFreeResourceLimitedMemory(xpmkey_t *,keys);
682
2.26k
  MagickFreeResourceLimitedMemory(char **,textlist);
683
2.26k
  MagickFreeResourceLimitedMemory(char *,xpm_buffer);
684
2.26k
  if (image->colors > MaxColormapSize)
685
15
    {
686
      /* Release temporary palette. */
687
15
      MagickFreeMemory(image->colormap);
688
15
      image->colors = 0;
689
15
      image->storage_class = DirectClass;
690
15
    }
691
2.26k
  CloseBlob(image);
692
2.26k
  StopTimer(&image->timer);
693
694
2.26k
  return(image);
695
8.74k
}
696

697
/*
698
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
699
%                                                                             %
700
%                                                                             %
701
%                                                                             %
702
%   R e g i s t e r X P M I m a g e                                           %
703
%                                                                             %
704
%                                                                             %
705
%                                                                             %
706
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
707
%
708
%  Method RegisterXPMImage adds attributes for the XPM image format to
709
%  the list of supported formats.  The attributes include the image format
710
%  tag, a method to read and/or write the format, whether the format
711
%  supports the saving of more than one frame to the same file or blob,
712
%  whether the format supports native in-memory I/O, and a brief
713
%  description of the format.
714
%
715
%  The format of the RegisterXPMImage method is:
716
%
717
%      RegisterXPMImage(void)
718
%
719
*/
720
ModuleExport void RegisterXPMImage(void)
721
6
{
722
6
  MagickInfo
723
6
    *entry;
724
725
6
  entry=SetMagickInfo("PICON");
726
6
  entry->decoder=(DecoderHandler) ReadXPMImage;
727
6
  entry->encoder=(EncoderHandler) WritePICONImage;
728
6
  entry->adjoin=False;
729
6
  entry->description="Personal Icon";
730
6
  entry->module="XPM";
731
6
  (void) RegisterMagickInfo(entry);
732
733
6
  entry=SetMagickInfo("PM");
734
6
  entry->decoder=(DecoderHandler) ReadXPMImage;
735
6
  entry->encoder=(EncoderHandler) WriteXPMImage;
736
6
  entry->adjoin=False;
737
6
  entry->stealth=True; /* Don't list in '-list format' output */
738
6
  entry->description="X Windows system pixmap (color)";
739
6
  entry->module="XPM";
740
6
  (void) RegisterMagickInfo(entry);
741
742
6
  entry=SetMagickInfo("XPM");
743
6
  entry->decoder=(DecoderHandler) ReadXPMImage;
744
6
  entry->encoder=(EncoderHandler) WriteXPMImage;
745
6
  entry->magick=(MagickHandler) IsXPM;
746
6
  entry->adjoin=False;
747
6
  entry->description="X Windows system pixmap (color)";
748
6
  entry->module="XPM";
749
6
  (void) RegisterMagickInfo(entry);
750
6
}
751

752
/*
753
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
754
%                                                                             %
755
%                                                                             %
756
%                                                                             %
757
%   U n r e g i s t e r X P M I m a g e                                       %
758
%                                                                             %
759
%                                                                             %
760
%                                                                             %
761
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
762
%
763
%  Method UnregisterXPMImage removes format registrations made by the
764
%  XPM module from the list of supported formats.
765
%
766
%  The format of the UnregisterXPMImage method is:
767
%
768
%      UnregisterXPMImage(void)
769
%
770
*/
771
ModuleExport void UnregisterXPMImage(void)
772
0
{
773
0
  (void) UnregisterMagickInfo("PICON");
774
0
  (void) UnregisterMagickInfo("PM");
775
0
  (void) UnregisterMagickInfo("XPM");
776
0
}
777

778
/*
779
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780
%                                                                             %
781
%                                                                             %
782
%                                                                             %
783
%   W r i t e P I C O N I m a g e                                             %
784
%                                                                             %
785
%                                                                             %
786
%                                                                             %
787
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788
%
789
%  Procedure WritePICONImage writes an image to a file in the Personal Icon
790
%  format.
791
%
792
%  The format of the WritePICONImage method is:
793
%
794
%      MagickPassFail WritePICONImage(const ImageInfo *image_info,Image *image)
795
%
796
%  A description of each parameter follows.
797
%
798
%    o status: Method WritePICONImage return MagickPass if the image is written.
799
%      MagickFail is returned is there is a memory shortage or if the image file
800
%      fails to write.
801
%
802
%    o image_info: Specifies a pointer to a ImageInfo structure.
803
%
804
%    o image:  A pointer to an Image structure.
805
%
806
%
807
*/
808
static MagickPassFail WritePICONImage(const ImageInfo *image_info,Image *image)
809
1.06k
{
810
1.06k
#define ColormapExtent  155
811
1.06k
#define GraymapExtent  95
812
1.06k
#define PiconGeometry  "48x48>"
813
814
1.06k
  static const unsigned char
815
1.06k
    Colormap[]=
816
1.06k
    {
817
1.06k
      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
818
1.06k
      0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
819
1.06k
      0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
820
1.06k
      0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
821
1.06k
      0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
822
1.06k
      0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
823
1.06k
      0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
824
1.06k
      0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
825
1.06k
      0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
826
1.06k
      0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
827
1.06k
      0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
828
1.06k
      0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
829
1.06k
      0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
830
1.06k
    },
831
1.06k
    Graymap[]=
832
1.06k
    {
833
1.06k
      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
834
1.06k
      0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
835
1.06k
      0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
836
1.06k
      0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
837
1.06k
      0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
838
1.06k
      0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
839
1.06k
      0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
840
1.06k
      0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
841
1.06k
    };
842
843
59.6k
#define MaxCixels  92
844
845
1.06k
  static const char
846
1.06k
    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
847
1.06k
                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
848
849
1.06k
  char
850
1.06k
    buffer[MaxTextExtent],
851
1.06k
    basename[MaxTextExtent],
852
1.06k
    name[MaxTextExtent],
853
1.06k
    symbol[MaxTextExtent];
854
855
1.06k
  Image
856
1.06k
    *picon = (Image *) NULL,
857
1.06k
    *map = (Image *) NULL;
858
859
1.06k
  int
860
1.06k
    j;
861
862
1.06k
  long
863
1.06k
    k,
864
1.06k
    y;
865
866
1.06k
  RectangleInfo
867
1.06k
    geometry;
868
869
1.06k
  register const PixelPacket
870
1.06k
    *p;
871
872
1.06k
  register long
873
1.06k
    i,
874
1.06k
    x;
875
876
1.06k
  register PixelPacket
877
1.06k
    *q;
878
879
1.06k
  unsigned int
880
1.06k
    status,
881
1.06k
    transparent;
882
883
1.06k
  unsigned long
884
1.06k
    characters_per_pixel,
885
1.06k
    colors;
886
887
1.06k
  ImageCharacteristics
888
1.06k
    characteristics;
889
890
1.06k
  assert(image_info != (const ImageInfo *) NULL);
891
1.06k
  assert(image_info->signature == MagickSignature);
892
1.06k
  assert(image != (Image *) NULL);
893
1.06k
  assert(image->signature == MagickSignature);
894
  /*
895
    Open output image file.
896
  */
897
1.06k
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
898
1.06k
  if (status == MagickFail)
899
1.06k
    ThrowWriterException(CoderError,UnableToOpenFile,image);
900
  /*
901
    Ensure that image is in an RGB space.
902
  */
903
1.06k
  if (TransformColorspace(image,RGBColorspace) == MagickFail)
904
1.06k
    ThrowWriterException(CoderError,UnableToTransformColorspace,image);
905
  /*
906
    Analyze image to be written.
907
  */
908
1.06k
  if (!GetImageCharacteristics(image,&characteristics,
909
1.06k
                               (OptimizeType == image_info->type),
910
1.06k
                               &image->exception))
911
1.06k
    ThrowWriterException(CoderError,UnableToGetImageCharacteristics,image);
912
913
1.06k
  SetGeometry(image,&geometry);
914
1.06k
  (void) GetMagickGeometry(PiconGeometry,&geometry.x,&geometry.y,
915
1.06k
                           &geometry.width,&geometry.height);
916
1.06k
  picon=ThumbnailImage(image,geometry.width,geometry.height,&image->exception);
917
1.06k
  if (picon == (Image *) NULL)
918
737
    ThrowWriterException(CoderError,UnableToResizeImage,image);
919
737
  if ((image_info->type != TrueColorType) &&
920
737
      (characteristics.grayscale))
921
264
    map=BlobToImage(image_info,Graymap,GraymapExtent,&image->exception);
922
473
  else
923
473
    map=BlobToImage(image_info,Colormap,ColormapExtent,&image->exception);
924
737
  if (map == (Image *) NULL)
925
0
    {
926
0
      DestroyImage(picon);
927
0
      picon=(Image *) NULL;
928
0
      ThrowWriterException(CoderError,UnableToDecodeImage,image);
929
0
      return MagickFail;
930
0
    }
931
737
  status=MapImage(picon,map,image_info->dither);
932
737
  DestroyImage(map);
933
737
  map=(Image *) NULL;
934
737
  transparent=MagickFalse;
935
737
  if (picon->storage_class == PseudoClass)
936
391
    {
937
391
      CompressImageColormap(picon);
938
391
      if (picon->matte)
939
0
        transparent=MagickTrue;
940
391
    }
941
346
  else
942
346
    {
943
      /*
944
        Convert DirectClass to PseudoClass picon.
945
      */
946
346
      if (picon->matte)
947
346
        {
948
          /*
949
            Map all the transparent pixels.
950
          */
951
4.04k
          for (y=0; y < (long) picon->rows; y++)
952
3.70k
            {
953
3.70k
              q=GetImagePixels(picon,0,y,picon->columns,1);
954
3.70k
              if (q == (PixelPacket *) NULL)
955
0
                break;
956
24.3k
              for (x=0; x < (long) picon->columns; x++)
957
20.6k
                {
958
20.6k
                  if (q->opacity == TransparentOpacity)
959
4.59k
                    transparent=True;
960
16.0k
                  else
961
16.0k
                    q->opacity=OpaqueOpacity;
962
20.6k
                  q++;
963
20.6k
                }
964
3.70k
              if (!SyncImagePixels(picon))
965
0
                break;
966
3.70k
            }
967
346
          if (picon->exception.severity >= ErrorException)
968
0
            {
969
0
              CopyException(&picon->exception,&image->exception);
970
0
              DestroyImage(picon);
971
0
              picon=(Image *) NULL;
972
0
              ThrowWriterException(CoderError,UnableToSetImageOpacity,image);
973
0
            }
974
346
        }
975
346
      if (SetImageType(picon,PaletteType) == MagickFail)
976
0
        {
977
0
          CopyException(&picon->exception,&image->exception);
978
0
          DestroyImage(picon);
979
0
          picon=(Image *) NULL;
980
0
          ThrowWriterException(CoderError,UnableToSetImageType,image);
981
0
        }
982
346
    }
983
737
  colors=picon->colors;
984
737
  if (transparent)
985
168
    {
986
168
      colors++;
987
168
      MagickReallocMemory(PixelPacket *,picon->colormap,colors*sizeof(PixelPacket));
988
168
      if (picon->colormap == (PixelPacket *) NULL)
989
0
        {
990
0
          DestroyImage(picon);
991
0
          picon=(Image *) NULL;
992
0
          ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
993
0
        }
994
168
      picon->colormap[colors-1].red=picon->colormap[colors-1].green=picon->colormap[colors-1].blue=0;
995
168
      picon->colormap[colors-1].opacity=TransparentOpacity;
996
1.40k
      for (y=0; y < (long) picon->rows; y++)
997
1.23k
        {
998
1.23k
          register IndexPacket
999
1.23k
            *indexes;
1000
1001
1.23k
          q=GetImagePixels(picon,0,y,picon->columns,1);
1002
1.23k
          if (q == (PixelPacket *) NULL)
1003
0
            break;
1004
1.23k
          indexes=AccessMutableIndexes(picon);
1005
17.4k
          for (x=0; x < (long) picon->columns; x++)
1006
16.1k
            {
1007
16.1k
              if (q->opacity == TransparentOpacity)
1008
4.59k
                indexes[x]=(IndexPacket) picon->colors;
1009
16.1k
              q++;
1010
16.1k
            }
1011
1.23k
          if (!SyncImagePixels(picon))
1012
0
            break;
1013
1.23k
        }
1014
168
      if (picon->exception.severity >= ErrorException)
1015
0
        {
1016
0
          CopyException(&picon->exception,&image->exception);
1017
0
          DestroyImage(picon);
1018
0
          picon=(Image *) NULL;
1019
0
          ThrowWriterException(CoderError,UnableToSetImageOpacity,image);
1020
0
        }
1021
168
    }
1022
  /*
1023
    Compute the character per pixel.
1024
  */
1025
737
  characters_per_pixel=1;
1026
737
  for (k=MaxCixels; (long) colors > k; k*=MaxCixels)
1027
0
    characters_per_pixel++;
1028
  /*
1029
    XPM header.
1030
  */
1031
737
  (void) WriteBlobString(image,"/* XPM */\n");
1032
737
  GetPathComponent(picon->filename,BasePath,basename);
1033
737
  (void) MagickFormatString(buffer,sizeof(buffer),"static char *%.1024s[] = {\n",basename);
1034
737
  (void) WriteBlobString(image,buffer);
1035
737
  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
1036
737
  MagickFormatString(buffer,sizeof(buffer),"\"%lu %lu %lu %ld\",\n",picon->columns,picon->rows,
1037
737
                     colors,characters_per_pixel);
1038
737
  (void) WriteBlobString(image,buffer);
1039
10.0k
  for (i=0; i < (long) colors; i++)
1040
9.31k
    {
1041
      /*
1042
        Define XPM color.
1043
      */
1044
9.31k
      picon->colormap[i].opacity=OpaqueOpacity;
1045
9.31k
      (void) QueryColorname(picon,picon->colormap+i,XPMCompliance,name,
1046
9.31k
                            &picon->exception);
1047
9.31k
      if (transparent)
1048
805
        {
1049
805
          if (i == (long) (colors-1))
1050
168
            (void) strlcpy(name,"grey75",sizeof(name));
1051
805
        }
1052
      /*
1053
        Write XPM color.
1054
      */
1055
9.31k
      k=i % MaxCixels;
1056
9.31k
      symbol[0]=Cixel[k];
1057
9.31k
      for (j=1; j < (long) characters_per_pixel; j++)
1058
0
        {
1059
0
          k=((i-k)/MaxCixels) % MaxCixels;
1060
0
          symbol[j]=Cixel[k];
1061
0
        }
1062
9.31k
      symbol[j]='\0';
1063
9.31k
      MagickFormatString(buffer,sizeof(buffer),"\"%.1024s c %.1024s\",\n",symbol,name);
1064
9.31k
      (void) WriteBlobString(image,buffer);
1065
9.31k
    }
1066
  /*
1067
    Define XPM pixels.
1068
  */
1069
737
  (void) WriteBlobString(image,"/* pixels */\n");
1070
9.52k
  for (y=0; y < (long) picon->rows; y++)
1071
8.78k
    {
1072
8.78k
      register const IndexPacket
1073
8.78k
        *indexes;
1074
1075
8.78k
      p=AcquireImagePixels(picon,0,y,picon->columns,1,&picon->exception);
1076
8.78k
      if (p == (const PixelPacket *) NULL)
1077
0
        break;
1078
8.78k
      indexes=AccessImmutableIndexes(picon);
1079
8.78k
      (void) WriteBlobString(image,"\"");
1080
58.3k
      for (x=0; x < (long) picon->columns; x++)
1081
49.5k
        {
1082
49.5k
          k=indexes[x] % MaxCixels;
1083
49.5k
          symbol[0]=Cixel[k];
1084
49.5k
          for (j=1; j < (long) characters_per_pixel; j++)
1085
0
            {
1086
0
              k=(((int) indexes[x]-k)/MaxCixels) % MaxCixels;
1087
0
              symbol[j]=Cixel[k];
1088
0
            }
1089
49.5k
          symbol[j]='\0';
1090
49.5k
          (void) strlcpy(buffer,symbol,MaxTextExtent);
1091
49.5k
          (void) WriteBlobString(image,buffer);
1092
49.5k
        }
1093
8.78k
      MagickFormatString(buffer,sizeof(buffer),"\"%.1024s\n",
1094
8.78k
                         (y == (long) (picon->rows-1) ? "" : ","));
1095
8.78k
      (void) WriteBlobString(image,buffer);
1096
8.78k
      if (QuantumTick(y,picon->rows))
1097
8.78k
        if (!MagickMonitorFormatted(y,picon->rows,&image->exception,
1098
8.78k
                                    SaveImageText,image->filename,
1099
8.78k
                                    image->columns,image->rows))
1100
0
          break;
1101
8.78k
    }
1102
737
  if (picon->exception.severity >= ErrorException)
1103
0
    {
1104
0
      GetImageException(picon,&image->exception);
1105
0
      DestroyImage(picon);
1106
0
      picon=(Image *) NULL;
1107
0
      CloseBlob(image);
1108
0
      return MagickFail;
1109
0
    }
1110
737
  DestroyImage(picon);
1111
737
  picon=(Image *) NULL;
1112
737
  (void) WriteBlobString(image,"};\n");
1113
737
  status &= CloseBlob(image);
1114
737
  return(status);
1115
737
}
1116

1117
/*
1118
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1119
%                                                                             %
1120
%                                                                             %
1121
%                                                                             %
1122
%   W r i t e X P M I m a g e                                                 %
1123
%                                                                             %
1124
%                                                                             %
1125
%                                                                             %
1126
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1127
%
1128
%  Procedure WriteXPMImage writes an image to a file in the X pixmap format.
1129
%
1130
%  The format of the WriteXPMImage method is:
1131
%
1132
%      unsigned int WriteXPMImage(const ImageInfo *image_info,Image *image)
1133
%
1134
%  A description of each parameter follows.
1135
%
1136
%    o status: Method WriteXPMImage return True if the image is written.
1137
%      False is returned is there is a memory shortage or if the image file
1138
%      fails to write.
1139
%
1140
%    o image_info: Specifies a pointer to a ImageInfo structure.
1141
%
1142
%    o image:  A pointer to an Image structure.
1143
%
1144
%
1145
*/
1146
static unsigned int WriteXPMImage(const ImageInfo *image_info,Image *image)
1147
1.19k
{
1148
11.3M
#define MaxCixels  92
1149
1150
1.19k
  static const char
1151
1.19k
    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
1152
1.19k
                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
1153
1154
1.19k
  char
1155
1.19k
    buffer[MaxTextExtent],
1156
1.19k
    basename[MaxTextExtent],
1157
1.19k
    name[MaxTextExtent],
1158
1.19k
    symbol[MaxTextExtent];
1159
1160
1.19k
  int
1161
1.19k
    j;
1162
1163
1.19k
  long
1164
1.19k
    k,
1165
1.19k
    y;
1166
1167
1.19k
  register const PixelPacket
1168
1.19k
    *p;
1169
1170
1.19k
  register long
1171
1.19k
    i,
1172
1.19k
    x;
1173
1174
1.19k
  register PixelPacket
1175
1.19k
    *q;
1176
1177
1.19k
  unsigned int
1178
1.19k
    status,
1179
1.19k
    transparent;
1180
1181
1.19k
  unsigned long
1182
1.19k
    characters_per_pixel,
1183
1.19k
    colors;
1184
1185
  /*
1186
    Open output image file.
1187
  */
1188
1.19k
  assert(image_info != (const ImageInfo *) NULL);
1189
1.19k
  assert(image_info->signature == MagickSignature);
1190
1.19k
  assert(image != (Image *) NULL);
1191
1.19k
  assert(image->signature == MagickSignature);
1192
1.19k
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1193
1.19k
  if (status == False)
1194
1.19k
    ThrowWriterException(FileOpenError,UnableToOpenFile,image);
1195
1.19k
  if (TransformColorspace(image,RGBColorspace) == MagickFail)
1196
1.19k
      ThrowWriterException(CoderError,UnableToTransformColorspace,image);
1197
  /*
1198
    Limit color resolution to what XPM can traditionally handle
1199
  */
1200
1.19k
  if (image->depth > 16)
1201
0
    image->depth=16;
1202
1.19k
  transparent=False;
1203
1.19k
  if (image->storage_class == PseudoClass)
1204
456
    {
1205
456
      CompressImageColormap(image);
1206
456
      if (image->matte)
1207
0
        transparent=True;
1208
456
    }
1209
741
  else
1210
741
    {
1211
      /*
1212
        Convert DirectClass to PseudoClass image.
1213
      */
1214
741
      if (image->matte)
1215
733
        {
1216
          /*
1217
            Map all the transparent pixels.
1218
          */
1219
37.4k
          for (y=0; y < (long) image->rows; y++)
1220
36.7k
          {
1221
36.7k
            q=GetImagePixels(image,0,y,image->columns,1);
1222
36.7k
            if (q == (PixelPacket *) NULL)
1223
60
              break;
1224
1.98M
            for (x=0; x < (long) image->columns; x++)
1225
1.95M
            {
1226
1.95M
              if (q->opacity == TransparentOpacity)
1227
11.8k
                transparent=True;
1228
1.94M
              else
1229
1.94M
                q->opacity=OpaqueOpacity;
1230
1.95M
              q++;
1231
1.95M
            }
1232
36.6k
            if (!SyncImagePixels(image))
1233
0
              break;
1234
36.6k
          }
1235
733
        }
1236
741
      if (SetImageType(image,PaletteType) == MagickFail)
1237
681
        ThrowWriterException(CoderError,UnableToSetImageType,image);
1238
681
    }
1239
1.13k
  colors=image->colors;
1240
1.13k
  if (transparent)
1241
342
    {
1242
342
      colors++;
1243
342
      MagickReallocMemory(PixelPacket *,image->colormap,colors*sizeof(PixelPacket));
1244
342
      if (image->colormap == (PixelPacket *) NULL)
1245
342
        ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image);
1246
342
      image->colormap[colors-1].red=image->colormap[colors-1].green=image->colormap[colors-1].blue=0;
1247
342
      image->colormap[colors-1].opacity=TransparentOpacity;
1248
13.1k
      for (y=0; y < (long) image->rows; y++)
1249
12.7k
      {
1250
12.7k
        register IndexPacket
1251
12.7k
          *indexes;
1252
1253
12.7k
        q=GetImagePixels(image,0,y,image->columns,1);
1254
12.7k
        if (q == (PixelPacket *) NULL)
1255
0
          break;
1256
12.7k
        indexes=AccessMutableIndexes(image);
1257
1.20M
        for (x=0; x < (long) image->columns; x++)
1258
1.19M
        {
1259
1.19M
          if (q->opacity == TransparentOpacity)
1260
11.8k
            indexes[x]=(IndexPacket) image->colors;
1261
1.19M
          q++;
1262
1.19M
        }
1263
12.7k
        if (!SyncImagePixels(image))
1264
0
          break;
1265
12.7k
      }
1266
342
    }
1267
  /*
1268
    Compute the character per pixel.
1269
  */
1270
1.13k
  characters_per_pixel=1;
1271
1.44k
  for (k=MaxCixels; (long) colors > k; k*=MaxCixels)
1272
309
    characters_per_pixel++;
1273
  /*
1274
    XPM header.
1275
  */
1276
1.13k
  (void) WriteBlobString(image,"/* XPM */\n");
1277
1.13k
  GetPathComponent(image->filename,BasePath,basename);
1278
1.13k
  (void) MagickFormatString(buffer,sizeof(buffer),"static char *%.1024s[] = {\n",basename);
1279
1.13k
  (void) WriteBlobString(image,buffer);
1280
1.13k
  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
1281
1.13k
  MagickFormatString(buffer,sizeof(buffer),"\"%lu %lu %lu %ld\",\n",image->columns,
1282
1.13k
    image->rows,colors,characters_per_pixel);
1283
1.13k
  (void) WriteBlobString(image,buffer);
1284
1.15M
  for (i=0; i < (long) colors; i++)
1285
1.15M
  {
1286
    /*
1287
      Define XPM color.
1288
    */
1289
1.15M
    image->colormap[i].opacity=OpaqueOpacity;
1290
1.15M
    (void) QueryColorname(image,image->colormap+i,XPMCompliance,name,
1291
1.15M
      &image->exception);
1292
1.15M
    if (transparent)
1293
25.4k
      {
1294
25.4k
        if (i == (long) (colors-1))
1295
342
          (void) strlcpy(name,"None",sizeof(name));
1296
25.4k
      }
1297
    /*
1298
      Write XPM color.
1299
    */
1300
1.15M
    k=i % MaxCixels;
1301
1.15M
    symbol[0]=Cixel[k];
1302
3.32M
    for (j=1; j < (long) characters_per_pixel; j++)
1303
2.16M
    {
1304
2.16M
      k=((i-k)/MaxCixels) % MaxCixels;
1305
2.16M
      symbol[j]=Cixel[k];
1306
2.16M
    }
1307
1.15M
    symbol[j]='\0';
1308
1.15M
    MagickFormatString(buffer,sizeof(buffer),"\"%.1024s c %.1024s\",\n",symbol,name);
1309
1.15M
    (void) WriteBlobString(image,buffer);
1310
1.15M
  }
1311
  /*
1312
    Define XPM pixels.
1313
  */
1314
1.13k
  (void) WriteBlobString(image,"/* pixels */\n");
1315
51.4k
  for (y=0; y < (long) image->rows; y++)
1316
50.3k
  {
1317
50.3k
    register const IndexPacket
1318
50.3k
      *indexes;
1319
1320
50.3k
    p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
1321
50.3k
    if (p == (const PixelPacket *) NULL)
1322
28
      break;
1323
50.3k
    indexes=AccessImmutableIndexes(image);
1324
50.3k
    (void) WriteBlobString(image,"\"");
1325
2.05M
    for (x=0; x < (long) image->columns; x++)
1326
2.00M
    {
1327
2.00M
      k=indexes[x] % MaxCixels;
1328
2.00M
      symbol[0]=Cixel[k];
1329
3.93M
      for (j=1; j < (long) characters_per_pixel; j++)
1330
1.92M
      {
1331
1.92M
        k=(((int) indexes[x]-k)/MaxCixels) % MaxCixels;
1332
1.92M
        symbol[j]=Cixel[k];
1333
1.92M
      }
1334
2.00M
      symbol[j]='\0';
1335
2.00M
      (void) strlcpy(buffer,symbol,MaxTextExtent);
1336
2.00M
      (void) WriteBlobString(image,buffer);
1337
2.00M
    }
1338
50.3k
    MagickFormatString(buffer,sizeof(buffer),"\"%.1024s\n",
1339
50.3k
      (y == (long) (image->rows-1) ? "" : ","));
1340
50.3k
    (void) WriteBlobString(image,buffer);
1341
50.3k
    if (QuantumTick(y,image->rows))
1342
29.6k
      if (!MagickMonitorFormatted(y,image->rows,&image->exception,
1343
29.6k
                                  SaveImageText,image->filename,
1344
29.6k
                                  image->columns,image->rows))
1345
0
        break;
1346
50.3k
  }
1347
1.13k
  (void) WriteBlobString(image,"};\n");
1348
1.13k
  status &= CloseBlob(image);
1349
1.13k
  return(status);
1350
1.13k
}