xpm.c revision 1d92f7952ace182649f73e84b7ecbba1798e8fff
1024598e40c84666cc311a42c256bbf880db3ac99sewardj/* 2024598e40c84666cc311a42c256bbf880db3ac99sewardj%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 4024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 5024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 6024598e40c84666cc311a42c256bbf880db3ac99sewardj% X X PPPP M M % 7024598e40c84666cc311a42c256bbf880db3ac99sewardj% X X P P MM MM % 8024598e40c84666cc311a42c256bbf880db3ac99sewardj% X PPPP M M M % 9024598e40c84666cc311a42c256bbf880db3ac99sewardj% X X P M M % 10024598e40c84666cc311a42c256bbf880db3ac99sewardj% X X P M M % 11024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 12024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 13024598e40c84666cc311a42c256bbf880db3ac99sewardj% Read/Write X Windows system Pixmap Format % 14024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 15024598e40c84666cc311a42c256bbf880db3ac99sewardj% Software Design % 16024598e40c84666cc311a42c256bbf880db3ac99sewardj% John Cristy % 17024598e40c84666cc311a42c256bbf880db3ac99sewardj% July 1992 % 18024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 19024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 20024598e40c84666cc311a42c256bbf880db3ac99sewardj% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization % 21024598e40c84666cc311a42c256bbf880db3ac99sewardj% dedicated to making software imaging solutions freely available. % 22024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 23024598e40c84666cc311a42c256bbf880db3ac99sewardj% You may not use this file except in compliance with the License. You may % 24024598e40c84666cc311a42c256bbf880db3ac99sewardj% obtain a copy of the License at % 25024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 26024598e40c84666cc311a42c256bbf880db3ac99sewardj% http://www.imagemagick.org/script/license.php % 27024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 28024598e40c84666cc311a42c256bbf880db3ac99sewardj% Unless required by applicable law or agreed to in writing, software % 29024598e40c84666cc311a42c256bbf880db3ac99sewardj% distributed under the License is distributed on an "AS IS" BASIS, % 30024598e40c84666cc311a42c256bbf880db3ac99sewardj% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31024598e40c84666cc311a42c256bbf880db3ac99sewardj% See the License for the specific language governing permissions and % 32024598e40c84666cc311a42c256bbf880db3ac99sewardj% limitations under the License. % 33024598e40c84666cc311a42c256bbf880db3ac99sewardj% % 34024598e40c84666cc311a42c256bbf880db3ac99sewardj%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35024598e40c84666cc311a42c256bbf880db3ac99sewardj% 36024598e40c84666cc311a42c256bbf880db3ac99sewardj% 37024598e40c84666cc311a42c256bbf880db3ac99sewardj*/ 38024598e40c84666cc311a42c256bbf880db3ac99sewardj 39024598e40c84666cc311a42c256bbf880db3ac99sewardj/* 40024598e40c84666cc311a42c256bbf880db3ac99sewardj Include declarations. 41024598e40c84666cc311a42c256bbf880db3ac99sewardj*/ 42024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/studio.h" 43024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/attribute.h" 44024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/blob.h" 45024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/blob-private.h" 46024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/cache.h" 47024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/color.h" 48024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/color-private.h" 49024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/colormap.h" 50024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/colorspace.h" 51024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/colorspace-private.h" 52024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/exception.h" 53024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/exception-private.h" 54024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/geometry.h" 55024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/image.h" 56024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/image-private.h" 57024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/list.h" 58024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/magick.h" 59024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/memory_.h" 60024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/monitor.h" 61024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/monitor-private.h" 62024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/pixel-accessor.h" 63024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/quantize.h" 64024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/quantum-private.h" 65024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/resize.h" 66024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/resource_.h" 67024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/splay-tree.h" 68024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/static.h" 69024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/string_.h" 70024598e40c84666cc311a42c256bbf880db3ac99sewardj#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 char *CopyXPMColor(char *destination,const char *source,size_t length) 155{ 156 while (length-- && (*source != '\0')) 157 *destination++=(*source++); 158 *destination='\0'; 159 return(destination-length); 160} 161 162static char *NextXPMLine(char *p) 163{ 164 assert(p != (char*)NULL); 165 p=strchr(p,'\n'); 166 if (p != (char *) NULL) 167 p++; 168 return(p); 169} 170 171static inline size_t MagickMin(const size_t x,const size_t y) 172{ 173 if (x < y) 174 return(x); 175 return(y); 176} 177 178static char *ParseXPMColor(char *color) 179{ 180#define NumberTargets 6 181 182 register char 183 *p, 184 *r; 185 186 register const char 187 *q; 188 189 register ssize_t 190 i; 191 192 static const char 193 *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " }; 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 220static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception) 221{ 222 char 223 key[MaxTextExtent], 224 target[MaxTextExtent], 225 *xpm_buffer; 226 227 Image 228 *image; 229 230 MagickBooleanType 231 active, 232 status; 233 234 register char 235 *p, 236 *q, 237 *next; 238 239 register ssize_t 240 x; 241 242 register Quantum 243 *r; 244 245 size_t 246 length; 247 248 SplayTreeInfo 249 *xpm_colors; 250 251 ssize_t 252 count, 253 j, 254 y; 255 256 unsigned long 257 colors, 258 columns, 259 rows, 260 width; 261 262 /* 263 Open image file. 264 */ 265 assert(image_info != (const ImageInfo *) NULL); 266 assert(image_info->signature == MagickSignature); 267 if (image_info->debug != MagickFalse) 268 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 269 image_info->filename); 270 assert(exception != (ExceptionInfo *) NULL); 271 assert(exception->signature == MagickSignature); 272 image=AcquireImage(image_info,exception); 273 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 274 if (status == MagickFalse) 275 { 276 image=DestroyImageList(image); 277 return((Image *) NULL); 278 } 279 /* 280 Read XPM file. 281 */ 282 length=MaxTextExtent; 283 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer)); 284 p=xpm_buffer; 285 if (xpm_buffer != (char *) NULL) 286 while (ReadBlobString(image,p) != (char *) NULL) 287 { 288 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n'))) 289 continue; 290 if ((*p == '}') && (*(p+1) == ';')) 291 break; 292 p+=strlen(p); 293 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length) 294 continue; 295 length<<=1; 296 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent, 297 sizeof(*xpm_buffer)); 298 if (xpm_buffer == (char *) NULL) 299 break; 300 p=xpm_buffer+strlen(xpm_buffer); 301 } 302 if (xpm_buffer == (char *) NULL) 303 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 304 /* 305 Remove comments. 306 */ 307 count=0; 308 for (p=xpm_buffer; *p != '\0'; p++) 309 { 310 if (*p != '"') 311 continue; 312 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width); 313 image->columns=columns; 314 image->rows=rows; 315 image->colors=colors; 316 if (count == 4) 317 break; 318 } 319 if ((count != 4) || (width > 10) || (image->columns == 0) || 320 (image->rows == 0) || (image->colors == 0)) 321 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 322 /* 323 Remove unquoted characters. 324 */ 325 active=MagickFalse; 326 q=xpm_buffer; 327 while (*p != '\0') 328 { 329 if (*p++ == '"') 330 { 331 if (active != MagickFalse) 332 *q++='\n'; 333 active=active != MagickFalse ? MagickFalse : MagickTrue; 334 } 335 if (active != MagickFalse) 336 *q++=(*p); 337 } 338 *q='\0'; 339 /* 340 Initialize image structure. 341 */ 342 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory, 343 (void *(*)(void *)) NULL); 344 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) 345 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 346 /* 347 Read image colormap. 348 */ 349 image->depth=1; 350 next=NextXPMLine(xpm_buffer); 351 for (j=0; (j < (ssize_t) image->colors) && (next != (char*) NULL); j++) 352 { 353 p=next; 354 next=NextXPMLine(p); 355 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent)); 356 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j); 357 /* 358 Parse color. 359 */ 360 (void) CopyMagickString(target,"gray",MaxTextExtent); 361 q=ParseXPMColor(p+width); 362 if (q != (char *) NULL) 363 { 364 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0')) 365 q++; 366 if (next != (char *) NULL) 367 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q), 368 MaxTextExtent)); 369 else 370 (void) CopyMagickString(target,q,MaxTextExtent); 371 q=ParseXPMColor(target); 372 if (q != (char *) NULL) 373 *q='\0'; 374 } 375 StripString(target); 376 if (LocaleCompare(target,"none") == 0) 377 { 378 image->storage_class=DirectClass; 379 image->alpha_trait=BlendPixelTrait; 380 } 381 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j], 382 exception); 383 if (status == MagickFalse) 384 break; 385 if (image->depth < image->colormap[j].depth) 386 image->depth=image->colormap[j].depth; 387 } 388 if (j < (ssize_t) image->colors) 389 ThrowReaderException(CorruptImageError,"CorruptImage"); 390 j=0; 391 if (image_info->ping == MagickFalse) 392 { 393 /* 394 Read image pixels. 395 */ 396 for (y=0; y < (ssize_t) image->rows; y++) 397 { 398 p=NextXPMLine(p); 399 if (p == (char *) NULL) 400 break; 401 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 402 if (r == (Quantum *) NULL) 403 break; 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 SetPixelIndex(image,j,r); 410 SetPixelInfoPixel(image,image->colormap+j,r); 411 p+=width; 412 r+=GetPixelChannels(image); 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,ExceptionInfo *exception) 525% 526% A description of each parameter follows. 527% 528% o image_info: the image info. 529% 530% o image: The image. 531% 532% o exception: return any errors or warnings in this structure. 533% 534*/ 535static MagickBooleanType WritePICONImage(const ImageInfo *image_info, 536 Image *image,ExceptionInfo *exception) 537{ 538#define ColormapExtent 155 539#define GraymapExtent 95 540#define PiconGeometry "48x48>" 541 542 static unsigned char 543 Colormap[]= 544 { 545 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05, 546 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e, 547 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 548 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff, 549 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd, 550 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00, 551 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff, 552 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4, 553 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 554 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 555 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08, 556 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49, 557 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b, 558 }, 559 Graymap[]= 560 { 561 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f, 562 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33, 563 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78, 564 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba, 565 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff, 566 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 567 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31, 568 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b, 569 }; 570 571#define MaxCixels 92 572 573 static const char 574 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" 575 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; 576 577 char 578 buffer[MaxTextExtent], 579 basename[MaxTextExtent], 580 name[MaxTextExtent], 581 symbol[MaxTextExtent]; 582 583 Image 584 *affinity_image, 585 *picon; 586 587 ImageInfo 588 *blob_info; 589 590 MagickBooleanType 591 status, 592 transparent; 593 594 PixelInfo 595 pixel; 596 597 QuantizeInfo 598 *quantize_info; 599 600 RectangleInfo 601 geometry; 602 603 register const Quantum 604 *p; 605 606 register ssize_t 607 i, 608 x; 609 610 register Quantum 611 *q; 612 613 size_t 614 characters_per_pixel, 615 colors; 616 617 ssize_t 618 j, 619 k, 620 y; 621 622 /* 623 Open output image file. 624 */ 625 assert(image_info != (const ImageInfo *) NULL); 626 assert(image_info->signature == MagickSignature); 627 assert(image != (Image *) NULL); 628 assert(image->signature == MagickSignature); 629 if (image->debug != MagickFalse) 630 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 631 assert(exception != (ExceptionInfo *) NULL); 632 assert(exception->signature == MagickSignature); 633 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 634 if (status == MagickFalse) 635 return(status); 636 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) 637 (void) TransformImageColorspace(image,sRGBColorspace,exception); 638 SetGeometry(image,&geometry); 639 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y, 640 &geometry.width,&geometry.height); 641 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter, 642 exception); 643 blob_info=CloneImageInfo(image_info); 644 (void) AcquireUniqueFilename(blob_info->filename); 645 if ((image_info->type != TrueColorType) && 646 (IsImageGray(image,exception) != MagickFalse)) 647 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception); 648 else 649 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception); 650 (void) RelinquishUniqueFileResource(blob_info->filename); 651 blob_info=DestroyImageInfo(blob_info); 652 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL)) 653 return(MagickFalse); 654 quantize_info=AcquireQuantizeInfo(image_info); 655 status=RemapImage(quantize_info,picon,affinity_image,exception); 656 quantize_info=DestroyQuantizeInfo(quantize_info); 657 affinity_image=DestroyImage(affinity_image); 658 transparent=MagickFalse; 659 if (picon->storage_class == PseudoClass) 660 { 661 (void) CompressImageColormap(picon,exception); 662 if (picon->alpha_trait == BlendPixelTrait) 663 transparent=MagickTrue; 664 } 665 else 666 { 667 /* 668 Convert DirectClass to PseudoClass picon. 669 */ 670 if (picon->alpha_trait == BlendPixelTrait) 671 { 672 /* 673 Map all the transparent pixels. 674 */ 675 for (y=0; y < (ssize_t) picon->rows; y++) 676 { 677 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); 678 if (q == (Quantum *) NULL) 679 break; 680 for (x=0; x < (ssize_t) picon->columns; x++) 681 { 682 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) 683 transparent=MagickTrue; 684 else 685 SetPixelAlpha(picon,OpaqueAlpha,q); 686 q+=GetPixelChannels(picon); 687 } 688 if (SyncAuthenticPixels(picon,exception) == MagickFalse) 689 break; 690 } 691 } 692 (void) SetImageType(picon,PaletteType,exception); 693 } 694 colors=picon->colors; 695 if (transparent != MagickFalse) 696 { 697 colors++; 698 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **) 699 picon->colormap,(size_t) colors,sizeof(*picon->colormap)); 700 if (picon->colormap == (PixelInfo *) NULL) 701 ThrowWriterException(ResourceLimitError,"MemoryAllocationError"); 702 for (y=0; y < (ssize_t) picon->rows; y++) 703 { 704 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception); 705 if (q == (Quantum *) NULL) 706 break; 707 for (x=0; x < (ssize_t) picon->columns; x++) 708 { 709 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha) 710 SetPixelIndex(picon,picon->colors,q); 711 q+=GetPixelChannels(picon); 712 } 713 if (SyncAuthenticPixels(picon,exception) == MagickFalse) 714 break; 715 } 716 } 717 /* 718 Compute the character per pixel. 719 */ 720 characters_per_pixel=1; 721 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels) 722 characters_per_pixel++; 723 /* 724 XPM header. 725 */ 726 (void) WriteBlobString(image,"/* XPM */\n"); 727 GetPathComponent(picon->filename,BasePath,basename); 728 (void) FormatLocaleString(buffer,MaxTextExtent, 729 "static char *%s[] = {\n",basename); 730 (void) WriteBlobString(image,buffer); 731 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); 732 (void) FormatLocaleString(buffer,MaxTextExtent, 733 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double) 734 picon->rows,(double) colors,(double) characters_per_pixel); 735 (void) WriteBlobString(image,buffer); 736 GetPixelInfo(image,&pixel); 737 for (i=0; i < (ssize_t) colors; i++) 738 { 739 /* 740 Define XPM color. 741 */ 742 pixel=picon->colormap[i]; 743 pixel.colorspace=sRGBColorspace; 744 pixel.depth=8; 745 pixel.alpha=(double) OpaqueAlpha; 746 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); 747 if (transparent != MagickFalse) 748 { 749 if (i == (ssize_t) (colors-1)) 750 (void) CopyMagickString(name,"grey75",MaxTextExtent); 751 } 752 /* 753 Write XPM color. 754 */ 755 k=i % MaxCixels; 756 symbol[0]=Cixel[k]; 757 for (j=1; j < (ssize_t) characters_per_pixel; j++) 758 { 759 k=((i-k)/MaxCixels) % MaxCixels; 760 symbol[j]=Cixel[k]; 761 } 762 symbol[j]='\0'; 763 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n", 764 symbol,name); 765 (void) WriteBlobString(image,buffer); 766 } 767 /* 768 Define XPM pixels. 769 */ 770 (void) WriteBlobString(image,"/* pixels */\n"); 771 for (y=0; y < (ssize_t) picon->rows; y++) 772 { 773 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception); 774 if (p == (const Quantum *) NULL) 775 break; 776 (void) WriteBlobString(image,"\""); 777 for (x=0; x < (ssize_t) picon->columns; x++) 778 { 779 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels); 780 symbol[0]=Cixel[k]; 781 for (j=1; j < (ssize_t) characters_per_pixel; j++) 782 { 783 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels; 784 symbol[j]=Cixel[k]; 785 } 786 symbol[j]='\0'; 787 (void) CopyMagickString(buffer,symbol,MaxTextExtent); 788 (void) WriteBlobString(image,buffer); 789 p+=GetPixelChannels(image); 790 } 791 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n", 792 y == (ssize_t) (picon->rows-1) ? "" : ","); 793 (void) WriteBlobString(image,buffer); 794 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 795 picon->rows); 796 if (status == MagickFalse) 797 break; 798 } 799 picon=DestroyImage(picon); 800 (void) WriteBlobString(image,"};\n"); 801 (void) CloseBlob(image); 802 return(MagickTrue); 803} 804 805/* 806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 807% % 808% % 809% % 810% W r i t e X P M I m a g e % 811% % 812% % 813% % 814%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 815% 816% WriteXPMImage() writes an image to a file in the X pixmap format. 817% 818% The format of the WriteXPMImage method is: 819% 820% MagickBooleanType WriteXPMImage(const ImageInfo *image_info, 821% Image *image,ExceptionInfo *exception) 822% 823% A description of each parameter follows. 824% 825% o image_info: the image info. 826% 827% o image: The image. 828% 829% o exception: return any errors or warnings in this structure. 830% 831*/ 832static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image, 833 ExceptionInfo *exception) 834{ 835#define MaxCixels 92 836 837 static const char 838 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" 839 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; 840 841 char 842 buffer[MaxTextExtent], 843 basename[MaxTextExtent], 844 name[MaxTextExtent], 845 symbol[MaxTextExtent]; 846 847 MagickBooleanType 848 status; 849 850 PixelInfo 851 pixel; 852 853 register const Quantum 854 *p; 855 856 register ssize_t 857 i, 858 x; 859 860 size_t 861 characters_per_pixel; 862 863 ssize_t 864 j, 865 k, 866 opacity, 867 y; 868 869 /* 870 Open output image file. 871 */ 872 assert(image_info != (const ImageInfo *) NULL); 873 assert(image_info->signature == MagickSignature); 874 assert(image != (Image *) NULL); 875 assert(image->signature == MagickSignature); 876 if (image->debug != MagickFalse) 877 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 878 assert(exception != (ExceptionInfo *) NULL); 879 assert(exception->signature == MagickSignature); 880 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 881 if (status == MagickFalse) 882 return(status); 883 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) 884 (void) TransformImageColorspace(image,sRGBColorspace,exception); 885 opacity=(-1); 886 if (image->alpha_trait != BlendPixelTrait) 887 { 888 if ((image->storage_class == DirectClass) || (image->colors > 256)) 889 (void) SetImageType(image,PaletteType,exception); 890 } 891 else 892 { 893 double 894 alpha, 895 beta; 896 897 /* 898 Identify transparent colormap index. 899 */ 900 if ((image->storage_class == DirectClass) || (image->colors > 256)) 901 (void) SetImageType(image,PaletteBilevelMatteType,exception); 902 for (i=0; i < (ssize_t) image->colors; i++) 903 if (image->colormap[i].alpha != OpaqueAlpha) 904 { 905 if (opacity < 0) 906 { 907 opacity=i; 908 continue; 909 } 910 alpha=(double) TransparentAlpha-(double) 911 image->colormap[i].alpha; 912 beta=(double) TransparentAlpha-(double) 913 image->colormap[opacity].alpha; 914 if (alpha < beta) 915 opacity=i; 916 } 917 if (opacity == -1) 918 { 919 (void) SetImageType(image,PaletteBilevelMatteType,exception); 920 for (i=0; i < (ssize_t) image->colors; i++) 921 if (image->colormap[i].alpha != OpaqueAlpha) 922 { 923 if (opacity < 0) 924 { 925 opacity=i; 926 continue; 927 } 928 alpha=(Quantum) TransparentAlpha-(double) 929 image->colormap[i].alpha; 930 beta=(Quantum) TransparentAlpha-(double) 931 image->colormap[opacity].alpha; 932 if (alpha < beta) 933 opacity=i; 934 } 935 } 936 if (opacity >= 0) 937 { 938 image->colormap[opacity].red=image->transparent_color.red; 939 image->colormap[opacity].green=image->transparent_color.green; 940 image->colormap[opacity].blue=image->transparent_color.blue; 941 } 942 } 943 /* 944 Compute the character per pixel. 945 */ 946 characters_per_pixel=1; 947 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels) 948 characters_per_pixel++; 949 /* 950 XPM header. 951 */ 952 (void) WriteBlobString(image,"/* XPM */\n"); 953 GetPathComponent(image->filename,BasePath,basename); 954 if (isalnum((int) ((unsigned char) *basename)) == 0) 955 { 956 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename); 957 (void) CopyMagickString(basename,buffer,MaxTextExtent); 958 } 959 if (isalpha((int) ((unsigned char) basename[0])) == 0) 960 basename[0]='_'; 961 for (i=1; basename[i] != '\0'; i++) 962 if (isalnum((int) ((unsigned char) basename[i])) == 0) 963 basename[i]='_'; 964 (void) FormatLocaleString(buffer,MaxTextExtent, 965 "static char *%s[] = {\n",basename); 966 (void) WriteBlobString(image,buffer); 967 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); 968 (void) FormatLocaleString(buffer,MaxTextExtent, 969 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double) 970 image->rows,(double) image->colors,(double) characters_per_pixel); 971 (void) WriteBlobString(image,buffer); 972 GetPixelInfo(image,&pixel); 973 for (i=0; i < (ssize_t) image->colors; i++) 974 { 975 /* 976 Define XPM color. 977 */ 978 pixel=image->colormap[i]; 979 pixel.colorspace=sRGBColorspace; 980 pixel.depth=8; 981 pixel.alpha=(double) OpaqueAlpha; 982 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception); 983 if (i == opacity) 984 (void) CopyMagickString(name,"None",MaxTextExtent); 985 /* 986 Write XPM color. 987 */ 988 k=i % MaxCixels; 989 symbol[0]=Cixel[k]; 990 for (j=1; j < (ssize_t) characters_per_pixel; j++) 991 { 992 k=((i-k)/MaxCixels) % MaxCixels; 993 symbol[j]=Cixel[k]; 994 } 995 symbol[j]='\0'; 996 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol, 997 name); 998 (void) WriteBlobString(image,buffer); 999 } 1000 /* 1001 Define XPM pixels. 1002 */ 1003 (void) WriteBlobString(image,"/* pixels */\n"); 1004 for (y=0; y < (ssize_t) image->rows; y++) 1005 { 1006 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 1007 if (p == (const Quantum *) NULL) 1008 break; 1009 (void) WriteBlobString(image,"\""); 1010 for (x=0; x < (ssize_t) image->columns; x++) 1011 { 1012 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels); 1013 symbol[0]=Cixel[k]; 1014 for (j=1; j < (ssize_t) characters_per_pixel; j++) 1015 { 1016 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels; 1017 symbol[j]=Cixel[k]; 1018 } 1019 symbol[j]='\0'; 1020 (void) CopyMagickString(buffer,symbol,MaxTextExtent); 1021 (void) WriteBlobString(image,buffer); 1022 p+=GetPixelChannels(image); 1023 } 1024 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n", 1025 (y == (ssize_t) (image->rows-1) ? "" : ",")); 1026 (void) WriteBlobString(image,buffer); 1027 if (image->previous == (Image *) NULL) 1028 { 1029 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 1030 image->rows); 1031 if (status == MagickFalse) 1032 break; 1033 } 1034 } 1035 (void) WriteBlobString(image,"};\n"); 1036 (void) CloseBlob(image); 1037 return(MagickTrue); 1038} 1039