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 char *ParseXPMColor(char *color,MagickBooleanType search_start)
177 #define NumberTargets 6
190 *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
192 if (search_start != MagickFalse)
194 for (i=0; i < NumberTargets; i++)
197 for (q=targets[i]; *p != '\0'; p++)
203 if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
216 return((char *) NULL);
218 for (p=color+1; *p != '\0'; p++)
222 if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
224 if (isspace((int) ((unsigned char) (*p))) != 0)
226 for (i=0; i < NumberTargets; i++)
228 if ((*p == *targets[i]) && (*(p+1) == *(targets[i]+1)))
235 static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
240 target[MaxTextExtent],
281 assert(image_info != (const ImageInfo *) NULL);
282 assert(image_info->signature == MagickSignature);
283 if (image_info->debug != MagickFalse)
284 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
285 image_info->filename);
286 assert(exception != (ExceptionInfo *) NULL);
287 assert(exception->signature == MagickSignature);
288 image=AcquireImage(image_info,exception);
289 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
290 if (status == MagickFalse)
292 image=DestroyImageList(image);
293 return((Image *) NULL);
298 length=MaxTextExtent;
299 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
300 if (xpm_buffer == (char *) NULL)
301 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
304 while (ReadBlobString(image,p) != (char *) NULL)
306 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
308 if ((*p == '}') && (*(p+1) == ';'))
311 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
314 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
315 sizeof(*xpm_buffer));
316 if (xpm_buffer == (char *) NULL)
318 p=xpm_buffer+strlen(xpm_buffer);
320 if (xpm_buffer == (char *) NULL)
321 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
327 for (p=xpm_buffer; *p != '\0'; p++)
331 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
332 image->columns=columns;
334 image->colors=colors;
338 if ((count != 4) || (width > 10) || (image->columns == 0) ||
339 (image->rows == 0) || (image->colors == 0))
340 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
342 Remove unquoted characters.
350 if (active != MagickFalse)
352 active=active != MagickFalse ? MagickFalse : MagickTrue;
354 if (active != MagickFalse)
359 Initialize image structure.
361 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
362 (void *(*)(void *)) NULL);
363 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
364 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
369 next=NextXPMLine(xpm_buffer);
370 for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
374 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent));
375 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
379 (void) CopyMagickString(target,"gray",MaxTextExtent);
380 q=ParseXPMColor(p+width,MagickTrue);
381 if (q != (char *) NULL)
383 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
387 if (next != (char *) NULL)
388 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
391 (void) CopyMagickString(target,q,MaxTextExtent);
392 q=ParseXPMColor(target,MagickFalse);
393 if (q != (char *) NULL)
397 grey=strstr(target,"grey");
398 if (grey != (char *) NULL)
400 if (LocaleCompare(target,"none") == 0)
402 image->storage_class=DirectClass;
403 image->alpha_trait=BlendPixelTrait;
405 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
407 if (status == MagickFalse)
409 if (image->depth < image->colormap[j].depth)
410 image->depth=image->colormap[j].depth;
412 if (j < (ssize_t) image->colors)
413 ThrowReaderException(CorruptImageError,"CorruptImage");
415 if (image_info->ping == MagickFalse)
420 status=SetImageExtent(image,image->columns,image->rows,exception);
421 if (status == MagickFalse)
422 return(DestroyImageList(image));
423 for (y=0; y < (ssize_t) image->rows; y++)
426 if (p == (char *) NULL)
428 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
429 if (r == (Quantum *) NULL)
431 for (x=0; x < (ssize_t) image->columns; x++)
433 p+=CopyXPMColor(key,p,MagickMin(width,MaxTextExtent));
434 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
435 if (image->storage_class == PseudoClass)
436 SetPixelIndex(image,(Quantum) j,r);
437 SetPixelInfoPixel(image,image->colormap+j,r);
438 r+=GetPixelChannels(image);
440 if (SyncAuthenticPixels(image,exception) == MagickFalse)
443 if (y < (ssize_t) image->rows)
444 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
447 Relinquish resources.
449 xpm_colors=DestroySplayTree(xpm_colors);
450 (void) CloseBlob(image);
451 return(GetFirstImageInList(image));
455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459 % R e g i s t e r X P M I m a g e %
463 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465 % RegisterXPMImage() adds attributes for the XPM image format to
466 % the list of supported formats. The attributes include the image format
467 % tag, a method to read and/or write the format, whether the format
468 % supports the saving of more than one frame to the same file or blob,
469 % whether the format supports native in-memory I/O, and a brief
470 % description of the format.
472 % The format of the RegisterXPMImage method is:
474 % size_t RegisterXPMImage(void)
477 ModuleExport size_t RegisterXPMImage(void)
482 entry=SetMagickInfo("PICON");
483 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
484 entry->encoder=(EncodeImageHandler *) WritePICONImage;
485 entry->adjoin=MagickFalse;
486 entry->description=ConstantString("Personal Icon");
487 entry->module=ConstantString("XPM");
488 (void) RegisterMagickInfo(entry);
489 entry=SetMagickInfo("PM");
490 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
491 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
492 entry->adjoin=MagickFalse;
493 entry->stealth=MagickTrue;
494 entry->description=ConstantString("X Windows system pixmap (color)");
495 entry->module=ConstantString("XPM");
496 (void) RegisterMagickInfo(entry);
497 entry=SetMagickInfo("XPM");
498 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
499 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
500 entry->magick=(IsImageFormatHandler *) IsXPM;
501 entry->adjoin=MagickFalse;
502 entry->description=ConstantString("X Windows system pixmap (color)");
503 entry->module=ConstantString("XPM");
504 (void) RegisterMagickInfo(entry);
505 return(MagickImageCoderSignature);
509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513 % U n r e g i s t e r X P M I m a g e %
517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519 % UnregisterXPMImage() removes format registrations made by the
520 % XPM module from the list of supported formats.
522 % The format of the UnregisterXPMImage method is:
524 % UnregisterXPMImage(void)
527 ModuleExport void UnregisterXPMImage(void)
529 (void) UnregisterMagickInfo("PICON");
530 (void) UnregisterMagickInfo("PM");
531 (void) UnregisterMagickInfo("XPM");
535 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
539 % W r i t e P I C O N I m a g e %
543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
545 % WritePICONImage() writes an image to a file in the Personal Icon format.
547 % The format of the WritePICONImage method is:
549 % MagickBooleanType WritePICONImage(const ImageInfo *image_info,
550 % Image *image,ExceptionInfo *exception)
552 % A description of each parameter follows.
554 % o image_info: the image info.
556 % o image: The image.
558 % o exception: return any errors or warnings in this structure.
561 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
562 Image *image,ExceptionInfo *exception)
564 #define ColormapExtent 155
565 #define GraymapExtent 95
566 #define PiconGeometry "48x48>"
571 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
572 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
573 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
574 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
575 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
576 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
577 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
578 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
579 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
580 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
581 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
582 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
583 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
587 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
588 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
589 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
590 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
591 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
592 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
593 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
594 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
600 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
601 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
604 buffer[MaxTextExtent],
605 basename[MaxTextExtent],
607 symbol[MaxTextExtent];
629 register const Quantum
640 characters_per_pixel,
649 Open output image file.
651 assert(image_info != (const ImageInfo *) NULL);
652 assert(image_info->signature == MagickSignature);
653 assert(image != (Image *) NULL);
654 assert(image->signature == MagickSignature);
655 if (image->debug != MagickFalse)
656 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
657 assert(exception != (ExceptionInfo *) NULL);
658 assert(exception->signature == MagickSignature);
659 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
660 if (status == MagickFalse)
662 (void) TransformImageColorspace(image,sRGBColorspace,exception);
663 SetGeometry(image,&geometry);
664 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
665 &geometry.width,&geometry.height);
666 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
668 blob_info=CloneImageInfo(image_info);
669 (void) AcquireUniqueFilename(blob_info->filename);
670 if ((image_info->type != TrueColorType) &&
671 (IsImageGray(image,exception) != MagickFalse))
672 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
674 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
675 (void) RelinquishUniqueFileResource(blob_info->filename);
676 blob_info=DestroyImageInfo(blob_info);
677 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
679 quantize_info=AcquireQuantizeInfo(image_info);
680 status=RemapImage(quantize_info,picon,affinity_image,exception);
681 quantize_info=DestroyQuantizeInfo(quantize_info);
682 affinity_image=DestroyImage(affinity_image);
683 transparent=MagickFalse;
684 if (picon->storage_class == PseudoClass)
686 (void) CompressImageColormap(picon,exception);
687 if (picon->alpha_trait != UndefinedPixelTrait)
688 transparent=MagickTrue;
693 Convert DirectClass to PseudoClass picon.
695 if (picon->alpha_trait != UndefinedPixelTrait)
698 Map all the transparent pixels.
700 for (y=0; y < (ssize_t) picon->rows; y++)
702 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
703 if (q == (Quantum *) NULL)
705 for (x=0; x < (ssize_t) picon->columns; x++)
707 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
708 transparent=MagickTrue;
710 SetPixelAlpha(picon,OpaqueAlpha,q);
711 q+=GetPixelChannels(picon);
713 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
717 (void) SetImageType(picon,PaletteType,exception);
719 colors=picon->colors;
720 if (transparent != MagickFalse)
723 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
724 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
725 if (picon->colormap == (PixelInfo *) NULL)
726 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
727 for (y=0; y < (ssize_t) picon->rows; y++)
729 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
730 if (q == (Quantum *) NULL)
732 for (x=0; x < (ssize_t) picon->columns; x++)
734 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
735 SetPixelIndex(picon,(Quantum) picon->colors,q);
736 q+=GetPixelChannels(picon);
738 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
743 Compute the character per pixel.
745 characters_per_pixel=1;
746 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
747 characters_per_pixel++;
751 (void) WriteBlobString(image,"/* XPM */\n");
752 GetPathComponent(picon->filename,BasePath,basename);
753 (void) FormatLocaleString(buffer,MaxTextExtent,
754 "static char *%s[] = {\n",basename);
755 (void) WriteBlobString(image,buffer);
756 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
757 (void) FormatLocaleString(buffer,MaxTextExtent,
758 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
759 picon->rows,(double) colors,(double) characters_per_pixel);
760 (void) WriteBlobString(image,buffer);
761 GetPixelInfo(image,&pixel);
762 for (i=0; i < (ssize_t) colors; i++)
767 pixel=picon->colormap[i];
768 pixel.colorspace=sRGBColorspace;
770 pixel.alpha=(double) OpaqueAlpha;
771 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
772 if (transparent != MagickFalse)
774 if (i == (ssize_t) (colors-1))
775 (void) CopyMagickString(name,"grey75",MaxTextExtent);
782 for (j=1; j < (ssize_t) characters_per_pixel; j++)
784 k=((i-k)/MaxCixels) % MaxCixels;
788 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
790 (void) WriteBlobString(image,buffer);
795 (void) WriteBlobString(image,"/* pixels */\n");
796 for (y=0; y < (ssize_t) picon->rows; y++)
798 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
799 if (p == (const Quantum *) NULL)
801 (void) WriteBlobString(image,"\"");
802 for (x=0; x < (ssize_t) picon->columns; x++)
804 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
806 for (j=1; j < (ssize_t) characters_per_pixel; j++)
808 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
812 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
813 (void) WriteBlobString(image,buffer);
814 p+=GetPixelChannels(image);
816 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
817 y == (ssize_t) (picon->rows-1) ? "" : ",");
818 (void) WriteBlobString(image,buffer);
819 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
821 if (status == MagickFalse)
824 picon=DestroyImage(picon);
825 (void) WriteBlobString(image,"};\n");
826 (void) CloseBlob(image);
831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
835 % W r i t e X P M I m a g e %
839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
841 % WriteXPMImage() writes an image to a file in the X pixmap format.
843 % The format of the WriteXPMImage method is:
845 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
846 % Image *image,ExceptionInfo *exception)
848 % A description of each parameter follows.
850 % o image_info: the image info.
852 % o image: The image.
854 % o exception: return any errors or warnings in this structure.
857 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
858 ExceptionInfo *exception)
863 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
864 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
867 buffer[MaxTextExtent],
868 basename[MaxTextExtent],
870 symbol[MaxTextExtent];
878 register const Quantum
886 characters_per_pixel;
895 Open output image file.
897 assert(image_info != (const ImageInfo *) NULL);
898 assert(image_info->signature == MagickSignature);
899 assert(image != (Image *) NULL);
900 assert(image->signature == MagickSignature);
901 if (image->debug != MagickFalse)
902 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
903 assert(exception != (ExceptionInfo *) NULL);
904 assert(exception->signature == MagickSignature);
905 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
906 if (status == MagickFalse)
908 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
909 (void) TransformImageColorspace(image,sRGBColorspace,exception);
911 if (image->alpha_trait == UndefinedPixelTrait)
913 if ((image->storage_class == DirectClass) || (image->colors > 256))
914 (void) SetImageType(image,PaletteType,exception);
923 Identify transparent colormap index.
925 if ((image->storage_class == DirectClass) || (image->colors > 256))
926 (void) SetImageType(image,PaletteBilevelMatteType,exception);
927 for (i=0; i < (ssize_t) image->colors; i++)
928 if (image->colormap[i].alpha != OpaqueAlpha)
935 alpha=(double) TransparentAlpha-(double)
936 image->colormap[i].alpha;
937 beta=(double) TransparentAlpha-(double)
938 image->colormap[opacity].alpha;
944 (void) SetImageType(image,PaletteBilevelMatteType,exception);
945 for (i=0; i < (ssize_t) image->colors; i++)
946 if (image->colormap[i].alpha != OpaqueAlpha)
953 alpha=(Quantum) TransparentAlpha-(double)
954 image->colormap[i].alpha;
955 beta=(Quantum) TransparentAlpha-(double)
956 image->colormap[opacity].alpha;
963 image->colormap[opacity].red=image->transparent_color.red;
964 image->colormap[opacity].green=image->transparent_color.green;
965 image->colormap[opacity].blue=image->transparent_color.blue;
969 Compute the character per pixel.
971 characters_per_pixel=1;
972 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
973 characters_per_pixel++;
977 (void) WriteBlobString(image,"/* XPM */\n");
978 GetPathComponent(image->filename,BasePath,basename);
979 if (isalnum((int) ((unsigned char) *basename)) == 0)
981 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
982 (void) CopyMagickString(basename,buffer,MaxTextExtent);
984 if (isalpha((int) ((unsigned char) basename[0])) == 0)
986 for (i=1; basename[i] != '\0'; i++)
987 if (isalnum((int) ((unsigned char) basename[i])) == 0)
989 (void) FormatLocaleString(buffer,MaxTextExtent,
990 "static char *%s[] = {\n",basename);
991 (void) WriteBlobString(image,buffer);
992 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
993 (void) FormatLocaleString(buffer,MaxTextExtent,
994 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
995 image->rows,(double) image->colors,(double) characters_per_pixel);
996 (void) WriteBlobString(image,buffer);
997 GetPixelInfo(image,&pixel);
998 for (i=0; i < (ssize_t) image->colors; i++)
1003 pixel=image->colormap[i];
1004 pixel.colorspace=sRGBColorspace;
1006 pixel.alpha=(double) OpaqueAlpha;
1007 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
1009 (void) CopyMagickString(name,"None",MaxTextExtent);
1015 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1017 k=((i-k)/MaxCixels) % MaxCixels;
1021 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
1023 (void) WriteBlobString(image,buffer);
1028 (void) WriteBlobString(image,"/* pixels */\n");
1029 for (y=0; y < (ssize_t) image->rows; y++)
1031 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1032 if (p == (const Quantum *) NULL)
1034 (void) WriteBlobString(image,"\"");
1035 for (x=0; x < (ssize_t) image->columns; x++)
1037 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1039 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1041 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1045 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1046 (void) WriteBlobString(image,buffer);
1047 p+=GetPixelChannels(image);
1049 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1050 (y == (ssize_t) (image->rows-1) ? "" : ","));
1051 (void) WriteBlobString(image,buffer);
1052 if (image->previous == (Image *) NULL)
1054 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1056 if (status == MagickFalse)
1060 (void) WriteBlobString(image,"};\n");
1061 (void) CloseBlob(image);