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