xpm.c revision de984cdc3631106b1cbbb8d3972b76a0fc27e8e8
12cfd52c507bd5790457a171eb9bcb39019cc6860Chris Lattner/*
2c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
4c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
5c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
6c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                            X   X  PPPP   M   M                              %
7c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                             X X   P   P  MM MM                              %
8c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                              X    PPPP   M M M                              %
9c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                             X X   P      M   M                              %
10c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                            X   X  P      M   M                              %
11c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
12c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
13c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                  Read/Write X Windows system Pixmap Format                  %
14c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
15c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                              Software Design                                %
16c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                   Cristy                                    %
17c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                 July 1992                                   %
18c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
19c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
20c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
21c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  dedicated to making software imaging solutions freely available.           %
22c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
23db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%  You may not use this file except in compliance with the License.  You may  %
24c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  obtain a copy of the License at                                            %
25c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
26c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%    http://www.imagemagick.org/script/license.php                            %
27c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
28c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  Unless required by applicable law or agreed to in writing, software        %
29c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  distributed under the License is distributed on an "AS IS" BASIS,          %
30c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  See the License for the specific language governing permissions and        %
32c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  limitations under the License.                                             %
33c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
34c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%
36c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%
37c140c4803dc3e10e08138670829bc0494986abe9David Goodwin*/
38c140c4803dc3e10e08138670829bc0494986abe9David Goodwin
39c140c4803dc3e10e08138670829bc0494986abe9David Goodwin/*
40c140c4803dc3e10e08138670829bc0494986abe9David Goodwin  Include declarations.
41c140c4803dc3e10e08138670829bc0494986abe9David Goodwin*/
42c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/studio.h"
43c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/attribute.h"
44c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/blob.h"
45c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/blob-private.h"
46c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/cache.h"
47c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/color.h"
48c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/color-private.h"
49db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin#include "MagickCore/colormap.h"
50c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/colorspace.h"
51c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/colorspace-private.h"
52c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/exception.h"
53c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/exception-private.h"
54c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/geometry.h"
55db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin#include "MagickCore/image.h"
56b5619f42f4fdf347380d28357549df09b9ca3946Evan Cheng#include "MagickCore/image-private.h"
57b5619f42f4fdf347380d28357549df09b9ca3946Evan Cheng#include "MagickCore/list.h"
58db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin#include "MagickCore/magick.h"
5977521f5232e679aa3de10aaaed2464aa91d7ff55David Goodwin#include "MagickCore/memory_.h"
6077521f5232e679aa3de10aaaed2464aa91d7ff55David Goodwin#include "MagickCore/monitor.h"
6177521f5232e679aa3de10aaaed2464aa91d7ff55David Goodwin#include "MagickCore/monitor-private.h"
62db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin#include "MagickCore/pixel-accessor.h"
63c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/quantize.h"
648295d99bff6f8e3dfdfdaf1871cb72adab423f20Evan Cheng#include "MagickCore/quantum-private.h"
658295d99bff6f8e3dfdfdaf1871cb72adab423f20Evan Cheng#include "MagickCore/resize.h"
668295d99bff6f8e3dfdfdaf1871cb72adab423f20Evan Cheng#include "MagickCore/resource_.h"
678295d99bff6f8e3dfdfdaf1871cb72adab423f20Evan Cheng#include "MagickCore/splay-tree.h"
68c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/static.h"
69c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/string_.h"
70c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/module.h"
71c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/threshold.h"
72c140c4803dc3e10e08138670829bc0494986abe9David Goodwin#include "MagickCore/utility.h"
73c140c4803dc3e10e08138670829bc0494986abe9David Goodwin
74c140c4803dc3e10e08138670829bc0494986abe9David Goodwin/*
75c140c4803dc3e10e08138670829bc0494986abe9David Goodwin  Forward declarations.
76c140c4803dc3e10e08138670829bc0494986abe9David Goodwin*/
772cfd52c507bd5790457a171eb9bcb39019cc6860Chris Lattnerstatic MagickBooleanType
78c140c4803dc3e10e08138670829bc0494986abe9David Goodwin  WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *),
79c140c4803dc3e10e08138670829bc0494986abe9David Goodwin  WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *);
80c140c4803dc3e10e08138670829bc0494986abe9David Goodwin
81c140c4803dc3e10e08138670829bc0494986abe9David Goodwin/*
82c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
84c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
85c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
86c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%   I s X P M                                                                 %
87c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
88c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
89c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%                                                                             %
90c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9198a0104014e9bb6ed89c2572f615351fd526674aEvan Cheng%
9298a0104014e9bb6ed89c2572f615351fd526674aEvan Cheng%  IsXPM() returns MagickTrue if the image format type, identified by the
93c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  magick string, is XPM.
94c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%
95c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  The format of the IsXPM method is:
96c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%
97c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%      MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
98c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%
99c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%  A description of each parameter follows:
100c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%
101c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%    o magick: compare image format pattern against these bytes. or
102c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%      blob.
103c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%
104c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%    o length: Specifies the length of the magick string.
105c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%
106c140c4803dc3e10e08138670829bc0494986abe9David Goodwin*/
107c140c4803dc3e10e08138670829bc0494986abe9David Goodwinstatic MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
108c140c4803dc3e10e08138670829bc0494986abe9David Goodwin{
109db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin  if (length < 9)
110db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin    return(MagickFalse);
111db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin  if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0)
112db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin    return(MagickTrue);
113db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin  return(MagickFalse);
11477521f5232e679aa3de10aaaed2464aa91d7ff55David Goodwin}
115378445303b10b092a898a75131141a8259cff50bEvan Cheng
116378445303b10b092a898a75131141a8259cff50bEvan Cheng/*
117db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
118db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%                                                                             %
119db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%                                                                             %
120db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%                                                                             %
121db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%   R e a d X P M I m a g e                                                   %
122db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%                                                                             %
123db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%                                                                             %
124db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%                                                                             %
125db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%
127db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%  ReadXPMImage() reads an X11 pixmap image file and returns it.  It
128db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%  allocates the memory necessary for the new Image structure and returns a
129db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%  pointer to the new image.
130db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%
1316495f63945e8dbde81f03a1dc2ab421993b9a495Evan Cheng%  The format of the ReadXPMImage method is:
1326495f63945e8dbde81f03a1dc2ab421993b9a495Evan Cheng%
133db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%      Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
134db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%
135db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%  A description of each parameter follows:
136db5a71a8e01ed9a0d93a19176df6ea0aea510d7bDavid Goodwin%
137c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%    o image_info: the image info.
138ee42fd309ee6a8febfafb97c2f3b6f2069758c5eEvan Cheng%
139ee42fd309ee6a8febfafb97c2f3b6f2069758c5eEvan Cheng%    o exception: return any errors or warnings in this structure.
140c140c4803dc3e10e08138670829bc0494986abe9David Goodwin%
141c140c4803dc3e10e08138670829bc0494986abe9David Goodwin*/
142c140c4803dc3e10e08138670829bc0494986abe9David Goodwin
143c140c4803dc3e10e08138670829bc0494986abe9David Goodwinstatic int CompareXPMColor(const void *target,const void *source)
144c140c4803dc3e10e08138670829bc0494986abe9David Goodwin{
145c140c4803dc3e10e08138670829bc0494986abe9David Goodwin  const char
146c140c4803dc3e10e08138670829bc0494986abe9David Goodwin    *p,
147c140c4803dc3e10e08138670829bc0494986abe9David Goodwin    *q;
148
149  p=(const char *) target;
150  q=(const char *) source;
151  return(strcmp(p,q));
152}
153
154static char *CopyXPMColor(char *destination,const char *source,size_t length)
155{
156  while (length-- && (*source != '\0'))
157    *destination++=(*source++);
158  *destination='\0';
159  return(destination-length);
160}
161
162static char *NextXPMLine(char *p)
163{
164  assert(p != (char*)NULL);
165  p=strchr(p,'\n');
166  if (p != (char *) NULL)
167    p++;
168  return(p);
169}
170
171static inline size_t MagickMin(const size_t x,const size_t y)
172{
173  if (x < y)
174    return(x);
175  return(y);
176}
177
178static char *ParseXPMColor(char *color)
179{
180#define NumberTargets  6
181
182  register char
183    *p,
184    *r;
185
186  register const char
187    *q;
188
189  register ssize_t
190    i;
191
192  static const char
193    *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
194
195  for (i=0; i < NumberTargets; i++)
196  {
197    p=color;
198    for (q=targets[i]; *p != '\0'; p++)
199    {
200      if (*p == '\n')
201        break;
202      if (*p != *q)
203        continue;
204      if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
205        continue;
206      r=p;
207      for ( ; ; )
208      {
209        if (*q == '\0')
210          return(p);
211        if (*r++ != *q++)
212          break;
213      }
214      q=targets[i];
215    }
216  }
217  return((char *) NULL);
218}
219
220static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
221{
222  char
223    key[MaxTextExtent],
224    target[MaxTextExtent],
225    *xpm_buffer;
226
227  Image
228    *image;
229
230  MagickBooleanType
231    active,
232    status;
233
234  register char
235    *p,
236    *q,
237    *next;
238
239  register ssize_t
240    x;
241
242  register Quantum
243    *r;
244
245  size_t
246    length;
247
248  SplayTreeInfo
249    *xpm_colors;
250
251  ssize_t
252    count,
253    j,
254    y;
255
256  unsigned long
257    colors,
258    columns,
259    rows,
260    width;
261
262  /*
263    Open image file.
264  */
265  assert(image_info != (const ImageInfo *) NULL);
266  assert(image_info->signature == MagickSignature);
267  if (image_info->debug != MagickFalse)
268    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
269      image_info->filename);
270  assert(exception != (ExceptionInfo *) NULL);
271  assert(exception->signature == MagickSignature);
272  image=AcquireImage(image_info,exception);
273  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
274  if (status == MagickFalse)
275    {
276      image=DestroyImageList(image);
277      return((Image *) NULL);
278    }
279  /*
280    Read XPM file.
281  */
282  length=MaxTextExtent;
283  xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
284  p=xpm_buffer;
285  if (xpm_buffer != (char *) NULL)
286    while (ReadBlobString(image,p) != (char *) NULL)
287    {
288      if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
289        continue;
290      if ((*p == '}') && (*(p+1) == ';'))
291        break;
292      p+=strlen(p);
293      if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
294        continue;
295      length<<=1;
296      xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
297        sizeof(*xpm_buffer));
298      if (xpm_buffer == (char *) NULL)
299        break;
300      p=xpm_buffer+strlen(xpm_buffer);
301    }
302  if (xpm_buffer == (char *) NULL)
303    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
304  /*
305    Remove comments.
306  */
307  count=0;
308  width=0;
309  for (p=xpm_buffer; *p != '\0'; p++)
310  {
311    if (*p != '"')
312      continue;
313    count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
314    image->columns=columns;
315    image->rows=rows;
316    image->colors=colors;
317    if (count == 4)
318      break;
319  }
320  if ((count != 4) || (width > 10) || (image->columns == 0) ||
321      (image->rows == 0) || (image->colors == 0))
322    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
323  /*
324    Remove unquoted characters.
325  */
326  active=MagickFalse;
327  q=xpm_buffer;
328  while (*p != '\0')
329  {
330    if (*p++ == '"')
331      {
332        if (active != MagickFalse)
333          *q++='\n';
334        active=active != MagickFalse ? MagickFalse : MagickTrue;
335      }
336    if (active != MagickFalse)
337      *q++=(*p);
338  }
339  *q='\0';
340  /*
341    Initialize image structure.
342  */
343  xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
344    (void *(*)(void *)) NULL);
345  if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
346    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
347  /*
348    Read image colormap.
349  */
350  image->depth=1;
351  next=NextXPMLine(xpm_buffer);
352  for (j=0; (j < (ssize_t) image->colors) && (next != (char*) NULL); j++)
353  {
354    p=next;
355    next=NextXPMLine(p);
356    (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent));
357    status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
358    /*
359      Parse color.
360    */
361    (void) CopyMagickString(target,"gray",MaxTextExtent);
362    q=ParseXPMColor(p+width);
363    if (q != (char *) NULL)
364      {
365        while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
366          q++;
367        if (next != (char *) NULL)
368          (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
369            MaxTextExtent));
370        else
371          (void) CopyMagickString(target,q,MaxTextExtent);
372        q=ParseXPMColor(target);
373        if (q != (char *) NULL)
374          *q='\0';
375      }
376    StripString(target);
377    if (LocaleCompare(target,"none") == 0)
378      {
379        image->storage_class=DirectClass;
380        image->alpha_trait=BlendPixelTrait;
381      }
382    status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
383      exception);
384    if (status == MagickFalse)
385      break;
386    if (image->depth < image->colormap[j].depth)
387      image->depth=image->colormap[j].depth;
388  }
389  if (j < (ssize_t) image->colors)
390    ThrowReaderException(CorruptImageError,"CorruptImage");
391  j=0;
392  if (image_info->ping == MagickFalse)
393    {
394      /*
395        Read image pixels.
396      */
397      for (y=0; y < (ssize_t) image->rows; y++)
398      {
399        p=NextXPMLine(p);
400        if (p == (char *) NULL)
401          break;
402        r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
403        if (r == (Quantum *) NULL)
404          break;
405        for (x=0; x < (ssize_t) image->columns; x++)
406        {
407          (void) CopyXPMColor(key,p,(size_t) width);
408          j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
409          if (image->storage_class == PseudoClass)
410            SetPixelIndex(image,j,r);
411          SetPixelInfoPixel(image,image->colormap+j,r);
412          p+=width;
413          r+=GetPixelChannels(image);
414        }
415        if (SyncAuthenticPixels(image,exception) == MagickFalse)
416          break;
417      }
418      if (y < (ssize_t) image->rows)
419        ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
420    }
421  /*
422    Relinquish resources.
423  */
424  xpm_colors=DestroySplayTree(xpm_colors);
425  (void) CloseBlob(image);
426  return(GetFirstImageInList(image));
427}
428
429/*
430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
431%                                                                             %
432%                                                                             %
433%                                                                             %
434%   R e g i s t e r X P M I m a g e                                           %
435%                                                                             %
436%                                                                             %
437%                                                                             %
438%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439%
440%  RegisterXPMImage() adds attributes for the XPM image format to
441%  the list of supported formats.  The attributes include the image format
442%  tag, a method to read and/or write the format, whether the format
443%  supports the saving of more than one frame to the same file or blob,
444%  whether the format supports native in-memory I/O, and a brief
445%  description of the format.
446%
447%  The format of the RegisterXPMImage method is:
448%
449%      size_t RegisterXPMImage(void)
450%
451*/
452ModuleExport size_t RegisterXPMImage(void)
453{
454  MagickInfo
455    *entry;
456
457  entry=SetMagickInfo("PICON");
458  entry->decoder=(DecodeImageHandler *) ReadXPMImage;
459  entry->encoder=(EncodeImageHandler *) WritePICONImage;
460  entry->adjoin=MagickFalse;
461  entry->description=ConstantString("Personal Icon");
462  entry->module=ConstantString("XPM");
463  (void) RegisterMagickInfo(entry);
464  entry=SetMagickInfo("PM");
465  entry->decoder=(DecodeImageHandler *) ReadXPMImage;
466  entry->encoder=(EncodeImageHandler *) WriteXPMImage;
467  entry->adjoin=MagickFalse;
468  entry->stealth=MagickTrue;
469  entry->description=ConstantString("X Windows system pixmap (color)");
470  entry->module=ConstantString("XPM");
471  (void) RegisterMagickInfo(entry);
472  entry=SetMagickInfo("XPM");
473  entry->decoder=(DecodeImageHandler *) ReadXPMImage;
474  entry->encoder=(EncodeImageHandler *) WriteXPMImage;
475  entry->magick=(IsImageFormatHandler *) IsXPM;
476  entry->adjoin=MagickFalse;
477  entry->description=ConstantString("X Windows system pixmap (color)");
478  entry->module=ConstantString("XPM");
479  (void) RegisterMagickInfo(entry);
480  return(MagickImageCoderSignature);
481}
482
483/*
484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485%                                                                             %
486%                                                                             %
487%                                                                             %
488%   U n r e g i s t e r X P M I m a g e                                       %
489%                                                                             %
490%                                                                             %
491%                                                                             %
492%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
493%
494%  UnregisterXPMImage() removes format registrations made by the
495%  XPM module from the list of supported formats.
496%
497%  The format of the UnregisterXPMImage method is:
498%
499%      UnregisterXPMImage(void)
500%
501*/
502ModuleExport void UnregisterXPMImage(void)
503{
504  (void) UnregisterMagickInfo("PICON");
505  (void) UnregisterMagickInfo("PM");
506  (void) UnregisterMagickInfo("XPM");
507}
508
509/*
510%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511%                                                                             %
512%                                                                             %
513%                                                                             %
514%   W r i t e P I C O N I m a g e                                             %
515%                                                                             %
516%                                                                             %
517%                                                                             %
518%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519%
520%  WritePICONImage() writes an image to a file in the Personal Icon format.
521%
522%  The format of the WritePICONImage method is:
523%
524%      MagickBooleanType WritePICONImage(const ImageInfo *image_info,
525%        Image *image,ExceptionInfo *exception)
526%
527%  A description of each parameter follows.
528%
529%    o image_info: the image info.
530%
531%    o image:  The image.
532%
533%    o exception: return any errors or warnings in this structure.
534%
535*/
536static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
537  Image *image,ExceptionInfo *exception)
538{
539#define ColormapExtent  155
540#define GraymapExtent  95
541#define PiconGeometry  "48x48>"
542
543  static unsigned char
544    Colormap[]=
545    {
546      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
547      0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
548      0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
549      0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
550      0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
551      0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
552      0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
553      0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
554      0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
555      0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
556      0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
557      0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
558      0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
559    },
560    Graymap[]=
561    {
562      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
563      0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
564      0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
565      0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
566      0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
567      0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
568      0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
569      0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
570    };
571
572#define MaxCixels  92
573
574  static const char
575    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
576                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
577
578  char
579    buffer[MaxTextExtent],
580    basename[MaxTextExtent],
581    name[MaxTextExtent],
582    symbol[MaxTextExtent];
583
584  Image
585    *affinity_image,
586    *picon;
587
588  ImageInfo
589    *blob_info;
590
591  MagickBooleanType
592    status,
593    transparent;
594
595  PixelInfo
596    pixel;
597
598  QuantizeInfo
599    *quantize_info;
600
601  RectangleInfo
602    geometry;
603
604  register const Quantum
605    *p;
606
607  register ssize_t
608    i,
609    x;
610
611  register Quantum
612    *q;
613
614  size_t
615    characters_per_pixel,
616    colors;
617
618  ssize_t
619    j,
620    k,
621    y;
622
623  /*
624    Open output image file.
625  */
626  assert(image_info != (const ImageInfo *) NULL);
627  assert(image_info->signature == MagickSignature);
628  assert(image != (Image *) NULL);
629  assert(image->signature == MagickSignature);
630  if (image->debug != MagickFalse)
631    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
632  assert(exception != (ExceptionInfo *) NULL);
633  assert(exception->signature == MagickSignature);
634  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
635  if (status == MagickFalse)
636    return(status);
637  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
638    (void) TransformImageColorspace(image,sRGBColorspace,exception);
639  SetGeometry(image,&geometry);
640  (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
641    &geometry.width,&geometry.height);
642  picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
643    exception);
644  blob_info=CloneImageInfo(image_info);
645  (void) AcquireUniqueFilename(blob_info->filename);
646  if ((image_info->type != TrueColorType) &&
647      (IsImageGray(image,exception) != MagickFalse))
648    affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
649  else
650    affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
651  (void) RelinquishUniqueFileResource(blob_info->filename);
652  blob_info=DestroyImageInfo(blob_info);
653  if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
654    return(MagickFalse);
655  quantize_info=AcquireQuantizeInfo(image_info);
656  status=RemapImage(quantize_info,picon,affinity_image,exception);
657  quantize_info=DestroyQuantizeInfo(quantize_info);
658  affinity_image=DestroyImage(affinity_image);
659  transparent=MagickFalse;
660  if (picon->storage_class == PseudoClass)
661    {
662      (void) CompressImageColormap(picon,exception);
663      if (picon->alpha_trait == BlendPixelTrait)
664        transparent=MagickTrue;
665    }
666  else
667    {
668      /*
669        Convert DirectClass to PseudoClass picon.
670      */
671      if (picon->alpha_trait == BlendPixelTrait)
672        {
673          /*
674            Map all the transparent pixels.
675          */
676          for (y=0; y < (ssize_t) picon->rows; y++)
677          {
678            q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
679            if (q == (Quantum *) NULL)
680              break;
681            for (x=0; x < (ssize_t) picon->columns; x++)
682            {
683              if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
684                transparent=MagickTrue;
685              else
686                SetPixelAlpha(picon,OpaqueAlpha,q);
687              q+=GetPixelChannels(picon);
688            }
689            if (SyncAuthenticPixels(picon,exception) == MagickFalse)
690              break;
691          }
692        }
693      (void) SetImageType(picon,PaletteType,exception);
694    }
695  colors=picon->colors;
696  if (transparent != MagickFalse)
697    {
698      colors++;
699      picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
700        picon->colormap,(size_t) colors,sizeof(*picon->colormap));
701      if (picon->colormap == (PixelInfo *) NULL)
702        ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
703      for (y=0; y < (ssize_t) picon->rows; y++)
704      {
705        q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
706        if (q == (Quantum *) NULL)
707          break;
708        for (x=0; x < (ssize_t) picon->columns; x++)
709        {
710          if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
711            SetPixelIndex(picon,picon->colors,q);
712          q+=GetPixelChannels(picon);
713        }
714        if (SyncAuthenticPixels(picon,exception) == MagickFalse)
715          break;
716      }
717    }
718  /*
719    Compute the character per pixel.
720  */
721  characters_per_pixel=1;
722  for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
723    characters_per_pixel++;
724  /*
725    XPM header.
726  */
727  (void) WriteBlobString(image,"/* XPM */\n");
728  GetPathComponent(picon->filename,BasePath,basename);
729  (void) FormatLocaleString(buffer,MaxTextExtent,
730    "static char *%s[] = {\n",basename);
731  (void) WriteBlobString(image,buffer);
732  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
733  (void) FormatLocaleString(buffer,MaxTextExtent,
734    "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
735    picon->rows,(double) colors,(double) characters_per_pixel);
736  (void) WriteBlobString(image,buffer);
737  GetPixelInfo(image,&pixel);
738  for (i=0; i < (ssize_t) colors; i++)
739  {
740    /*
741      Define XPM color.
742    */
743    pixel=picon->colormap[i];
744    pixel.colorspace=sRGBColorspace;
745    pixel.depth=8;
746    pixel.alpha=(double) OpaqueAlpha;
747    (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
748    if (transparent != MagickFalse)
749      {
750        if (i == (ssize_t) (colors-1))
751          (void) CopyMagickString(name,"grey75",MaxTextExtent);
752      }
753    /*
754      Write XPM color.
755    */
756    k=i % MaxCixels;
757    symbol[0]=Cixel[k];
758    for (j=1; j < (ssize_t) characters_per_pixel; j++)
759    {
760      k=((i-k)/MaxCixels) % MaxCixels;
761      symbol[j]=Cixel[k];
762    }
763    symbol[j]='\0';
764    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
765       symbol,name);
766    (void) WriteBlobString(image,buffer);
767  }
768  /*
769    Define XPM pixels.
770  */
771  (void) WriteBlobString(image,"/* pixels */\n");
772  for (y=0; y < (ssize_t) picon->rows; y++)
773  {
774    p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
775    if (p == (const Quantum *) NULL)
776      break;
777    (void) WriteBlobString(image,"\"");
778    for (x=0; x < (ssize_t) picon->columns; x++)
779    {
780      k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
781      symbol[0]=Cixel[k];
782      for (j=1; j < (ssize_t) characters_per_pixel; j++)
783      {
784        k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
785        symbol[j]=Cixel[k];
786      }
787      symbol[j]='\0';
788      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
789      (void) WriteBlobString(image,buffer);
790      p+=GetPixelChannels(image);
791    }
792    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
793      y == (ssize_t) (picon->rows-1) ? "" : ",");
794    (void) WriteBlobString(image,buffer);
795    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
796      picon->rows);
797    if (status == MagickFalse)
798      break;
799  }
800  picon=DestroyImage(picon);
801  (void) WriteBlobString(image,"};\n");
802  (void) CloseBlob(image);
803  return(MagickTrue);
804}
805
806/*
807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808%                                                                             %
809%                                                                             %
810%                                                                             %
811%   W r i t e X P M I m a g e                                                 %
812%                                                                             %
813%                                                                             %
814%                                                                             %
815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
816%
817%  WriteXPMImage() writes an image to a file in the X pixmap format.
818%
819%  The format of the WriteXPMImage method is:
820%
821%      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
822%        Image *image,ExceptionInfo *exception)
823%
824%  A description of each parameter follows.
825%
826%    o image_info: the image info.
827%
828%    o image:  The image.
829%
830%    o exception: return any errors or warnings in this structure.
831%
832*/
833static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
834  ExceptionInfo *exception)
835{
836#define MaxCixels  92
837
838  static const char
839    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
840                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
841
842  char
843    buffer[MaxTextExtent],
844    basename[MaxTextExtent],
845    name[MaxTextExtent],
846    symbol[MaxTextExtent];
847
848  MagickBooleanType
849    status;
850
851  PixelInfo
852    pixel;
853
854  register const Quantum
855    *p;
856
857  register ssize_t
858    i,
859    x;
860
861  size_t
862    characters_per_pixel;
863
864  ssize_t
865    j,
866    k,
867    opacity,
868    y;
869
870  /*
871    Open output image file.
872  */
873  assert(image_info != (const ImageInfo *) NULL);
874  assert(image_info->signature == MagickSignature);
875  assert(image != (Image *) NULL);
876  assert(image->signature == MagickSignature);
877  if (image->debug != MagickFalse)
878    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
879  assert(exception != (ExceptionInfo *) NULL);
880  assert(exception->signature == MagickSignature);
881  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
882  if (status == MagickFalse)
883    return(status);
884  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
885    (void) TransformImageColorspace(image,sRGBColorspace,exception);
886  opacity=(-1);
887  if (image->alpha_trait != BlendPixelTrait)
888    {
889      if ((image->storage_class == DirectClass) || (image->colors > 256))
890        (void) SetImageType(image,PaletteType,exception);
891    }
892  else
893    {
894      double
895        alpha,
896        beta;
897
898      /*
899        Identify transparent colormap index.
900      */
901      if ((image->storage_class == DirectClass) || (image->colors > 256))
902        (void) SetImageType(image,PaletteBilevelMatteType,exception);
903      for (i=0; i < (ssize_t) image->colors; i++)
904        if (image->colormap[i].alpha != OpaqueAlpha)
905          {
906            if (opacity < 0)
907              {
908                opacity=i;
909                continue;
910              }
911            alpha=(double) TransparentAlpha-(double)
912              image->colormap[i].alpha;
913            beta=(double) TransparentAlpha-(double)
914              image->colormap[opacity].alpha;
915            if (alpha < beta)
916              opacity=i;
917          }
918      if (opacity == -1)
919        {
920          (void) SetImageType(image,PaletteBilevelMatteType,exception);
921          for (i=0; i < (ssize_t) image->colors; i++)
922            if (image->colormap[i].alpha != OpaqueAlpha)
923              {
924                if (opacity < 0)
925                  {
926                    opacity=i;
927                    continue;
928                  }
929                alpha=(Quantum) TransparentAlpha-(double)
930                  image->colormap[i].alpha;
931                beta=(Quantum) TransparentAlpha-(double)
932                  image->colormap[opacity].alpha;
933                if (alpha < beta)
934                  opacity=i;
935              }
936        }
937      if (opacity >= 0)
938        {
939          image->colormap[opacity].red=image->transparent_color.red;
940          image->colormap[opacity].green=image->transparent_color.green;
941          image->colormap[opacity].blue=image->transparent_color.blue;
942        }
943    }
944  /*
945    Compute the character per pixel.
946  */
947  characters_per_pixel=1;
948  for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
949    characters_per_pixel++;
950  /*
951    XPM header.
952  */
953  (void) WriteBlobString(image,"/* XPM */\n");
954  GetPathComponent(image->filename,BasePath,basename);
955  if (isalnum((int) ((unsigned char) *basename)) == 0)
956    {
957      (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
958      (void) CopyMagickString(basename,buffer,MaxTextExtent);
959    }
960  if (isalpha((int) ((unsigned char) basename[0])) == 0)
961    basename[0]='_';
962  for (i=1; basename[i] != '\0'; i++)
963    if (isalnum((int) ((unsigned char) basename[i])) == 0)
964      basename[i]='_';
965  (void) FormatLocaleString(buffer,MaxTextExtent,
966    "static char *%s[] = {\n",basename);
967  (void) WriteBlobString(image,buffer);
968  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
969  (void) FormatLocaleString(buffer,MaxTextExtent,
970    "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
971    image->rows,(double) image->colors,(double) characters_per_pixel);
972  (void) WriteBlobString(image,buffer);
973  GetPixelInfo(image,&pixel);
974  for (i=0; i < (ssize_t) image->colors; i++)
975  {
976    /*
977      Define XPM color.
978    */
979    pixel=image->colormap[i];
980    pixel.colorspace=sRGBColorspace;
981    pixel.depth=8;
982    pixel.alpha=(double) OpaqueAlpha;
983    (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
984    if (i == opacity)
985      (void) CopyMagickString(name,"None",MaxTextExtent);
986    /*
987      Write XPM color.
988    */
989    k=i % MaxCixels;
990    symbol[0]=Cixel[k];
991    for (j=1; j < (ssize_t) characters_per_pixel; j++)
992    {
993      k=((i-k)/MaxCixels) % MaxCixels;
994      symbol[j]=Cixel[k];
995    }
996    symbol[j]='\0';
997    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
998      name);
999    (void) WriteBlobString(image,buffer);
1000  }
1001  /*
1002    Define XPM pixels.
1003  */
1004  (void) WriteBlobString(image,"/* pixels */\n");
1005  for (y=0; y < (ssize_t) image->rows; y++)
1006  {
1007    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1008    if (p == (const Quantum *) NULL)
1009      break;
1010    (void) WriteBlobString(image,"\"");
1011    for (x=0; x < (ssize_t) image->columns; x++)
1012    {
1013      k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1014      symbol[0]=Cixel[k];
1015      for (j=1; j < (ssize_t) characters_per_pixel; j++)
1016      {
1017        k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1018        symbol[j]=Cixel[k];
1019      }
1020      symbol[j]='\0';
1021      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1022      (void) WriteBlobString(image,buffer);
1023      p+=GetPixelChannels(image);
1024    }
1025    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1026      (y == (ssize_t) (image->rows-1) ? "" : ","));
1027    (void) WriteBlobString(image,buffer);
1028    if (image->previous == (Image *) NULL)
1029      {
1030        status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1031          image->rows);
1032        if (status == MagickFalse)
1033          break;
1034      }
1035  }
1036  (void) WriteBlobString(image,"};\n");
1037  (void) CloseBlob(image);
1038  return(MagickTrue);
1039}
1040