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-2016 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 /* 41 Include declarations. 42 */ 43 #include "MagickCore/studio.h" 44 #include "MagickCore/attribute.h" 45 #include "MagickCore/blob.h" 46 #include "MagickCore/blob-private.h" 47 #include "MagickCore/cache.h" 48 #include "MagickCore/color.h" 49 #include "MagickCore/color-private.h" 50 #include "MagickCore/colormap.h" 51 #include "MagickCore/colorspace.h" 52 #include "MagickCore/colorspace-private.h" 53 #include "MagickCore/exception.h" 54 #include "MagickCore/exception-private.h" 55 #include "MagickCore/geometry.h" 56 #include "MagickCore/image.h" 57 #include "MagickCore/image-private.h" 58 #include "MagickCore/list.h" 59 #include "MagickCore/magick.h" 60 #include "MagickCore/memory_.h" 61 #include "MagickCore/monitor.h" 62 #include "MagickCore/monitor-private.h" 63 #include "MagickCore/pixel-accessor.h" 64 #include "MagickCore/quantize.h" 65 #include "MagickCore/quantum-private.h" 66 #include "MagickCore/resize.h" 67 #include "MagickCore/resource_.h" 68 #include "MagickCore/splay-tree.h" 69 #include "MagickCore/static.h" 70 #include "MagickCore/string_.h" 71 #include "MagickCore/module.h" 72 #include "MagickCore/threshold.h" 73 #include "MagickCore/utility.h" 74 75 /* 77 Forward declarations. 78 */ 79 static MagickBooleanType 80 WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *), 81 WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *); 82 83 /* 85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 86 % % 87 % % 88 % % 89 % I s X P M % 90 % % 91 % % 92 % % 93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 94 % 95 % IsXPM() returns MagickTrue if the image format type, identified by the 96 % magick string, is XPM. 97 % 98 % The format of the IsXPM method is: 99 % 100 % MagickBooleanType IsXPM(const unsigned char *magick,const size_t length) 101 % 102 % A description of each parameter follows: 103 % 104 % o magick: compare image format pattern against these bytes. or 105 % blob. 106 % 107 % o length: Specifies the length of the magick string. 108 % 109 */ 110 static MagickBooleanType IsXPM(const unsigned char *magick,const size_t length) 111 { 112 if (length < 9) 113 return(MagickFalse); 114 if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0) 115 return(MagickTrue); 116 return(MagickFalse); 117 } 118 119 /* 121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 122 % % 123 % % 124 % % 125 % R e a d X P M I m a g e % 126 % % 127 % % 128 % % 129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 130 % 131 % ReadXPMImage() reads an X11 pixmap image file and returns it. It 132 % allocates the memory necessary for the new Image structure and returns a 133 % pointer to the new image. 134 % 135 % The format of the ReadXPMImage method is: 136 % 137 % Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception) 138 % 139 % A description of each parameter follows: 140 % 141 % o image_info: the image info. 142 % 143 % o exception: return any errors or warnings in this structure. 144 % 145 */ 146 147 static int CompareXPMColor(const void *target,const void *source) 148 { 149 const char 150 *p, 151 *q; 152 153 p=(const char *) target; 154 q=(const char *) source; 155 return(strcmp(p,q)); 156 } 157 158 static ssize_t CopyXPMColor(char *destination,const char *source,size_t length) 159 { 160 register const char 161 *p; 162 163 p=source; 164 while (length-- && (*p != '\0')) 165 *destination++=(*p++); 166 if (length != 0) 167 *destination='\0'; 168 return((ssize_t) (p-source)); 169 } 170 171 static char *NextXPMLine(char *p) 172 { 173 assert(p != (char *) NULL); 174 p=strchr(p,'\n'); 175 if (p != (char *) NULL) 176 p++; 177 return(p); 178 } 179 180 static char *ParseXPMColor(char *color,MagickBooleanType search_start) 181 { 182 #define NumberTargets 6 183 184 register char 185 *p, 186 *r; 187 188 register const char 189 *q; 190 191 register ssize_t 192 i; 193 194 static const char 195 *const targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " }; 196 197 if (search_start != MagickFalse) 198 { 199 for (i=0; i < NumberTargets; i++) 200 { 201 p=color; 202 for (q=targets[i]; *p != '\0'; p++) 203 { 204 if (*p == '\n') 205 break; 206 if (*p != *q) 207 continue; 208 if (isspace((int) ((unsigned char) (*(p-1)))) == 0) 209 continue; 210 r=p; 211 for ( ; ; ) 212 { 213 if (*q == '\0') 214 return(p); 215 if (*r++ != *q++) 216 break; 217 } 218 q=targets[i]; 219 } 220 } 221 return((char *) NULL); 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 static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception) 241 { 242 char 243 *grey, 244 key[MagickPathExtent], 245 target[MagickPathExtent], 246 *xpm_buffer; 247 248 Image 249 *image; 250 251 MagickBooleanType 252 active, 253 status; 254 255 register char 256 *next, 257 *p, 258 *q; 259 260 register ssize_t 261 x; 262 263 register Quantum 264 *r; 265 266 size_t 267 length; 268 269 SplayTreeInfo 270 *xpm_colors; 271 272 ssize_t 273 count, 274 j, 275 y; 276 277 unsigned long 278 colors, 279 columns, 280 rows, 281 width; 282 283 /* 284 Open image file. 285 */ 286 assert(image_info != (const ImageInfo *) NULL); 287 assert(image_info->signature == MagickCoreSignature); 288 if (image_info->debug != MagickFalse) 289 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 290 image_info->filename); 291 assert(exception != (ExceptionInfo *) NULL); 292 assert(exception->signature == MagickCoreSignature); 293 image=AcquireImage(image_info,exception); 294 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 295 if (status == MagickFalse) 296 { 297 image=DestroyImageList(image); 298 return((Image *) NULL); 299 } 300 /* 301 Read XPM file. 302 */ 303 length=MagickPathExtent; 304 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer)); 305 if (xpm_buffer == (char *) NULL) 306 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 307 *xpm_buffer='\0'; 308 p=xpm_buffer; 309 while (ReadBlobString(image,p) != (char *) NULL) 310 { 311 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n'))) 312 continue; 313 if ((*p == '}') && (*(p+1) == ';')) 314 break; 315 p+=strlen(p); 316 if ((size_t) (p-xpm_buffer+MagickPathExtent) < length) 317 continue; 318 length<<=1; 319 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MagickPathExtent, 320 sizeof(*xpm_buffer)); 321 if (xpm_buffer == (char *) NULL) 322 break; 323 p=xpm_buffer+strlen(xpm_buffer); 324 } 325 if (xpm_buffer == (char *) NULL) 326 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 327 /* 328 Remove comments. 329 */ 330 count=0; 331 width=0; 332 for (p=xpm_buffer; *p != '\0'; p++) 333 { 334 if (*p != '"') 335 continue; 336 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width); 337 image->columns=columns; 338 image->rows=rows; 339 image->colors=colors; 340 if (count == 4) 341 break; 342 } 343 if ((count != 4) || (width == 0) || (width > 2) || 344 (image->columns == 0) || (image->rows == 0) || 345 (image->colors == 0) || (image->colors > MaxColormapSize)) 346 { 347 xpm_buffer=DestroyString(xpm_buffer); 348 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 349 } 350 /* 351 Remove unquoted characters. 352 */ 353 active=MagickFalse; 354 for (q=xpm_buffer; *p != '\0'; ) 355 { 356 if (*p++ == '"') 357 { 358 if (active != MagickFalse) 359 *q++='\n'; 360 active=active != MagickFalse ? MagickFalse : MagickTrue; 361 } 362 if (active != MagickFalse) 363 *q++=(*p); 364 } 365 *q='\0'; 366 /* 367 Initialize image structure. 368 */ 369 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory, 370 (void *(*)(void *)) NULL); 371 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) 372 { 373 xpm_buffer=DestroyString(xpm_buffer); 374 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 375 } 376 /* 377 Read image colormap. 378 */ 379 image->depth=1; 380 next=NextXPMLine(xpm_buffer); 381 for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++) 382 { 383 p=next; 384 next=NextXPMLine(p); 385 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MagickPathExtent-1)); 386 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j); 387 /* 388 Parse color. 389 */ 390 (void) CopyMagickString(target,"gray",MagickPathExtent); 391 q=ParseXPMColor(p+width,MagickTrue); 392 if (q != (char *) NULL) 393 { 394 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0')) 395 q++; 396 if ((next-q) < 0) 397 break; 398 if (next != (char *) NULL) 399 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q), 400 MagickPathExtent-1)); 401 else 402 (void) CopyMagickString(target,q,MagickPathExtent); 403 q=ParseXPMColor(target,MagickFalse); 404 if (q != (char *) NULL) 405 *q='\0'; 406 } 407 StripString(target); 408 grey=strstr(target,"grey"); 409 if (grey != (char *) NULL) 410 grey[2]='a'; 411 if (LocaleCompare(target,"none") == 0) 412 { 413 image->storage_class=DirectClass; 414 image->alpha_trait=BlendPixelTrait; 415 } 416 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j], 417 exception); 418 if (status == MagickFalse) 419 break; 420 if (image->depth < image->colormap[j].depth) 421 image->depth=image->colormap[j].depth; 422 } 423 if (j < (ssize_t) image->colors) 424 { 425 xpm_colors=DestroySplayTree(xpm_colors); 426 xpm_buffer=DestroyString(xpm_buffer); 427 ThrowReaderException(CorruptImageError,"CorruptImage"); 428 } 429 j=0; 430 if (image_info->ping == MagickFalse) 431 { 432 /* 433 Read image pixels. 434 */ 435 status=SetImageExtent(image,image->columns,image->rows,exception); 436 if (status == MagickFalse) 437 return(DestroyImageList(image)); 438 for (y=0; y < (ssize_t) image->rows; y++) 439 { 440 p=NextXPMLine(p); 441 if (p == (char *) NULL) 442 break; 443 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 444 if (r == (Quantum *) NULL) 445 break; 446 for (x=0; x < (ssize_t) image->columns; x++) 447 { 448 ssize_t count=CopyXPMColor(key,p,MagickMin(width,MagickPathExtent-1)); 449 if (count != (ssize_t) width) 450 break; 451 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key); 452 if (image->storage_class == PseudoClass) 453 SetPixelIndex(image,(Quantum) j,r); 454 SetPixelViaPixelInfo(image,image->colormap+j,r); 455 p+=count; 456 r+=GetPixelChannels(image); 457 } 458 if (x < (ssize_t) image->columns) 459 break; 460 if (SyncAuthenticPixels(image,exception) == MagickFalse) 461 break; 462 } 463 if (y < (ssize_t) image->rows) 464 { 465 xpm_colors=DestroySplayTree(xpm_colors); 466 xpm_buffer=DestroyString(xpm_buffer); 467 ThrowReaderException(CorruptImageError,"NotEnoughPixelData"); 468 } 469 } 470 /* 471 Relinquish resources. 472 */ 473 xpm_buffer=DestroyString(xpm_buffer); 474 xpm_colors=DestroySplayTree(xpm_colors); 475 (void) CloseBlob(image); 476 return(GetFirstImageInList(image)); 477 } 478 479 /* 481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 482 % % 483 % % 484 % % 485 % R e g i s t e r X P M I m a g e % 486 % % 487 % % 488 % % 489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 490 % 491 % RegisterXPMImage() adds attributes for the XPM image format to 492 % the list of supported formats. The attributes include the image format 493 % tag, a method to read and/or write the format, whether the format 494 % supports the saving of more than one frame to the same file or blob, 495 % whether the format supports native in-memory I/O, and a brief 496 % description of the format. 497 % 498 % The format of the RegisterXPMImage method is: 499 % 500 % size_t RegisterXPMImage(void) 501 % 502 */ 503 ModuleExport size_t RegisterXPMImage(void) 504 { 505 MagickInfo 506 *entry; 507 508 entry=AcquireMagickInfo("XPM","PICON","Personal Icon"); 509 entry->decoder=(DecodeImageHandler *) ReadXPMImage; 510 entry->encoder=(EncodeImageHandler *) WritePICONImage; 511 entry->flags^=CoderAdjoinFlag; 512 (void) RegisterMagickInfo(entry); 513 entry=AcquireMagickInfo("XPM","PM","X Windows system pixmap (color)"); 514 entry->decoder=(DecodeImageHandler *) ReadXPMImage; 515 entry->encoder=(EncodeImageHandler *) WriteXPMImage; 516 entry->flags^=CoderAdjoinFlag; 517 entry->flags|=CoderStealthFlag; 518 (void) RegisterMagickInfo(entry); 519 entry=AcquireMagickInfo("XPM","XPM","X Windows system pixmap (color)"); 520 entry->decoder=(DecodeImageHandler *) ReadXPMImage; 521 entry->encoder=(EncodeImageHandler *) WriteXPMImage; 522 entry->magick=(IsImageFormatHandler *) IsXPM; 523 entry->flags^=CoderAdjoinFlag; 524 (void) RegisterMagickInfo(entry); 525 return(MagickImageCoderSignature); 526 } 527 528 /* 530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 531 % % 532 % % 533 % % 534 % U n r e g i s t e r X P M I m a g e % 535 % % 536 % % 537 % % 538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 539 % 540 % UnregisterXPMImage() removes format registrations made by the 541 % XPM module from the list of supported formats. 542 % 543 % The format of the UnregisterXPMImage method is: 544 % 545 % UnregisterXPMImage(void) 546 % 547 */ 548 ModuleExport void UnregisterXPMImage(void) 549 { 550 (void) UnregisterMagickInfo("PICON"); 551 (void) UnregisterMagickInfo("PM"); 552 (void) UnregisterMagickInfo("XPM"); 553 } 554 555 /* 557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 558 % % 559 % % 560 % % 561 % W r i t e P I C O N I m a g e % 562 % % 563 % % 564 % % 565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 566 % 567 % WritePICONImage() writes an image to a file in the Personal Icon format. 568 % 569 % The format of the WritePICONImage method is: 570 % 571 % MagickBooleanType WritePICONImage(const ImageInfo *image_info, 572 % Image *image,ExceptionInfo *exception) 573 % 574 % A description of each parameter follows. 575 % 576 % o image_info: the image info. 577 % 578 % o image: The image. 579 % 580 % o exception: return any errors or warnings in this structure. 581 % 582 */ 583 static MagickBooleanType WritePICONImage(const ImageInfo *image_info, 584 Image *image,ExceptionInfo *exception) 585 { 586 #define ColormapExtent 155 587 #define GraymapExtent 95 588 #define PiconGeometry "48x48>" 589 590 static unsigned char 591 Colormap[]= 592 { 593 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05, 594 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e, 595 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 596 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff, 597 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd, 598 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00, 599 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff, 600 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4, 601 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 602 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 603 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08, 604 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49, 605 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b, 606 }, 607 Graymap[]= 608 { 609 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f, 610 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33, 611 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78, 612 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba, 613 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff, 614 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 615 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31, 616 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b, 617 }; 618 619 #define MaxCixels 92 620 621 static const char 622 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" 623 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; 624 625 char 626 buffer[MagickPathExtent], 627 basename[MagickPathExtent], 628 name[MagickPathExtent], 629 symbol[MagickPathExtent]; 630 631 Image 632 *affinity_image, 633 *picon; 634 635 ImageInfo 636 *blob_info; 637 638 MagickBooleanType 639 status, 640 transparent; 641 642 PixelInfo 643 pixel; 644 645 QuantizeInfo 646 *quantize_info; 647 648 RectangleInfo 649 geometry; 650 651 register const Quantum 652 *p; 653 654 register ssize_t 655 i, 656 x; 657 658 register Quantum 659 *q; 660 661 size_t 662 characters_per_pixel, 663 colors; 664 665 ssize_t 666 j, 667 k, 668 y; 669 670 /* 671 Open output image file. 672 */ 673 assert(image_info != (const ImageInfo *) NULL); 674 assert(image_info->signature == MagickCoreSignature); 675 assert(image != (Image *) NULL); 676 assert(image->signature == MagickCoreSignature); 677 if (image->debug != MagickFalse) 678 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 679 assert(exception != (ExceptionInfo *) NULL); 680 assert(exception->signature == MagickCoreSignature); 681 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 682 if (status == MagickFalse) 683 return(status); 684 (void) TransformImageColorspace(image,sRGBColorspace,exception); 685 SetGeometry(image,&geometry); 686 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y, 687 &geometry.width,&geometry.height); 688 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter, 689 exception); 690 blob_info=CloneImageInfo(image_info); 691 (void) AcquireUniqueFilename(blob_info->filename); 692 if ((image_info->type != TrueColorType) && 693 (SetImageGray(image,exception) != MagickFalse)) 694 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception); 695 else 696 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception); 697 (void) RelinquishUniqueFileResource(blob_info->filename); 698 blob_info=DestroyImageInfo(blob_info); 699 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL)) 700 return(MagickFalse); 701 quantize_info=AcquireQuantizeInfo(image_info); 702 status=RemapImage(quantize_info,picon,affinity_image,exception); 703 quantize_info=DestroyQuantizeInfo(quantize_info); 704 affinity_image=DestroyImage(affinity_image); 705 transparent=MagickFalse; 706 if (picon->storage_class == PseudoClass) 707 { 708 (void) CompressImageColormap(picon,exception); 709 if (picon->alpha_trait != UndefinedPixelTrait) 710 transparent=MagickTrue; 711 } 712 else 713 { 714 /* 715 Convert DirectClass to PseudoClass picon. 716 */ 717 if (picon->alpha_trait != UndefinedPixelTrait) 718 { 719 /* 720 Map all the transparent pixels. 721 */ 722 for (y=0; y < (ssize_t) picon->rows; y++) 723 { 724 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); 725 if (q == (Quantum *) NULL) 726 break; 727 for (x=0; x < (ssize_t) picon->columns; x++) 728 { 729 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) 730 transparent=MagickTrue; 731 else 732 SetPixelAlpha(picon,OpaqueAlpha,q); 733 q+=GetPixelChannels(picon); 734 } 735 if (SyncAuthenticPixels(picon,exception) == MagickFalse) 736 break; 737 } 738 } 739 (void) SetImageType(picon,PaletteType,exception); 740 } 741 colors=picon->colors; 742 if (transparent != MagickFalse) 743 { 744 colors++; 745 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **) 746 picon->colormap,(size_t) colors,sizeof(*picon->colormap)); 747 if (picon->colormap == (PixelInfo *) NULL) 748 ThrowWriterException(ResourceLimitError,"MemoryAllocationError"); 749 for (y=0; y < (ssize_t) picon->rows; y++) 750 { 751 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); 752 if (q == (Quantum *) NULL) 753 break; 754 for (x=0; x < (ssize_t) picon->columns; x++) 755 { 756 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) 757 SetPixelIndex(picon,(Quantum) picon->colors,q); 758 q+=GetPixelChannels(picon); 759 } 760 if (SyncAuthenticPixels(picon,exception) == MagickFalse) 761 break; 762 } 763 } 764 /* 765 Compute the character per pixel. 766 */ 767 characters_per_pixel=1; 768 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels) 769 characters_per_pixel++; 770 /* 771 XPM header. 772 */ 773 (void) WriteBlobString(image,"/* XPM */\n"); 774 GetPathComponent(picon->filename,BasePath,basename); 775 (void) FormatLocaleString(buffer,MagickPathExtent, 776 "static char *%s[] = {\n",basename); 777 (void) WriteBlobString(image,buffer); 778 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); 779 (void) FormatLocaleString(buffer,MagickPathExtent, 780 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double) 781 picon->rows,(double) colors,(double) characters_per_pixel); 782 (void) WriteBlobString(image,buffer); 783 GetPixelInfo(image,&pixel); 784 for (i=0; i < (ssize_t) colors; i++) 785 { 786 /* 787 Define XPM color. 788 */ 789 pixel=picon->colormap[i]; 790 pixel.colorspace=sRGBColorspace; 791 pixel.depth=8; 792 pixel.alpha=(double) OpaqueAlpha; 793 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); 794 if (transparent != MagickFalse) 795 { 796 if (i == (ssize_t) (colors-1)) 797 (void) CopyMagickString(name,"grey75",MagickPathExtent); 798 } 799 /* 800 Write XPM color. 801 */ 802 k=i % MaxCixels; 803 symbol[0]=Cixel[k]; 804 for (j=1; j < (ssize_t) characters_per_pixel; j++) 805 { 806 k=((i-k)/MaxCixels) % MaxCixels; 807 symbol[j]=Cixel[k]; 808 } 809 symbol[j]='\0'; 810 (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n", 811 symbol,name); 812 (void) WriteBlobString(image,buffer); 813 } 814 /* 815 Define XPM pixels. 816 */ 817 (void) WriteBlobString(image,"/* pixels */\n"); 818 for (y=0; y < (ssize_t) picon->rows; y++) 819 { 820 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception); 821 if (p == (const Quantum *) NULL) 822 break; 823 (void) WriteBlobString(image,"\""); 824 for (x=0; x < (ssize_t) picon->columns; x++) 825 { 826 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels); 827 symbol[0]=Cixel[k]; 828 for (j=1; j < (ssize_t) characters_per_pixel; j++) 829 { 830 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels; 831 symbol[j]=Cixel[k]; 832 } 833 symbol[j]='\0'; 834 (void) CopyMagickString(buffer,symbol,MagickPathExtent); 835 (void) WriteBlobString(image,buffer); 836 p+=GetPixelChannels(image); 837 } 838 (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n", 839 y == (ssize_t) (picon->rows-1) ? "" : ","); 840 (void) WriteBlobString(image,buffer); 841 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 842 picon->rows); 843 if (status == MagickFalse) 844 break; 845 } 846 picon=DestroyImage(picon); 847 (void) WriteBlobString(image,"};\n"); 848 (void) CloseBlob(image); 849 return(MagickTrue); 850 } 851 852 /* 854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 855 % % 856 % % 857 % % 858 % W r i t e X P M I m a g e % 859 % % 860 % % 861 % % 862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 863 % 864 % WriteXPMImage() writes an image to a file in the X pixmap format. 865 % 866 % The format of the WriteXPMImage method is: 867 % 868 % MagickBooleanType WriteXPMImage(const ImageInfo *image_info, 869 % Image *image,ExceptionInfo *exception) 870 % 871 % A description of each parameter follows. 872 % 873 % o image_info: the image info. 874 % 875 % o image: The image. 876 % 877 % o exception: return any errors or warnings in this structure. 878 % 879 */ 880 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image, 881 ExceptionInfo *exception) 882 { 883 #define MaxCixels 92 884 885 static const char 886 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" 887 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; 888 889 char 890 buffer[MagickPathExtent], 891 basename[MagickPathExtent], 892 name[MagickPathExtent], 893 symbol[MagickPathExtent]; 894 895 MagickBooleanType 896 status; 897 898 PixelInfo 899 pixel; 900 901 register const Quantum 902 *p; 903 904 register ssize_t 905 i, 906 x; 907 908 size_t 909 characters_per_pixel; 910 911 ssize_t 912 j, 913 k, 914 opacity, 915 y; 916 917 /* 918 Open output image file. 919 */ 920 assert(image_info != (const ImageInfo *) NULL); 921 assert(image_info->signature == MagickCoreSignature); 922 assert(image != (Image *) NULL); 923 assert(image->signature == MagickCoreSignature); 924 if (image->debug != MagickFalse) 925 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 926 assert(exception != (ExceptionInfo *) NULL); 927 assert(exception->signature == MagickCoreSignature); 928 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 929 if (status == MagickFalse) 930 return(status); 931 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) 932 (void) TransformImageColorspace(image,sRGBColorspace,exception); 933 opacity=(-1); 934 if (image->alpha_trait == UndefinedPixelTrait) 935 { 936 if ((image->storage_class == DirectClass) || (image->colors > 256)) 937 (void) SetImageType(image,PaletteType,exception); 938 } 939 else 940 { 941 double 942 alpha, 943 beta; 944 945 /* 946 Identify transparent colormap index. 947 */ 948 if ((image->storage_class == DirectClass) || (image->colors > 256)) 949 (void) SetImageType(image,PaletteBilevelAlphaType,exception); 950 for (i=0; i < (ssize_t) image->colors; i++) 951 if (image->colormap[i].alpha != OpaqueAlpha) 952 { 953 if (opacity < 0) 954 { 955 opacity=i; 956 continue; 957 } 958 alpha=(double) TransparentAlpha-(double) 959 image->colormap[i].alpha; 960 beta=(double) TransparentAlpha-(double) 961 image->colormap[opacity].alpha; 962 if (alpha < beta) 963 opacity=i; 964 } 965 if (opacity == -1) 966 { 967 (void) SetImageType(image,PaletteBilevelAlphaType,exception); 968 for (i=0; i < (ssize_t) image->colors; i++) 969 if (image->colormap[i].alpha != OpaqueAlpha) 970 { 971 if (opacity < 0) 972 { 973 opacity=i; 974 continue; 975 } 976 alpha=(Quantum) TransparentAlpha-(double) 977 image->colormap[i].alpha; 978 beta=(Quantum) TransparentAlpha-(double) 979 image->colormap[opacity].alpha; 980 if (alpha < beta) 981 opacity=i; 982 } 983 } 984 if (opacity >= 0) 985 { 986 image->colormap[opacity].red=image->transparent_color.red; 987 image->colormap[opacity].green=image->transparent_color.green; 988 image->colormap[opacity].blue=image->transparent_color.blue; 989 } 990 } 991 /* 992 Compute the character per pixel. 993 */ 994 characters_per_pixel=1; 995 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels) 996 characters_per_pixel++; 997 /* 998 XPM header. 999 */ 1000 (void) WriteBlobString(image,"/* XPM */\n"); 1001 GetPathComponent(image->filename,BasePath,basename); 1002 if (isalnum((int) ((unsigned char) *basename)) == 0) 1003 { 1004 (void) FormatLocaleString(buffer,MagickPathExtent,"xpm_%s",basename); 1005 (void) CopyMagickString(basename,buffer,MagickPathExtent); 1006 } 1007 if (isalpha((int) ((unsigned char) basename[0])) == 0) 1008 basename[0]='_'; 1009 for (i=1; basename[i] != '\0'; i++) 1010 if (isalnum((int) ((unsigned char) basename[i])) == 0) 1011 basename[i]='_'; 1012 (void) FormatLocaleString(buffer,MagickPathExtent, 1013 "static char *%s[] = {\n",basename); 1014 (void) WriteBlobString(image,buffer); 1015 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); 1016 (void) FormatLocaleString(buffer,MagickPathExtent, 1017 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double) 1018 image->rows,(double) image->colors,(double) characters_per_pixel); 1019 (void) WriteBlobString(image,buffer); 1020 GetPixelInfo(image,&pixel); 1021 for (i=0; i < (ssize_t) image->colors; i++) 1022 { 1023 /* 1024 Define XPM color. 1025 */ 1026 pixel=image->colormap[i]; 1027 pixel.colorspace=sRGBColorspace; 1028 pixel.depth=8; 1029 pixel.alpha=(double) OpaqueAlpha; 1030 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); 1031 if (i == opacity) 1032 (void) CopyMagickString(name,"None",MagickPathExtent); 1033 /* 1034 Write XPM color. 1035 */ 1036 k=i % MaxCixels; 1037 symbol[0]=Cixel[k]; 1038 for (j=1; j < (ssize_t) characters_per_pixel; j++) 1039 { 1040 k=((i-k)/MaxCixels) % MaxCixels; 1041 symbol[j]=Cixel[k]; 1042 } 1043 symbol[j]='\0'; 1044 (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",symbol, 1045 name); 1046 (void) WriteBlobString(image,buffer); 1047 } 1048 /* 1049 Define XPM pixels. 1050 */ 1051 (void) WriteBlobString(image,"/* pixels */\n"); 1052 for (y=0; y < (ssize_t) image->rows; y++) 1053 { 1054 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 1055 if (p == (const Quantum *) NULL) 1056 break; 1057 (void) WriteBlobString(image,"\""); 1058 for (x=0; x < (ssize_t) image->columns; x++) 1059 { 1060 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels); 1061 symbol[0]=Cixel[k]; 1062 for (j=1; j < (ssize_t) characters_per_pixel; j++) 1063 { 1064 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels; 1065 symbol[j]=Cixel[k]; 1066 } 1067 symbol[j]='\0'; 1068 (void) CopyMagickString(buffer,symbol,MagickPathExtent); 1069 (void) WriteBlobString(image,buffer); 1070 p+=GetPixelChannels(image); 1071 } 1072 (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n", 1073 (y == (ssize_t) (image->rows-1) ? "" : ",")); 1074 (void) WriteBlobString(image,buffer); 1075 if (image->previous == (Image *) NULL) 1076 { 1077 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 1078 image->rows); 1079 if (status == MagickFalse) 1080 break; 1081 } 1082 } 1083 (void) WriteBlobString(image,"};\n"); 1084 (void) CloseBlob(image); 1085 return(MagickTrue); 1086 } 1087