| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % X X PPPP M M % |
| % X X P P MM MM % |
| % X PPPP M M M % |
| % X X P M M % |
| % X X P M M % |
| % % |
| % % |
| % Read/Write X Windows system Pixmap Format % |
| % % |
| % Software Design % |
| % Cristy % |
| % July 1992 % |
| % % |
| % % |
| % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization % |
| % dedicated to making software imaging solutions freely available. % |
| % % |
| % You may not use this file except in compliance with the License. You may % |
| % obtain a copy of the License at % |
| % % |
| % https://www.imagemagick.org/script/license.php % |
| % % |
| % Unless required by applicable law or agreed to in writing, software % |
| % distributed under the License is distributed on an "AS IS" BASIS, % |
| % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
| % See the License for the specific language governing permissions and % |
| % limitations under the License. % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % |
| */ |
| |
| /* |
| Include declarations. |
| */ |
| #include "MagickCore/studio.h" |
| #include "MagickCore/attribute.h" |
| #include "MagickCore/blob.h" |
| #include "MagickCore/blob-private.h" |
| #include "MagickCore/cache.h" |
| #include "MagickCore/color.h" |
| #include "MagickCore/color-private.h" |
| #include "MagickCore/colormap.h" |
| #include "MagickCore/colorspace.h" |
| #include "MagickCore/colorspace-private.h" |
| #include "MagickCore/exception.h" |
| #include "MagickCore/exception-private.h" |
| #include "MagickCore/geometry.h" |
| #include "MagickCore/image.h" |
| #include "MagickCore/image-private.h" |
| #include "MagickCore/list.h" |
| #include "MagickCore/magick.h" |
| #include "MagickCore/memory_.h" |
| #include "MagickCore/monitor.h" |
| #include "MagickCore/monitor-private.h" |
| #include "MagickCore/pixel-accessor.h" |
| #include "MagickCore/quantize.h" |
| #include "MagickCore/quantum-private.h" |
| #include "MagickCore/resize.h" |
| #include "MagickCore/resource_.h" |
| #include "MagickCore/splay-tree.h" |
| #include "MagickCore/static.h" |
| #include "MagickCore/string_.h" |
| #include "MagickCore/module.h" |
| #include "MagickCore/threshold.h" |
| #include "MagickCore/utility.h" |
| |
| /* |
| Forward declarations. |
| */ |
| static MagickBooleanType |
| WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *), |
| WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *); |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % I s X P M % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % IsXPM() returns MagickTrue if the image format type, identified by the |
| % magick string, is XPM. |
| % |
| % The format of the IsXPM method is: |
| % |
| % MagickBooleanType IsXPM(const unsigned char *magick,const size_t length) |
| % |
| % A description of each parameter follows: |
| % |
| % o magick: compare image format pattern against these bytes. or |
| % blob. |
| % |
| % o length: Specifies the length of the magick string. |
| % |
| */ |
| static MagickBooleanType IsXPM(const unsigned char *magick,const size_t length) |
| { |
| if (length < 9) |
| return(MagickFalse); |
| if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0) |
| return(MagickTrue); |
| return(MagickFalse); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % R e a d X P M I m a g e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % ReadXPMImage() reads an X11 pixmap image file and returns it. It |
| % allocates the memory necessary for the new Image structure and returns a |
| % pointer to the new image. |
| % |
| % The format of the ReadXPMImage method is: |
| % |
| % Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception) |
| % |
| % A description of each parameter follows: |
| % |
| % o image_info: the image info. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| |
| static int CompareXPMColor(const void *target,const void *source) |
| { |
| const char |
| *p, |
| *q; |
| |
| p=(const char *) target; |
| q=(const char *) source; |
| return(strcmp(p,q)); |
| } |
| |
| static ssize_t CopyXPMColor(char *destination,const char *source,size_t length) |
| { |
| register const char |
| *p; |
| |
| p=source; |
| while (length-- && (*p != '\0')) |
| *destination++=(*p++); |
| if (length != 0) |
| *destination='\0'; |
| return((ssize_t) (p-source)); |
| } |
| |
| static char *NextXPMLine(char *p) |
| { |
| assert(p != (char *) NULL); |
| p=strchr(p,'\n'); |
| if (p != (char *) NULL) |
| p++; |
| return(p); |
| } |
| |
| static char *ParseXPMColor(char *color,MagickBooleanType search_start) |
| { |
| #define NumberTargets 6 |
| |
| register char |
| *p, |
| *r; |
| |
| register const char |
| *q; |
| |
| register ssize_t |
| i; |
| |
| static const char |
| *const targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " }; |
| |
| if (search_start != MagickFalse) |
| { |
| for (i=0; i < NumberTargets; i++) |
| { |
| p=color; |
| for (q=targets[i]; *p != '\0'; p++) |
| { |
| if (*p == '\n') |
| break; |
| if (*p != *q) |
| continue; |
| if (isspace((int) ((unsigned char) (*(p-1)))) == 0) |
| continue; |
| r=p; |
| for ( ; ; ) |
| { |
| if (*q == '\0') |
| return(p); |
| if (*r++ != *q++) |
| break; |
| } |
| q=targets[i]; |
| } |
| } |
| return((char *) NULL); |
| } |
| for (p=color+1; *p != '\0'; p++) |
| { |
| if (*p == '\n') |
| break; |
| if (isspace((int) ((unsigned char) (*(p-1)))) == 0) |
| continue; |
| if (isspace((int) ((unsigned char) (*p))) != 0) |
| continue; |
| for (i=0; i < NumberTargets; i++) |
| { |
| if ((*p == *targets[i]) && (*(p+1) == *(targets[i]+1))) |
| return(p); |
| } |
| } |
| return(p); |
| } |
| |
| static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception) |
| { |
| char |
| *grey, |
| key[MagickPathExtent], |
| target[MagickPathExtent], |
| *xpm_buffer; |
| |
| Image |
| *image; |
| |
| MagickBooleanType |
| active, |
| status; |
| |
| register char |
| *next, |
| *p, |
| *q; |
| |
| register ssize_t |
| x; |
| |
| register Quantum |
| *r; |
| |
| size_t |
| length; |
| |
| SplayTreeInfo |
| *xpm_colors; |
| |
| ssize_t |
| count, |
| j, |
| y; |
| |
| unsigned long |
| colors, |
| columns, |
| rows, |
| width; |
| |
| /* |
| Open image file. |
| */ |
| assert(image_info != (const ImageInfo *) NULL); |
| assert(image_info->signature == MagickCoreSignature); |
| if (image_info->debug != MagickFalse) |
| (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
| image_info->filename); |
| assert(exception != (ExceptionInfo *) NULL); |
| assert(exception->signature == MagickCoreSignature); |
| image=AcquireImage(image_info,exception); |
| status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
| if (status == MagickFalse) |
| { |
| image=DestroyImageList(image); |
| return((Image *) NULL); |
| } |
| /* |
| Read XPM file. |
| */ |
| length=MagickPathExtent; |
| xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer)); |
| if (xpm_buffer == (char *) NULL) |
| ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
| *xpm_buffer='\0'; |
| p=xpm_buffer; |
| while (ReadBlobString(image,p) != (char *) NULL) |
| { |
| if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n'))) |
| continue; |
| if ((*p == '}') && (*(p+1) == ';')) |
| break; |
| p+=strlen(p); |
| if ((size_t) (p-xpm_buffer+MagickPathExtent) < length) |
| continue; |
| length<<=1; |
| xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MagickPathExtent, |
| sizeof(*xpm_buffer)); |
| if (xpm_buffer == (char *) NULL) |
| break; |
| p=xpm_buffer+strlen(xpm_buffer); |
| } |
| if (xpm_buffer == (char *) NULL) |
| ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
| /* |
| Remove comments. |
| */ |
| count=0; |
| width=0; |
| for (p=xpm_buffer; *p != '\0'; p++) |
| { |
| if (*p != '"') |
| continue; |
| count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width); |
| image->columns=columns; |
| image->rows=rows; |
| image->colors=colors; |
| if (count == 4) |
| break; |
| } |
| if ((count != 4) || (width == 0) || (width > 3) || |
| (image->columns == 0) || (image->rows == 0) || |
| (image->colors == 0) || (image->colors > MaxColormapSize)) |
| { |
| xpm_buffer=DestroyString(xpm_buffer); |
| ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
| } |
| /* |
| Remove unquoted characters. |
| */ |
| active=MagickFalse; |
| for (q=xpm_buffer; *p != '\0'; ) |
| { |
| if (*p++ == '"') |
| { |
| if (active != MagickFalse) |
| *q++='\n'; |
| active=active != MagickFalse ? MagickFalse : MagickTrue; |
| } |
| if (active != MagickFalse) |
| *q++=(*p); |
| } |
| *q='\0'; |
| /* |
| Initialize image structure. |
| */ |
| xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory, |
| (void *(*)(void *)) NULL); |
| if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) |
| { |
| xpm_colors=DestroySplayTree(xpm_colors); |
| xpm_buffer=DestroyString(xpm_buffer); |
| ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
| } |
| /* |
| Read image colormap. |
| */ |
| image->depth=1; |
| next=NextXPMLine(xpm_buffer); |
| for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++) |
| { |
| p=next; |
| next=NextXPMLine(p); |
| if (next == (char *) NULL) |
| break; |
| (void) CopyXPMColor(key,p,MagickMin((size_t) width,MagickPathExtent-1)); |
| status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j); |
| /* |
| Parse color. |
| */ |
| (void) CopyMagickString(target,"gray",MagickPathExtent); |
| q=ParseXPMColor(p+width,MagickTrue); |
| if (q != (char *) NULL) |
| { |
| while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0')) |
| q++; |
| if ((next-q) < 0) |
| break; |
| if (next != (char *) NULL) |
| (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q), |
| MagickPathExtent-1)); |
| else |
| (void) CopyMagickString(target,q,MagickPathExtent); |
| q=ParseXPMColor(target,MagickFalse); |
| if (q != (char *) NULL) |
| *q='\0'; |
| } |
| StripString(target); |
| grey=strstr(target,"grey"); |
| if (grey != (char *) NULL) |
| grey[2]='a'; |
| if (LocaleCompare(target,"none") == 0) |
| { |
| image->storage_class=DirectClass; |
| image->alpha_trait=BlendPixelTrait; |
| } |
| status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j], |
| exception); |
| if (status == MagickFalse) |
| break; |
| if (image->depth < image->colormap[j].depth) |
| image->depth=image->colormap[j].depth; |
| } |
| if (j < (ssize_t) image->colors) |
| { |
| xpm_colors=DestroySplayTree(xpm_colors); |
| xpm_buffer=DestroyString(xpm_buffer); |
| ThrowReaderException(CorruptImageError,"CorruptImage"); |
| } |
| j=0; |
| if (image_info->ping == MagickFalse) |
| { |
| /* |
| Read image pixels. |
| */ |
| status=SetImageExtent(image,image->columns,image->rows,exception); |
| if (status == MagickFalse) |
| { |
| xpm_colors=DestroySplayTree(xpm_colors); |
| xpm_buffer=DestroyString(xpm_buffer); |
| return(DestroyImageList(image)); |
| } |
| for (y=0; y < (ssize_t) image->rows; y++) |
| { |
| p=NextXPMLine(p); |
| if (p == (char *) NULL) |
| break; |
| r=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
| if (r == (Quantum *) NULL) |
| break; |
| for (x=0; x < (ssize_t) image->columns; x++) |
| { |
| ssize_t count=CopyXPMColor(key,p,MagickMin(width,MagickPathExtent-1)); |
| if (count != (ssize_t) width) |
| break; |
| j=(ssize_t) GetValueFromSplayTree(xpm_colors,key); |
| if (image->storage_class == PseudoClass) |
| SetPixelIndex(image,(Quantum) j,r); |
| SetPixelViaPixelInfo(image,image->colormap+j,r); |
| p+=count; |
| r+=GetPixelChannels(image); |
| } |
| if (x < (ssize_t) image->columns) |
| break; |
| if (SyncAuthenticPixels(image,exception) == MagickFalse) |
| break; |
| } |
| if (y < (ssize_t) image->rows) |
| { |
| xpm_colors=DestroySplayTree(xpm_colors); |
| xpm_buffer=DestroyString(xpm_buffer); |
| ThrowReaderException(CorruptImageError,"NotEnoughPixelData"); |
| } |
| } |
| /* |
| Relinquish resources. |
| */ |
| xpm_buffer=DestroyString(xpm_buffer); |
| xpm_colors=DestroySplayTree(xpm_colors); |
| (void) CloseBlob(image); |
| return(GetFirstImageInList(image)); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % R e g i s t e r X P M I m a g e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % RegisterXPMImage() adds attributes for the XPM image format to |
| % the list of supported formats. The attributes include the image format |
| % tag, a method to read and/or write the format, whether the format |
| % supports the saving of more than one frame to the same file or blob, |
| % whether the format supports native in-memory I/O, and a brief |
| % description of the format. |
| % |
| % The format of the RegisterXPMImage method is: |
| % |
| % size_t RegisterXPMImage(void) |
| % |
| */ |
| ModuleExport size_t RegisterXPMImage(void) |
| { |
| MagickInfo |
| *entry; |
| |
| entry=AcquireMagickInfo("XPM","PICON","Personal Icon"); |
| entry->decoder=(DecodeImageHandler *) ReadXPMImage; |
| entry->encoder=(EncodeImageHandler *) WritePICONImage; |
| entry->flags^=CoderAdjoinFlag; |
| (void) RegisterMagickInfo(entry); |
| entry=AcquireMagickInfo("XPM","PM","X Windows system pixmap (color)"); |
| entry->decoder=(DecodeImageHandler *) ReadXPMImage; |
| entry->encoder=(EncodeImageHandler *) WriteXPMImage; |
| entry->flags^=CoderAdjoinFlag; |
| entry->flags|=CoderStealthFlag; |
| (void) RegisterMagickInfo(entry); |
| entry=AcquireMagickInfo("XPM","XPM","X Windows system pixmap (color)"); |
| entry->decoder=(DecodeImageHandler *) ReadXPMImage; |
| entry->encoder=(EncodeImageHandler *) WriteXPMImage; |
| entry->magick=(IsImageFormatHandler *) IsXPM; |
| entry->flags^=CoderAdjoinFlag; |
| (void) RegisterMagickInfo(entry); |
| return(MagickImageCoderSignature); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % U n r e g i s t e r X P M I m a g e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % UnregisterXPMImage() removes format registrations made by the |
| % XPM module from the list of supported formats. |
| % |
| % The format of the UnregisterXPMImage method is: |
| % |
| % UnregisterXPMImage(void) |
| % |
| */ |
| ModuleExport void UnregisterXPMImage(void) |
| { |
| (void) UnregisterMagickInfo("PICON"); |
| (void) UnregisterMagickInfo("PM"); |
| (void) UnregisterMagickInfo("XPM"); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % W r i t e P I C O N I m a g e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % WritePICONImage() writes an image to a file in the Personal Icon format. |
| % |
| % The format of the WritePICONImage method is: |
| % |
| % MagickBooleanType WritePICONImage(const ImageInfo *image_info, |
| % Image *image,ExceptionInfo *exception) |
| % |
| % A description of each parameter follows. |
| % |
| % o image_info: the image info. |
| % |
| % o image: The image. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| static MagickBooleanType WritePICONImage(const ImageInfo *image_info, |
| Image *image,ExceptionInfo *exception) |
| { |
| #define ColormapExtent 155 |
| #define GraymapExtent 95 |
| #define PiconGeometry "48x48>" |
| |
| static unsigned char |
| Colormap[]= |
| { |
| 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05, |
| 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e, |
| 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, |
| 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff, |
| 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd, |
| 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00, |
| 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff, |
| 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4, |
| 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, |
| 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08, |
| 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49, |
| 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b, |
| }, |
| Graymap[]= |
| { |
| 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f, |
| 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33, |
| 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78, |
| 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba, |
| 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff, |
| 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, |
| 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31, |
| 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b, |
| }; |
| |
| #define MaxCixels 92 |
| |
| static const char |
| Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" |
| "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; |
| |
| char |
| buffer[MagickPathExtent], |
| basename[MagickPathExtent], |
| name[MagickPathExtent], |
| symbol[MagickPathExtent]; |
| |
| Image |
| *affinity_image, |
| *picon; |
| |
| ImageInfo |
| *blob_info; |
| |
| MagickBooleanType |
| status, |
| transparent; |
| |
| PixelInfo |
| pixel; |
| |
| QuantizeInfo |
| *quantize_info; |
| |
| RectangleInfo |
| geometry; |
| |
| register const Quantum |
| *p; |
| |
| register ssize_t |
| i, |
| x; |
| |
| register Quantum |
| *q; |
| |
| size_t |
| characters_per_pixel, |
| colors; |
| |
| ssize_t |
| j, |
| k, |
| y; |
| |
| /* |
| Open output image file. |
| */ |
| assert(image_info != (const ImageInfo *) NULL); |
| assert(image_info->signature == MagickCoreSignature); |
| assert(image != (Image *) NULL); |
| assert(image->signature == MagickCoreSignature); |
| if (image->debug != MagickFalse) |
| (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
| assert(exception != (ExceptionInfo *) NULL); |
| assert(exception->signature == MagickCoreSignature); |
| status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); |
| if (status == MagickFalse) |
| return(status); |
| (void) TransformImageColorspace(image,sRGBColorspace,exception); |
| SetGeometry(image,&geometry); |
| (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y, |
| &geometry.width,&geometry.height); |
| picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter, |
| exception); |
| blob_info=CloneImageInfo(image_info); |
| (void) AcquireUniqueFilename(blob_info->filename); |
| if ((image_info->type != TrueColorType) && |
| (SetImageGray(image,exception) != MagickFalse)) |
| affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception); |
| else |
| affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception); |
| (void) RelinquishUniqueFileResource(blob_info->filename); |
| blob_info=DestroyImageInfo(blob_info); |
| if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL)) |
| { |
| if (affinity_image != (Image *) NULL) |
| affinity_image=DestroyImage(affinity_image); |
| if (picon != (Image *) NULL) |
| picon=DestroyImage(picon); |
| return(MagickFalse); |
| } |
| quantize_info=AcquireQuantizeInfo(image_info); |
| status=RemapImage(quantize_info,picon,affinity_image,exception); |
| quantize_info=DestroyQuantizeInfo(quantize_info); |
| affinity_image=DestroyImage(affinity_image); |
| transparent=MagickFalse; |
| if (picon->storage_class == PseudoClass) |
| { |
| (void) CompressImageColormap(picon,exception); |
| if (picon->alpha_trait != UndefinedPixelTrait) |
| transparent=MagickTrue; |
| } |
| else |
| { |
| /* |
| Convert DirectClass to PseudoClass picon. |
| */ |
| if (picon->alpha_trait != UndefinedPixelTrait) |
| { |
| /* |
| Map all the transparent pixels. |
| */ |
| for (y=0; y < (ssize_t) picon->rows; y++) |
| { |
| q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); |
| if (q == (Quantum *) NULL) |
| break; |
| for (x=0; x < (ssize_t) picon->columns; x++) |
| { |
| if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) |
| transparent=MagickTrue; |
| else |
| SetPixelAlpha(picon,OpaqueAlpha,q); |
| q+=GetPixelChannels(picon); |
| } |
| if (SyncAuthenticPixels(picon,exception) == MagickFalse) |
| break; |
| } |
| } |
| (void) SetImageType(picon,PaletteType,exception); |
| } |
| colors=picon->colors; |
| if (transparent != MagickFalse) |
| { |
| colors++; |
| picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **) |
| picon->colormap,(size_t) colors,sizeof(*picon->colormap)); |
| if (picon->colormap == (PixelInfo *) NULL) |
| ThrowWriterException(ResourceLimitError,"MemoryAllocationError"); |
| for (y=0; y < (ssize_t) picon->rows; y++) |
| { |
| q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); |
| if (q == (Quantum *) NULL) |
| break; |
| for (x=0; x < (ssize_t) picon->columns; x++) |
| { |
| if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) |
| SetPixelIndex(picon,(Quantum) picon->colors,q); |
| q+=GetPixelChannels(picon); |
| } |
| if (SyncAuthenticPixels(picon,exception) == MagickFalse) |
| break; |
| } |
| } |
| /* |
| Compute the character per pixel. |
| */ |
| characters_per_pixel=1; |
| for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels) |
| characters_per_pixel++; |
| /* |
| XPM header. |
| */ |
| (void) WriteBlobString(image,"/* XPM */\n"); |
| GetPathComponent(picon->filename,BasePath,basename); |
| (void) FormatLocaleString(buffer,MagickPathExtent, |
| "static char *%s[] = {\n",basename); |
| (void) WriteBlobString(image,buffer); |
| (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); |
| (void) FormatLocaleString(buffer,MagickPathExtent, |
| "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double) |
| picon->rows,(double) colors,(double) characters_per_pixel); |
| (void) WriteBlobString(image,buffer); |
| GetPixelInfo(image,&pixel); |
| for (i=0; i < (ssize_t) colors; i++) |
| { |
| /* |
| Define XPM color. |
| */ |
| pixel=picon->colormap[i]; |
| pixel.colorspace=sRGBColorspace; |
| pixel.depth=8; |
| pixel.alpha=(double) OpaqueAlpha; |
| (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); |
| if (transparent != MagickFalse) |
| { |
| if (i == (ssize_t) (colors-1)) |
| (void) CopyMagickString(name,"grey75",MagickPathExtent); |
| } |
| /* |
| Write XPM color. |
| */ |
| k=i % MaxCixels; |
| symbol[0]=Cixel[k]; |
| for (j=1; j < (ssize_t) characters_per_pixel; j++) |
| { |
| k=((i-k)/MaxCixels) % MaxCixels; |
| symbol[j]=Cixel[k]; |
| } |
| symbol[j]='\0'; |
| (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n", |
| symbol,name); |
| (void) WriteBlobString(image,buffer); |
| } |
| /* |
| Define XPM pixels. |
| */ |
| (void) WriteBlobString(image,"/* pixels */\n"); |
| for (y=0; y < (ssize_t) picon->rows; y++) |
| { |
| p=GetVirtualPixels(picon,0,y,picon->columns,1,exception); |
| if (p == (const Quantum *) NULL) |
| break; |
| (void) WriteBlobString(image,"\""); |
| for (x=0; x < (ssize_t) picon->columns; x++) |
| { |
| k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels); |
| symbol[0]=Cixel[k]; |
| for (j=1; j < (ssize_t) characters_per_pixel; j++) |
| { |
| k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels; |
| symbol[j]=Cixel[k]; |
| } |
| symbol[j]='\0'; |
| (void) CopyMagickString(buffer,symbol,MagickPathExtent); |
| (void) WriteBlobString(image,buffer); |
| p+=GetPixelChannels(picon); |
| } |
| (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n", |
| y == (ssize_t) (picon->rows-1) ? "" : ","); |
| (void) WriteBlobString(image,buffer); |
| status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, |
| picon->rows); |
| if (status == MagickFalse) |
| break; |
| } |
| picon=DestroyImage(picon); |
| (void) WriteBlobString(image,"};\n"); |
| (void) CloseBlob(image); |
| return(MagickTrue); |
| } |
| |
| /* |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % % |
| % % |
| % % |
| % W r i t e X P M I m a g e % |
| % % |
| % % |
| % % |
| %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
| % |
| % WriteXPMImage() writes an image to a file in the X pixmap format. |
| % |
| % The format of the WriteXPMImage method is: |
| % |
| % MagickBooleanType WriteXPMImage(const ImageInfo *image_info, |
| % Image *image,ExceptionInfo *exception) |
| % |
| % A description of each parameter follows. |
| % |
| % o image_info: the image info. |
| % |
| % o image: The image. |
| % |
| % o exception: return any errors or warnings in this structure. |
| % |
| */ |
| static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image, |
| ExceptionInfo *exception) |
| { |
| #define MaxCixels 92 |
| |
| static const char |
| Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" |
| "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; |
| |
| char |
| buffer[MagickPathExtent], |
| basename[MagickPathExtent], |
| name[MagickPathExtent], |
| symbol[MagickPathExtent]; |
| |
| MagickBooleanType |
| status; |
| |
| PixelInfo |
| pixel; |
| |
| register const Quantum |
| *p; |
| |
| register ssize_t |
| i, |
| x; |
| |
| size_t |
| characters_per_pixel; |
| |
| ssize_t |
| j, |
| k, |
| opacity, |
| y; |
| |
| /* |
| Open output image file. |
| */ |
| assert(image_info != (const ImageInfo *) NULL); |
| assert(image_info->signature == MagickCoreSignature); |
| assert(image != (Image *) NULL); |
| assert(image->signature == MagickCoreSignature); |
| if (image->debug != MagickFalse) |
| (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
| assert(exception != (ExceptionInfo *) NULL); |
| assert(exception->signature == MagickCoreSignature); |
| status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); |
| if (status == MagickFalse) |
| return(status); |
| if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
| (void) TransformImageColorspace(image,sRGBColorspace,exception); |
| opacity=(-1); |
| if (image->alpha_trait == UndefinedPixelTrait) |
| { |
| if ((image->storage_class == DirectClass) || (image->colors > 256)) |
| (void) SetImageType(image,PaletteType,exception); |
| } |
| else |
| { |
| double |
| alpha, |
| beta; |
| |
| /* |
| Identify transparent colormap index. |
| */ |
| if ((image->storage_class == DirectClass) || (image->colors > 256)) |
| (void) SetImageType(image,PaletteBilevelAlphaType,exception); |
| for (i=0; i < (ssize_t) image->colors; i++) |
| if (image->colormap[i].alpha != OpaqueAlpha) |
| { |
| if (opacity < 0) |
| { |
| opacity=i; |
| continue; |
| } |
| alpha=(double) TransparentAlpha-(double) |
| image->colormap[i].alpha; |
| beta=(double) TransparentAlpha-(double) |
| image->colormap[opacity].alpha; |
| if (alpha < beta) |
| opacity=i; |
| } |
| if (opacity == -1) |
| { |
| (void) SetImageType(image,PaletteBilevelAlphaType,exception); |
| for (i=0; i < (ssize_t) image->colors; i++) |
| if (image->colormap[i].alpha != OpaqueAlpha) |
| { |
| if (opacity < 0) |
| { |
| opacity=i; |
| continue; |
| } |
| alpha=(Quantum) TransparentAlpha-(double) |
| image->colormap[i].alpha; |
| beta=(Quantum) TransparentAlpha-(double) |
| image->colormap[opacity].alpha; |
| if (alpha < beta) |
| opacity=i; |
| } |
| } |
| if (opacity >= 0) |
| { |
| image->colormap[opacity].red=image->transparent_color.red; |
| image->colormap[opacity].green=image->transparent_color.green; |
| image->colormap[opacity].blue=image->transparent_color.blue; |
| } |
| } |
| /* |
| Compute the character per pixel. |
| */ |
| characters_per_pixel=1; |
| for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels) |
| characters_per_pixel++; |
| /* |
| XPM header. |
| */ |
| (void) WriteBlobString(image,"/* XPM */\n"); |
| GetPathComponent(image->filename,BasePath,basename); |
| if (isalnum((int) ((unsigned char) *basename)) == 0) |
| { |
| (void) FormatLocaleString(buffer,MagickPathExtent,"xpm_%s",basename); |
| (void) CopyMagickString(basename,buffer,MagickPathExtent); |
| } |
| if (isalpha((int) ((unsigned char) basename[0])) == 0) |
| basename[0]='_'; |
| for (i=1; basename[i] != '\0'; i++) |
| if (isalnum((int) ((unsigned char) basename[i])) == 0) |
| basename[i]='_'; |
| (void) FormatLocaleString(buffer,MagickPathExtent, |
| "static char *%s[] = {\n",basename); |
| (void) WriteBlobString(image,buffer); |
| (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); |
| (void) FormatLocaleString(buffer,MagickPathExtent, |
| "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double) |
| image->rows,(double) image->colors,(double) characters_per_pixel); |
| (void) WriteBlobString(image,buffer); |
| GetPixelInfo(image,&pixel); |
| for (i=0; i < (ssize_t) image->colors; i++) |
| { |
| /* |
| Define XPM color. |
| */ |
| pixel=image->colormap[i]; |
| pixel.colorspace=sRGBColorspace; |
| pixel.depth=8; |
| pixel.alpha=(double) OpaqueAlpha; |
| (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); |
| if (i == opacity) |
| (void) CopyMagickString(name,"None",MagickPathExtent); |
| /* |
| Write XPM color. |
| */ |
| k=i % MaxCixels; |
| symbol[0]=Cixel[k]; |
| for (j=1; j < (ssize_t) characters_per_pixel; j++) |
| { |
| k=((i-k)/MaxCixels) % MaxCixels; |
| symbol[j]=Cixel[k]; |
| } |
| symbol[j]='\0'; |
| (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",symbol, |
| name); |
| (void) WriteBlobString(image,buffer); |
| } |
| /* |
| Define XPM pixels. |
| */ |
| (void) WriteBlobString(image,"/* pixels */\n"); |
| for (y=0; y < (ssize_t) image->rows; y++) |
| { |
| p=GetVirtualPixels(image,0,y,image->columns,1,exception); |
| if (p == (const Quantum *) NULL) |
| break; |
| (void) WriteBlobString(image,"\""); |
| for (x=0; x < (ssize_t) image->columns; x++) |
| { |
| k=((ssize_t) GetPixelIndex(image,p) % MaxCixels); |
| symbol[0]=Cixel[k]; |
| for (j=1; j < (ssize_t) characters_per_pixel; j++) |
| { |
| k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels; |
| symbol[j]=Cixel[k]; |
| } |
| symbol[j]='\0'; |
| (void) CopyMagickString(buffer,symbol,MagickPathExtent); |
| (void) WriteBlobString(image,buffer); |
| p+=GetPixelChannels(image); |
| } |
| (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n", |
| (y == (ssize_t) (image->rows-1) ? "" : ",")); |
| (void) WriteBlobString(image,buffer); |
| if (image->previous == (Image *) NULL) |
| { |
| status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, |
| image->rows); |
| if (status == MagickFalse) |
| break; |
| } |
| } |
| (void) WriteBlobString(image,"};\n"); |
| (void) CloseBlob(image); |
| return(MagickTrue); |
| } |