2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write X Windows system Pixmap Format %
20 % Copyright 1999-2013 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 "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/blob-private.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/colorspace-private.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/geometry.h"
55 #include "MagickCore/image.h"
56 #include "MagickCore/image-private.h"
57 #include "MagickCore/list.h"
58 #include "MagickCore/magick.h"
59 #include "MagickCore/memory_.h"
60 #include "MagickCore/monitor.h"
61 #include "MagickCore/monitor-private.h"
62 #include "MagickCore/pixel-accessor.h"
63 #include "MagickCore/quantize.h"
64 #include "MagickCore/quantum-private.h"
65 #include "MagickCore/resize.h"
66 #include "MagickCore/resource_.h"
67 #include "MagickCore/splay-tree.h"
68 #include "MagickCore/static.h"
69 #include "MagickCore/string_.h"
70 #include "MagickCore/module.h"
71 #include "MagickCore/threshold.h"
72 #include "MagickCore/utility.h"
77 static MagickBooleanType
78 WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *),
79 WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *);
82 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 % IsXPM() returns MagickTrue if the image format type, identified by the
93 % magick string, is XPM.
95 % The format of the IsXPM method is:
97 % MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
99 % A description of each parameter follows:
101 % o magick: compare image format pattern against these bytes. or
104 % o length: Specifies the length of the magick string.
107 static MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
111 if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0)
117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 % R e a d X P M I m a g e %
125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127 % ReadXPMImage() reads an X11 pixmap image file and returns it. It
128 % allocates the memory necessary for the new Image structure and returns a
129 % pointer to the new image.
131 % The format of the ReadXPMImage method is:
133 % Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
135 % A description of each parameter follows:
137 % o image_info: the image info.
139 % o exception: return any errors or warnings in this structure.
143 static int CompareXPMColor(const void *target,const void *source)
149 p=(const char *) target;
150 q=(const char *) source;
154 static char *CopyXPMColor(char *destination,const char *source,size_t length)
156 while (length-- && (*source != '\0'))
157 *destination++=(*source++);
159 return(destination-length);
162 static char *NextXPMLine(char *p)
164 assert(p != (char*)NULL);
166 if (p != (char *) NULL)
171 static inline size_t MagickMin(const size_t x,const size_t y)
178 static char *ParseXPMColor(char *color)
180 #define NumberTargets 6
193 *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
195 for (i=0; i < NumberTargets; i++)
198 for (q=targets[i]; *p != '\0'; p++)
204 if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
217 return((char *) NULL);
220 static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
224 target[MaxTextExtent],
265 assert(image_info != (const ImageInfo *) NULL);
266 assert(image_info->signature == MagickSignature);
267 if (image_info->debug != MagickFalse)
268 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
269 image_info->filename);
270 assert(exception != (ExceptionInfo *) NULL);
271 assert(exception->signature == MagickSignature);
272 image=AcquireImage(image_info,exception);
273 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
274 if (status == MagickFalse)
276 image=DestroyImageList(image);
277 return((Image *) NULL);
282 length=MaxTextExtent;
283 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
285 if (xpm_buffer != (char *) NULL)
286 while (ReadBlobString(image,p) != (char *) NULL)
288 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
290 if ((*p == '}') && (*(p+1) == ';'))
293 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
296 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
297 sizeof(*xpm_buffer));
298 if (xpm_buffer == (char *) NULL)
300 p=xpm_buffer+strlen(xpm_buffer);
302 if (xpm_buffer == (char *) NULL)
303 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");
324 Remove unquoted characters.
332 if (active != MagickFalse)
334 active=active != MagickFalse ? MagickFalse : MagickTrue;
336 if (active != MagickFalse)
341 Initialize image structure.
343 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
344 (void *(*)(void *)) NULL);
345 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
346 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->alpha_trait=BlendPixelTrait;
382 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
384 if (status == MagickFalse)
386 if (image->depth < image->colormap[j].depth)
387 image->depth=image->colormap[j].depth;
389 if (j < (ssize_t) image->colors)
390 ThrowReaderException(CorruptImageError,"CorruptImage");
392 if (image_info->ping == MagickFalse)
397 for (y=0; y < (ssize_t) image->rows; y++)
400 if (p == (char *) NULL)
402 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
403 if (r == (Quantum *) NULL)
405 for (x=0; x < (ssize_t) image->columns; x++)
407 (void) CopyXPMColor(key,p,(size_t) width);
408 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
409 if (image->storage_class == PseudoClass)
410 SetPixelIndex(image,j,r);
411 SetPixelInfoPixel(image,image->colormap+j,r);
413 r+=GetPixelChannels(image);
415 if (SyncAuthenticPixels(image,exception) == MagickFalse)
418 if (y < (ssize_t) image->rows)
419 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
422 Relinquish resources.
424 xpm_colors=DestroySplayTree(xpm_colors);
425 (void) CloseBlob(image);
426 return(GetFirstImageInList(image));
430 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
434 % R e g i s t e r X P M I m a g e %
438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
440 % RegisterXPMImage() adds attributes for the XPM image format to
441 % the list of supported formats. The attributes include the image format
442 % tag, a method to read and/or write the format, whether the format
443 % supports the saving of more than one frame to the same file or blob,
444 % whether the format supports native in-memory I/O, and a brief
445 % description of the format.
447 % The format of the RegisterXPMImage method is:
449 % size_t RegisterXPMImage(void)
452 ModuleExport size_t RegisterXPMImage(void)
457 entry=SetMagickInfo("PICON");
458 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
459 entry->encoder=(EncodeImageHandler *) WritePICONImage;
460 entry->adjoin=MagickFalse;
461 entry->description=ConstantString("Personal Icon");
462 entry->module=ConstantString("XPM");
463 (void) RegisterMagickInfo(entry);
464 entry=SetMagickInfo("PM");
465 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
466 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
467 entry->adjoin=MagickFalse;
468 entry->stealth=MagickTrue;
469 entry->description=ConstantString("X Windows system pixmap (color)");
470 entry->module=ConstantString("XPM");
471 (void) RegisterMagickInfo(entry);
472 entry=SetMagickInfo("XPM");
473 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
474 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
475 entry->magick=(IsImageFormatHandler *) IsXPM;
476 entry->adjoin=MagickFalse;
477 entry->description=ConstantString("X Windows system pixmap (color)");
478 entry->module=ConstantString("XPM");
479 (void) RegisterMagickInfo(entry);
480 return(MagickImageCoderSignature);
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
488 % U n r e g i s t e r X P M I m a g e %
492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
494 % UnregisterXPMImage() removes format registrations made by the
495 % XPM module from the list of supported formats.
497 % The format of the UnregisterXPMImage method is:
499 % UnregisterXPMImage(void)
502 ModuleExport void UnregisterXPMImage(void)
504 (void) UnregisterMagickInfo("PICON");
505 (void) UnregisterMagickInfo("PM");
506 (void) UnregisterMagickInfo("XPM");
510 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514 % W r i t e P I C O N I m a g e %
518 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
520 % WritePICONImage() writes an image to a file in the Personal Icon format.
522 % The format of the WritePICONImage method is:
524 % MagickBooleanType WritePICONImage(const ImageInfo *image_info,
525 % Image *image,ExceptionInfo *exception)
527 % A description of each parameter follows.
529 % o image_info: the image info.
531 % o image: The image.
533 % o exception: return any errors or warnings in this structure.
536 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
537 Image *image,ExceptionInfo *exception)
539 #define ColormapExtent 155
540 #define GraymapExtent 95
541 #define PiconGeometry "48x48>"
546 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
547 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
548 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
549 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
550 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
551 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
552 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
553 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
554 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
555 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
556 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
557 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
558 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
562 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
563 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
564 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
565 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
566 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
567 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
568 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
569 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
575 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
576 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
579 buffer[MaxTextExtent],
580 basename[MaxTextExtent],
582 symbol[MaxTextExtent];
604 register const Quantum
615 characters_per_pixel,
624 Open output image file.
626 assert(image_info != (const ImageInfo *) NULL);
627 assert(image_info->signature == MagickSignature);
628 assert(image != (Image *) NULL);
629 assert(image->signature == MagickSignature);
630 if (image->debug != MagickFalse)
631 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
632 assert(exception != (ExceptionInfo *) NULL);
633 assert(exception->signature == MagickSignature);
634 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
635 if (status == MagickFalse)
637 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
638 (void) TransformImageColorspace(image,sRGBColorspace,exception);
639 SetGeometry(image,&geometry);
640 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
641 &geometry.width,&geometry.height);
642 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
644 blob_info=CloneImageInfo(image_info);
645 (void) AcquireUniqueFilename(blob_info->filename);
646 if ((image_info->type != TrueColorType) &&
647 (IsImageGray(image,exception) != MagickFalse))
648 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
650 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
651 (void) RelinquishUniqueFileResource(blob_info->filename);
652 blob_info=DestroyImageInfo(blob_info);
653 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
655 quantize_info=AcquireQuantizeInfo(image_info);
656 status=RemapImage(quantize_info,picon,affinity_image,exception);
657 quantize_info=DestroyQuantizeInfo(quantize_info);
658 affinity_image=DestroyImage(affinity_image);
659 transparent=MagickFalse;
660 if (picon->storage_class == PseudoClass)
662 (void) CompressImageColormap(picon,exception);
663 if (picon->alpha_trait == BlendPixelTrait)
664 transparent=MagickTrue;
669 Convert DirectClass to PseudoClass picon.
671 if (picon->alpha_trait == BlendPixelTrait)
674 Map all the transparent pixels.
676 for (y=0; y < (ssize_t) picon->rows; y++)
678 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
679 if (q == (Quantum *) NULL)
681 for (x=0; x < (ssize_t) picon->columns; x++)
683 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
684 transparent=MagickTrue;
686 SetPixelAlpha(picon,OpaqueAlpha,q);
687 q+=GetPixelChannels(picon);
689 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
693 (void) SetImageType(picon,PaletteType,exception);
695 colors=picon->colors;
696 if (transparent != MagickFalse)
699 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
700 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
701 if (picon->colormap == (PixelInfo *) NULL)
702 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
703 for (y=0; y < (ssize_t) picon->rows; y++)
705 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
706 if (q == (Quantum *) NULL)
708 for (x=0; x < (ssize_t) picon->columns; x++)
710 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
711 SetPixelIndex(picon,picon->colors,q);
712 q+=GetPixelChannels(picon);
714 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
719 Compute the character per pixel.
721 characters_per_pixel=1;
722 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
723 characters_per_pixel++;
727 (void) WriteBlobString(image,"/* XPM */\n");
728 GetPathComponent(picon->filename,BasePath,basename);
729 (void) FormatLocaleString(buffer,MaxTextExtent,
730 "static char *%s[] = {\n",basename);
731 (void) WriteBlobString(image,buffer);
732 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
733 (void) FormatLocaleString(buffer,MaxTextExtent,
734 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
735 picon->rows,(double) colors,(double) characters_per_pixel);
736 (void) WriteBlobString(image,buffer);
737 GetPixelInfo(image,&pixel);
738 for (i=0; i < (ssize_t) colors; i++)
743 pixel=picon->colormap[i];
744 pixel.colorspace=sRGBColorspace;
746 pixel.alpha=(double) OpaqueAlpha;
747 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
748 if (transparent != MagickFalse)
750 if (i == (ssize_t) (colors-1))
751 (void) CopyMagickString(name,"grey75",MaxTextExtent);
758 for (j=1; j < (ssize_t) characters_per_pixel; j++)
760 k=((i-k)/MaxCixels) % MaxCixels;
764 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
766 (void) WriteBlobString(image,buffer);
771 (void) WriteBlobString(image,"/* pixels */\n");
772 for (y=0; y < (ssize_t) picon->rows; y++)
774 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
775 if (p == (const Quantum *) NULL)
777 (void) WriteBlobString(image,"\"");
778 for (x=0; x < (ssize_t) picon->columns; x++)
780 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
782 for (j=1; j < (ssize_t) characters_per_pixel; j++)
784 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
788 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
789 (void) WriteBlobString(image,buffer);
790 p+=GetPixelChannels(image);
792 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
793 y == (ssize_t) (picon->rows-1) ? "" : ",");
794 (void) WriteBlobString(image,buffer);
795 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
797 if (status == MagickFalse)
800 picon=DestroyImage(picon);
801 (void) WriteBlobString(image,"};\n");
802 (void) CloseBlob(image);
807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
811 % W r i t e X P M I m a g e %
815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
817 % WriteXPMImage() writes an image to a file in the X pixmap format.
819 % The format of the WriteXPMImage method is:
821 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
822 % Image *image,ExceptionInfo *exception)
824 % A description of each parameter follows.
826 % o image_info: the image info.
828 % o image: The image.
830 % o exception: return any errors or warnings in this structure.
833 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
834 ExceptionInfo *exception)
839 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
840 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
843 buffer[MaxTextExtent],
844 basename[MaxTextExtent],
846 symbol[MaxTextExtent];
854 register const Quantum
862 characters_per_pixel;
871 Open output image file.
873 assert(image_info != (const ImageInfo *) NULL);
874 assert(image_info->signature == MagickSignature);
875 assert(image != (Image *) NULL);
876 assert(image->signature == MagickSignature);
877 if (image->debug != MagickFalse)
878 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
879 assert(exception != (ExceptionInfo *) NULL);
880 assert(exception->signature == MagickSignature);
881 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
882 if (status == MagickFalse)
884 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
885 (void) TransformImageColorspace(image,sRGBColorspace,exception);
887 if (image->alpha_trait != BlendPixelTrait)
889 if ((image->storage_class == DirectClass) || (image->colors > 256))
890 (void) SetImageType(image,PaletteType,exception);
899 Identify transparent colormap index.
901 if ((image->storage_class == DirectClass) || (image->colors > 256))
902 (void) SetImageType(image,PaletteBilevelMatteType,exception);
903 for (i=0; i < (ssize_t) image->colors; i++)
904 if (image->colormap[i].alpha != OpaqueAlpha)
911 alpha=(double) TransparentAlpha-(double)
912 image->colormap[i].alpha;
913 beta=(double) TransparentAlpha-(double)
914 image->colormap[opacity].alpha;
920 (void) SetImageType(image,PaletteBilevelMatteType,exception);
921 for (i=0; i < (ssize_t) image->colors; i++)
922 if (image->colormap[i].alpha != OpaqueAlpha)
929 alpha=(Quantum) TransparentAlpha-(double)
930 image->colormap[i].alpha;
931 beta=(Quantum) TransparentAlpha-(double)
932 image->colormap[opacity].alpha;
939 image->colormap[opacity].red=image->transparent_color.red;
940 image->colormap[opacity].green=image->transparent_color.green;
941 image->colormap[opacity].blue=image->transparent_color.blue;
945 Compute the character per pixel.
947 characters_per_pixel=1;
948 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
949 characters_per_pixel++;
953 (void) WriteBlobString(image,"/* XPM */\n");
954 GetPathComponent(image->filename,BasePath,basename);
955 if (isalnum((int) ((unsigned char) *basename)) == 0)
957 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
958 (void) CopyMagickString(basename,buffer,MaxTextExtent);
960 if (isalpha((int) ((unsigned char) basename[0])) == 0)
962 for (i=1; basename[i] != '\0'; i++)
963 if (isalnum((int) ((unsigned char) basename[i])) == 0)
965 (void) FormatLocaleString(buffer,MaxTextExtent,
966 "static char *%s[] = {\n",basename);
967 (void) WriteBlobString(image,buffer);
968 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
969 (void) FormatLocaleString(buffer,MaxTextExtent,
970 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
971 image->rows,(double) image->colors,(double) characters_per_pixel);
972 (void) WriteBlobString(image,buffer);
973 GetPixelInfo(image,&pixel);
974 for (i=0; i < (ssize_t) image->colors; i++)
979 pixel=image->colormap[i];
980 pixel.colorspace=sRGBColorspace;
982 pixel.alpha=(double) OpaqueAlpha;
983 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
985 (void) CopyMagickString(name,"None",MaxTextExtent);
991 for (j=1; j < (ssize_t) characters_per_pixel; j++)
993 k=((i-k)/MaxCixels) % MaxCixels;
997 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
999 (void) WriteBlobString(image,buffer);
1004 (void) WriteBlobString(image,"/* pixels */\n");
1005 for (y=0; y < (ssize_t) image->rows; y++)
1007 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1008 if (p == (const Quantum *) NULL)
1010 (void) WriteBlobString(image,"\"");
1011 for (x=0; x < (ssize_t) image->columns; x++)
1013 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1015 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1017 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1021 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1022 (void) WriteBlobString(image,buffer);
1023 p+=GetPixelChannels(image);
1025 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1026 (y == (ssize_t) (image->rows-1) ? "" : ","));
1027 (void) WriteBlobString(image,buffer);
1028 if (image->previous == (Image *) NULL)
1030 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1032 if (status == MagickFalse)
1036 (void) WriteBlobString(image,"};\n");
1037 (void) CloseBlob(image);