2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write X Windows system Pixmap Format %
20 % Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "magick/studio.h"
43 #include "magick/blob.h"
44 #include "magick/blob-private.h"
45 #include "magick/cache.h"
46 #include "magick/color.h"
47 #include "magick/color-private.h"
48 #include "magick/colormap.h"
49 #include "magick/colorspace.h"
50 #include "magick/exception.h"
51 #include "magick/exception-private.h"
52 #include "magick/geometry.h"
53 #include "magick/image.h"
54 #include "magick/image-private.h"
55 #include "magick/list.h"
56 #include "magick/magick.h"
57 #include "magick/memory_.h"
58 #include "magick/monitor.h"
59 #include "magick/monitor-private.h"
60 #include "magick/pixel-private.h"
61 #include "magick/quantize.h"
62 #include "magick/quantum-private.h"
63 #include "magick/resize.h"
64 #include "magick/resource_.h"
65 #include "magick/splay-tree.h"
66 #include "magick/static.h"
67 #include "magick/string_.h"
68 #include "magick/module.h"
69 #include "magick/threshold.h"
70 #include "magick/utility.h"
75 static MagickBooleanType
76 WritePICONImage(const ImageInfo *,Image *),
77 WriteXPMImage(const ImageInfo *,Image *);
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 % IsXPM() returns MagickTrue if the image format type, identified by the
91 % magick string, is XPM.
93 % The format of the IsXPM method is:
95 % MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
97 % A description of each parameter follows:
99 % o magick: compare image format pattern against these bytes. or
102 % o length: Specifies the length of the magick string.
105 static MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
109 if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0)
115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119 % R e a d X P M I m a g e %
123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125 % ReadXPMImage() reads an X11 pixmap image file and returns it. It
126 % allocates the memory necessary for the new Image structure and returns a
127 % pointer to the new image.
129 % The format of the ReadXPMImage method is:
131 % Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
133 % A description of each parameter follows:
135 % o image_info: the image info.
137 % o exception: return any errors or warnings in this structure.
141 static int CompareXPMColor(const void *target,const void *source)
147 p=(const char *) target;
148 q=(const char *) source;
152 static char *CopyXPMColor(char *destination,const char *source,size_t length)
154 while (length-- && (*source != '\0'))
155 *destination++=(*source++);
157 return(destination-length);
160 static char *NextXPMLine(char *p)
162 assert(p != (char*)NULL);
164 if (p != (char *) NULL)
169 static inline size_t MagickMin(const size_t x,const size_t y)
176 static char *ParseXPMColor(char *color)
178 #define NumberTargets 6
191 *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
193 for (i=0; i < NumberTargets; i++)
196 for (q=targets[i]; *p != '\0'; p++)
202 if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
215 return((char *) NULL);
218 static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
222 target[MaxTextExtent],
269 assert(image_info != (const ImageInfo *) NULL);
270 assert(image_info->signature == MagickSignature);
271 if (image_info->debug != MagickFalse)
272 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
273 image_info->filename);
274 assert(exception != (ExceptionInfo *) NULL);
275 assert(exception->signature == MagickSignature);
276 image=AcquireImage(image_info);
277 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
278 if (status == MagickFalse)
280 image=DestroyImageList(image);
281 return((Image *) NULL);
286 length=MaxTextExtent;
287 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
289 if (xpm_buffer != (char *) NULL)
290 while (ReadBlobString(image,p) != (char *) NULL)
292 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
294 if ((*p == '}') && (*(p+1) == ';'))
297 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
300 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
301 sizeof(*xpm_buffer));
302 if (xpm_buffer == (char *) NULL)
304 p=xpm_buffer+strlen(xpm_buffer);
306 if (xpm_buffer == (char *) NULL)
307 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
312 for (p=xpm_buffer; *p != '\0'; p++)
316 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
317 image->columns=columns;
319 image->colors=colors;
323 if ((count != 4) || (width > 10) || (image->columns == 0) ||
324 (image->rows == 0) || (image->colors == 0))
325 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
328 Remove unquoted characters.
337 if (active != MagickFalse)
339 active=active != MagickFalse ? MagickFalse : MagickTrue;
341 if (active != MagickFalse)
346 Initialize image structure.
348 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
349 (void *(*)(void *)) NULL);
350 if (AcquireImageColormap(image,image->colors) == MagickFalse)
351 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
356 next=NextXPMLine(xpm_buffer);
357 for (j=0; (j < (ssize_t) image->colors) && (next != (char*) NULL); j++)
361 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent));
362 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
366 (void) CopyMagickString(target,"gray",MaxTextExtent);
367 q=ParseXPMColor(p+width);
368 if (q != (char *) NULL)
370 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
372 if (next != (char *) NULL)
373 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
376 (void) CopyMagickString(target,q,MaxTextExtent);
377 q=ParseXPMColor(target);
378 if (q != (char *) NULL)
382 if (LocaleCompare(target,"none") == 0)
384 image->storage_class=DirectClass;
385 image->matte=MagickTrue;
387 if (QueryColorDatabase(target,&image->colormap[j],exception) == MagickFalse)
390 if (j < (ssize_t) image->colors)
391 ThrowReaderException(CorruptImageError,"CorruptImage");
393 if (image_info->ping == MagickFalse)
398 for (y=0; y < (ssize_t) image->rows; y++)
401 if (p == (char *) NULL)
403 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
404 if (r == (PixelPacket *) NULL)
406 indexes=GetAuthenticIndexQueue(image);
407 for (x=0; x < (ssize_t) image->columns; x++)
409 (void) CopyXPMColor(key,p,(size_t) width);
410 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
411 if (image->storage_class == PseudoClass)
412 indexes[x]=(IndexPacket) j;
413 *r=image->colormap[j];
417 if (SyncAuthenticPixels(image,exception) == MagickFalse)
420 if (y < (ssize_t) image->rows)
421 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
424 Relinquish resources.
426 xpm_colors=DestroySplayTree(xpm_colors);
427 (void) CloseBlob(image);
428 return(GetFirstImageInList(image));
432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436 % R e g i s t e r X P M I m a g e %
440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
442 % RegisterXPMImage() adds attributes for the XPM image format to
443 % the list of supported formats. The attributes include the image format
444 % tag, a method to read and/or write the format, whether the format
445 % supports the saving of more than one frame to the same file or blob,
446 % whether the format supports native in-memory I/O, and a brief
447 % description of the format.
449 % The format of the RegisterXPMImage method is:
451 % size_t RegisterXPMImage(void)
454 ModuleExport size_t RegisterXPMImage(void)
459 entry=SetMagickInfo("PICON");
460 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
461 entry->encoder=(EncodeImageHandler *) WritePICONImage;
462 entry->adjoin=MagickFalse;
463 entry->description=ConstantString("Personal Icon");
464 entry->module=ConstantString("XPM");
465 (void) RegisterMagickInfo(entry);
466 entry=SetMagickInfo("PM");
467 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
468 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
469 entry->adjoin=MagickFalse;
470 entry->stealth=MagickTrue;
471 entry->description=ConstantString("X Windows system pixmap (color)");
472 entry->module=ConstantString("XPM");
473 (void) RegisterMagickInfo(entry);
474 entry=SetMagickInfo("XPM");
475 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
476 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
477 entry->magick=(IsImageFormatHandler *) IsXPM;
478 entry->adjoin=MagickFalse;
479 entry->description=ConstantString("X Windows system pixmap (color)");
480 entry->module=ConstantString("XPM");
481 (void) RegisterMagickInfo(entry);
482 return(MagickImageCoderSignature);
486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490 % U n r e g i s t e r X P M I m a g e %
494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
496 % UnregisterXPMImage() removes format registrations made by the
497 % XPM module from the list of supported formats.
499 % The format of the UnregisterXPMImage method is:
501 % UnregisterXPMImage(void)
504 ModuleExport void UnregisterXPMImage(void)
506 (void) UnregisterMagickInfo("PICON");
507 (void) UnregisterMagickInfo("PM");
508 (void) UnregisterMagickInfo("XPM");
512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516 % W r i t e P I C O N I m a g e %
520 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522 % Procedure WritePICONImage() writes an image to a file in the Personal Icon
525 % The format of the WritePICONImage method is:
527 % MagickBooleanType WritePICONImage(const ImageInfo *image_info,
530 % A description of each parameter follows.
532 % o image_info: the image info.
534 % o image: The image.
537 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
540 #define ColormapExtent 155
541 #define GraymapExtent 95
542 #define PiconGeometry "48x48>"
547 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
548 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
549 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
550 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
551 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
552 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
553 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
554 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
555 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
556 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
557 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
558 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
559 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
563 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
564 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
565 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
566 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
567 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
568 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
569 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
570 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
576 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
577 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
580 buffer[MaxTextExtent],
581 basename[MaxTextExtent],
583 symbol[MaxTextExtent];
613 register const IndexPacket
616 register const PixelPacket
627 characters_per_pixel,
631 Open output image file.
633 assert(image_info != (const ImageInfo *) NULL);
634 assert(image_info->signature == MagickSignature);
635 assert(image != (Image *) NULL);
636 assert(image->signature == MagickSignature);
637 if (image->debug != MagickFalse)
638 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
639 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
640 if (status == MagickFalse)
642 if (image->colorspace != RGBColorspace)
643 (void) TransformImageColorspace(image,RGBColorspace);
644 SetGeometry(image,&geometry);
645 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
646 &geometry.width,&geometry.height);
647 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,1.0,
649 blob_info=CloneImageInfo(image_info);
650 (void) AcquireUniqueFilename(blob_info->filename);
651 if ((image_info->type != TrueColorType) &&
652 (IsGrayImage(image,&image->exception) != MagickFalse))
653 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,
656 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,
658 (void) RelinquishUniqueFileResource(blob_info->filename);
659 blob_info=DestroyImageInfo(blob_info);
660 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
662 quantize_info=AcquireQuantizeInfo(image_info);
663 status=RemapImage(quantize_info,picon,affinity_image);
664 quantize_info=DestroyQuantizeInfo(quantize_info);
665 affinity_image=DestroyImage(affinity_image);
666 transparent=MagickFalse;
667 exception=(&image->exception);
668 if (picon->storage_class == PseudoClass)
670 (void) CompressImageColormap(picon);
671 if (picon->matte != MagickFalse)
672 transparent=MagickTrue;
677 Convert DirectClass to PseudoClass picon.
679 if (picon->matte != MagickFalse)
682 Map all the transparent pixels.
684 for (y=0; y < (ssize_t) picon->rows; y++)
686 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
687 if (q == (PixelPacket *) NULL)
689 for (x=0; x < (ssize_t) picon->columns; x++)
691 if (q->opacity == (Quantum) TransparentOpacity)
692 transparent=MagickTrue;
694 SetOpacityPixelComponent(q,OpaqueOpacity);
697 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
701 (void) SetImageType(picon,PaletteType);
703 colors=picon->colors;
704 if (transparent != MagickFalse)
710 picon->colormap=(PixelPacket *) ResizeQuantumMemory((void **)
711 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
712 if (picon->colormap == (PixelPacket *) NULL)
713 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
714 for (y=0; y < (ssize_t) picon->rows; y++)
716 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
717 if (q == (PixelPacket *) NULL)
719 indexes=GetAuthenticIndexQueue(picon);
720 for (x=0; x < (ssize_t) picon->columns; x++)
722 if (q->opacity == (Quantum) TransparentOpacity)
723 indexes[x]=(IndexPacket) picon->colors;
726 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
731 Compute the character per pixel.
733 characters_per_pixel=1;
734 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
735 characters_per_pixel++;
739 (void) WriteBlobString(image,"/* XPM */\n");
740 GetPathComponent(picon->filename,BasePath,basename);
741 (void) FormatMagickString(buffer,MaxTextExtent,
742 "static char *%s[] = {\n",basename);
743 (void) WriteBlobString(image,buffer);
744 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
745 (void) FormatMagickString(buffer,MaxTextExtent,"\"%lu %lu %lu %lu\",\n",
746 (unsigned long) picon->columns,(unsigned long) picon->rows,(unsigned long)
747 colors,(unsigned long) characters_per_pixel);
748 (void) WriteBlobString(image,buffer);
749 GetMagickPixelPacket(image,&pixel);
750 for (i=0; i < (ssize_t) colors; i++)
755 SetMagickPixelPacket(image,picon->colormap+i,(IndexPacket *) NULL,&pixel);
756 pixel.colorspace=RGBColorspace;
758 pixel.opacity=(MagickRealType) OpaqueOpacity;
759 (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
761 if (transparent != MagickFalse)
763 if (i == (ssize_t) (colors-1))
764 (void) CopyMagickString(name,"grey75",MaxTextExtent);
771 for (j=1; j < (ssize_t) characters_per_pixel; j++)
773 k=((i-k)/MaxCixels) % MaxCixels;
777 (void) FormatMagickString(buffer,MaxTextExtent,"\"%s c %s\",\n",
779 (void) WriteBlobString(image,buffer);
784 (void) WriteBlobString(image,"/* pixels */\n");
785 for (y=0; y < (ssize_t) picon->rows; y++)
787 p=GetVirtualPixels(picon,0,y,picon->columns,1,&picon->exception);
788 if (p == (const PixelPacket *) NULL)
790 indexes=GetVirtualIndexQueue(picon);
791 (void) WriteBlobString(image,"\"");
792 for (x=0; x < (ssize_t) picon->columns; x++)
794 k=((ssize_t) indexes[x] % MaxCixels);
796 for (j=1; j < (ssize_t) characters_per_pixel; j++)
798 k=(((int) indexes[x]-k)/MaxCixels) % MaxCixels;
802 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
803 (void) WriteBlobString(image,buffer);
805 (void) FormatMagickString(buffer,MaxTextExtent,"\"%s\n",
806 y == (ssize_t) (picon->rows-1) ? "" : ",");
807 (void) WriteBlobString(image,buffer);
808 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
810 if (status == MagickFalse)
813 picon=DestroyImage(picon);
814 (void) WriteBlobString(image,"};\n");
815 (void) CloseBlob(image);
820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
824 % W r i t e X P M I m a g e %
828 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
830 % Procedure WriteXPMImage() writes an image to a file in the X pixmap format.
832 % The format of the WriteXPMImage method is:
834 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
836 % A description of each parameter follows.
838 % o image_info: the image info.
840 % o image: The image.
844 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
849 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
850 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
853 buffer[MaxTextExtent],
854 basename[MaxTextExtent],
856 symbol[MaxTextExtent];
870 register const IndexPacket
873 register const PixelPacket
881 characters_per_pixel;
884 Open output image file.
886 assert(image_info != (const ImageInfo *) NULL);
887 assert(image_info->signature == MagickSignature);
888 assert(image != (Image *) NULL);
889 assert(image->signature == MagickSignature);
890 if (image->debug != MagickFalse)
891 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
892 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
893 if (status == MagickFalse)
895 if (image->colorspace != RGBColorspace)
896 (void) TransformImageColorspace(image,RGBColorspace);
898 if (image->matte == MagickFalse)
900 if ((image->storage_class == DirectClass) || (image->colors > 256))
901 (void) SetImageType(image,PaletteType);
910 Identify transparent colormap index.
912 if ((image->storage_class == DirectClass) || (image->colors > 256))
913 (void) SetImageType(image,PaletteBilevelMatteType);
914 for (i=0; i < (ssize_t) image->colors; i++)
915 if (image->colormap[i].opacity != OpaqueOpacity)
922 alpha=(Quantum) TransparentOpacity-(MagickRealType)
923 image->colormap[i].opacity;
924 beta=(Quantum) TransparentOpacity-(MagickRealType)
925 image->colormap[opacity].opacity;
931 (void) SetImageType(image,PaletteBilevelMatteType);
932 for (i=0; i < (ssize_t) image->colors; i++)
933 if (image->colormap[i].opacity != OpaqueOpacity)
940 alpha=(Quantum) TransparentOpacity-(MagickRealType)
941 image->colormap[i].opacity;
942 beta=(Quantum) TransparentOpacity-(MagickRealType)
943 image->colormap[opacity].opacity;
950 image->colormap[opacity].red=image->transparent_color.red;
951 image->colormap[opacity].green=image->transparent_color.green;
952 image->colormap[opacity].blue=image->transparent_color.blue;
956 Compute the character per pixel.
958 characters_per_pixel=1;
959 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
960 characters_per_pixel++;
964 (void) WriteBlobString(image,"/* XPM */\n");
965 GetPathComponent(image->filename,BasePath,basename);
966 if (isalnum((int) ((unsigned char) *basename)) == 0)
968 (void) FormatMagickString(buffer,MaxTextExtent,"xpm_%s",basename);
969 (void) CopyMagickString(basename,buffer,MaxTextExtent);
971 for (i=0; basename[i] != '\0'; i++)
972 if (isalpha((int) ((unsigned char) basename[i])) == 0)
974 (void) FormatMagickString(buffer,MaxTextExtent,
975 "static char *%s[] = {\n",basename);
976 (void) WriteBlobString(image,buffer);
977 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
978 (void) FormatMagickString(buffer,MaxTextExtent,"\"%lu %lu %lu %lu\",\n",
979 (unsigned long) image->columns,(unsigned long) image->rows,(unsigned long)
980 image->colors,(unsigned long) characters_per_pixel);
981 (void) WriteBlobString(image,buffer);
982 GetMagickPixelPacket(image,&pixel);
983 for (i=0; i < (ssize_t) image->colors; i++)
988 SetMagickPixelPacket(image,image->colormap+i,(IndexPacket *) NULL,&pixel);
989 pixel.colorspace=RGBColorspace;
991 pixel.opacity=(MagickRealType) OpaqueOpacity;
992 (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
995 (void) CopyMagickString(name,"None",MaxTextExtent);
1001 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1003 k=((i-k)/MaxCixels) % MaxCixels;
1007 (void) FormatMagickString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
1009 (void) WriteBlobString(image,buffer);
1014 (void) WriteBlobString(image,"/* pixels */\n");
1015 for (y=0; y < (ssize_t) image->rows; y++)
1017 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1018 if (p == (const PixelPacket *) NULL)
1020 indexes=GetVirtualIndexQueue(image);
1021 (void) WriteBlobString(image,"\"");
1022 for (x=0; x < (ssize_t) image->columns; x++)
1024 k=((ssize_t) indexes[x] % MaxCixels);
1026 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1028 k=(((int) indexes[x]-k)/MaxCixels) % MaxCixels;
1032 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1033 (void) WriteBlobString(image,buffer);
1035 (void) FormatMagickString(buffer,MaxTextExtent,"\"%s\n",
1036 (y == (ssize_t) (image->rows-1) ? "" : ","));
1037 (void) WriteBlobString(image,buffer);
1038 if (image->previous == (Image *) NULL)
1040 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1042 if (status == MagickFalse)
1046 (void) WriteBlobString(image,"};\n");
1047 (void) CloseBlob(image);