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);
225 for (p=color+1; *p != '\0'; p++)
229 if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
231 if (isspace((int) ((unsigned char) (*p))) != 0)
233 for (i=0; i < NumberTargets; i++)
235 if ((*p == *targets[i]) && (*(p+1) == *(targets[i]+1)))
242 static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
247 target[MaxTextExtent],
288 assert(image_info != (const ImageInfo *) NULL);
289 assert(image_info->signature == MagickSignature);
290 if (image_info->debug != MagickFalse)
291 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
292 image_info->filename);
293 assert(exception != (ExceptionInfo *) NULL);
294 assert(exception->signature == MagickSignature);
295 image=AcquireImage(image_info,exception);
296 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
297 if (status == MagickFalse)
299 image=DestroyImageList(image);
300 return((Image *) NULL);
305 length=MaxTextExtent;
306 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
307 if (xpm_buffer == (char *) NULL)
308 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
311 while (ReadBlobString(image,p) != (char *) NULL)
313 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
315 if ((*p == '}') && (*(p+1) == ';'))
318 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
321 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
322 sizeof(*xpm_buffer));
323 if (xpm_buffer == (char *) NULL)
325 p=xpm_buffer+strlen(xpm_buffer);
327 if (xpm_buffer == (char *) NULL)
328 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
334 for (p=xpm_buffer; *p != '\0'; p++)
338 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
339 image->columns=columns;
341 image->colors=colors;
345 if ((count != 4) || (width > 10) || (image->columns == 0) ||
346 (image->rows == 0) || (image->colors == 0))
347 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
349 Remove unquoted characters.
357 if (active != MagickFalse)
359 active=active != MagickFalse ? MagickFalse : MagickTrue;
361 if (active != MagickFalse)
366 Initialize image structure.
368 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
369 (void *(*)(void *)) NULL);
370 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
371 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
376 next=NextXPMLine(xpm_buffer);
377 for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
381 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent));
382 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
386 (void) CopyMagickString(target,"gray",MaxTextExtent);
387 q=ParseXPMColor(p+width,MagickTrue);
388 if (q != (char *) NULL)
390 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
394 if (next != (char *) NULL)
395 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
398 (void) CopyMagickString(target,q,MaxTextExtent);
399 q=ParseXPMColor(target,MagickFalse);
400 if (q != (char *) NULL)
404 grey=strstr(target,"grey");
405 if (grey != (char *) NULL)
407 if (LocaleCompare(target,"none") == 0)
409 image->storage_class=DirectClass;
410 image->alpha_trait=BlendPixelTrait;
412 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
414 if (status == MagickFalse)
416 if (image->depth < image->colormap[j].depth)
417 image->depth=image->colormap[j].depth;
419 if (j < (ssize_t) image->colors)
420 ThrowReaderException(CorruptImageError,"CorruptImage");
422 if (image_info->ping == MagickFalse)
427 for (y=0; y < (ssize_t) image->rows; y++)
430 if (p == (char *) NULL)
432 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
433 if (r == (Quantum *) NULL)
435 for (x=0; x < (ssize_t) image->columns; x++)
437 p+=CopyXPMColor(key,p,MagickMin(width,MaxTextExtent));
438 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
439 if (image->storage_class == PseudoClass)
440 SetPixelIndex(image,(Quantum) j,r);
441 SetPixelInfoPixel(image,image->colormap+j,r);
442 r+=GetPixelChannels(image);
444 if (SyncAuthenticPixels(image,exception) == MagickFalse)
447 if (y < (ssize_t) image->rows)
448 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
451 Relinquish resources.
453 xpm_colors=DestroySplayTree(xpm_colors);
454 (void) CloseBlob(image);
455 return(GetFirstImageInList(image));
459 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 % R e g i s t e r X P M I m a g e %
467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469 % RegisterXPMImage() adds attributes for the XPM image format to
470 % the list of supported formats. The attributes include the image format
471 % tag, a method to read and/or write the format, whether the format
472 % supports the saving of more than one frame to the same file or blob,
473 % whether the format supports native in-memory I/O, and a brief
474 % description of the format.
476 % The format of the RegisterXPMImage method is:
478 % size_t RegisterXPMImage(void)
481 ModuleExport size_t RegisterXPMImage(void)
486 entry=SetMagickInfo("PICON");
487 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
488 entry->encoder=(EncodeImageHandler *) WritePICONImage;
489 entry->adjoin=MagickFalse;
490 entry->description=ConstantString("Personal Icon");
491 entry->module=ConstantString("XPM");
492 (void) RegisterMagickInfo(entry);
493 entry=SetMagickInfo("PM");
494 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
495 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
496 entry->adjoin=MagickFalse;
497 entry->stealth=MagickTrue;
498 entry->description=ConstantString("X Windows system pixmap (color)");
499 entry->module=ConstantString("XPM");
500 (void) RegisterMagickInfo(entry);
501 entry=SetMagickInfo("XPM");
502 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
503 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
504 entry->magick=(IsImageFormatHandler *) IsXPM;
505 entry->adjoin=MagickFalse;
506 entry->description=ConstantString("X Windows system pixmap (color)");
507 entry->module=ConstantString("XPM");
508 (void) RegisterMagickInfo(entry);
509 return(MagickImageCoderSignature);
513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
517 % U n r e g i s t e r X P M I m a g e %
521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
523 % UnregisterXPMImage() removes format registrations made by the
524 % XPM module from the list of supported formats.
526 % The format of the UnregisterXPMImage method is:
528 % UnregisterXPMImage(void)
531 ModuleExport void UnregisterXPMImage(void)
533 (void) UnregisterMagickInfo("PICON");
534 (void) UnregisterMagickInfo("PM");
535 (void) UnregisterMagickInfo("XPM");
539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
543 % W r i t e P I C O N I m a g e %
547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
549 % WritePICONImage() writes an image to a file in the Personal Icon format.
551 % The format of the WritePICONImage method is:
553 % MagickBooleanType WritePICONImage(const ImageInfo *image_info,
554 % Image *image,ExceptionInfo *exception)
556 % A description of each parameter follows.
558 % o image_info: the image info.
560 % o image: The image.
562 % o exception: return any errors or warnings in this structure.
565 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
566 Image *image,ExceptionInfo *exception)
568 #define ColormapExtent 155
569 #define GraymapExtent 95
570 #define PiconGeometry "48x48>"
575 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
576 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
577 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
578 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
579 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
580 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
581 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
582 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
583 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
584 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
585 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
586 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
587 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
591 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
592 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
593 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
594 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
595 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
596 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
597 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
598 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
604 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
605 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
608 buffer[MaxTextExtent],
609 basename[MaxTextExtent],
611 symbol[MaxTextExtent];
633 register const Quantum
644 characters_per_pixel,
653 Open output image file.
655 assert(image_info != (const ImageInfo *) NULL);
656 assert(image_info->signature == MagickSignature);
657 assert(image != (Image *) NULL);
658 assert(image->signature == MagickSignature);
659 if (image->debug != MagickFalse)
660 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
661 assert(exception != (ExceptionInfo *) NULL);
662 assert(exception->signature == MagickSignature);
663 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
664 if (status == MagickFalse)
666 (void) TransformImageColorspace(image,sRGBColorspace,exception);
667 SetGeometry(image,&geometry);
668 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
669 &geometry.width,&geometry.height);
670 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
672 blob_info=CloneImageInfo(image_info);
673 (void) AcquireUniqueFilename(blob_info->filename);
674 if ((image_info->type != TrueColorType) &&
675 (IsImageGray(image,exception) != MagickFalse))
676 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
678 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
679 (void) RelinquishUniqueFileResource(blob_info->filename);
680 blob_info=DestroyImageInfo(blob_info);
681 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
683 quantize_info=AcquireQuantizeInfo(image_info);
684 status=RemapImage(quantize_info,picon,affinity_image,exception);
685 quantize_info=DestroyQuantizeInfo(quantize_info);
686 affinity_image=DestroyImage(affinity_image);
687 transparent=MagickFalse;
688 if (picon->storage_class == PseudoClass)
690 (void) CompressImageColormap(picon,exception);
691 if (picon->alpha_trait == BlendPixelTrait)
692 transparent=MagickTrue;
697 Convert DirectClass to PseudoClass picon.
699 if (picon->alpha_trait == BlendPixelTrait)
702 Map all the transparent pixels.
704 for (y=0; y < (ssize_t) picon->rows; y++)
706 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
707 if (q == (Quantum *) NULL)
709 for (x=0; x < (ssize_t) picon->columns; x++)
711 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
712 transparent=MagickTrue;
714 SetPixelAlpha(picon,OpaqueAlpha,q);
715 q+=GetPixelChannels(picon);
717 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
721 (void) SetImageType(picon,PaletteType,exception);
723 colors=picon->colors;
724 if (transparent != MagickFalse)
727 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
728 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
729 if (picon->colormap == (PixelInfo *) NULL)
730 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
731 for (y=0; y < (ssize_t) picon->rows; y++)
733 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
734 if (q == (Quantum *) NULL)
736 for (x=0; x < (ssize_t) picon->columns; x++)
738 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
739 SetPixelIndex(picon,(Quantum) picon->colors,q);
740 q+=GetPixelChannels(picon);
742 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
747 Compute the character per pixel.
749 characters_per_pixel=1;
750 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
751 characters_per_pixel++;
755 (void) WriteBlobString(image,"/* XPM */\n");
756 GetPathComponent(picon->filename,BasePath,basename);
757 (void) FormatLocaleString(buffer,MaxTextExtent,
758 "static char *%s[] = {\n",basename);
759 (void) WriteBlobString(image,buffer);
760 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
761 (void) FormatLocaleString(buffer,MaxTextExtent,
762 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
763 picon->rows,(double) colors,(double) characters_per_pixel);
764 (void) WriteBlobString(image,buffer);
765 GetPixelInfo(image,&pixel);
766 for (i=0; i < (ssize_t) colors; i++)
771 pixel=picon->colormap[i];
772 pixel.colorspace=sRGBColorspace;
774 pixel.alpha=(double) OpaqueAlpha;
775 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
776 if (transparent != MagickFalse)
778 if (i == (ssize_t) (colors-1))
779 (void) CopyMagickString(name,"grey75",MaxTextExtent);
786 for (j=1; j < (ssize_t) characters_per_pixel; j++)
788 k=((i-k)/MaxCixels) % MaxCixels;
792 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
794 (void) WriteBlobString(image,buffer);
799 (void) WriteBlobString(image,"/* pixels */\n");
800 for (y=0; y < (ssize_t) picon->rows; y++)
802 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
803 if (p == (const Quantum *) NULL)
805 (void) WriteBlobString(image,"\"");
806 for (x=0; x < (ssize_t) picon->columns; x++)
808 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
810 for (j=1; j < (ssize_t) characters_per_pixel; j++)
812 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
816 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
817 (void) WriteBlobString(image,buffer);
818 p+=GetPixelChannels(image);
820 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
821 y == (ssize_t) (picon->rows-1) ? "" : ",");
822 (void) WriteBlobString(image,buffer);
823 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
825 if (status == MagickFalse)
828 picon=DestroyImage(picon);
829 (void) WriteBlobString(image,"};\n");
830 (void) CloseBlob(image);
835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839 % W r i t e X P M I m a g e %
843 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845 % WriteXPMImage() writes an image to a file in the X pixmap format.
847 % The format of the WriteXPMImage method is:
849 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
850 % Image *image,ExceptionInfo *exception)
852 % A description of each parameter follows.
854 % o image_info: the image info.
856 % o image: The image.
858 % o exception: return any errors or warnings in this structure.
861 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
862 ExceptionInfo *exception)
867 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
868 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
871 buffer[MaxTextExtent],
872 basename[MaxTextExtent],
874 symbol[MaxTextExtent];
882 register const Quantum
890 characters_per_pixel;
899 Open output image file.
901 assert(image_info != (const ImageInfo *) NULL);
902 assert(image_info->signature == MagickSignature);
903 assert(image != (Image *) NULL);
904 assert(image->signature == MagickSignature);
905 if (image->debug != MagickFalse)
906 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
907 assert(exception != (ExceptionInfo *) NULL);
908 assert(exception->signature == MagickSignature);
909 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
910 if (status == MagickFalse)
912 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
913 (void) TransformImageColorspace(image,sRGBColorspace,exception);
915 if (image->alpha_trait != BlendPixelTrait)
917 if ((image->storage_class == DirectClass) || (image->colors > 256))
918 (void) SetImageType(image,PaletteType,exception);
927 Identify transparent colormap index.
929 if ((image->storage_class == DirectClass) || (image->colors > 256))
930 (void) SetImageType(image,PaletteBilevelMatteType,exception);
931 for (i=0; i < (ssize_t) image->colors; i++)
932 if (image->colormap[i].alpha != OpaqueAlpha)
939 alpha=(double) TransparentAlpha-(double)
940 image->colormap[i].alpha;
941 beta=(double) TransparentAlpha-(double)
942 image->colormap[opacity].alpha;
948 (void) SetImageType(image,PaletteBilevelMatteType,exception);
949 for (i=0; i < (ssize_t) image->colors; i++)
950 if (image->colormap[i].alpha != OpaqueAlpha)
957 alpha=(Quantum) TransparentAlpha-(double)
958 image->colormap[i].alpha;
959 beta=(Quantum) TransparentAlpha-(double)
960 image->colormap[opacity].alpha;
967 image->colormap[opacity].red=image->transparent_color.red;
968 image->colormap[opacity].green=image->transparent_color.green;
969 image->colormap[opacity].blue=image->transparent_color.blue;
973 Compute the character per pixel.
975 characters_per_pixel=1;
976 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
977 characters_per_pixel++;
981 (void) WriteBlobString(image,"/* XPM */\n");
982 GetPathComponent(image->filename,BasePath,basename);
983 if (isalnum((int) ((unsigned char) *basename)) == 0)
985 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
986 (void) CopyMagickString(basename,buffer,MaxTextExtent);
988 if (isalpha((int) ((unsigned char) basename[0])) == 0)
990 for (i=1; basename[i] != '\0'; i++)
991 if (isalnum((int) ((unsigned char) basename[i])) == 0)
993 (void) FormatLocaleString(buffer,MaxTextExtent,
994 "static char *%s[] = {\n",basename);
995 (void) WriteBlobString(image,buffer);
996 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
997 (void) FormatLocaleString(buffer,MaxTextExtent,
998 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
999 image->rows,(double) image->colors,(double) characters_per_pixel);
1000 (void) WriteBlobString(image,buffer);
1001 GetPixelInfo(image,&pixel);
1002 for (i=0; i < (ssize_t) image->colors; i++)
1007 pixel=image->colormap[i];
1008 pixel.colorspace=sRGBColorspace;
1010 pixel.alpha=(double) OpaqueAlpha;
1011 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
1013 (void) CopyMagickString(name,"None",MaxTextExtent);
1019 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1021 k=((i-k)/MaxCixels) % MaxCixels;
1025 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
1027 (void) WriteBlobString(image,buffer);
1032 (void) WriteBlobString(image,"/* pixels */\n");
1033 for (y=0; y < (ssize_t) image->rows; y++)
1035 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1036 if (p == (const Quantum *) NULL)
1038 (void) WriteBlobString(image,"\"");
1039 for (x=0; x < (ssize_t) image->columns; x++)
1041 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1043 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1045 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1049 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1050 (void) WriteBlobString(image,buffer);
1051 p+=GetPixelChannels(image);
1053 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1054 (y == (ssize_t) (image->rows-1) ? "" : ","));
1055 (void) WriteBlobString(image,buffer);
1056 if (image->previous == (Image *) NULL)
1058 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1060 if (status == MagickFalse)
1064 (void) WriteBlobString(image,"};\n");
1065 (void) CloseBlob(image);