xpm.c revision 35553db9b7d0d812b53b09e3906628239d908e1c
1/* 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3% % 4% % 5% % 6% X X PPPP M M % 7% X X P P MM MM % 8% X PPPP M M M % 9% X X P M M % 10% X X P M M % 11% % 12% % 13% Read/Write X Windows system Pixmap Format % 14% % 15% Software Design % 16% Cristy % 17% July 1992 % 18% % 19% % 20% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization % 21% dedicated to making software imaging solutions freely available. % 22% % 23% You may not use this file except in compliance with the License. You may % 24% obtain a copy of the License at % 25% % 26% http://www.imagemagick.org/script/license.php % 27% % 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. % 33% % 34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35% 36% 37*/ 38 39/* 40 Include declarations. 41*/ 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" 73 74/* 75 Forward declarations. 76*/ 77static MagickBooleanType 78 WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *), 79 WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *); 80 81/* 82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 83% % 84% % 85% % 86% I s X P M % 87% % 88% % 89% % 90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 91% 92% IsXPM() returns MagickTrue if the image format type, identified by the 93% magick string, is XPM. 94% 95% The format of the IsXPM method is: 96% 97% MagickBooleanType IsXPM(const unsigned char *magick,const size_t length) 98% 99% A description of each parameter follows: 100% 101% o magick: compare image format pattern against these bytes. or 102% blob. 103% 104% o length: Specifies the length of the magick string. 105% 106*/ 107static MagickBooleanType IsXPM(const unsigned char *magick,const size_t length) 108{ 109 if (length < 9) 110 return(MagickFalse); 111 if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0) 112 return(MagickTrue); 113 return(MagickFalse); 114} 115 116/* 117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 118% % 119% % 120% % 121% R e a d X P M I m a g e % 122% % 123% % 124% % 125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 126% 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. 130% 131% The format of the ReadXPMImage method is: 132% 133% Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception) 134% 135% A description of each parameter follows: 136% 137% o image_info: the image info. 138% 139% o exception: return any errors or warnings in this structure. 140% 141*/ 142 143static int CompareXPMColor(const void *target,const void *source) 144{ 145 const char 146 *p, 147 *q; 148 149 p=(const char *) target; 150 q=(const char *) source; 151 return(strcmp(p,q)); 152} 153 154static char *CopyXPMColor(char *destination,const char *source,size_t length) 155{ 156 while (length-- && (*source != '\0')) 157 *destination++=(*source++); 158 *destination='\0'; 159 return(destination-length); 160} 161 162static char *NextXPMLine(char *p) 163{ 164 assert(p != (char*)NULL); 165 p=strchr(p,'\n'); 166 if (p != (char *) NULL) 167 p++; 168 return(p); 169} 170 171static inline size_t MagickMin(const size_t x,const size_t y) 172{ 173 if (x < y) 174 return(x); 175 return(y); 176} 177 178static char *ParseXPMColor(char *color,MagickBooleanType search_start) 179{ 180#define NumberTargets 6 181 182 register char 183 *p, 184 *r; 185 186 register const char 187 *q; 188 189 register ssize_t 190 i; 191 192 static const char 193 *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " }; 194 195 if (search_start != MagickFalse) 196 { 197 for (i=0; i < NumberTargets; i++) 198 { 199 p=color; 200 for (q=targets[i]; *p != '\0'; p++) 201 { 202 if (*p == '\n') 203 break; 204 if (*p != *q) 205 continue; 206 if (isspace((int) ((unsigned char) (*(p-1)))) == 0) 207 continue; 208 r=p; 209 for ( ; ; ) 210 { 211 if (*q == '\0') 212 return(p); 213 if (*r++ != *q++) 214 break; 215 } 216 q=targets[i]; 217 } 218 } 219 return((char *) NULL); 220 } 221 else 222 { 223 for (p=color+1; *p != '\0'; p++) 224 { 225 if (*p == '\n') 226 break; 227 if (isspace((int) ((unsigned char) (*(p-1)))) == 0) 228 continue; 229 if (isspace((int) ((unsigned char) (*p))) != 0) 230 continue; 231 for (i=0; i < NumberTargets; i++) 232 { 233 if (*p == *targets[i] && *(p+1) == *(targets[i]+1)) 234 return(p); 235 } 236 } 237 return(p); 238 } 239} 240 241static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception) 242{ 243 char 244 *grey, 245 key[MaxTextExtent], 246 target[MaxTextExtent], 247 *xpm_buffer; 248 249 Image 250 *image; 251 252 MagickBooleanType 253 active, 254 status; 255 256 register char 257 *next, 258 *p, 259 *q; 260 261 register ssize_t 262 x; 263 264 register Quantum 265 *r; 266 267 size_t 268 length; 269 270 SplayTreeInfo 271 *xpm_colors; 272 273 ssize_t 274 count, 275 j, 276 y; 277 278 unsigned long 279 colors, 280 columns, 281 rows, 282 width; 283 284 /* 285 Open image file. 286 */ 287 assert(image_info != (const ImageInfo *) NULL); 288 assert(image_info->signature == MagickSignature); 289 if (image_info->debug != MagickFalse) 290 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 291 image_info->filename); 292 assert(exception != (ExceptionInfo *) NULL); 293 assert(exception->signature == MagickSignature); 294 image=AcquireImage(image_info,exception); 295 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 296 if (status == MagickFalse) 297 { 298 image=DestroyImageList(image); 299 return((Image *) NULL); 300 } 301 /* 302 Read XPM file. 303 */ 304 length=MaxTextExtent; 305 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer)); 306 p=xpm_buffer; 307 if (xpm_buffer != (char *) NULL) 308 while (ReadBlobString(image,p) != (char *) NULL) 309 { 310 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n'))) 311 continue; 312 if ((*p == '}') && (*(p+1) == ';')) 313 break; 314 p+=strlen(p); 315 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length) 316 continue; 317 length<<=1; 318 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent, 319 sizeof(*xpm_buffer)); 320 if (xpm_buffer == (char *) NULL) 321 break; 322 p=xpm_buffer+strlen(xpm_buffer); 323 } 324 if (xpm_buffer == (char *) NULL) 325 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 326 /* 327 Remove comments. 328 */ 329 count=0; 330 width=0; 331 for (p=xpm_buffer; *p != '\0'; p++) 332 { 333 if (*p != '"') 334 continue; 335 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width); 336 image->columns=columns; 337 image->rows=rows; 338 image->colors=colors; 339 if (count == 4) 340 break; 341 } 342 if ((count != 4) || (width > 10) || (image->columns == 0) || 343 (image->rows == 0) || (image->colors == 0)) 344 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 345 /* 346 Remove unquoted characters. 347 */ 348 active=MagickFalse; 349 q=xpm_buffer; 350 while (*p != '\0') 351 { 352 if (*p++ == '"') 353 { 354 if (active != MagickFalse) 355 *q++='\n'; 356 active=active != MagickFalse ? MagickFalse : MagickTrue; 357 } 358 if (active != MagickFalse) 359 *q++=(*p); 360 } 361 *q='\0'; 362 /* 363 Initialize image structure. 364 */ 365 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory, 366 (void *(*)(void *)) NULL); 367 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) 368 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 369 /* 370 Read image colormap. 371 */ 372 image->depth=1; 373 next=NextXPMLine(xpm_buffer); 374 for (j=0; (j < (ssize_t) image->colors) && (next != (char*) NULL); j++) 375 { 376 p=next; 377 next=NextXPMLine(p); 378 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent)); 379 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j); 380 /* 381 Parse color. 382 */ 383 (void) CopyMagickString(target,"gray",MaxTextExtent); 384 q=ParseXPMColor(p+width,MagickTrue); 385 if (q != (char *) NULL) 386 { 387 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0')) 388 q++; 389 if (next != (char *) NULL) 390 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q), 391 MaxTextExtent)); 392 else 393 (void) CopyMagickString(target,q,MaxTextExtent); 394 q=ParseXPMColor(target,MagickFalse); 395 if (q != (char *) NULL) 396 *q='\0'; 397 } 398 StripString(target); 399 grey=strstr(target,"grey"); 400 if (grey != (char *) NULL) 401 grey[2]='a'; 402 if (LocaleCompare(target,"none") == 0) 403 { 404 image->storage_class=DirectClass; 405 image->alpha_trait=BlendPixelTrait; 406 } 407 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j], 408 exception); 409 if (status == MagickFalse) 410 break; 411 if (image->depth < image->colormap[j].depth) 412 image->depth=image->colormap[j].depth; 413 } 414 if (j < (ssize_t) image->colors) 415 ThrowReaderException(CorruptImageError,"CorruptImage"); 416 j=0; 417 if (image_info->ping == MagickFalse) 418 { 419 /* 420 Read image pixels. 421 */ 422 for (y=0; y < (ssize_t) image->rows; y++) 423 { 424 p=NextXPMLine(p); 425 if (p == (char *) NULL) 426 break; 427 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 428 if (r == (Quantum *) NULL) 429 break; 430 for (x=0; x < (ssize_t) image->columns; x++) 431 { 432 (void) CopyXPMColor(key,p,(size_t) width); 433 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key); 434 if (image->storage_class == PseudoClass) 435 SetPixelIndex(image,j,r); 436 SetPixelInfoPixel(image,image->colormap+j,r); 437 p+=width; 438 r+=GetPixelChannels(image); 439 } 440 if (SyncAuthenticPixels(image,exception) == MagickFalse) 441 break; 442 } 443 if (y < (ssize_t) image->rows) 444 ThrowReaderException(CorruptImageError,"NotEnoughPixelData"); 445 } 446 /* 447 Relinquish resources. 448 */ 449 xpm_colors=DestroySplayTree(xpm_colors); 450 (void) CloseBlob(image); 451 return(GetFirstImageInList(image)); 452} 453 454/* 455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 456% % 457% % 458% % 459% R e g i s t e r X P M I m a g e % 460% % 461% % 462% % 463%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 464% 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. 471% 472% The format of the RegisterXPMImage method is: 473% 474% size_t RegisterXPMImage(void) 475% 476*/ 477ModuleExport size_t RegisterXPMImage(void) 478{ 479 MagickInfo 480 *entry; 481 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); 506} 507 508/* 509%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 510% % 511% % 512% % 513% U n r e g i s t e r X P M I m a g e % 514% % 515% % 516% % 517%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 518% 519% UnregisterXPMImage() removes format registrations made by the 520% XPM module from the list of supported formats. 521% 522% The format of the UnregisterXPMImage method is: 523% 524% UnregisterXPMImage(void) 525% 526*/ 527ModuleExport void UnregisterXPMImage(void) 528{ 529 (void) UnregisterMagickInfo("PICON"); 530 (void) UnregisterMagickInfo("PM"); 531 (void) UnregisterMagickInfo("XPM"); 532} 533 534/* 535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 536% % 537% % 538% % 539% W r i t e P I C O N I m a g e % 540% % 541% % 542% % 543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 544% 545% WritePICONImage() writes an image to a file in the Personal Icon format. 546% 547% The format of the WritePICONImage method is: 548% 549% MagickBooleanType WritePICONImage(const ImageInfo *image_info, 550% Image *image,ExceptionInfo *exception) 551% 552% A description of each parameter follows. 553% 554% o image_info: the image info. 555% 556% o image: The image. 557% 558% o exception: return any errors or warnings in this structure. 559% 560*/ 561static MagickBooleanType WritePICONImage(const ImageInfo *image_info, 562 Image *image,ExceptionInfo *exception) 563{ 564#define ColormapExtent 155 565#define GraymapExtent 95 566#define PiconGeometry "48x48>" 567 568 static unsigned char 569 Colormap[]= 570 { 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, 584 }, 585 Graymap[]= 586 { 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, 595 }; 596 597#define MaxCixels 92 598 599 static const char 600 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" 601 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; 602 603 char 604 buffer[MaxTextExtent], 605 basename[MaxTextExtent], 606 name[MaxTextExtent], 607 symbol[MaxTextExtent]; 608 609 Image 610 *affinity_image, 611 *picon; 612 613 ImageInfo 614 *blob_info; 615 616 MagickBooleanType 617 status, 618 transparent; 619 620 PixelInfo 621 pixel; 622 623 QuantizeInfo 624 *quantize_info; 625 626 RectangleInfo 627 geometry; 628 629 register const Quantum 630 *p; 631 632 register ssize_t 633 i, 634 x; 635 636 register Quantum 637 *q; 638 639 size_t 640 characters_per_pixel, 641 colors; 642 643 ssize_t 644 j, 645 k, 646 y; 647 648 /* 649 Open output image file. 650 */ 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) 661 return(status); 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, 667 exception); 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); 673 else 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)) 678 return(MagickFalse); 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) 685 { 686 (void) CompressImageColormap(picon,exception); 687 if (picon->alpha_trait == BlendPixelTrait) 688 transparent=MagickTrue; 689 } 690 else 691 { 692 /* 693 Convert DirectClass to PseudoClass picon. 694 */ 695 if (picon->alpha_trait == BlendPixelTrait) 696 { 697 /* 698 Map all the transparent pixels. 699 */ 700 for (y=0; y < (ssize_t) picon->rows; y++) 701 { 702 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); 703 if (q == (Quantum *) NULL) 704 break; 705 for (x=0; x < (ssize_t) picon->columns; x++) 706 { 707 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) 708 transparent=MagickTrue; 709 else 710 SetPixelAlpha(picon,OpaqueAlpha,q); 711 q+=GetPixelChannels(picon); 712 } 713 if (SyncAuthenticPixels(picon,exception) == MagickFalse) 714 break; 715 } 716 } 717 (void) SetImageType(picon,PaletteType,exception); 718 } 719 colors=picon->colors; 720 if (transparent != MagickFalse) 721 { 722 colors++; 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++) 728 { 729 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); 730 if (q == (Quantum *) NULL) 731 break; 732 for (x=0; x < (ssize_t) picon->columns; x++) 733 { 734 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) 735 SetPixelIndex(picon,picon->colors,q); 736 q+=GetPixelChannels(picon); 737 } 738 if (SyncAuthenticPixels(picon,exception) == MagickFalse) 739 break; 740 } 741 } 742 /* 743 Compute the character per pixel. 744 */ 745 characters_per_pixel=1; 746 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels) 747 characters_per_pixel++; 748 /* 749 XPM header. 750 */ 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++) 763 { 764 /* 765 Define XPM color. 766 */ 767 pixel=picon->colormap[i]; 768 pixel.colorspace=sRGBColorspace; 769 pixel.depth=8; 770 pixel.alpha=(double) OpaqueAlpha; 771 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); 772 if (transparent != MagickFalse) 773 { 774 if (i == (ssize_t) (colors-1)) 775 (void) CopyMagickString(name,"grey75",MaxTextExtent); 776 } 777 /* 778 Write XPM color. 779 */ 780 k=i % MaxCixels; 781 symbol[0]=Cixel[k]; 782 for (j=1; j < (ssize_t) characters_per_pixel; j++) 783 { 784 k=((i-k)/MaxCixels) % MaxCixels; 785 symbol[j]=Cixel[k]; 786 } 787 symbol[j]='\0'; 788 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n", 789 symbol,name); 790 (void) WriteBlobString(image,buffer); 791 } 792 /* 793 Define XPM pixels. 794 */ 795 (void) WriteBlobString(image,"/* pixels */\n"); 796 for (y=0; y < (ssize_t) picon->rows; y++) 797 { 798 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception); 799 if (p == (const Quantum *) NULL) 800 break; 801 (void) WriteBlobString(image,"\""); 802 for (x=0; x < (ssize_t) picon->columns; x++) 803 { 804 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels); 805 symbol[0]=Cixel[k]; 806 for (j=1; j < (ssize_t) characters_per_pixel; j++) 807 { 808 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels; 809 symbol[j]=Cixel[k]; 810 } 811 symbol[j]='\0'; 812 (void) CopyMagickString(buffer,symbol,MaxTextExtent); 813 (void) WriteBlobString(image,buffer); 814 p+=GetPixelChannels(image); 815 } 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, 820 picon->rows); 821 if (status == MagickFalse) 822 break; 823 } 824 picon=DestroyImage(picon); 825 (void) WriteBlobString(image,"};\n"); 826 (void) CloseBlob(image); 827 return(MagickTrue); 828} 829 830/* 831%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 832% % 833% % 834% % 835% W r i t e X P M I m a g e % 836% % 837% % 838% % 839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 840% 841% WriteXPMImage() writes an image to a file in the X pixmap format. 842% 843% The format of the WriteXPMImage method is: 844% 845% MagickBooleanType WriteXPMImage(const ImageInfo *image_info, 846% Image *image,ExceptionInfo *exception) 847% 848% A description of each parameter follows. 849% 850% o image_info: the image info. 851% 852% o image: The image. 853% 854% o exception: return any errors or warnings in this structure. 855% 856*/ 857static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image, 858 ExceptionInfo *exception) 859{ 860#define MaxCixels 92 861 862 static const char 863 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" 864 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; 865 866 char 867 buffer[MaxTextExtent], 868 basename[MaxTextExtent], 869 name[MaxTextExtent], 870 symbol[MaxTextExtent]; 871 872 MagickBooleanType 873 status; 874 875 PixelInfo 876 pixel; 877 878 register const Quantum 879 *p; 880 881 register ssize_t 882 i, 883 x; 884 885 size_t 886 characters_per_pixel; 887 888 ssize_t 889 j, 890 k, 891 opacity, 892 y; 893 894 /* 895 Open output image file. 896 */ 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) 907 return(status); 908 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) 909 (void) TransformImageColorspace(image,sRGBColorspace,exception); 910 opacity=(-1); 911 if (image->alpha_trait != BlendPixelTrait) 912 { 913 if ((image->storage_class == DirectClass) || (image->colors > 256)) 914 (void) SetImageType(image,PaletteType,exception); 915 } 916 else 917 { 918 double 919 alpha, 920 beta; 921 922 /* 923 Identify transparent colormap index. 924 */ 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) 929 { 930 if (opacity < 0) 931 { 932 opacity=i; 933 continue; 934 } 935 alpha=(double) TransparentAlpha-(double) 936 image->colormap[i].alpha; 937 beta=(double) TransparentAlpha-(double) 938 image->colormap[opacity].alpha; 939 if (alpha < beta) 940 opacity=i; 941 } 942 if (opacity == -1) 943 { 944 (void) SetImageType(image,PaletteBilevelMatteType,exception); 945 for (i=0; i < (ssize_t) image->colors; i++) 946 if (image->colormap[i].alpha != OpaqueAlpha) 947 { 948 if (opacity < 0) 949 { 950 opacity=i; 951 continue; 952 } 953 alpha=(Quantum) TransparentAlpha-(double) 954 image->colormap[i].alpha; 955 beta=(Quantum) TransparentAlpha-(double) 956 image->colormap[opacity].alpha; 957 if (alpha < beta) 958 opacity=i; 959 } 960 } 961 if (opacity >= 0) 962 { 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; 966 } 967 } 968 /* 969 Compute the character per pixel. 970 */ 971 characters_per_pixel=1; 972 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels) 973 characters_per_pixel++; 974 /* 975 XPM header. 976 */ 977 (void) WriteBlobString(image,"/* XPM */\n"); 978 GetPathComponent(image->filename,BasePath,basename); 979 if (isalnum((int) ((unsigned char) *basename)) == 0) 980 { 981 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename); 982 (void) CopyMagickString(basename,buffer,MaxTextExtent); 983 } 984 if (isalpha((int) ((unsigned char) basename[0])) == 0) 985 basename[0]='_'; 986 for (i=1; basename[i] != '\0'; i++) 987 if (isalnum((int) ((unsigned char) basename[i])) == 0) 988 basename[i]='_'; 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++) 999 { 1000 /* 1001 Define XPM color. 1002 */ 1003 pixel=image->colormap[i]; 1004 pixel.colorspace=sRGBColorspace; 1005 pixel.depth=8; 1006 pixel.alpha=(double) OpaqueAlpha; 1007 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); 1008 if (i == opacity) 1009 (void) CopyMagickString(name,"None",MaxTextExtent); 1010 /* 1011 Write XPM color. 1012 */ 1013 k=i % MaxCixels; 1014 symbol[0]=Cixel[k]; 1015 for (j=1; j < (ssize_t) characters_per_pixel; j++) 1016 { 1017 k=((i-k)/MaxCixels) % MaxCixels; 1018 symbol[j]=Cixel[k]; 1019 } 1020 symbol[j]='\0'; 1021 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol, 1022 name); 1023 (void) WriteBlobString(image,buffer); 1024 } 1025 /* 1026 Define XPM pixels. 1027 */ 1028 (void) WriteBlobString(image,"/* pixels */\n"); 1029 for (y=0; y < (ssize_t) image->rows; y++) 1030 { 1031 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 1032 if (p == (const Quantum *) NULL) 1033 break; 1034 (void) WriteBlobString(image,"\""); 1035 for (x=0; x < (ssize_t) image->columns; x++) 1036 { 1037 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels); 1038 symbol[0]=Cixel[k]; 1039 for (j=1; j < (ssize_t) characters_per_pixel; j++) 1040 { 1041 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels; 1042 symbol[j]=Cixel[k]; 1043 } 1044 symbol[j]='\0'; 1045 (void) CopyMagickString(buffer,symbol,MaxTextExtent); 1046 (void) WriteBlobString(image,buffer); 1047 p+=GetPixelChannels(image); 1048 } 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) 1053 { 1054 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 1055 image->rows); 1056 if (status == MagickFalse) 1057 break; 1058 } 1059 } 1060 (void) WriteBlobString(image,"};\n"); 1061 (void) CloseBlob(image); 1062 return(MagickTrue); 1063} 1064