xpm.c revision c6da28e61bb609d2b2cfdcc7752106c973415edb
1b9f2cc816dc3e5369507db9509bea6d10b524a94Howard Hinnant/*
2b9f2cc816dc3e5369507db9509bea6d10b524a94Howard Hinnant%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3b9f2cc816dc3e5369507db9509bea6d10b524a94Howard Hinnant%                                                                             %
4b9f2cc816dc3e5369507db9509bea6d10b524a94Howard Hinnant%                                                                             %
5b9f2cc816dc3e5369507db9509bea6d10b524a94Howard Hinnant%                                                                             %
6b9f2cc816dc3e5369507db9509bea6d10b524a94Howard Hinnant%                            X   X  PPPP   M   M                              %
7b9f2cc816dc3e5369507db9509bea6d10b524a94Howard Hinnant%                             X X   P   P  MM MM                              %
8b9f2cc816dc3e5369507db9509bea6d10b524a94Howard Hinnant%                              X    PPPP   M M M                              %
9b9f2cc816dc3e5369507db9509bea6d10b524a94Howard Hinnant%                             X X   P      M   M                              %
1047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                            X   X  P      M   M                              %
1147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
1247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
1347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                  Read/Write X Windows system Pixmap Format                  %
1447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
1547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                              Software Design                                %
1647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                John Cristy                                  %
1747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                 July 1992                                   %
1847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
1960a178840d23fbf169a22fde1cef5e558ef232f9Howard Hinnant%                                                                             %
2047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
2147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  dedicated to making software imaging solutions freely available.           %
2247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
2347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  You may not use this file except in compliance with the License.  You may  %
2447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  obtain a copy of the License at                                            %
2547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
2647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%    http://www.imagemagick.org/script/license.php                            %
2747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
2847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  Unless required by applicable law or agreed to in writing, software        %
2947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  distributed under the License is distributed on an "AS IS" BASIS,          %
3047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
3147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  See the License for the specific language governing permissions and        %
3247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  limitations under the License.                                             %
3347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
3447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
3647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
3747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow*/
3847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
3947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow/*
4047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  Include declarations.
4147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow*/
4247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/studio.h"
4347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/blob.h"
4447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/blob-private.h"
4547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/cache.h"
4647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/color.h"
4747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/color-private.h"
4847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/colormap.h"
4947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/colorspace.h"
5047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/exception.h"
5147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/exception-private.h"
5247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/geometry.h"
5347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/image.h"
5447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/image-private.h"
5547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/list.h"
5647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/magick.h"
5747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/memory_.h"
5847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/monitor.h"
5947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/monitor-private.h"
6047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/pixel-private.h"
6147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/quantize.h"
6247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/quantum-private.h"
6347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/resize.h"
6447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/resource_.h"
6547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/splay-tree.h"
6647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/static.h"
6747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/string_.h"
6847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/module.h"
6947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/threshold.h"
7047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#include "magick/utility.h"
7147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
7247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow/*
7347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  Forward declarations.
7447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow*/
7547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clowstatic MagickBooleanType
7647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  WritePICONImage(const ImageInfo *,Image *),
7747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  WriteXPMImage(const ImageInfo *,Image *);
7847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
7947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow/*
8047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
8247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
8347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
8447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%   I s X P M                                                                 %
8547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
8647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
8747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
8847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
9047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  IsXPM() returns MagickTrue if the image format type, identified by the
9147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  magick string, is XPM.
9247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
9347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  The format of the IsXPM method is:
9447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
9547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%      MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
9647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
9747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  A description of each parameter follows:
9847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
9947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%    o magick: compare image format pattern against these bytes. or
10047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%      blob.
10147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
10247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%    o length: Specifies the length of the magick string.
10347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
10447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow*/
10547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clowstatic MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
10647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow{
10747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  if (length < 9)
10847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    return(MagickFalse);
10947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0)
11047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    return(MagickTrue);
11147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  return(MagickFalse);
11247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow}
11347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
11447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow/*
11547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
11747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
11847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
11947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%   R e a d X P M I m a g e                                                   %
12047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
12147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
12247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%                                                                             %
12347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
12547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  ReadXPMImage() reads an X11 pixmap image file and returns it.  It
12647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  allocates the memory necessary for the new Image structure and returns a
12747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  pointer to the new image.
12847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
12947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  The format of the ReadXPMImage method is:
13047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
13147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%      Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
13247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
13347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%  A description of each parameter follows:
13447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
13547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%    o image_info: the image info.
13647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
13747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%    o exception: return any errors or warnings in this structure.
13847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow%
13947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow*/
14047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
14147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clowstatic int CompareXPMColor(const void *target,const void *source)
14247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow{
14347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  const char
14447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    *p,
14547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    *q;
14647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
14747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  p=(const char *) target;
14847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  q=(const char *) source;
14947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  return(strcmp(p,q));
15047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow}
15147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
15247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clowstatic char *CopyXPMColor(char *destination,const char *source,size_t length)
15347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow{
15447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  while (length-- && (*source != '\0'))
15547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    *destination++=(*source++);
15647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  *destination='\0';
15747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  return(destination-length);
15847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow}
15947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
16047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clowstatic char *NextXPMLine(char *p)
16147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow{
16247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  assert(p != (char*)NULL);
16347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  p=strchr(p,'\n');
16447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  if (p != (char *) NULL)
16547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    p++;
16647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  return(p);
16747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow}
16847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
16947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clowstatic inline size_t MagickMin(const size_t x,const size_t y)
17047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow{
17147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  if (x < y)
17247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    return(x);
17347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  return(y);
17447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow}
17547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
17647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clowstatic char *ParseXPMColor(char *color)
17747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow{
17847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow#define NumberTargets  6
17947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
18047c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  register char
18147c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    *p,
18247c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    *r;
18347c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
18447c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  register const char
18547c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    *q;
18647c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
18747c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow  register ssize_t
18847c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow    i;
18947c29eb784f53ff170074ea00cd238e0e95af82fMarshall Clow
190  static const char
191    *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
192
193  for (i=0; i < NumberTargets; i++)
194  {
195    p=color;
196    for (q=targets[i]; *p != '\0'; p++)
197    {
198      if (*p == '\n')
199        break;
200      if (*p != *q)
201        continue;
202      if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
203        continue;
204      r=p;
205      for ( ; ; )
206      {
207        if (*q == '\0')
208          return(p);
209        if (*r++ != *q++)
210          break;
211      }
212      q=targets[i];
213    }
214  }
215  return((char *) NULL);
216}
217
218static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
219{
220  char
221    key[MaxTextExtent],
222    target[MaxTextExtent],
223    *xpm_buffer;
224
225  Image
226    *image;
227
228  MagickBooleanType
229    active,
230    status;
231
232  register char
233    *p,
234    *q,
235    *next;
236
237  register IndexPacket
238    *indexes;
239
240  register ssize_t
241    x;
242
243  register PixelPacket
244    *r;
245
246  size_t
247    length;
248
249  SplayTreeInfo
250    *xpm_colors;
251
252  ssize_t
253    count,
254    j,
255    y;
256
257  unsigned long
258    colors,
259    columns,
260    rows,
261    width;
262
263  /*
264    Open image file.
265  */
266  assert(image_info != (const ImageInfo *) NULL);
267  assert(image_info->signature == MagickSignature);
268  if (image_info->debug != MagickFalse)
269    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
270      image_info->filename);
271  assert(exception != (ExceptionInfo *) NULL);
272  assert(exception->signature == MagickSignature);
273  image=AcquireImage(image_info);
274  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
275  if (status == MagickFalse)
276    {
277      image=DestroyImageList(image);
278      return((Image *) NULL);
279    }
280  /*
281    Read XPM file.
282  */
283  length=MaxTextExtent;
284  xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
285  p=xpm_buffer;
286  if (xpm_buffer != (char *) NULL)
287    while (ReadBlobString(image,p) != (char *) NULL)
288    {
289      if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
290        continue;
291      if ((*p == '}') && (*(p+1) == ';'))
292        break;
293      p+=strlen(p);
294      if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
295        continue;
296      length<<=1;
297      xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
298        sizeof(*xpm_buffer));
299      if (xpm_buffer == (char *) NULL)
300        break;
301      p=xpm_buffer+strlen(xpm_buffer);
302    }
303  if (xpm_buffer == (char *) NULL)
304    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
305  /*
306    Remove comments.
307  */
308  count=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  image->depth=16;
324  /*
325    Remove unquoted characters.
326  */
327  active=MagickFalse;
328  q=xpm_buffer;
329  while (*p != '\0')
330  {
331    if (*p++ == '"')
332      {
333        if (active != MagickFalse)
334          *q++='\n';
335        active=active != MagickFalse ? MagickFalse : MagickTrue;
336      }
337    if (active != MagickFalse)
338      *q++=(*p);
339  }
340  *q='\0';
341  /*
342    Initialize image structure.
343  */
344  xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
345    (void *(*)(void *)) NULL);
346  if (AcquireImageColormap(image,image->colors) == MagickFalse)
347    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
348  /*
349    Read image colormap.
350  */
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->matte=MagickTrue;
381      }
382    status=QueryColorCompliance(target,AllCompliance,&image->colormap[j],
383      exception);
384    if (status == MagickFalse)
385      break;
386  }
387  if (j < (ssize_t) image->colors)
388    ThrowReaderException(CorruptImageError,"CorruptImage");
389  j=0;
390  if (image_info->ping == MagickFalse)
391    {
392      /*
393        Read image pixels.
394      */
395      for (y=0; y < (ssize_t) image->rows; y++)
396      {
397        p=NextXPMLine(p);
398        if (p == (char *) NULL)
399          break;
400        r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
401        if (r == (PixelPacket *) NULL)
402          break;
403        indexes=GetAuthenticIndexQueue(image);
404        for (x=0; x < (ssize_t) image->columns; x++)
405        {
406          (void) CopyXPMColor(key,p,(size_t) width);
407          j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
408          if (image->storage_class == PseudoClass)
409            SetIndexPixelComponent(indexes+x,j);
410          *r=image->colormap[j];
411          r++;
412          p+=width;
413        }
414        if (SyncAuthenticPixels(image,exception) == MagickFalse)
415          break;
416      }
417      if (y < (ssize_t) image->rows)
418        ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
419    }
420  /*
421    Relinquish resources.
422  */
423  xpm_colors=DestroySplayTree(xpm_colors);
424  (void) CloseBlob(image);
425  return(GetFirstImageInList(image));
426}
427
428/*
429%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
430%                                                                             %
431%                                                                             %
432%                                                                             %
433%   R e g i s t e r X P M I m a g e                                           %
434%                                                                             %
435%                                                                             %
436%                                                                             %
437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438%
439%  RegisterXPMImage() adds attributes for the XPM image format to
440%  the list of supported formats.  The attributes include the image format
441%  tag, a method to read and/or write the format, whether the format
442%  supports the saving of more than one frame to the same file or blob,
443%  whether the format supports native in-memory I/O, and a brief
444%  description of the format.
445%
446%  The format of the RegisterXPMImage method is:
447%
448%      size_t RegisterXPMImage(void)
449%
450*/
451ModuleExport size_t RegisterXPMImage(void)
452{
453  MagickInfo
454    *entry;
455
456  entry=SetMagickInfo("PICON");
457  entry->decoder=(DecodeImageHandler *) ReadXPMImage;
458  entry->encoder=(EncodeImageHandler *) WritePICONImage;
459  entry->adjoin=MagickFalse;
460  entry->description=ConstantString("Personal Icon");
461  entry->module=ConstantString("XPM");
462  (void) RegisterMagickInfo(entry);
463  entry=SetMagickInfo("PM");
464  entry->decoder=(DecodeImageHandler *) ReadXPMImage;
465  entry->encoder=(EncodeImageHandler *) WriteXPMImage;
466  entry->adjoin=MagickFalse;
467  entry->stealth=MagickTrue;
468  entry->description=ConstantString("X Windows system pixmap (color)");
469  entry->module=ConstantString("XPM");
470  (void) RegisterMagickInfo(entry);
471  entry=SetMagickInfo("XPM");
472  entry->decoder=(DecodeImageHandler *) ReadXPMImage;
473  entry->encoder=(EncodeImageHandler *) WriteXPMImage;
474  entry->magick=(IsImageFormatHandler *) IsXPM;
475  entry->adjoin=MagickFalse;
476  entry->description=ConstantString("X Windows system pixmap (color)");
477  entry->module=ConstantString("XPM");
478  (void) RegisterMagickInfo(entry);
479  return(MagickImageCoderSignature);
480}
481
482/*
483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
484%                                                                             %
485%                                                                             %
486%                                                                             %
487%   U n r e g i s t e r X P M I m a g e                                       %
488%                                                                             %
489%                                                                             %
490%                                                                             %
491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
492%
493%  UnregisterXPMImage() removes format registrations made by the
494%  XPM module from the list of supported formats.
495%
496%  The format of the UnregisterXPMImage method is:
497%
498%      UnregisterXPMImage(void)
499%
500*/
501ModuleExport void UnregisterXPMImage(void)
502{
503  (void) UnregisterMagickInfo("PICON");
504  (void) UnregisterMagickInfo("PM");
505  (void) UnregisterMagickInfo("XPM");
506}
507
508/*
509%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510%                                                                             %
511%                                                                             %
512%                                                                             %
513%   W r i t e P I C O N I m a g e                                             %
514%                                                                             %
515%                                                                             %
516%                                                                             %
517%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
518%
519%  WritePICONImage() writes an image to a file in the Personal Icon format.
520%
521%  The format of the WritePICONImage method is:
522%
523%      MagickBooleanType WritePICONImage(const ImageInfo *image_info,
524%        Image *image)
525%
526%  A description of each parameter follows.
527%
528%    o image_info: the image info.
529%
530%    o image:  The image.
531%
532*/
533static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
534  Image *image)
535{
536#define ColormapExtent  155
537#define GraymapExtent  95
538#define PiconGeometry  "48x48>"
539
540  static unsigned char
541    Colormap[]=
542    {
543      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
544      0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
545      0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
546      0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
547      0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
548      0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
549      0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
550      0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
551      0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
552      0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
553      0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
554      0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
555      0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
556    },
557    Graymap[]=
558    {
559      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
560      0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
561      0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
562      0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
563      0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
564      0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
565      0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
566      0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
567    };
568
569#define MaxCixels  92
570
571  static const char
572    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
573                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
574
575  char
576    buffer[MaxTextExtent],
577    basename[MaxTextExtent],
578    name[MaxTextExtent],
579    symbol[MaxTextExtent];
580
581  ExceptionInfo
582    *exception;
583
584  Image
585    *affinity_image,
586    *picon;
587
588  ImageInfo
589    *blob_info;
590
591  MagickBooleanType
592    status,
593    transparent;
594
595  MagickPixelPacket
596    pixel;
597
598  QuantizeInfo
599    *quantize_info;
600
601  RectangleInfo
602    geometry;
603
604  register const IndexPacket
605    *indexes;
606
607  register const PixelPacket
608    *p;
609
610  register ssize_t
611    i,
612    x;
613
614  register PixelPacket
615    *q;
616
617  size_t
618    characters_per_pixel,
619    colors;
620
621  ssize_t
622    j,
623    k,
624    y;
625
626  /*
627    Open output image file.
628  */
629  assert(image_info != (const ImageInfo *) NULL);
630  assert(image_info->signature == MagickSignature);
631  assert(image != (Image *) NULL);
632  assert(image->signature == MagickSignature);
633  if (image->debug != MagickFalse)
634    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
635  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
636  if (status == MagickFalse)
637    return(status);
638  if (image->colorspace != RGBColorspace)
639    (void) TransformImageColorspace(image,RGBColorspace);
640  SetGeometry(image,&geometry);
641  (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
642    &geometry.width,&geometry.height);
643  picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,1.0,
644    &image->exception);
645  blob_info=CloneImageInfo(image_info);
646  (void) AcquireUniqueFilename(blob_info->filename);
647  if ((image_info->type != TrueColorType) &&
648      (IsGrayImage(image,&image->exception) != MagickFalse))
649    affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,
650      &image->exception);
651  else
652    affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,
653      &image->exception);
654  (void) RelinquishUniqueFileResource(blob_info->filename);
655  blob_info=DestroyImageInfo(blob_info);
656  if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
657    return(MagickFalse);
658  quantize_info=AcquireQuantizeInfo(image_info);
659  status=RemapImage(quantize_info,picon,affinity_image);
660  quantize_info=DestroyQuantizeInfo(quantize_info);
661  affinity_image=DestroyImage(affinity_image);
662  transparent=MagickFalse;
663  exception=(&image->exception);
664  if (picon->storage_class == PseudoClass)
665    {
666      (void) CompressImageColormap(picon);
667      if (picon->matte != MagickFalse)
668        transparent=MagickTrue;
669    }
670  else
671    {
672      /*
673        Convert DirectClass to PseudoClass picon.
674      */
675      if (picon->matte != MagickFalse)
676        {
677          /*
678            Map all the transparent pixels.
679          */
680          for (y=0; y < (ssize_t) picon->rows; y++)
681          {
682            q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
683            if (q == (PixelPacket *) NULL)
684              break;
685            for (x=0; x < (ssize_t) picon->columns; x++)
686            {
687              if (q->opacity == (Quantum) TransparentOpacity)
688                transparent=MagickTrue;
689              else
690                SetOpacityPixelComponent(q,OpaqueOpacity);
691              q++;
692            }
693            if (SyncAuthenticPixels(picon,exception) == MagickFalse)
694              break;
695          }
696        }
697      (void) SetImageType(picon,PaletteType);
698    }
699  colors=picon->colors;
700  if (transparent != MagickFalse)
701    {
702      register IndexPacket
703        *indexes;
704
705      colors++;
706      picon->colormap=(PixelPacket *) ResizeQuantumMemory((void **)
707        picon->colormap,(size_t) colors,sizeof(*picon->colormap));
708      if (picon->colormap == (PixelPacket *) NULL)
709        ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
710      for (y=0; y < (ssize_t) picon->rows; y++)
711      {
712        q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
713        if (q == (PixelPacket *) NULL)
714          break;
715        indexes=GetAuthenticIndexQueue(picon);
716        for (x=0; x < (ssize_t) picon->columns; x++)
717        {
718          if (q->opacity == (Quantum) TransparentOpacity)
719            SetIndexPixelComponent(indexes+x,picon->colors);
720          q++;
721        }
722        if (SyncAuthenticPixels(picon,exception) == MagickFalse)
723          break;
724      }
725    }
726  /*
727    Compute the character per pixel.
728  */
729  characters_per_pixel=1;
730  for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
731    characters_per_pixel++;
732  /*
733    XPM header.
734  */
735  (void) WriteBlobString(image,"/* XPM */\n");
736  GetPathComponent(picon->filename,BasePath,basename);
737  (void) FormatMagickString(buffer,MaxTextExtent,
738    "static char *%s[] = {\n",basename);
739  (void) WriteBlobString(image,buffer);
740  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
741  (void) FormatMagickString(buffer,MaxTextExtent,
742    "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
743    picon->rows,(double) colors,(double) characters_per_pixel);
744  (void) WriteBlobString(image,buffer);
745  GetMagickPixelPacket(image,&pixel);
746  for (i=0; i < (ssize_t) colors; i++)
747  {
748    /*
749      Define XPM color.
750    */
751    SetMagickPixelPacket(image,picon->colormap+i,(IndexPacket *) NULL,&pixel);
752    pixel.colorspace=RGBColorspace;
753    pixel.depth=8;
754    pixel.opacity=(MagickRealType) OpaqueOpacity;
755    (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
756      &image->exception);
757    if (transparent != MagickFalse)
758      {
759        if (i == (ssize_t) (colors-1))
760          (void) CopyMagickString(name,"grey75",MaxTextExtent);
761      }
762    /*
763      Write XPM color.
764    */
765    k=i % MaxCixels;
766    symbol[0]=Cixel[k];
767    for (j=1; j < (ssize_t) characters_per_pixel; j++)
768    {
769      k=((i-k)/MaxCixels) % MaxCixels;
770      symbol[j]=Cixel[k];
771    }
772    symbol[j]='\0';
773    (void) FormatMagickString(buffer,MaxTextExtent,"\"%s c %s\",\n",
774       symbol,name);
775    (void) WriteBlobString(image,buffer);
776  }
777  /*
778    Define XPM pixels.
779  */
780  (void) WriteBlobString(image,"/* pixels */\n");
781  for (y=0; y < (ssize_t) picon->rows; y++)
782  {
783    p=GetVirtualPixels(picon,0,y,picon->columns,1,&picon->exception);
784    if (p == (const PixelPacket *) NULL)
785      break;
786    indexes=GetVirtualIndexQueue(picon);
787    (void) WriteBlobString(image,"\"");
788    for (x=0; x < (ssize_t) picon->columns; x++)
789    {
790      k=((ssize_t) GetIndexPixelComponent(indexes+x) % MaxCixels);
791      symbol[0]=Cixel[k];
792      for (j=1; j < (ssize_t) characters_per_pixel; j++)
793      {
794        k=(((int) GetIndexPixelComponent(indexes+x)-k)/MaxCixels) % MaxCixels;
795        symbol[j]=Cixel[k];
796      }
797      symbol[j]='\0';
798      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
799      (void) WriteBlobString(image,buffer);
800    }
801    (void) FormatMagickString(buffer,MaxTextExtent,"\"%s\n",
802      y == (ssize_t) (picon->rows-1) ? "" : ",");
803    (void) WriteBlobString(image,buffer);
804    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
805      picon->rows);
806    if (status == MagickFalse)
807      break;
808  }
809  picon=DestroyImage(picon);
810  (void) WriteBlobString(image,"};\n");
811  (void) CloseBlob(image);
812  return(MagickTrue);
813}
814
815/*
816%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
817%                                                                             %
818%                                                                             %
819%                                                                             %
820%   W r i t e X P M I m a g e                                                 %
821%                                                                             %
822%                                                                             %
823%                                                                             %
824%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
825%
826%  WriteXPMImage() writes an image to a file in the X pixmap format.
827%
828%  The format of the WriteXPMImage method is:
829%
830%      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
831%
832%  A description of each parameter follows.
833%
834%    o image_info: the image info.
835%
836%    o image:  The image.
837%
838*/
839static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
840{
841#define MaxCixels  92
842
843  static const char
844    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
845                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
846
847  char
848    buffer[MaxTextExtent],
849    basename[MaxTextExtent],
850    name[MaxTextExtent],
851    symbol[MaxTextExtent];
852
853  MagickBooleanType
854    status;
855
856  MagickPixelPacket
857    pixel;
858
859  register const IndexPacket
860    *indexes;
861
862  register const PixelPacket
863    *p;
864
865  register ssize_t
866    i,
867    x;
868
869  size_t
870    characters_per_pixel;
871
872  ssize_t
873    j,
874    k,
875    opacity,
876    y;
877
878  /*
879    Open output image file.
880  */
881  assert(image_info != (const ImageInfo *) NULL);
882  assert(image_info->signature == MagickSignature);
883  assert(image != (Image *) NULL);
884  assert(image->signature == MagickSignature);
885  if (image->debug != MagickFalse)
886    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
887  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
888  if (status == MagickFalse)
889    return(status);
890  if (image->colorspace != RGBColorspace)
891    (void) TransformImageColorspace(image,RGBColorspace);
892  opacity=(-1);
893  if (image->matte == MagickFalse)
894    {
895      if ((image->storage_class == DirectClass) || (image->colors > 256))
896        (void) SetImageType(image,PaletteType);
897    }
898  else
899    {
900      MagickRealType
901        alpha,
902        beta;
903
904      /*
905        Identify transparent colormap index.
906      */
907      if ((image->storage_class == DirectClass) || (image->colors > 256))
908        (void) SetImageType(image,PaletteBilevelMatteType);
909      for (i=0; i < (ssize_t) image->colors; i++)
910        if (image->colormap[i].opacity != OpaqueOpacity)
911          {
912            if (opacity < 0)
913              {
914                opacity=i;
915                continue;
916              }
917            alpha=(Quantum) TransparentOpacity-(MagickRealType)
918              image->colormap[i].opacity;
919            beta=(Quantum) TransparentOpacity-(MagickRealType)
920              image->colormap[opacity].opacity;
921            if (alpha < beta)
922              opacity=i;
923          }
924      if (opacity == -1)
925        {
926          (void) SetImageType(image,PaletteBilevelMatteType);
927          for (i=0; i < (ssize_t) image->colors; i++)
928            if (image->colormap[i].opacity != OpaqueOpacity)
929              {
930                if (opacity < 0)
931                  {
932                    opacity=i;
933                    continue;
934                  }
935                alpha=(Quantum) TransparentOpacity-(MagickRealType)
936                  image->colormap[i].opacity;
937                beta=(Quantum) TransparentOpacity-(MagickRealType)
938                  image->colormap[opacity].opacity;
939                if (alpha < beta)
940                  opacity=i;
941              }
942        }
943      if (opacity >= 0)
944        {
945          image->colormap[opacity].red=image->transparent_color.red;
946          image->colormap[opacity].green=image->transparent_color.green;
947          image->colormap[opacity].blue=image->transparent_color.blue;
948        }
949    }
950  /*
951    Compute the character per pixel.
952  */
953  characters_per_pixel=1;
954  for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
955    characters_per_pixel++;
956  /*
957    XPM header.
958  */
959  (void) WriteBlobString(image,"/* XPM */\n");
960  GetPathComponent(image->filename,BasePath,basename);
961  if (isalnum((int) ((unsigned char) *basename)) == 0)
962    {
963      (void) FormatMagickString(buffer,MaxTextExtent,"xpm_%s",basename);
964      (void) CopyMagickString(basename,buffer,MaxTextExtent);
965    }
966  if (isalpha((int) ((unsigned char) basename[0])) == 0)
967    basename[0]='_';
968  for (i=1; basename[i] != '\0'; i++)
969    if (isalnum((int) ((unsigned char) basename[i])) == 0)
970      basename[i]='_';
971  (void) FormatMagickString(buffer,MaxTextExtent,
972    "static char *%s[] = {\n",basename);
973  (void) WriteBlobString(image,buffer);
974  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
975  (void) FormatMagickString(buffer,MaxTextExtent,
976    "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
977    image->rows,(double) image->colors,(double) characters_per_pixel);
978  (void) WriteBlobString(image,buffer);
979  GetMagickPixelPacket(image,&pixel);
980  for (i=0; i < (ssize_t) image->colors; i++)
981  {
982    /*
983      Define XPM color.
984    */
985    SetMagickPixelPacket(image,image->colormap+i,(IndexPacket *) NULL,&pixel);
986    pixel.colorspace=RGBColorspace;
987    pixel.depth=8;
988    pixel.opacity=(MagickRealType) OpaqueOpacity;
989    (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
990      &image->exception);
991    if (i == opacity)
992      (void) CopyMagickString(name,"None",MaxTextExtent);
993    /*
994      Write XPM color.
995    */
996    k=i % MaxCixels;
997    symbol[0]=Cixel[k];
998    for (j=1; j < (ssize_t) characters_per_pixel; j++)
999    {
1000      k=((i-k)/MaxCixels) % MaxCixels;
1001      symbol[j]=Cixel[k];
1002    }
1003    symbol[j]='\0';
1004    (void) FormatMagickString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
1005      name);
1006    (void) WriteBlobString(image,buffer);
1007  }
1008  /*
1009    Define XPM pixels.
1010  */
1011  (void) WriteBlobString(image,"/* pixels */\n");
1012  for (y=0; y < (ssize_t) image->rows; y++)
1013  {
1014    p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1015    if (p == (const PixelPacket *) NULL)
1016      break;
1017    indexes=GetVirtualIndexQueue(image);
1018    (void) WriteBlobString(image,"\"");
1019    for (x=0; x < (ssize_t) image->columns; x++)
1020    {
1021      k=((ssize_t) GetIndexPixelComponent(indexes+x) % MaxCixels);
1022      symbol[0]=Cixel[k];
1023      for (j=1; j < (ssize_t) characters_per_pixel; j++)
1024      {
1025        k=(((int) GetIndexPixelComponent(indexes+x)-k)/MaxCixels) % MaxCixels;
1026        symbol[j]=Cixel[k];
1027      }
1028      symbol[j]='\0';
1029      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1030      (void) WriteBlobString(image,buffer);
1031    }
1032    (void) FormatMagickString(buffer,MaxTextExtent,"\"%s\n",
1033      (y == (ssize_t) (image->rows-1) ? "" : ","));
1034    (void) WriteBlobString(image,buffer);
1035    if (image->previous == (Image *) NULL)
1036      {
1037        status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1038          image->rows);
1039        if (status == MagickFalse)
1040          break;
1041      }
1042  }
1043  (void) WriteBlobString(image,"};\n");
1044  (void) CloseBlob(image);
1045  return(MagickTrue);
1046}
1047