2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write X Windows system Pixmap Format %
20 % Copyright 1999-2011 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],
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)
277 image=DestroyImageList(image);
278 return((Image *) NULL);
283 length=MaxTextExtent;
284 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
286 if (xpm_buffer != (char *) NULL)
287 while (ReadBlobString(image,p) != (char *) NULL)
289 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
291 if ((*p == '}') && (*(p+1) == ';'))
294 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
297 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
298 sizeof(*xpm_buffer));
299 if (xpm_buffer == (char *) NULL)
301 p=xpm_buffer+strlen(xpm_buffer);
303 if (xpm_buffer == (char *) NULL)
304 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
309 for (p=xpm_buffer; *p != '\0'; p++)
313 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
314 image->columns=columns;
316 image->colors=colors;
320 if ((count != 4) || (width > 10) || (image->columns == 0) ||
321 (image->rows == 0) || (image->colors == 0))
322 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
325 Remove unquoted characters.
333 if (active != MagickFalse)
335 active=active != MagickFalse ? MagickFalse : MagickTrue;
337 if (active != MagickFalse)
342 Initialize image structure.
344 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
345 (void *(*)(void *)) NULL);
346 if (AcquireImageColormap(image,image->colors) == MagickFalse)
347 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
351 next=NextXPMLine(xpm_buffer);
352 for (j=0; (j < (ssize_t) image->colors) && (next != (char*) NULL); j++)
356 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent));
357 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
361 (void) CopyMagickString(target,"gray",MaxTextExtent);
362 q=ParseXPMColor(p+width);
363 if (q != (char *) NULL)
365 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
367 if (next != (char *) NULL)
368 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
371 (void) CopyMagickString(target,q,MaxTextExtent);
372 q=ParseXPMColor(target);
373 if (q != (char *) NULL)
377 if (LocaleCompare(target,"none") == 0)
379 image->storage_class=DirectClass;
380 image->matte=MagickTrue;
382 status=QueryColorCompliance(target,AllCompliance,&image->colormap[j],
384 if (status == MagickFalse)
387 if (j < (ssize_t) image->colors)
388 ThrowReaderException(CorruptImageError,"CorruptImage");
390 if (image_info->ping == MagickFalse)
395 for (y=0; y < (ssize_t) image->rows; y++)
398 if (p == (char *) NULL)
400 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
401 if (r == (PixelPacket *) NULL)
403 indexes=GetAuthenticIndexQueue(image);
404 for (x=0; x < (ssize_t) image->columns; x++)
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];
414 if (SyncAuthenticPixels(image,exception) == MagickFalse)
417 if (y < (ssize_t) image->rows)
418 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
421 Relinquish resources.
423 xpm_colors=DestroySplayTree(xpm_colors);
424 (void) CloseBlob(image);
425 return(GetFirstImageInList(image));
429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433 % R e g i s t e r X P M I m a g e %
437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
446 % The format of the RegisterXPMImage method is:
448 % size_t RegisterXPMImage(void)
451 ModuleExport size_t RegisterXPMImage(void)
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);
483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487 % U n r e g i s t e r X P M I m a g e %
491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
493 % UnregisterXPMImage() removes format registrations made by the
494 % XPM module from the list of supported formats.
496 % The format of the UnregisterXPMImage method is:
498 % UnregisterXPMImage(void)
501 ModuleExport void UnregisterXPMImage(void)
503 (void) UnregisterMagickInfo("PICON");
504 (void) UnregisterMagickInfo("PM");
505 (void) UnregisterMagickInfo("XPM");
509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513 % W r i t e P I C O N I m a g e %
517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519 % WritePICONImage() writes an image to a file in the Personal Icon format.
521 % The format of the WritePICONImage method is:
523 % MagickBooleanType WritePICONImage(const ImageInfo *image_info,
526 % A description of each parameter follows.
528 % o image_info: the image info.
530 % o image: The image.
533 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
536 #define ColormapExtent 155
537 #define GraymapExtent 95
538 #define PiconGeometry "48x48>"
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,
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,
572 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
573 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
576 buffer[MaxTextExtent],
577 basename[MaxTextExtent],
579 symbol[MaxTextExtent];
604 register const IndexPacket
607 register const PixelPacket
618 characters_per_pixel,
627 Open output image file.
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)
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,
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,
652 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,
654 (void) RelinquishUniqueFileResource(blob_info->filename);
655 blob_info=DestroyImageInfo(blob_info);
656 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
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)
666 (void) CompressImageColormap(picon);
667 if (picon->matte != MagickFalse)
668 transparent=MagickTrue;
673 Convert DirectClass to PseudoClass picon.
675 if (picon->matte != MagickFalse)
678 Map all the transparent pixels.
680 for (y=0; y < (ssize_t) picon->rows; y++)
682 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
683 if (q == (PixelPacket *) NULL)
685 for (x=0; x < (ssize_t) picon->columns; x++)
687 if (q->opacity == (Quantum) TransparentOpacity)
688 transparent=MagickTrue;
690 SetOpacityPixelComponent(q,OpaqueOpacity);
693 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
697 (void) SetImageType(picon,PaletteType);
699 colors=picon->colors;
700 if (transparent != MagickFalse)
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++)
712 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
713 if (q == (PixelPacket *) NULL)
715 indexes=GetAuthenticIndexQueue(picon);
716 for (x=0; x < (ssize_t) picon->columns; x++)
718 if (q->opacity == (Quantum) TransparentOpacity)
719 SetIndexPixelComponent(indexes+x,picon->colors);
722 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
727 Compute the character per pixel.
729 characters_per_pixel=1;
730 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
731 characters_per_pixel++;
735 (void) WriteBlobString(image,"/* XPM */\n");
736 GetPathComponent(picon->filename,BasePath,basename);
737 (void) FormatLocaleString(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) FormatLocaleString(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++)
751 SetMagickPixelPacket(image,picon->colormap+i,(IndexPacket *) NULL,&pixel);
752 pixel.colorspace=RGBColorspace;
754 pixel.opacity=(MagickRealType) OpaqueOpacity;
755 (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
757 if (transparent != MagickFalse)
759 if (i == (ssize_t) (colors-1))
760 (void) CopyMagickString(name,"grey75",MaxTextExtent);
767 for (j=1; j < (ssize_t) characters_per_pixel; j++)
769 k=((i-k)/MaxCixels) % MaxCixels;
773 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
775 (void) WriteBlobString(image,buffer);
780 (void) WriteBlobString(image,"/* pixels */\n");
781 for (y=0; y < (ssize_t) picon->rows; y++)
783 p=GetVirtualPixels(picon,0,y,picon->columns,1,&picon->exception);
784 if (p == (const PixelPacket *) NULL)
786 indexes=GetVirtualIndexQueue(picon);
787 (void) WriteBlobString(image,"\"");
788 for (x=0; x < (ssize_t) picon->columns; x++)
790 k=((ssize_t) GetIndexPixelComponent(indexes+x) % MaxCixels);
792 for (j=1; j < (ssize_t) characters_per_pixel; j++)
794 k=(((int) GetIndexPixelComponent(indexes+x)-k)/MaxCixels) % MaxCixels;
798 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
799 (void) WriteBlobString(image,buffer);
801 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
802 y == (ssize_t) (picon->rows-1) ? "" : ",");
803 (void) WriteBlobString(image,buffer);
804 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
806 if (status == MagickFalse)
809 picon=DestroyImage(picon);
810 (void) WriteBlobString(image,"};\n");
811 (void) CloseBlob(image);
816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
820 % W r i t e X P M I m a g e %
824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
826 % WriteXPMImage() writes an image to a file in the X pixmap format.
828 % The format of the WriteXPMImage method is:
830 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
832 % A description of each parameter follows.
834 % o image_info: the image info.
836 % o image: The image.
839 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
844 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
845 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
848 buffer[MaxTextExtent],
849 basename[MaxTextExtent],
851 symbol[MaxTextExtent];
859 register const IndexPacket
862 register const PixelPacket
870 characters_per_pixel;
879 Open output image file.
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)
890 if (image->colorspace != RGBColorspace)
891 (void) TransformImageColorspace(image,RGBColorspace);
893 if (image->matte == MagickFalse)
895 if ((image->storage_class == DirectClass) || (image->colors > 256))
896 (void) SetImageType(image,PaletteType);
905 Identify transparent colormap index.
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)
917 alpha=(Quantum) TransparentOpacity-(MagickRealType)
918 image->colormap[i].opacity;
919 beta=(Quantum) TransparentOpacity-(MagickRealType)
920 image->colormap[opacity].opacity;
926 (void) SetImageType(image,PaletteBilevelMatteType);
927 for (i=0; i < (ssize_t) image->colors; i++)
928 if (image->colormap[i].opacity != OpaqueOpacity)
935 alpha=(Quantum) TransparentOpacity-(MagickRealType)
936 image->colormap[i].opacity;
937 beta=(Quantum) TransparentOpacity-(MagickRealType)
938 image->colormap[opacity].opacity;
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;
951 Compute the character per pixel.
953 characters_per_pixel=1;
954 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
955 characters_per_pixel++;
959 (void) WriteBlobString(image,"/* XPM */\n");
960 GetPathComponent(image->filename,BasePath,basename);
961 if (isalnum((int) ((unsigned char) *basename)) == 0)
963 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
964 (void) CopyMagickString(basename,buffer,MaxTextExtent);
966 if (isalpha((int) ((unsigned char) basename[0])) == 0)
968 for (i=1; basename[i] != '\0'; i++)
969 if (isalnum((int) ((unsigned char) basename[i])) == 0)
971 (void) FormatLocaleString(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) FormatLocaleString(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++)
985 SetMagickPixelPacket(image,image->colormap+i,(IndexPacket *) NULL,&pixel);
986 pixel.colorspace=RGBColorspace;
988 pixel.opacity=(MagickRealType) OpaqueOpacity;
989 (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
992 (void) CopyMagickString(name,"None",MaxTextExtent);
998 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1000 k=((i-k)/MaxCixels) % MaxCixels;
1004 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
1006 (void) WriteBlobString(image,buffer);
1011 (void) WriteBlobString(image,"/* pixels */\n");
1012 for (y=0; y < (ssize_t) image->rows; y++)
1014 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1015 if (p == (const PixelPacket *) NULL)
1017 indexes=GetVirtualIndexQueue(image);
1018 (void) WriteBlobString(image,"\"");
1019 for (x=0; x < (ssize_t) image->columns; x++)
1021 k=((ssize_t) GetIndexPixelComponent(indexes+x) % MaxCixels);
1023 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1025 k=(((int) GetIndexPixelComponent(indexes+x)-k)/MaxCixels) % MaxCixels;
1029 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1030 (void) WriteBlobString(image,buffer);
1032 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1033 (y == (ssize_t) (image->rows-1) ? "" : ","));
1034 (void) WriteBlobString(image,buffer);
1035 if (image->previous == (Image *) NULL)
1037 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1039 if (status == MagickFalse)
1043 (void) WriteBlobString(image,"};\n");
1044 (void) CloseBlob(image);