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++);
164 return((size_t) (p-source-1));
167 static char *NextXPMLine(char *p)
169 assert(p != (char *) NULL);
171 if (p != (char *) NULL)
176 static char *ParseXPMColor(char *color,MagickBooleanType search_start)
178 #define NumberTargets 6
191 *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
193 if (search_start != MagickFalse)
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);
219 for (p=color+1; *p != '\0'; p++)
223 if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
225 if (isspace((int) ((unsigned char) (*p))) != 0)
227 for (i=0; i < NumberTargets; i++)
229 if ((*p == *targets[i]) && (*(p+1) == *(targets[i]+1)))
236 static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
241 target[MaxTextExtent],
282 assert(image_info != (const ImageInfo *) NULL);
283 assert(image_info->signature == MagickSignature);
284 if (image_info->debug != MagickFalse)
285 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
286 image_info->filename);
287 assert(exception != (ExceptionInfo *) NULL);
288 assert(exception->signature == MagickSignature);
289 image=AcquireImage(image_info,exception);
290 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
291 if (status == MagickFalse)
293 image=DestroyImageList(image);
294 return((Image *) NULL);
299 length=MaxTextExtent;
300 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
301 if (xpm_buffer == (char *) NULL)
302 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
305 while (ReadBlobString(image,p) != (char *) NULL)
307 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
309 if ((*p == '}') && (*(p+1) == ';'))
312 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
315 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
316 sizeof(*xpm_buffer));
317 if (xpm_buffer == (char *) NULL)
319 p=xpm_buffer+strlen(xpm_buffer);
321 if (xpm_buffer == (char *) NULL)
322 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
328 for (p=xpm_buffer; *p != '\0'; p++)
332 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
333 image->columns=columns;
335 image->colors=colors;
339 if ((count != 4) || (width > 10) || (image->columns == 0) ||
340 (image->rows == 0) || (image->colors == 0))
341 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
343 Remove unquoted characters.
351 if (active != MagickFalse)
353 active=active != MagickFalse ? MagickFalse : MagickTrue;
355 if (active != MagickFalse)
360 Initialize image structure.
362 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
363 (void *(*)(void *)) NULL);
364 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
365 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
370 next=NextXPMLine(xpm_buffer);
371 for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
375 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent-1));
376 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
380 (void) CopyMagickString(target,"gray",MaxTextExtent);
381 q=ParseXPMColor(p+width,MagickTrue);
382 if (q != (char *) NULL)
384 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
388 if (next != (char *) NULL)
389 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
392 (void) CopyMagickString(target,q,MaxTextExtent);
393 q=ParseXPMColor(target,MagickFalse);
394 if (q != (char *) NULL)
398 grey=strstr(target,"grey");
399 if (grey != (char *) NULL)
401 if (LocaleCompare(target,"none") == 0)
403 image->storage_class=DirectClass;
404 image->alpha_trait=BlendPixelTrait;
406 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
408 if (status == MagickFalse)
410 if (image->depth < image->colormap[j].depth)
411 image->depth=image->colormap[j].depth;
413 if (j < (ssize_t) image->colors)
414 ThrowReaderException(CorruptImageError,"CorruptImage");
416 if (image_info->ping == MagickFalse)
421 status=SetImageExtent(image,image->columns,image->rows,exception);
422 if (status == MagickFalse)
423 return(DestroyImageList(image));
424 for (y=0; y < (ssize_t) image->rows; y++)
427 if (p == (char *) NULL)
429 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
430 if (r == (Quantum *) NULL)
432 for (x=0; x < (ssize_t) image->columns; x++)
434 p+=CopyXPMColor(key,p,MagickMin(width,MaxTextExtent-1));
435 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
436 if (image->storage_class == PseudoClass)
437 SetPixelIndex(image,(Quantum) j,r);
438 SetPixelInfoPixel(image,image->colormap+j,r);
439 r+=GetPixelChannels(image);
441 if (SyncAuthenticPixels(image,exception) == MagickFalse)
444 if (y < (ssize_t) image->rows)
445 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
448 Relinquish resources.
450 xpm_colors=DestroySplayTree(xpm_colors);
451 (void) CloseBlob(image);
452 return(GetFirstImageInList(image));
456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
460 % R e g i s t e r X P M I m a g e %
464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
466 % RegisterXPMImage() adds attributes for the XPM image format to
467 % the list of supported formats. The attributes include the image format
468 % tag, a method to read and/or write the format, whether the format
469 % supports the saving of more than one frame to the same file or blob,
470 % whether the format supports native in-memory I/O, and a brief
471 % description of the format.
473 % The format of the RegisterXPMImage method is:
475 % size_t RegisterXPMImage(void)
478 ModuleExport size_t RegisterXPMImage(void)
483 entry=SetMagickInfo("PICON");
484 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
485 entry->encoder=(EncodeImageHandler *) WritePICONImage;
486 entry->adjoin=MagickFalse;
487 entry->description=ConstantString("Personal Icon");
488 entry->module=ConstantString("XPM");
489 (void) RegisterMagickInfo(entry);
490 entry=SetMagickInfo("PM");
491 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
492 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
493 entry->adjoin=MagickFalse;
494 entry->stealth=MagickTrue;
495 entry->description=ConstantString("X Windows system pixmap (color)");
496 entry->module=ConstantString("XPM");
497 (void) RegisterMagickInfo(entry);
498 entry=SetMagickInfo("XPM");
499 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
500 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
501 entry->magick=(IsImageFormatHandler *) IsXPM;
502 entry->adjoin=MagickFalse;
503 entry->description=ConstantString("X Windows system pixmap (color)");
504 entry->module=ConstantString("XPM");
505 (void) RegisterMagickInfo(entry);
506 return(MagickImageCoderSignature);
510 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514 % U n r e g i s t e r X P M I m a g e %
518 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
520 % UnregisterXPMImage() removes format registrations made by the
521 % XPM module from the list of supported formats.
523 % The format of the UnregisterXPMImage method is:
525 % UnregisterXPMImage(void)
528 ModuleExport void UnregisterXPMImage(void)
530 (void) UnregisterMagickInfo("PICON");
531 (void) UnregisterMagickInfo("PM");
532 (void) UnregisterMagickInfo("XPM");
536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
540 % W r i t e P I C O N I m a g e %
544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546 % WritePICONImage() writes an image to a file in the Personal Icon format.
548 % The format of the WritePICONImage method is:
550 % MagickBooleanType WritePICONImage(const ImageInfo *image_info,
551 % Image *image,ExceptionInfo *exception)
553 % A description of each parameter follows.
555 % o image_info: the image info.
557 % o image: The image.
559 % o exception: return any errors or warnings in this structure.
562 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
563 Image *image,ExceptionInfo *exception)
565 #define ColormapExtent 155
566 #define GraymapExtent 95
567 #define PiconGeometry "48x48>"
572 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
573 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
574 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
575 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
576 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
577 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
578 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
579 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
580 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
581 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
582 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
583 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
584 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
588 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
589 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
590 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
591 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
592 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
593 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
594 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
595 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
601 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
602 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
605 buffer[MaxTextExtent],
606 basename[MaxTextExtent],
608 symbol[MaxTextExtent];
630 register const Quantum
641 characters_per_pixel,
650 Open output image file.
652 assert(image_info != (const ImageInfo *) NULL);
653 assert(image_info->signature == MagickSignature);
654 assert(image != (Image *) NULL);
655 assert(image->signature == MagickSignature);
656 if (image->debug != MagickFalse)
657 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
658 assert(exception != (ExceptionInfo *) NULL);
659 assert(exception->signature == MagickSignature);
660 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
661 if (status == MagickFalse)
663 (void) TransformImageColorspace(image,sRGBColorspace,exception);
664 SetGeometry(image,&geometry);
665 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
666 &geometry.width,&geometry.height);
667 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
669 blob_info=CloneImageInfo(image_info);
670 (void) AcquireUniqueFilename(blob_info->filename);
671 if ((image_info->type != TrueColorType) &&
672 (IsImageGray(image,exception) != MagickFalse))
673 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
675 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
676 (void) RelinquishUniqueFileResource(blob_info->filename);
677 blob_info=DestroyImageInfo(blob_info);
678 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
680 quantize_info=AcquireQuantizeInfo(image_info);
681 status=RemapImage(quantize_info,picon,affinity_image,exception);
682 quantize_info=DestroyQuantizeInfo(quantize_info);
683 affinity_image=DestroyImage(affinity_image);
684 transparent=MagickFalse;
685 if (picon->storage_class == PseudoClass)
687 (void) CompressImageColormap(picon,exception);
688 if (picon->alpha_trait != UndefinedPixelTrait)
689 transparent=MagickTrue;
694 Convert DirectClass to PseudoClass picon.
696 if (picon->alpha_trait != UndefinedPixelTrait)
699 Map all the transparent pixels.
701 for (y=0; y < (ssize_t) picon->rows; y++)
703 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
704 if (q == (Quantum *) NULL)
706 for (x=0; x < (ssize_t) picon->columns; x++)
708 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
709 transparent=MagickTrue;
711 SetPixelAlpha(picon,OpaqueAlpha,q);
712 q+=GetPixelChannels(picon);
714 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
718 (void) SetImageType(picon,PaletteType,exception);
720 colors=picon->colors;
721 if (transparent != MagickFalse)
724 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
725 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
726 if (picon->colormap == (PixelInfo *) NULL)
727 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
728 for (y=0; y < (ssize_t) picon->rows; y++)
730 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
731 if (q == (Quantum *) NULL)
733 for (x=0; x < (ssize_t) picon->columns; x++)
735 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
736 SetPixelIndex(picon,(Quantum) picon->colors,q);
737 q+=GetPixelChannels(picon);
739 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
744 Compute the character per pixel.
746 characters_per_pixel=1;
747 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
748 characters_per_pixel++;
752 (void) WriteBlobString(image,"/* XPM */\n");
753 GetPathComponent(picon->filename,BasePath,basename);
754 (void) FormatLocaleString(buffer,MaxTextExtent,
755 "static char *%s[] = {\n",basename);
756 (void) WriteBlobString(image,buffer);
757 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
758 (void) FormatLocaleString(buffer,MaxTextExtent,
759 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
760 picon->rows,(double) colors,(double) characters_per_pixel);
761 (void) WriteBlobString(image,buffer);
762 GetPixelInfo(image,&pixel);
763 for (i=0; i < (ssize_t) colors; i++)
768 pixel=picon->colormap[i];
769 pixel.colorspace=sRGBColorspace;
771 pixel.alpha=(double) OpaqueAlpha;
772 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
773 if (transparent != MagickFalse)
775 if (i == (ssize_t) (colors-1))
776 (void) CopyMagickString(name,"grey75",MaxTextExtent);
783 for (j=1; j < (ssize_t) characters_per_pixel; j++)
785 k=((i-k)/MaxCixels) % MaxCixels;
789 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
791 (void) WriteBlobString(image,buffer);
796 (void) WriteBlobString(image,"/* pixels */\n");
797 for (y=0; y < (ssize_t) picon->rows; y++)
799 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
800 if (p == (const Quantum *) NULL)
802 (void) WriteBlobString(image,"\"");
803 for (x=0; x < (ssize_t) picon->columns; x++)
805 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
807 for (j=1; j < (ssize_t) characters_per_pixel; j++)
809 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
813 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
814 (void) WriteBlobString(image,buffer);
815 p+=GetPixelChannels(image);
817 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
818 y == (ssize_t) (picon->rows-1) ? "" : ",");
819 (void) WriteBlobString(image,buffer);
820 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
822 if (status == MagickFalse)
825 picon=DestroyImage(picon);
826 (void) WriteBlobString(image,"};\n");
827 (void) CloseBlob(image);
832 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
836 % W r i t e X P M I m a g e %
840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
842 % WriteXPMImage() writes an image to a file in the X pixmap format.
844 % The format of the WriteXPMImage method is:
846 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
847 % Image *image,ExceptionInfo *exception)
849 % A description of each parameter follows.
851 % o image_info: the image info.
853 % o image: The image.
855 % o exception: return any errors or warnings in this structure.
858 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
859 ExceptionInfo *exception)
864 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
865 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
868 buffer[MaxTextExtent],
869 basename[MaxTextExtent],
871 symbol[MaxTextExtent];
879 register const Quantum
887 characters_per_pixel;
896 Open output image file.
898 assert(image_info != (const ImageInfo *) NULL);
899 assert(image_info->signature == MagickSignature);
900 assert(image != (Image *) NULL);
901 assert(image->signature == MagickSignature);
902 if (image->debug != MagickFalse)
903 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
904 assert(exception != (ExceptionInfo *) NULL);
905 assert(exception->signature == MagickSignature);
906 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
907 if (status == MagickFalse)
909 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
910 (void) TransformImageColorspace(image,sRGBColorspace,exception);
912 if (image->alpha_trait == UndefinedPixelTrait)
914 if ((image->storage_class == DirectClass) || (image->colors > 256))
915 (void) SetImageType(image,PaletteType,exception);
924 Identify transparent colormap index.
926 if ((image->storage_class == DirectClass) || (image->colors > 256))
927 (void) SetImageType(image,PaletteBilevelMatteType,exception);
928 for (i=0; i < (ssize_t) image->colors; i++)
929 if (image->colormap[i].alpha != OpaqueAlpha)
936 alpha=(double) TransparentAlpha-(double)
937 image->colormap[i].alpha;
938 beta=(double) TransparentAlpha-(double)
939 image->colormap[opacity].alpha;
945 (void) SetImageType(image,PaletteBilevelMatteType,exception);
946 for (i=0; i < (ssize_t) image->colors; i++)
947 if (image->colormap[i].alpha != OpaqueAlpha)
954 alpha=(Quantum) TransparentAlpha-(double)
955 image->colormap[i].alpha;
956 beta=(Quantum) TransparentAlpha-(double)
957 image->colormap[opacity].alpha;
964 image->colormap[opacity].red=image->transparent_color.red;
965 image->colormap[opacity].green=image->transparent_color.green;
966 image->colormap[opacity].blue=image->transparent_color.blue;
970 Compute the character per pixel.
972 characters_per_pixel=1;
973 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
974 characters_per_pixel++;
978 (void) WriteBlobString(image,"/* XPM */\n");
979 GetPathComponent(image->filename,BasePath,basename);
980 if (isalnum((int) ((unsigned char) *basename)) == 0)
982 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
983 (void) CopyMagickString(basename,buffer,MaxTextExtent);
985 if (isalpha((int) ((unsigned char) basename[0])) == 0)
987 for (i=1; basename[i] != '\0'; i++)
988 if (isalnum((int) ((unsigned char) basename[i])) == 0)
990 (void) FormatLocaleString(buffer,MaxTextExtent,
991 "static char *%s[] = {\n",basename);
992 (void) WriteBlobString(image,buffer);
993 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
994 (void) FormatLocaleString(buffer,MaxTextExtent,
995 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
996 image->rows,(double) image->colors,(double) characters_per_pixel);
997 (void) WriteBlobString(image,buffer);
998 GetPixelInfo(image,&pixel);
999 for (i=0; i < (ssize_t) image->colors; i++)
1004 pixel=image->colormap[i];
1005 pixel.colorspace=sRGBColorspace;
1007 pixel.alpha=(double) OpaqueAlpha;
1008 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
1010 (void) CopyMagickString(name,"None",MaxTextExtent);
1016 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1018 k=((i-k)/MaxCixels) % MaxCixels;
1022 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
1024 (void) WriteBlobString(image,buffer);
1029 (void) WriteBlobString(image,"/* pixels */\n");
1030 for (y=0; y < (ssize_t) image->rows; y++)
1032 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1033 if (p == (const Quantum *) NULL)
1035 (void) WriteBlobString(image,"\"");
1036 for (x=0; x < (ssize_t) image->columns; x++)
1038 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1040 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1042 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1046 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1047 (void) WriteBlobString(image,buffer);
1048 p+=GetPixelChannels(image);
1050 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1051 (y == (ssize_t) (image->rows-1) ? "" : ","));
1052 (void) WriteBlobString(image,buffer);
1053 if (image->previous == (Image *) NULL)
1055 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1057 if (status == MagickFalse)
1061 (void) WriteBlobString(image,"};\n");
1062 (void) CloseBlob(image);