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