2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write X Windows system Pixmap Format %
20 % Copyright 1999-2011 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");
324 Remove unquoted characters.
332 if (active != MagickFalse)
334 active=active != MagickFalse ? MagickFalse : MagickTrue;
336 if (active != MagickFalse)
341 Initialize image structure.
343 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
344 (void *(*)(void *)) NULL);
345 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
346 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->matte=MagickTrue;
381 status=QueryColorCompliance(target,AllCompliance,&image->colormap[j],
383 if (status == MagickFalse)
386 if (j < (ssize_t) image->colors)
387 ThrowReaderException(CorruptImageError,"CorruptImage");
389 if (image_info->ping == MagickFalse)
394 for (y=0; y < (ssize_t) image->rows; y++)
397 if (p == (char *) NULL)
399 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
400 if (r == (Quantum *) NULL)
402 for (x=0; x < (ssize_t) image->columns; x++)
404 (void) CopyXPMColor(key,p,(size_t) width);
405 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
406 if (image->storage_class == PseudoClass)
407 SetPixelIndex(image,j,r);
408 SetPixelPixelInfo(image,image->colormap+j,r);
410 r+=GetPixelChannels(image);
412 if (SyncAuthenticPixels(image,exception) == MagickFalse)
415 if (y < (ssize_t) image->rows)
416 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
419 Relinquish resources.
421 xpm_colors=DestroySplayTree(xpm_colors);
422 (void) CloseBlob(image);
423 return(GetFirstImageInList(image));
427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
431 % R e g i s t e r X P M I m a g e %
435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
437 % RegisterXPMImage() adds attributes for the XPM image format to
438 % the list of supported formats. The attributes include the image format
439 % tag, a method to read and/or write the format, whether the format
440 % supports the saving of more than one frame to the same file or blob,
441 % whether the format supports native in-memory I/O, and a brief
442 % description of the format.
444 % The format of the RegisterXPMImage method is:
446 % size_t RegisterXPMImage(void)
449 ModuleExport size_t RegisterXPMImage(void)
454 entry=SetMagickInfo("PICON");
455 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
456 entry->encoder=(EncodeImageHandler *) WritePICONImage;
457 entry->adjoin=MagickFalse;
458 entry->description=ConstantString("Personal Icon");
459 entry->module=ConstantString("XPM");
460 (void) RegisterMagickInfo(entry);
461 entry=SetMagickInfo("PM");
462 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
463 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
464 entry->adjoin=MagickFalse;
465 entry->stealth=MagickTrue;
466 entry->description=ConstantString("X Windows system pixmap (color)");
467 entry->module=ConstantString("XPM");
468 (void) RegisterMagickInfo(entry);
469 entry=SetMagickInfo("XPM");
470 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
471 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
472 entry->magick=(IsImageFormatHandler *) IsXPM;
473 entry->adjoin=MagickFalse;
474 entry->description=ConstantString("X Windows system pixmap (color)");
475 entry->module=ConstantString("XPM");
476 (void) RegisterMagickInfo(entry);
477 return(MagickImageCoderSignature);
481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 % U n r e g i s t e r X P M I m a g e %
489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
491 % UnregisterXPMImage() removes format registrations made by the
492 % XPM module from the list of supported formats.
494 % The format of the UnregisterXPMImage method is:
496 % UnregisterXPMImage(void)
499 ModuleExport void UnregisterXPMImage(void)
501 (void) UnregisterMagickInfo("PICON");
502 (void) UnregisterMagickInfo("PM");
503 (void) UnregisterMagickInfo("XPM");
507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511 % W r i t e P I C O N I m a g e %
515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
517 % WritePICONImage() writes an image to a file in the Personal Icon format.
519 % The format of the WritePICONImage method is:
521 % MagickBooleanType WritePICONImage(const ImageInfo *image_info,
522 % Image *image,ExceptionInfo *exception)
524 % A description of each parameter follows.
526 % o image_info: the image info.
528 % o image: The image.
530 % o exception: return any errors or warnings in this structure.
533 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
534 Image *image,ExceptionInfo *exception)
536 #define ColormapExtent 155
537 #define GraymapExtent 95
538 #define PiconGeometry "48x48>"
543 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
544 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
545 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
546 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
547 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
548 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
549 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
550 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
551 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
552 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
553 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
554 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
555 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
559 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
560 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
561 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
562 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
563 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
564 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
565 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
566 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
572 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
573 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
576 buffer[MaxTextExtent],
577 basename[MaxTextExtent],
579 symbol[MaxTextExtent];
601 register const Quantum
612 characters_per_pixel,
621 Open output image file.
623 assert(image_info != (const ImageInfo *) NULL);
624 assert(image_info->signature == MagickSignature);
625 assert(image != (Image *) NULL);
626 assert(image->signature == MagickSignature);
627 if (image->debug != MagickFalse)
628 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
629 assert(exception != (ExceptionInfo *) NULL);
630 assert(exception->signature == MagickSignature);
631 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
632 if (status == MagickFalse)
634 if (IsRGBColorspace(image->colorspace) == MagickFalse)
635 (void) TransformImageColorspace(image,RGBColorspace,exception);
636 SetGeometry(image,&geometry);
637 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
638 &geometry.width,&geometry.height);
639 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,1.0,
641 blob_info=CloneImageInfo(image_info);
642 (void) AcquireUniqueFilename(blob_info->filename);
643 if ((image_info->type != TrueColorType) &&
644 (IsImageGray(image,exception) != MagickFalse))
645 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
647 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
648 (void) RelinquishUniqueFileResource(blob_info->filename);
649 blob_info=DestroyImageInfo(blob_info);
650 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
652 quantize_info=AcquireQuantizeInfo(image_info);
653 status=RemapImage(quantize_info,picon,affinity_image,exception);
654 quantize_info=DestroyQuantizeInfo(quantize_info);
655 affinity_image=DestroyImage(affinity_image);
656 transparent=MagickFalse;
657 if (picon->storage_class == PseudoClass)
659 (void) CompressImageColormap(picon,exception);
660 if (picon->matte != MagickFalse)
661 transparent=MagickTrue;
666 Convert DirectClass to PseudoClass picon.
668 if (picon->matte != MagickFalse)
671 Map all the transparent pixels.
673 for (y=0; y < (ssize_t) picon->rows; y++)
675 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
676 if (q == (Quantum *) NULL)
678 for (x=0; x < (ssize_t) picon->columns; x++)
680 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
681 transparent=MagickTrue;
683 SetPixelAlpha(picon,OpaqueAlpha,q);
684 q+=GetPixelChannels(picon);
686 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
690 (void) SetImageType(picon,PaletteType,exception);
692 colors=picon->colors;
693 if (transparent != MagickFalse)
696 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
697 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
698 if (picon->colormap == (PixelInfo *) NULL)
699 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
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 SetPixelIndex(picon,picon->colors,q);
709 q+=GetPixelChannels(picon);
711 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
716 Compute the character per pixel.
718 characters_per_pixel=1;
719 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
720 characters_per_pixel++;
724 (void) WriteBlobString(image,"/* XPM */\n");
725 GetPathComponent(picon->filename,BasePath,basename);
726 (void) FormatLocaleString(buffer,MaxTextExtent,
727 "static char *%s[] = {\n",basename);
728 (void) WriteBlobString(image,buffer);
729 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
730 (void) FormatLocaleString(buffer,MaxTextExtent,
731 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
732 picon->rows,(double) colors,(double) characters_per_pixel);
733 (void) WriteBlobString(image,buffer);
734 GetPixelInfo(image,&pixel);
735 for (i=0; i < (ssize_t) colors; i++)
740 pixel=picon->colormap[i];
741 pixel.colorspace=RGBColorspace;
743 pixel.alpha=(MagickRealType) OpaqueAlpha;
744 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
745 if (transparent != MagickFalse)
747 if (i == (ssize_t) (colors-1))
748 (void) CopyMagickString(name,"grey75",MaxTextExtent);
755 for (j=1; j < (ssize_t) characters_per_pixel; j++)
757 k=((i-k)/MaxCixels) % MaxCixels;
761 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
763 (void) WriteBlobString(image,buffer);
768 (void) WriteBlobString(image,"/* pixels */\n");
769 for (y=0; y < (ssize_t) picon->rows; y++)
771 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
772 if (p == (const Quantum *) NULL)
774 (void) WriteBlobString(image,"\"");
775 for (x=0; x < (ssize_t) picon->columns; x++)
777 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
779 for (j=1; j < (ssize_t) characters_per_pixel; j++)
781 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
785 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
786 (void) WriteBlobString(image,buffer);
787 p+=GetPixelChannels(image);
789 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
790 y == (ssize_t) (picon->rows-1) ? "" : ",");
791 (void) WriteBlobString(image,buffer);
792 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
794 if (status == MagickFalse)
797 picon=DestroyImage(picon);
798 (void) WriteBlobString(image,"};\n");
799 (void) CloseBlob(image);
804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808 % W r i t e X P M I m a g e %
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814 % WriteXPMImage() writes an image to a file in the X pixmap format.
816 % The format of the WriteXPMImage method is:
818 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
819 % Image *image,ExceptionInfo *exception)
821 % A description of each parameter follows.
823 % o image_info: the image info.
825 % o image: The image.
827 % o exception: return any errors or warnings in this structure.
830 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
831 ExceptionInfo *exception)
836 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
837 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
840 buffer[MaxTextExtent],
841 basename[MaxTextExtent],
843 symbol[MaxTextExtent];
851 register const Quantum
859 characters_per_pixel;
868 Open output image file.
870 assert(image_info != (const ImageInfo *) NULL);
871 assert(image_info->signature == MagickSignature);
872 assert(image != (Image *) NULL);
873 assert(image->signature == MagickSignature);
874 if (image->debug != MagickFalse)
875 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
876 assert(exception != (ExceptionInfo *) NULL);
877 assert(exception->signature == MagickSignature);
878 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
879 if (status == MagickFalse)
881 if (IsRGBColorspace(image->colorspace) == MagickFalse)
882 (void) TransformImageColorspace(image,RGBColorspace,exception);
884 if (image->matte == MagickFalse)
886 if ((image->storage_class == DirectClass) || (image->colors > 256))
887 (void) SetImageType(image,PaletteType,exception);
896 Identify transparent colormap index.
898 if ((image->storage_class == DirectClass) || (image->colors > 256))
899 (void) SetImageType(image,PaletteBilevelMatteType,exception);
900 for (i=0; i < (ssize_t) image->colors; i++)
901 if (image->colormap[i].alpha != OpaqueAlpha)
908 alpha=(MagickRealType) TransparentAlpha-(MagickRealType)
909 image->colormap[i].alpha;
910 beta=(MagickRealType) TransparentAlpha-(MagickRealType)
911 image->colormap[opacity].alpha;
917 (void) SetImageType(image,PaletteBilevelMatteType,exception);
918 for (i=0; i < (ssize_t) image->colors; i++)
919 if (image->colormap[i].alpha != OpaqueAlpha)
926 alpha=(Quantum) TransparentAlpha-(MagickRealType)
927 image->colormap[i].alpha;
928 beta=(Quantum) TransparentAlpha-(MagickRealType)
929 image->colormap[opacity].alpha;
936 image->colormap[opacity].red=image->transparent_color.red;
937 image->colormap[opacity].green=image->transparent_color.green;
938 image->colormap[opacity].blue=image->transparent_color.blue;
942 Compute the character per pixel.
944 characters_per_pixel=1;
945 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
946 characters_per_pixel++;
950 (void) WriteBlobString(image,"/* XPM */\n");
951 GetPathComponent(image->filename,BasePath,basename);
952 if (isalnum((int) ((unsigned char) *basename)) == 0)
954 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
955 (void) CopyMagickString(basename,buffer,MaxTextExtent);
957 if (isalpha((int) ((unsigned char) basename[0])) == 0)
959 for (i=1; basename[i] != '\0'; i++)
960 if (isalnum((int) ((unsigned char) basename[i])) == 0)
962 (void) FormatLocaleString(buffer,MaxTextExtent,
963 "static char *%s[] = {\n",basename);
964 (void) WriteBlobString(image,buffer);
965 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
966 (void) FormatLocaleString(buffer,MaxTextExtent,
967 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
968 image->rows,(double) image->colors,(double) characters_per_pixel);
969 (void) WriteBlobString(image,buffer);
970 GetPixelInfo(image,&pixel);
971 for (i=0; i < (ssize_t) image->colors; i++)
976 pixel=image->colormap[i];
977 pixel.colorspace=RGBColorspace;
979 pixel.alpha=(MagickRealType) OpaqueAlpha;
980 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
982 (void) CopyMagickString(name,"None",MaxTextExtent);
988 for (j=1; j < (ssize_t) characters_per_pixel; j++)
990 k=((i-k)/MaxCixels) % MaxCixels;
994 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
996 (void) WriteBlobString(image,buffer);
1001 (void) WriteBlobString(image,"/* pixels */\n");
1002 for (y=0; y < (ssize_t) image->rows; y++)
1004 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1005 if (p == (const Quantum *) NULL)
1007 (void) WriteBlobString(image,"\"");
1008 for (x=0; x < (ssize_t) image->columns; x++)
1010 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1012 for (j=1; j < (ssize_t) characters_per_pixel; j++)
1014 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1018 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1019 (void) WriteBlobString(image,buffer);
1020 p+=GetPixelChannels(image);
1022 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1023 (y == (ssize_t) (image->rows-1) ? "" : ","));
1024 (void) WriteBlobString(image,buffer);
1025 if (image->previous == (Image *) NULL)
1027 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1029 if (status == MagickFalse)
1033 (void) WriteBlobString(image,"};\n");
1034 (void) CloseBlob(image);