2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write X Windows system Pixmap Format %
20 % Copyright 1999-2015 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 size_t CopyXPMColor(char *destination,const char *source,size_t length)
160 while (length-- && (*p != '\0'))
161 *destination++=(*p++);
163 return((size_t) (p-source));
166 static char *NextXPMLine(char *p)
168 assert(p != (char*)NULL);
170 if (p != (char *) NULL)
175 static inline size_t MagickMin(const size_t x,const size_t y)
182 static char *ParseXPMColor(char *color,MagickBooleanType search_start)
184 #define NumberTargets 6
197 *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
199 if (search_start != MagickFalse)
201 for (i=0; i < NumberTargets; i++)
204 for (q=targets[i]; *p != '\0'; p++)
210 if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
223 return((char *) NULL);
227 for (p=color+1; *p != '\0'; p++)
231 if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
233 if (isspace((int) ((unsigned char) (*p))) != 0)
235 for (i=0; i < NumberTargets; i++)
237 if (*p == *targets[i] && *(p+1) == *(targets[i]+1))
245 static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
250 target[MaxTextExtent],
291 assert(image_info != (const ImageInfo *) NULL);
292 assert(image_info->signature == MagickSignature);
293 if (image_info->debug != MagickFalse)
294 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
295 image_info->filename);
296 assert(exception != (ExceptionInfo *) NULL);
297 assert(exception->signature == MagickSignature);
298 image=AcquireImage(image_info,exception);
299 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
300 if (status == MagickFalse)
302 image=DestroyImageList(image);
303 return((Image *) NULL);
308 length=MaxTextExtent;
309 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
310 if (xpm_buffer == (char *) NULL)
311 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
314 while (ReadBlobString(image,p) != (char *) NULL)
316 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
318 if ((*p == '}') && (*(p+1) == ';'))
321 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
324 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
325 sizeof(*xpm_buffer));
326 if (xpm_buffer == (char *) NULL)
328 p=xpm_buffer+strlen(xpm_buffer);
330 if (xpm_buffer == (char *) NULL)
331 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
337 for (p=xpm_buffer; *p != '\0'; p++)
341 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
342 image->columns=columns;
344 image->colors=colors;
348 if ((count != 4) || (width > 10) || (image->columns == 0) ||
349 (image->rows == 0) || (image->colors == 0))
350 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
352 Remove unquoted characters.
360 if (active != MagickFalse)
362 active=active != MagickFalse ? MagickFalse : MagickTrue;
364 if (active != MagickFalse)
369 Initialize image structure.
371 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
372 (void *(*)(void *)) NULL);
373 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
374 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
379 next=NextXPMLine(xpm_buffer);
380 for (j=0; (j < (ssize_t) image->colors) && (next != (char*) NULL); j++)
384 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent));
385 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
389 (void) CopyMagickString(target,"gray",MaxTextExtent);
390 q=ParseXPMColor(p+width,MagickTrue);
391 if (q != (char *) NULL)
393 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
395 if (next != (char *) NULL)
396 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
399 (void) CopyMagickString(target,q,MaxTextExtent);
400 q=ParseXPMColor(target,MagickFalse);
401 if (q != (char *) NULL)
405 grey=strstr(target,"grey");
406 if (grey != (char *) NULL)
408 if (LocaleCompare(target,"none") == 0)
410 image->storage_class=DirectClass;
411 image->alpha_trait=BlendPixelTrait;
413 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
415 if (status == MagickFalse)
417 if (image->depth < image->colormap[j].depth)
418 image->depth=image->colormap[j].depth;
420 if (j < (ssize_t) image->colors)
421 ThrowReaderException(CorruptImageError,"CorruptImage");
423 if (image_info->ping == MagickFalse)
428 for (y=0; y < (ssize_t) image->rows; y++)
431 if (p == (char *) NULL)
433 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
434 if (r == (Quantum *) NULL)
436 for (x=0; x < (ssize_t) image->columns; x++)
438 p+=CopyXPMColor(key,p,MagickMin(width,MaxTextExtent));
439 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
440 if (image->storage_class == PseudoClass)
441 SetPixelIndex(image,(Quantum) j,r);
442 SetPixelInfoPixel(image,image->colormap+j,r);
443 r+=GetPixelChannels(image);
445 if (SyncAuthenticPixels(image,exception) == MagickFalse)
448 if (y < (ssize_t) image->rows)
449 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
452 Relinquish resources.
454 xpm_colors=DestroySplayTree(xpm_colors);
455 (void) CloseBlob(image);
456 return(GetFirstImageInList(image));
460 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
464 % R e g i s t e r X P M I m a g e %
468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470 % RegisterXPMImage() adds attributes for the XPM image format to
471 % the list of supported formats. The attributes include the image format
472 % tag, a method to read and/or write the format, whether the format
473 % supports the saving of more than one frame to the same file or blob,
474 % whether the format supports native in-memory I/O, and a brief
475 % description of the format.
477 % The format of the RegisterXPMImage method is:
479 % size_t RegisterXPMImage(void)
482 ModuleExport size_t RegisterXPMImage(void)
487 entry=SetMagickInfo("PICON");
488 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
489 entry->encoder=(EncodeImageHandler *) WritePICONImage;
490 entry->adjoin=MagickFalse;
491 entry->description=ConstantString("Personal Icon");
492 entry->module=ConstantString("XPM");
493 (void) RegisterMagickInfo(entry);
494 entry=SetMagickInfo("PM");
495 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
496 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
497 entry->adjoin=MagickFalse;
498 entry->stealth=MagickTrue;
499 entry->description=ConstantString("X Windows system pixmap (color)");
500 entry->module=ConstantString("XPM");
501 (void) RegisterMagickInfo(entry);
502 entry=SetMagickInfo("XPM");
503 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
504 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
505 entry->magick=(IsImageFormatHandler *) IsXPM;
506 entry->adjoin=MagickFalse;
507 entry->description=ConstantString("X Windows system pixmap (color)");
508 entry->module=ConstantString("XPM");
509 (void) RegisterMagickInfo(entry);
510 return(MagickImageCoderSignature);
514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
518 % U n r e g i s t e r X P M I m a g e %
522 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
524 % UnregisterXPMImage() removes format registrations made by the
525 % XPM module from the list of supported formats.
527 % The format of the UnregisterXPMImage method is:
529 % UnregisterXPMImage(void)
532 ModuleExport void UnregisterXPMImage(void)
534 (void) UnregisterMagickInfo("PICON");
535 (void) UnregisterMagickInfo("PM");
536 (void) UnregisterMagickInfo("XPM");
540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
544 % W r i t e P I C O N I m a g e %
548 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
550 % WritePICONImage() writes an image to a file in the Personal Icon format.
552 % The format of the WritePICONImage method is:
554 % MagickBooleanType WritePICONImage(const ImageInfo *image_info,
555 % Image *image,ExceptionInfo *exception)
557 % A description of each parameter follows.
559 % o image_info: the image info.
561 % o image: The image.
563 % o exception: return any errors or warnings in this structure.
566 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
567 Image *image,ExceptionInfo *exception)
569 #define ColormapExtent 155
570 #define GraymapExtent 95
571 #define PiconGeometry "48x48>"
576 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
577 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
578 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
579 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
580 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
581 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
582 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
583 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
584 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
585 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
586 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
587 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
588 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
592 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
593 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
594 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
595 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
596 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
597 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
598 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
599 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
605 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
606 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
609 buffer[MaxTextExtent],
610 basename[MaxTextExtent],
612 symbol[MaxTextExtent];
634 register const Quantum
645 characters_per_pixel,
654 Open output image file.
656 assert(image_info != (const ImageInfo *) NULL);
657 assert(image_info->signature == MagickSignature);
658 assert(image != (Image *) NULL);
659 assert(image->signature == MagickSignature);
660 if (image->debug != MagickFalse)
661 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
662 assert(exception != (ExceptionInfo *) NULL);
663 assert(exception->signature == MagickSignature);
664 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
665 if (status == MagickFalse)
667 (void) TransformImageColorspace(image,sRGBColorspace,exception);
668 SetGeometry(image,&geometry);
669 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
670 &geometry.width,&geometry.height);
671 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
673 blob_info=CloneImageInfo(image_info);
674 (void) AcquireUniqueFilename(blob_info->filename);
675 if ((image_info->type != TrueColorType) &&
676 (IsImageGray(image,exception) != MagickFalse))
677 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
679 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
680 (void) RelinquishUniqueFileResource(blob_info->filename);
681 blob_info=DestroyImageInfo(blob_info);
682 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
684 quantize_info=AcquireQuantizeInfo(image_info);
685 status=RemapImage(quantize_info,picon,affinity_image,exception);
686 quantize_info=DestroyQuantizeInfo(quantize_info);
687 affinity_image=DestroyImage(affinity_image);
688 transparent=MagickFalse;
689 if (picon->storage_class == PseudoClass)
691 (void) CompressImageColormap(picon,exception);
692 if (picon->alpha_trait == BlendPixelTrait)
693 transparent=MagickTrue;
698 Convert DirectClass to PseudoClass picon.
700 if (picon->alpha_trait == BlendPixelTrait)
703 Map all the transparent pixels.
705 for (y=0; y < (ssize_t) picon->rows; y++)
707 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
708 if (q == (Quantum *) NULL)
710 for (x=0; x < (ssize_t) picon->columns; x++)
712 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
713 transparent=MagickTrue;
715 SetPixelAlpha(picon,OpaqueAlpha,q);
716 q+=GetPixelChannels(picon);
718 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
722 (void) SetImageType(picon,PaletteType,exception);
724 colors=picon->colors;
725 if (transparent != MagickFalse)
728 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
729 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
730 if (picon->colormap == (PixelInfo *) NULL)
731 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
732 for (y=0; y < (ssize_t) picon->rows; y++)
734 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
735 if (q == (Quantum *) NULL)
737 for (x=0; x < (ssize_t) picon->columns; x++)
739 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
740 SetPixelIndex(picon,(Quantum) picon->colors,q);
741 q+=GetPixelChannels(picon);
743 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
748 Compute the character per pixel.
750 characters_per_pixel=1;
751 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
752 characters_per_pixel++;
756 (void) WriteBlobString(image,"/* XPM */\n");
757 GetPathComponent(picon->filename,BasePath,basename);
758 (void) FormatLocaleString(buffer,MaxTextExtent,
759 "static char *%s[] = {\n",basename);
760 (void) WriteBlobString(image,buffer);
761 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
762 (void) FormatLocaleString(buffer,MaxTextExtent,
763 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
764 picon->rows,(double) colors,(double) characters_per_pixel);
765 (void) WriteBlobString(image,buffer);
766 GetPixelInfo(image,&pixel);
767 for (i=0; i < (ssize_t) colors; i++)
772 pixel=picon->colormap[i];
773 pixel.colorspace=sRGBColorspace;
775 pixel.alpha=(double) OpaqueAlpha;
776 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
777 if (transparent != MagickFalse)
779 if (i == (ssize_t) (colors-1))
780 (void) CopyMagickString(name,"grey75",MaxTextExtent);
787 for (j=1; j < (ssize_t) characters_per_pixel; j++)
789 k=((i-k)/MaxCixels) % MaxCixels;
793 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
795 (void) WriteBlobString(image,buffer);
800 (void) WriteBlobString(image,"/* pixels */\n");
801 for (y=0; y < (ssize_t) picon->rows; y++)
803 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
804 if (p == (const Quantum *) NULL)
806 (void) WriteBlobString(image,"\"");
807 for (x=0; x < (ssize_t) picon->columns; x++)
809 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
811 for (j=1; j < (ssize_t) characters_per_pixel; j++)
813 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
817 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
818 (void) WriteBlobString(image,buffer);
819 p+=GetPixelChannels(image);
821 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
822 y == (ssize_t) (picon->rows-1) ? "" : ",");
823 (void) WriteBlobString(image,buffer);
824 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
826 if (status == MagickFalse)
829 picon=DestroyImage(picon);
830 (void) WriteBlobString(image,"};\n");
831 (void) CloseBlob(image);
836 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
840 % W r i t e X P M I m a g e %
844 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846 % WriteXPMImage() writes an image to a file in the X pixmap format.
848 % The format of the WriteXPMImage method is:
850 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
851 % Image *image,ExceptionInfo *exception)
853 % A description of each parameter follows.
855 % o image_info: the image info.
857 % o image: The image.
859 % o exception: return any errors or warnings in this structure.
862 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
863 ExceptionInfo *exception)
868 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
869 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
872 buffer[MaxTextExtent],
873 basename[MaxTextExtent],
875 symbol[MaxTextExtent];
883 register const Quantum
891 characters_per_pixel;
900 Open output image file.
902 assert(image_info != (const ImageInfo *) NULL);
903 assert(image_info->signature == MagickSignature);
904 assert(image != (Image *) NULL);
905 assert(image->signature == MagickSignature);
906 if (image->debug != MagickFalse)
907 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
908 assert(exception != (ExceptionInfo *) NULL);
909 assert(exception->signature == MagickSignature);
910 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
911 if (status == MagickFalse)
913 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
914 (void) TransformImageColorspace(image,sRGBColorspace,exception);
916 if (image->alpha_trait != BlendPixelTrait)
918 if ((image->storage_class == DirectClass) || (image->colors > 256))
919 (void) SetImageType(image,PaletteType,exception);
928 Identify transparent colormap index.
930 if ((image->storage_class == DirectClass) || (image->colors > 256))
931 (void) SetImageType(image,PaletteBilevelMatteType,exception);
932 for (i=0; i < (ssize_t) image->colors; i++)
933 if (image->colormap[i].alpha != OpaqueAlpha)
940 alpha=(double) TransparentAlpha-(double)
941 image->colormap[i].alpha;
942 beta=(double) TransparentAlpha-(double)
943 image->colormap[opacity].alpha;
949 (void) SetImageType(image,PaletteBilevelMatteType,exception);
950 for (i=0; i < (ssize_t) image->colors; i++)
951 if (image->colormap[i].alpha != OpaqueAlpha)
958 alpha=(Quantum) TransparentAlpha-(double)
959 image->colormap[i].alpha;
960 beta=(Quantum) TransparentAlpha-(double)
961 image->colormap[opacity].alpha;
968 image->colormap[opacity].red=image->transparent_color.red;
969 image->colormap[opacity].green=image->transparent_color.green;
970 image->colormap[opacity].blue=image->transparent_color.blue;
974 Compute the character per pixel.
976 characters_per_pixel=1;
977 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
978 characters_per_pixel++;
982 (void) WriteBlobString(image,"/* XPM */\n");
983 GetPathComponent(image->filename,BasePath,basename);
984 if (isalnum((int) ((unsigned char) *basename)) == 0)
986 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
987 (void) CopyMagickString(basename,buffer,MaxTextExtent);
989 if (isalpha((int) ((unsigned char) basename[0])) == 0)
991 for (i=1; basename[i] != '\0'; i++)
992 if (isalnum((int) ((unsigned char) basename[i])) == 0)
994 (void) FormatLocaleString(buffer,MaxTextExtent,
995 "static char *%s[] = {\n",basename);
996 (void) WriteBlobString(image,buffer);
997 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
998 (void) FormatLocaleString(buffer,MaxTextExtent,
999 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
1000 image->rows,(double) image->colors,(double) characters_per_pixel);
1001 (void) WriteBlobString(image,buffer);
1002 GetPixelInfo(image,&pixel);
1003 for (i=0; i < (ssize_t) image->colors; i++)
1008 pixel=image->colormap[i];
1009 pixel.colorspace=sRGBColorspace;
1011 pixel.alpha=(double) OpaqueAlpha;
1012 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
1014 (void) CopyMagickString(name,"None",MaxTextExtent);
1020 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1022 k=((i-k)/MaxCixels) % MaxCixels;
1026 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
1028 (void) WriteBlobString(image,buffer);
1033 (void) WriteBlobString(image,"/* pixels */\n");
1034 for (y=0; y < (ssize_t) image->rows; y++)
1036 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1037 if (p == (const Quantum *) NULL)
1039 (void) WriteBlobString(image,"\"");
1040 for (x=0; x < (ssize_t) image->columns; x++)
1042 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1044 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1046 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1050 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1051 (void) WriteBlobString(image,buffer);
1052 p+=GetPixelChannels(image);
1054 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1055 (y == (ssize_t) (image->rows-1) ? "" : ","));
1056 (void) WriteBlobString(image,buffer);
1057 if (image->previous == (Image *) NULL)
1059 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1061 if (status == MagickFalse)
1065 (void) WriteBlobString(image,"};\n");
1066 (void) CloseBlob(image);