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");
308 for (p=xpm_buffer; *p != '\0'; p++)
312 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
313 image->columns=columns;
315 image->colors=colors;
319 if ((count != 4) || (width > 10) || (image->columns == 0) ||
320 (image->rows == 0) || (image->colors == 0))
321 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
323 Remove unquoted characters.
331 if (active != MagickFalse)
333 active=active != MagickFalse ? MagickFalse : MagickTrue;
335 if (active != MagickFalse)
340 Initialize image structure.
342 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
343 (void *(*)(void *)) NULL);
344 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
345 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
350 next=NextXPMLine(xpm_buffer);
351 for (j=0; (j < (ssize_t) image->colors) && (next != (char*) NULL); j++)
355 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent));
356 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
360 (void) CopyMagickString(target,"gray",MaxTextExtent);
361 q=ParseXPMColor(p+width);
362 if (q != (char *) NULL)
364 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
366 if (next != (char *) NULL)
367 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
370 (void) CopyMagickString(target,q,MaxTextExtent);
371 q=ParseXPMColor(target);
372 if (q != (char *) NULL)
376 if (LocaleCompare(target,"none") == 0)
378 image->storage_class=DirectClass;
379 image->alpha_trait=BlendPixelTrait;
381 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
383 if (status == MagickFalse)
385 if (image->depth < image->colormap[j].depth)
386 image->depth=image->colormap[j].depth;
388 if (j < (ssize_t) image->colors)
389 ThrowReaderException(CorruptImageError,"CorruptImage");
391 if (image_info->ping == MagickFalse)
396 for (y=0; y < (ssize_t) image->rows; y++)
399 if (p == (char *) NULL)
401 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
402 if (r == (Quantum *) NULL)
404 for (x=0; x < (ssize_t) image->columns; x++)
406 (void) CopyXPMColor(key,p,(size_t) width);
407 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
408 if (image->storage_class == PseudoClass)
409 SetPixelIndex(image,j,r);
410 SetPixelInfoPixel(image,image->colormap+j,r);
412 r+=GetPixelChannels(image);
414 if (SyncAuthenticPixels(image,exception) == MagickFalse)
417 if (y < (ssize_t) image->rows)
418 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
421 Relinquish resources.
423 xpm_colors=DestroySplayTree(xpm_colors);
424 (void) CloseBlob(image);
425 return(GetFirstImageInList(image));
429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433 % R e g i s t e r X P M I m a g e %
437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439 % RegisterXPMImage() adds attributes for the XPM image format to
440 % the list of supported formats. The attributes include the image format
441 % tag, a method to read and/or write the format, whether the format
442 % supports the saving of more than one frame to the same file or blob,
443 % whether the format supports native in-memory I/O, and a brief
444 % description of the format.
446 % The format of the RegisterXPMImage method is:
448 % size_t RegisterXPMImage(void)
451 ModuleExport size_t RegisterXPMImage(void)
456 entry=SetMagickInfo("PICON");
457 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
458 entry->encoder=(EncodeImageHandler *) WritePICONImage;
459 entry->adjoin=MagickFalse;
460 entry->description=ConstantString("Personal Icon");
461 entry->module=ConstantString("XPM");
462 (void) RegisterMagickInfo(entry);
463 entry=SetMagickInfo("PM");
464 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
465 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
466 entry->adjoin=MagickFalse;
467 entry->stealth=MagickTrue;
468 entry->description=ConstantString("X Windows system pixmap (color)");
469 entry->module=ConstantString("XPM");
470 (void) RegisterMagickInfo(entry);
471 entry=SetMagickInfo("XPM");
472 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
473 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
474 entry->magick=(IsImageFormatHandler *) IsXPM;
475 entry->adjoin=MagickFalse;
476 entry->description=ConstantString("X Windows system pixmap (color)");
477 entry->module=ConstantString("XPM");
478 (void) RegisterMagickInfo(entry);
479 return(MagickImageCoderSignature);
483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487 % U n r e g i s t e r X P M I m a g e %
491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
493 % UnregisterXPMImage() removes format registrations made by the
494 % XPM module from the list of supported formats.
496 % The format of the UnregisterXPMImage method is:
498 % UnregisterXPMImage(void)
501 ModuleExport void UnregisterXPMImage(void)
503 (void) UnregisterMagickInfo("PICON");
504 (void) UnregisterMagickInfo("PM");
505 (void) UnregisterMagickInfo("XPM");
509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513 % W r i t e P I C O N I m a g e %
517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519 % WritePICONImage() writes an image to a file in the Personal Icon format.
521 % The format of the WritePICONImage method is:
523 % MagickBooleanType WritePICONImage(const ImageInfo *image_info,
524 % Image *image,ExceptionInfo *exception)
526 % A description of each parameter follows.
528 % o image_info: the image info.
530 % o image: The image.
532 % o exception: return any errors or warnings in this structure.
535 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
536 Image *image,ExceptionInfo *exception)
538 #define ColormapExtent 155
539 #define GraymapExtent 95
540 #define PiconGeometry "48x48>"
545 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
546 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
547 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
548 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
549 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
550 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
551 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
552 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
553 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
554 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
555 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
556 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
557 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
561 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
562 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
563 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
564 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
565 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
566 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
567 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
568 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
574 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
575 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
578 buffer[MaxTextExtent],
579 basename[MaxTextExtent],
581 symbol[MaxTextExtent];
603 register const Quantum
614 characters_per_pixel,
623 Open output image file.
625 assert(image_info != (const ImageInfo *) NULL);
626 assert(image_info->signature == MagickSignature);
627 assert(image != (Image *) NULL);
628 assert(image->signature == MagickSignature);
629 if (image->debug != MagickFalse)
630 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
631 assert(exception != (ExceptionInfo *) NULL);
632 assert(exception->signature == MagickSignature);
633 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
634 if (status == MagickFalse)
636 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
637 (void) TransformImageColorspace(image,sRGBColorspace,exception);
638 SetGeometry(image,&geometry);
639 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
640 &geometry.width,&geometry.height);
641 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
643 blob_info=CloneImageInfo(image_info);
644 (void) AcquireUniqueFilename(blob_info->filename);
645 if ((image_info->type != TrueColorType) &&
646 (IsImageGray(image,exception) != MagickFalse))
647 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
649 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
650 (void) RelinquishUniqueFileResource(blob_info->filename);
651 blob_info=DestroyImageInfo(blob_info);
652 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
654 quantize_info=AcquireQuantizeInfo(image_info);
655 status=RemapImage(quantize_info,picon,affinity_image,exception);
656 quantize_info=DestroyQuantizeInfo(quantize_info);
657 affinity_image=DestroyImage(affinity_image);
658 transparent=MagickFalse;
659 if (picon->storage_class == PseudoClass)
661 (void) CompressImageColormap(picon,exception);
662 if (picon->alpha_trait == BlendPixelTrait)
663 transparent=MagickTrue;
668 Convert DirectClass to PseudoClass picon.
670 if (picon->alpha_trait == BlendPixelTrait)
673 Map all the transparent pixels.
675 for (y=0; y < (ssize_t) picon->rows; y++)
677 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
678 if (q == (Quantum *) NULL)
680 for (x=0; x < (ssize_t) picon->columns; x++)
682 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
683 transparent=MagickTrue;
685 SetPixelAlpha(picon,OpaqueAlpha,q);
686 q+=GetPixelChannels(picon);
688 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
692 (void) SetImageType(picon,PaletteType,exception);
694 colors=picon->colors;
695 if (transparent != MagickFalse)
698 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
699 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
700 if (picon->colormap == (PixelInfo *) NULL)
701 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
702 for (y=0; y < (ssize_t) picon->rows; y++)
704 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
705 if (q == (Quantum *) NULL)
707 for (x=0; x < (ssize_t) picon->columns; x++)
709 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
710 SetPixelIndex(picon,picon->colors,q);
711 q+=GetPixelChannels(picon);
713 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
718 Compute the character per pixel.
720 characters_per_pixel=1;
721 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
722 characters_per_pixel++;
726 (void) WriteBlobString(image,"/* XPM */\n");
727 GetPathComponent(picon->filename,BasePath,basename);
728 (void) FormatLocaleString(buffer,MaxTextExtent,
729 "static char *%s[] = {\n",basename);
730 (void) WriteBlobString(image,buffer);
731 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
732 (void) FormatLocaleString(buffer,MaxTextExtent,
733 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
734 picon->rows,(double) colors,(double) characters_per_pixel);
735 (void) WriteBlobString(image,buffer);
736 GetPixelInfo(image,&pixel);
737 for (i=0; i < (ssize_t) colors; i++)
742 pixel=picon->colormap[i];
743 pixel.colorspace=sRGBColorspace;
745 pixel.alpha=(double) OpaqueAlpha;
746 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
747 if (transparent != MagickFalse)
749 if (i == (ssize_t) (colors-1))
750 (void) CopyMagickString(name,"grey75",MaxTextExtent);
757 for (j=1; j < (ssize_t) characters_per_pixel; j++)
759 k=((i-k)/MaxCixels) % MaxCixels;
763 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
765 (void) WriteBlobString(image,buffer);
770 (void) WriteBlobString(image,"/* pixels */\n");
771 for (y=0; y < (ssize_t) picon->rows; y++)
773 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
774 if (p == (const Quantum *) NULL)
776 (void) WriteBlobString(image,"\"");
777 for (x=0; x < (ssize_t) picon->columns; x++)
779 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
781 for (j=1; j < (ssize_t) characters_per_pixel; j++)
783 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
787 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
788 (void) WriteBlobString(image,buffer);
789 p+=GetPixelChannels(image);
791 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
792 y == (ssize_t) (picon->rows-1) ? "" : ",");
793 (void) WriteBlobString(image,buffer);
794 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
796 if (status == MagickFalse)
799 picon=DestroyImage(picon);
800 (void) WriteBlobString(image,"};\n");
801 (void) CloseBlob(image);
806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
810 % W r i t e X P M I m a g e %
814 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
816 % WriteXPMImage() writes an image to a file in the X pixmap format.
818 % The format of the WriteXPMImage method is:
820 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
821 % Image *image,ExceptionInfo *exception)
823 % A description of each parameter follows.
825 % o image_info: the image info.
827 % o image: The image.
829 % o exception: return any errors or warnings in this structure.
832 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
833 ExceptionInfo *exception)
838 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
839 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
842 buffer[MaxTextExtent],
843 basename[MaxTextExtent],
845 symbol[MaxTextExtent];
853 register const Quantum
861 characters_per_pixel;
870 Open output image file.
872 assert(image_info != (const ImageInfo *) NULL);
873 assert(image_info->signature == MagickSignature);
874 assert(image != (Image *) NULL);
875 assert(image->signature == MagickSignature);
876 if (image->debug != MagickFalse)
877 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
878 assert(exception != (ExceptionInfo *) NULL);
879 assert(exception->signature == MagickSignature);
880 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
881 if (status == MagickFalse)
883 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
884 (void) TransformImageColorspace(image,sRGBColorspace,exception);
886 if (image->alpha_trait != BlendPixelTrait)
888 if ((image->storage_class == DirectClass) || (image->colors > 256))
889 (void) SetImageType(image,PaletteType,exception);
898 Identify transparent colormap index.
900 if ((image->storage_class == DirectClass) || (image->colors > 256))
901 (void) SetImageType(image,PaletteBilevelMatteType,exception);
902 for (i=0; i < (ssize_t) image->colors; i++)
903 if (image->colormap[i].alpha != OpaqueAlpha)
910 alpha=(double) TransparentAlpha-(double)
911 image->colormap[i].alpha;
912 beta=(double) TransparentAlpha-(double)
913 image->colormap[opacity].alpha;
919 (void) SetImageType(image,PaletteBilevelMatteType,exception);
920 for (i=0; i < (ssize_t) image->colors; i++)
921 if (image->colormap[i].alpha != OpaqueAlpha)
928 alpha=(Quantum) TransparentAlpha-(double)
929 image->colormap[i].alpha;
930 beta=(Quantum) TransparentAlpha-(double)
931 image->colormap[opacity].alpha;
938 image->colormap[opacity].red=image->transparent_color.red;
939 image->colormap[opacity].green=image->transparent_color.green;
940 image->colormap[opacity].blue=image->transparent_color.blue;
944 Compute the character per pixel.
946 characters_per_pixel=1;
947 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
948 characters_per_pixel++;
952 (void) WriteBlobString(image,"/* XPM */\n");
953 GetPathComponent(image->filename,BasePath,basename);
954 if (isalnum((int) ((unsigned char) *basename)) == 0)
956 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
957 (void) CopyMagickString(basename,buffer,MaxTextExtent);
959 if (isalpha((int) ((unsigned char) basename[0])) == 0)
961 for (i=1; basename[i] != '\0'; i++)
962 if (isalnum((int) ((unsigned char) basename[i])) == 0)
964 (void) FormatLocaleString(buffer,MaxTextExtent,
965 "static char *%s[] = {\n",basename);
966 (void) WriteBlobString(image,buffer);
967 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
968 (void) FormatLocaleString(buffer,MaxTextExtent,
969 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
970 image->rows,(double) image->colors,(double) characters_per_pixel);
971 (void) WriteBlobString(image,buffer);
972 GetPixelInfo(image,&pixel);
973 for (i=0; i < (ssize_t) image->colors; i++)
978 pixel=image->colormap[i];
979 pixel.colorspace=sRGBColorspace;
981 pixel.alpha=(double) OpaqueAlpha;
982 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
984 (void) CopyMagickString(name,"None",MaxTextExtent);
990 for (j=1; j < (ssize_t) characters_per_pixel; j++)
992 k=((i-k)/MaxCixels) % MaxCixels;
996 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
998 (void) WriteBlobString(image,buffer);
1003 (void) WriteBlobString(image,"/* pixels */\n");
1004 for (y=0; y < (ssize_t) image->rows; y++)
1006 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1007 if (p == (const Quantum *) NULL)
1009 (void) WriteBlobString(image,"\"");
1010 for (x=0; x < (ssize_t) image->columns; x++)
1012 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1014 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1016 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1020 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1021 (void) WriteBlobString(image,buffer);
1022 p+=GetPixelChannels(image);
1024 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1025 (y == (ssize_t) (image->rows-1) ? "" : ","));
1026 (void) WriteBlobString(image,buffer);
1027 if (image->previous == (Image *) NULL)
1029 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1031 if (status == MagickFalse)
1035 (void) WriteBlobString(image,"};\n");
1036 (void) CloseBlob(image);