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