xpm.c revision f1d8548abecaf5ca89d453fd9fc0cde77d20672b
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[MaxTextExtent], 241 target[MaxTextExtent], 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 == MagickSignature); 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 == MagickSignature); 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=MaxTextExtent; 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+MaxTextExtent) < length) 313 continue; 314 length<<=1; 315 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent, 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,MaxTextExtent-1)); 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-q) < 0) 390 break; 391 if (next != (char *) NULL) 392 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q), 393 MaxTextExtent-1)); 394 else 395 (void) CopyMagickString(target,q,MaxTextExtent); 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,MaxTextExtent-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=SetMagickInfo("PICON"); 501 entry->decoder=(DecodeImageHandler *) ReadXPMImage; 502 entry->encoder=(EncodeImageHandler *) WritePICONImage; 503 entry->flags^=CoderAdjoinFlag; 504 entry->description=ConstantString("Personal Icon"); 505 entry->module=ConstantString("XPM"); 506 (void) RegisterMagickInfo(entry); 507 entry=SetMagickInfo("PM"); 508 entry->decoder=(DecodeImageHandler *) ReadXPMImage; 509 entry->encoder=(EncodeImageHandler *) WriteXPMImage; 510 entry->flags^=CoderAdjoinFlag; 511 entry->flags|=CoderStealthFlag; 512 entry->description=ConstantString("X Windows system pixmap (color)"); 513 entry->module=ConstantString("XPM"); 514 (void) RegisterMagickInfo(entry); 515 entry=SetMagickInfo("XPM"); 516 entry->decoder=(DecodeImageHandler *) ReadXPMImage; 517 entry->encoder=(EncodeImageHandler *) WriteXPMImage; 518 entry->magick=(IsImageFormatHandler *) IsXPM; 519 entry->flags^=CoderAdjoinFlag; 520 entry->description=ConstantString("X Windows system pixmap (color)"); 521 entry->module=ConstantString("XPM"); 522 (void) RegisterMagickInfo(entry); 523 return(MagickImageCoderSignature); 524} 525 526/* 527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 528% % 529% % 530% % 531% U n r e g i s t e r X P M I m a g e % 532% % 533% % 534% % 535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 536% 537% UnregisterXPMImage() removes format registrations made by the 538% XPM module from the list of supported formats. 539% 540% The format of the UnregisterXPMImage method is: 541% 542% UnregisterXPMImage(void) 543% 544*/ 545ModuleExport void UnregisterXPMImage(void) 546{ 547 (void) UnregisterMagickInfo("PICON"); 548 (void) UnregisterMagickInfo("PM"); 549 (void) UnregisterMagickInfo("XPM"); 550} 551 552/* 553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 554% % 555% % 556% % 557% W r i t e P I C O N I m a g e % 558% % 559% % 560% % 561%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 562% 563% WritePICONImage() writes an image to a file in the Personal Icon format. 564% 565% The format of the WritePICONImage method is: 566% 567% MagickBooleanType WritePICONImage(const ImageInfo *image_info, 568% Image *image,ExceptionInfo *exception) 569% 570% A description of each parameter follows. 571% 572% o image_info: the image info. 573% 574% o image: The image. 575% 576% o exception: return any errors or warnings in this structure. 577% 578*/ 579static MagickBooleanType WritePICONImage(const ImageInfo *image_info, 580 Image *image,ExceptionInfo *exception) 581{ 582#define ColormapExtent 155 583#define GraymapExtent 95 584#define PiconGeometry "48x48>" 585 586 static unsigned char 587 Colormap[]= 588 { 589 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05, 590 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e, 591 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 592 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff, 593 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd, 594 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00, 595 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff, 596 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4, 597 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 598 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 599 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08, 600 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49, 601 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b, 602 }, 603 Graymap[]= 604 { 605 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f, 606 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33, 607 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78, 608 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba, 609 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff, 610 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 611 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31, 612 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b, 613 }; 614 615#define MaxCixels 92 616 617 static const char 618 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" 619 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; 620 621 char 622 buffer[MaxTextExtent], 623 basename[MaxTextExtent], 624 name[MaxTextExtent], 625 symbol[MaxTextExtent]; 626 627 Image 628 *affinity_image, 629 *picon; 630 631 ImageInfo 632 *blob_info; 633 634 MagickBooleanType 635 status, 636 transparent; 637 638 PixelInfo 639 pixel; 640 641 QuantizeInfo 642 *quantize_info; 643 644 RectangleInfo 645 geometry; 646 647 register const Quantum 648 *p; 649 650 register ssize_t 651 i, 652 x; 653 654 register Quantum 655 *q; 656 657 size_t 658 characters_per_pixel, 659 colors; 660 661 ssize_t 662 j, 663 k, 664 y; 665 666 /* 667 Open output image file. 668 */ 669 assert(image_info != (const ImageInfo *) NULL); 670 assert(image_info->signature == MagickSignature); 671 assert(image != (Image *) NULL); 672 assert(image->signature == MagickSignature); 673 if (image->debug != MagickFalse) 674 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 675 assert(exception != (ExceptionInfo *) NULL); 676 assert(exception->signature == MagickSignature); 677 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 678 if (status == MagickFalse) 679 return(status); 680 (void) TransformImageColorspace(image,sRGBColorspace,exception); 681 SetGeometry(image,&geometry); 682 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y, 683 &geometry.width,&geometry.height); 684 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter, 685 exception); 686 blob_info=CloneImageInfo(image_info); 687 (void) AcquireUniqueFilename(blob_info->filename); 688 if ((image_info->type != TrueColorType) && 689 (SetImageGray(image,exception) != MagickFalse)) 690 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception); 691 else 692 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception); 693 (void) RelinquishUniqueFileResource(blob_info->filename); 694 blob_info=DestroyImageInfo(blob_info); 695 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL)) 696 return(MagickFalse); 697 quantize_info=AcquireQuantizeInfo(image_info); 698 status=RemapImage(quantize_info,picon,affinity_image,exception); 699 quantize_info=DestroyQuantizeInfo(quantize_info); 700 affinity_image=DestroyImage(affinity_image); 701 transparent=MagickFalse; 702 if (picon->storage_class == PseudoClass) 703 { 704 (void) CompressImageColormap(picon,exception); 705 if (picon->alpha_trait != UndefinedPixelTrait) 706 transparent=MagickTrue; 707 } 708 else 709 { 710 /* 711 Convert DirectClass to PseudoClass picon. 712 */ 713 if (picon->alpha_trait != UndefinedPixelTrait) 714 { 715 /* 716 Map all the transparent pixels. 717 */ 718 for (y=0; y < (ssize_t) picon->rows; y++) 719 { 720 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); 721 if (q == (Quantum *) NULL) 722 break; 723 for (x=0; x < (ssize_t) picon->columns; x++) 724 { 725 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) 726 transparent=MagickTrue; 727 else 728 SetPixelAlpha(picon,OpaqueAlpha,q); 729 q+=GetPixelChannels(picon); 730 } 731 if (SyncAuthenticPixels(picon,exception) == MagickFalse) 732 break; 733 } 734 } 735 (void) SetImageType(picon,PaletteType,exception); 736 } 737 colors=picon->colors; 738 if (transparent != MagickFalse) 739 { 740 colors++; 741 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **) 742 picon->colormap,(size_t) colors,sizeof(*picon->colormap)); 743 if (picon->colormap == (PixelInfo *) NULL) 744 ThrowWriterException(ResourceLimitError,"MemoryAllocationError"); 745 for (y=0; y < (ssize_t) picon->rows; y++) 746 { 747 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); 748 if (q == (Quantum *) NULL) 749 break; 750 for (x=0; x < (ssize_t) picon->columns; x++) 751 { 752 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) 753 SetPixelIndex(picon,(Quantum) picon->colors,q); 754 q+=GetPixelChannels(picon); 755 } 756 if (SyncAuthenticPixels(picon,exception) == MagickFalse) 757 break; 758 } 759 } 760 /* 761 Compute the character per pixel. 762 */ 763 characters_per_pixel=1; 764 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels) 765 characters_per_pixel++; 766 /* 767 XPM header. 768 */ 769 (void) WriteBlobString(image,"/* XPM */\n"); 770 GetPathComponent(picon->filename,BasePath,basename); 771 (void) FormatLocaleString(buffer,MaxTextExtent, 772 "static char *%s[] = {\n",basename); 773 (void) WriteBlobString(image,buffer); 774 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); 775 (void) FormatLocaleString(buffer,MaxTextExtent, 776 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double) 777 picon->rows,(double) colors,(double) characters_per_pixel); 778 (void) WriteBlobString(image,buffer); 779 GetPixelInfo(image,&pixel); 780 for (i=0; i < (ssize_t) colors; i++) 781 { 782 /* 783 Define XPM color. 784 */ 785 pixel=picon->colormap[i]; 786 pixel.colorspace=sRGBColorspace; 787 pixel.depth=8; 788 pixel.alpha=(double) OpaqueAlpha; 789 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); 790 if (transparent != MagickFalse) 791 { 792 if (i == (ssize_t) (colors-1)) 793 (void) CopyMagickString(name,"grey75",MaxTextExtent); 794 } 795 /* 796 Write XPM color. 797 */ 798 k=i % MaxCixels; 799 symbol[0]=Cixel[k]; 800 for (j=1; j < (ssize_t) characters_per_pixel; j++) 801 { 802 k=((i-k)/MaxCixels) % MaxCixels; 803 symbol[j]=Cixel[k]; 804 } 805 symbol[j]='\0'; 806 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n", 807 symbol,name); 808 (void) WriteBlobString(image,buffer); 809 } 810 /* 811 Define XPM pixels. 812 */ 813 (void) WriteBlobString(image,"/* pixels */\n"); 814 for (y=0; y < (ssize_t) picon->rows; y++) 815 { 816 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception); 817 if (p == (const Quantum *) NULL) 818 break; 819 (void) WriteBlobString(image,"\""); 820 for (x=0; x < (ssize_t) picon->columns; x++) 821 { 822 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels); 823 symbol[0]=Cixel[k]; 824 for (j=1; j < (ssize_t) characters_per_pixel; j++) 825 { 826 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels; 827 symbol[j]=Cixel[k]; 828 } 829 symbol[j]='\0'; 830 (void) CopyMagickString(buffer,symbol,MaxTextExtent); 831 (void) WriteBlobString(image,buffer); 832 p+=GetPixelChannels(image); 833 } 834 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n", 835 y == (ssize_t) (picon->rows-1) ? "" : ","); 836 (void) WriteBlobString(image,buffer); 837 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 838 picon->rows); 839 if (status == MagickFalse) 840 break; 841 } 842 picon=DestroyImage(picon); 843 (void) WriteBlobString(image,"};\n"); 844 (void) CloseBlob(image); 845 return(MagickTrue); 846} 847 848/* 849%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 850% % 851% % 852% % 853% W r i t e X P M I m a g e % 854% % 855% % 856% % 857%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 858% 859% WriteXPMImage() writes an image to a file in the X pixmap format. 860% 861% The format of the WriteXPMImage method is: 862% 863% MagickBooleanType WriteXPMImage(const ImageInfo *image_info, 864% Image *image,ExceptionInfo *exception) 865% 866% A description of each parameter follows. 867% 868% o image_info: the image info. 869% 870% o image: The image. 871% 872% o exception: return any errors or warnings in this structure. 873% 874*/ 875static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image, 876 ExceptionInfo *exception) 877{ 878#define MaxCixels 92 879 880 static const char 881 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" 882 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; 883 884 char 885 buffer[MaxTextExtent], 886 basename[MaxTextExtent], 887 name[MaxTextExtent], 888 symbol[MaxTextExtent]; 889 890 MagickBooleanType 891 status; 892 893 PixelInfo 894 pixel; 895 896 register const Quantum 897 *p; 898 899 register ssize_t 900 i, 901 x; 902 903 size_t 904 characters_per_pixel; 905 906 ssize_t 907 j, 908 k, 909 opacity, 910 y; 911 912 /* 913 Open output image file. 914 */ 915 assert(image_info != (const ImageInfo *) NULL); 916 assert(image_info->signature == MagickSignature); 917 assert(image != (Image *) NULL); 918 assert(image->signature == MagickSignature); 919 if (image->debug != MagickFalse) 920 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 921 assert(exception != (ExceptionInfo *) NULL); 922 assert(exception->signature == MagickSignature); 923 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 924 if (status == MagickFalse) 925 return(status); 926 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) 927 (void) TransformImageColorspace(image,sRGBColorspace,exception); 928 opacity=(-1); 929 if (image->alpha_trait == UndefinedPixelTrait) 930 { 931 if ((image->storage_class == DirectClass) || (image->colors > 256)) 932 (void) SetImageType(image,PaletteType,exception); 933 } 934 else 935 { 936 double 937 alpha, 938 beta; 939 940 /* 941 Identify transparent colormap index. 942 */ 943 if ((image->storage_class == DirectClass) || (image->colors > 256)) 944 (void) SetImageType(image,PaletteBilevelAlphaType,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=(double) TransparentAlpha-(double) 954 image->colormap[i].alpha; 955 beta=(double) TransparentAlpha-(double) 956 image->colormap[opacity].alpha; 957 if (alpha < beta) 958 opacity=i; 959 } 960 if (opacity == -1) 961 { 962 (void) SetImageType(image,PaletteBilevelAlphaType,exception); 963 for (i=0; i < (ssize_t) image->colors; i++) 964 if (image->colormap[i].alpha != OpaqueAlpha) 965 { 966 if (opacity < 0) 967 { 968 opacity=i; 969 continue; 970 } 971 alpha=(Quantum) TransparentAlpha-(double) 972 image->colormap[i].alpha; 973 beta=(Quantum) TransparentAlpha-(double) 974 image->colormap[opacity].alpha; 975 if (alpha < beta) 976 opacity=i; 977 } 978 } 979 if (opacity >= 0) 980 { 981 image->colormap[opacity].red=image->transparent_color.red; 982 image->colormap[opacity].green=image->transparent_color.green; 983 image->colormap[opacity].blue=image->transparent_color.blue; 984 } 985 } 986 /* 987 Compute the character per pixel. 988 */ 989 characters_per_pixel=1; 990 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels) 991 characters_per_pixel++; 992 /* 993 XPM header. 994 */ 995 (void) WriteBlobString(image,"/* XPM */\n"); 996 GetPathComponent(image->filename,BasePath,basename); 997 if (isalnum((int) ((unsigned char) *basename)) == 0) 998 { 999 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename); 1000 (void) CopyMagickString(basename,buffer,MaxTextExtent); 1001 } 1002 if (isalpha((int) ((unsigned char) basename[0])) == 0) 1003 basename[0]='_'; 1004 for (i=1; basename[i] != '\0'; i++) 1005 if (isalnum((int) ((unsigned char) basename[i])) == 0) 1006 basename[i]='_'; 1007 (void) FormatLocaleString(buffer,MaxTextExtent, 1008 "static char *%s[] = {\n",basename); 1009 (void) WriteBlobString(image,buffer); 1010 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); 1011 (void) FormatLocaleString(buffer,MaxTextExtent, 1012 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double) 1013 image->rows,(double) image->colors,(double) characters_per_pixel); 1014 (void) WriteBlobString(image,buffer); 1015 GetPixelInfo(image,&pixel); 1016 for (i=0; i < (ssize_t) image->colors; i++) 1017 { 1018 /* 1019 Define XPM color. 1020 */ 1021 pixel=image->colormap[i]; 1022 pixel.colorspace=sRGBColorspace; 1023 pixel.depth=8; 1024 pixel.alpha=(double) OpaqueAlpha; 1025 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); 1026 if (i == opacity) 1027 (void) CopyMagickString(name,"None",MaxTextExtent); 1028 /* 1029 Write XPM color. 1030 */ 1031 k=i % MaxCixels; 1032 symbol[0]=Cixel[k]; 1033 for (j=1; j < (ssize_t) characters_per_pixel; j++) 1034 { 1035 k=((i-k)/MaxCixels) % MaxCixels; 1036 symbol[j]=Cixel[k]; 1037 } 1038 symbol[j]='\0'; 1039 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol, 1040 name); 1041 (void) WriteBlobString(image,buffer); 1042 } 1043 /* 1044 Define XPM pixels. 1045 */ 1046 (void) WriteBlobString(image,"/* pixels */\n"); 1047 for (y=0; y < (ssize_t) image->rows; y++) 1048 { 1049 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 1050 if (p == (const Quantum *) NULL) 1051 break; 1052 (void) WriteBlobString(image,"\""); 1053 for (x=0; x < (ssize_t) image->columns; x++) 1054 { 1055 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels); 1056 symbol[0]=Cixel[k]; 1057 for (j=1; j < (ssize_t) characters_per_pixel; j++) 1058 { 1059 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels; 1060 symbol[j]=Cixel[k]; 1061 } 1062 symbol[j]='\0'; 1063 (void) CopyMagickString(buffer,symbol,MaxTextExtent); 1064 (void) WriteBlobString(image,buffer); 1065 p+=GetPixelChannels(image); 1066 } 1067 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n", 1068 (y == (ssize_t) (image->rows-1) ? "" : ",")); 1069 (void) WriteBlobString(image,buffer); 1070 if (image->previous == (Image *) NULL) 1071 { 1072 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 1073 image->rows); 1074 if (status == MagickFalse) 1075 break; 1076 } 1077 } 1078 (void) WriteBlobString(image,"};\n"); 1079 (void) CloseBlob(image); 1080 return(MagickTrue); 1081} 1082