1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % X X BBBB M M % 7 % X X B B MM MM % 8 % X BBBB M M M % 9 % X X B B M M % 10 % X X BBBB M M % 11 % % 12 % % 13 % Read/Write X Windows System Bitmap Format % 14 % % 15 % Software Design % 16 % Cristy % 17 % July 1992 % 18 % % 19 % % 20 % Copyright 1999-2016 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 /* 41 Include declarations. 42 */ 43 #include "MagickCore/studio.h" 44 #include "MagickCore/attribute.h" 45 #include "MagickCore/blob.h" 46 #include "MagickCore/blob-private.h" 47 #include "MagickCore/cache.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/image.h" 55 #include "MagickCore/image-private.h" 56 #include "MagickCore/list.h" 57 #include "MagickCore/magick.h" 58 #include "MagickCore/memory_.h" 59 #include "MagickCore/monitor.h" 60 #include "MagickCore/monitor-private.h" 61 #include "MagickCore/pixel-accessor.h" 62 #include "MagickCore/quantum-private.h" 63 #include "MagickCore/static.h" 64 #include "MagickCore/string_.h" 65 #include "MagickCore/module.h" 66 #include "MagickCore/utility.h" 67 68 /* 70 Forward declarations. 71 */ 72 static MagickBooleanType 73 WriteXBMImage(const ImageInfo *,Image *,ExceptionInfo *); 74 75 /* 77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 78 % % 79 % % 80 % % 81 % I s X B M % 82 % % 83 % % 84 % % 85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 86 % 87 % IsXBM() returns MagickTrue if the image format type, identified by the 88 % magick string, is XBM. 89 % 90 % The format of the IsXBM method is: 91 % 92 % MagickBooleanType IsXBM(const unsigned char *magick,const size_t length) 93 % 94 % A description of each parameter follows: 95 % 96 % o magick: compare image format pattern against these bytes. 97 % 98 % o length: Specifies the length of the magick string. 99 % 100 */ 101 static MagickBooleanType IsXBM(const unsigned char *magick,const size_t length) 102 { 103 if (length < 7) 104 return(MagickFalse); 105 if (memcmp(magick,"#define",7) == 0) 106 return(MagickTrue); 107 return(MagickFalse); 108 } 109 110 /* 112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 113 % % 114 % % 115 % % 116 % R e a d X B M I m a g e % 117 % % 118 % % 119 % % 120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 121 % 122 % ReadXBMImage() reads an X11 bitmap image file and returns it. It 123 % allocates the memory necessary for the new Image structure and returns a 124 % pointer to the new image. 125 % 126 % The format of the ReadXBMImage method is: 127 % 128 % Image *ReadXBMImage(const ImageInfo *image_info,ExceptionInfo *exception) 129 % 130 % A description of each parameter follows: 131 % 132 % o image_info: the image info. 133 % 134 % o exception: return any errors or warnings in this structure. 135 % 136 */ 137 138 static unsigned int XBMInteger(Image *image,short int *hex_digits) 139 { 140 int 141 c; 142 143 unsigned int 144 value; 145 146 /* 147 Skip any leading whitespace. 148 */ 149 do 150 { 151 c=ReadBlobByte(image); 152 if (c == EOF) 153 return(0); 154 } while ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')); 155 /* 156 Evaluate number. 157 */ 158 value=0; 159 do 160 { 161 if (value > (unsigned int) (INT_MAX/10)) 162 break; 163 value*=16; 164 c&=0xff; 165 if (value > (unsigned int) (INT_MAX-hex_digits[c])) 166 break; 167 value+=hex_digits[c]; 168 c=ReadBlobByte(image); 169 if (c == EOF) 170 return(0); 171 } while (hex_digits[c] >= 0); 172 return(value); 173 } 174 175 static Image *ReadXBMImage(const ImageInfo *image_info,ExceptionInfo *exception) 176 { 177 char 178 buffer[MagickPathExtent], 179 name[MagickPathExtent]; 180 181 Image 182 *image; 183 184 MagickBooleanType 185 status; 186 187 register ssize_t 188 i, 189 x; 190 191 register Quantum 192 *q; 193 194 register unsigned char 195 *p; 196 197 short int 198 hex_digits[256]; 199 200 ssize_t 201 y; 202 203 unsigned char 204 *data; 205 206 unsigned int 207 bit, 208 byte, 209 bytes_per_line, 210 height, 211 length, 212 padding, 213 value, 214 version, 215 width; 216 217 /* 218 Open image file. 219 */ 220 assert(image_info != (const ImageInfo *) NULL); 221 assert(image_info->signature == MagickCoreSignature); 222 if (image_info->debug != MagickFalse) 223 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 224 image_info->filename); 225 assert(exception != (ExceptionInfo *) NULL); 226 assert(exception->signature == MagickCoreSignature); 227 image=AcquireImage(image_info,exception); 228 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 229 if (status == MagickFalse) 230 { 231 image=DestroyImageList(image); 232 return((Image *) NULL); 233 } 234 /* 235 Read X bitmap header. 236 */ 237 width=0; 238 height=0; 239 while (ReadBlobString(image,buffer) != (char *) NULL) 240 if (sscanf(buffer,"#define %32s %u",name,&width) == 2) 241 if ((strlen(name) >= 6) && 242 (LocaleCompare(name+strlen(name)-6,"_width") == 0)) 243 break; 244 while (ReadBlobString(image,buffer) != (char *) NULL) 245 if (sscanf(buffer,"#define %32s %u",name,&height) == 2) 246 if ((strlen(name) >= 7) && 247 (LocaleCompare(name+strlen(name)-7,"_height") == 0)) 248 break; 249 image->columns=width; 250 image->rows=height; 251 image->depth=8; 252 image->storage_class=PseudoClass; 253 image->colors=2; 254 /* 255 Scan until hex digits. 256 */ 257 version=11; 258 while (ReadBlobString(image,buffer) != (char *) NULL) 259 { 260 if (sscanf(buffer,"static short %32s = {",name) == 1) 261 version=10; 262 else 263 if (sscanf(buffer,"static unsigned char %32s = {",name) == 1) 264 version=11; 265 else 266 if (sscanf(buffer,"static char %32s = {",name) == 1) 267 version=11; 268 else 269 continue; 270 p=(unsigned char *) strrchr(name,'_'); 271 if (p == (unsigned char *) NULL) 272 p=(unsigned char *) name; 273 else 274 p++; 275 if (LocaleCompare("bits[]",(char *) p) == 0) 276 break; 277 } 278 if ((image->columns == 0) || (image->rows == 0) || 279 (EOFBlob(image) != MagickFalse)) 280 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 281 /* 282 Initialize image structure. 283 */ 284 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse) 285 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 286 /* 287 Initialize colormap. 288 */ 289 image->colormap[0].red=QuantumRange; 290 image->colormap[0].green=QuantumRange; 291 image->colormap[0].blue=QuantumRange; 292 image->colormap[1].red=(Quantum) 0; 293 image->colormap[1].green=(Quantum) 0; 294 image->colormap[1].blue=(Quantum) 0; 295 if (image_info->ping != MagickFalse) 296 { 297 (void) CloseBlob(image); 298 return(GetFirstImageInList(image)); 299 } 300 status=SetImageExtent(image,image->columns,image->rows,exception); 301 if (status == MagickFalse) 302 return(DestroyImageList(image)); 303 /* 304 Initialize hex values. 305 */ 306 hex_digits[(int) '0']=0; 307 hex_digits[(int) '1']=1; 308 hex_digits[(int) '2']=2; 309 hex_digits[(int) '3']=3; 310 hex_digits[(int) '4']=4; 311 hex_digits[(int) '5']=5; 312 hex_digits[(int) '6']=6; 313 hex_digits[(int) '7']=7; 314 hex_digits[(int) '8']=8; 315 hex_digits[(int) '9']=9; 316 hex_digits[(int) 'A']=10; 317 hex_digits[(int) 'B']=11; 318 hex_digits[(int) 'C']=12; 319 hex_digits[(int) 'D']=13; 320 hex_digits[(int) 'E']=14; 321 hex_digits[(int) 'F']=15; 322 hex_digits[(int) 'a']=10; 323 hex_digits[(int) 'b']=11; 324 hex_digits[(int) 'c']=12; 325 hex_digits[(int) 'd']=13; 326 hex_digits[(int) 'e']=14; 327 hex_digits[(int) 'f']=15; 328 hex_digits[(int) 'x']=0; 329 hex_digits[(int) ' ']=(-1); 330 hex_digits[(int) ',']=(-1); 331 hex_digits[(int) '}']=(-1); 332 hex_digits[(int) '\n']=(-1); 333 hex_digits[(int) '\t']=(-1); 334 /* 335 Read hex image data. 336 */ 337 padding=0; 338 if (((image->columns % 16) != 0) && ((image->columns % 16) < 9) && 339 (version == 10)) 340 padding=1; 341 bytes_per_line=(unsigned int) (image->columns+7)/8+padding; 342 length=(unsigned int) image->rows; 343 data=(unsigned char *) AcquireQuantumMemory(length,bytes_per_line* 344 sizeof(*data)); 345 if (data == (unsigned char *) NULL) 346 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 347 p=data; 348 if (version == 10) 349 for (i=0; i < (ssize_t) (bytes_per_line*image->rows); (i+=2)) 350 { 351 value=XBMInteger(image,hex_digits); 352 *p++=(unsigned char) value; 353 if ((padding == 0) || (((i+2) % bytes_per_line) != 0)) 354 *p++=(unsigned char) (value >> 8); 355 } 356 else 357 for (i=0; i < (ssize_t) (bytes_per_line*image->rows); i++) 358 { 359 value=XBMInteger(image,hex_digits); 360 *p++=(unsigned char) value; 361 } 362 /* 363 Convert X bitmap image to pixel packets. 364 */ 365 p=data; 366 for (y=0; y < (ssize_t) image->rows; y++) 367 { 368 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 369 if (q == (Quantum *) NULL) 370 break; 371 bit=0; 372 byte=0; 373 for (x=0; x < (ssize_t) image->columns; x++) 374 { 375 if (bit == 0) 376 byte=(size_t) (*p++); 377 SetPixelIndex(image,(Quantum) ((byte & 0x01) != 0 ? 0x01 : 0x00),q); 378 bit++; 379 byte>>=1; 380 if (bit == 8) 381 bit=0; 382 q+=GetPixelChannels(image); 383 } 384 if (SyncAuthenticPixels(image,exception) == MagickFalse) 385 break; 386 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 387 image->rows); 388 if (status == MagickFalse) 389 break; 390 } 391 data=(unsigned char *) RelinquishMagickMemory(data); 392 (void) SyncImage(image,exception); 393 (void) CloseBlob(image); 394 return(GetFirstImageInList(image)); 395 } 396 397 /* 399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 400 % % 401 % % 402 % % 403 % R e g i s t e r X B M I m a g e % 404 % % 405 % % 406 % % 407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 408 % 409 % RegisterXBMImage() adds attributes for the XBM image format to 410 % the list of supported formats. The attributes include the image format 411 % tag, a method to read and/or write the format, whether the format 412 % supports the saving of more than one frame to the same file or blob, 413 % whether the format supports native in-memory I/O, and a brief 414 % description of the format. 415 % 416 % The format of the RegisterXBMImage method is: 417 % 418 % size_t RegisterXBMImage(void) 419 % 420 */ 421 ModuleExport size_t RegisterXBMImage(void) 422 { 423 MagickInfo 424 *entry; 425 426 entry=AcquireMagickInfo("XBM","XBM", 427 "X Windows system bitmap (black and white)"); 428 entry->decoder=(DecodeImageHandler *) ReadXBMImage; 429 entry->encoder=(EncodeImageHandler *) WriteXBMImage; 430 entry->magick=(IsImageFormatHandler *) IsXBM; 431 entry->flags^=CoderAdjoinFlag; 432 (void) RegisterMagickInfo(entry); 433 return(MagickImageCoderSignature); 434 } 435 436 /* 438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 439 % % 440 % % 441 % % 442 % U n r e g i s t e r X B M I m a g e % 443 % % 444 % % 445 % % 446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 447 % 448 % UnregisterXBMImage() removes format registrations made by the 449 % XBM module from the list of supported formats. 450 % 451 % The format of the UnregisterXBMImage method is: 452 % 453 % UnregisterXBMImage(void) 454 % 455 */ 456 ModuleExport void UnregisterXBMImage(void) 457 { 458 (void) UnregisterMagickInfo("XBM"); 459 } 460 461 /* 463 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 464 % % 465 % % 466 % % 467 % W r i t e X B M I m a g e % 468 % % 469 % % 470 % % 471 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 472 % 473 % WriteXBMImage() writes an image to a file in the X bitmap format. 474 % 475 % The format of the WriteXBMImage method is: 476 % 477 % MagickBooleanType WriteXBMImage(const ImageInfo *image_info, 478 % Image *image,ExceptionInfo *exception) 479 % 480 % A description of each parameter follows. 481 % 482 % o image_info: the image info. 483 % 484 % o image: The image. 485 % 486 % o exception: return any errors or warnings in this structure. 487 % 488 */ 489 static MagickBooleanType WriteXBMImage(const ImageInfo *image_info,Image *image, 490 ExceptionInfo *exception) 491 { 492 char 493 basename[MagickPathExtent], 494 buffer[MagickPathExtent]; 495 496 MagickBooleanType 497 status; 498 499 register const Quantum 500 *p; 501 502 register ssize_t 503 x; 504 505 size_t 506 bit, 507 byte; 508 509 ssize_t 510 count, 511 y; 512 513 /* 514 Open output image file. 515 */ 516 assert(image_info != (const ImageInfo *) NULL); 517 assert(image_info->signature == MagickCoreSignature); 518 assert(image != (Image *) NULL); 519 assert(image->signature == MagickCoreSignature); 520 if (image->debug != MagickFalse) 521 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 522 assert(exception != (ExceptionInfo *) NULL); 523 assert(exception->signature == MagickCoreSignature); 524 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 525 if (status == MagickFalse) 526 return(status); 527 (void) TransformImageColorspace(image,sRGBColorspace,exception); 528 /* 529 Write X bitmap header. 530 */ 531 GetPathComponent(image->filename,BasePath,basename); 532 (void) FormatLocaleString(buffer,MagickPathExtent,"#define %s_width %.20g\n", 533 basename,(double) image->columns); 534 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 535 (void) FormatLocaleString(buffer,MagickPathExtent,"#define %s_height %.20g\n", 536 basename,(double) image->rows); 537 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 538 (void) FormatLocaleString(buffer,MagickPathExtent, 539 "static char %s_bits[] = {\n",basename); 540 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 541 (void) CopyMagickString(buffer," ",MagickPathExtent); 542 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 543 /* 544 Convert MIFF to X bitmap pixels. 545 */ 546 (void) SetImageType(image,BilevelType,exception); 547 bit=0; 548 byte=0; 549 count=0; 550 x=0; 551 y=0; 552 (void) CopyMagickString(buffer," ",MagickPathExtent); 553 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 554 for (y=0; y < (ssize_t) image->rows; y++) 555 { 556 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 557 if (p == (const Quantum *) NULL) 558 break; 559 for (x=0; x < (ssize_t) image->columns; x++) 560 { 561 byte>>=1; 562 if (GetPixelLuma(image,p) < (QuantumRange/2)) 563 byte|=0x80; 564 bit++; 565 if (bit == 8) 566 { 567 /* 568 Write a bitmap byte to the image file. 569 */ 570 (void) FormatLocaleString(buffer,MagickPathExtent,"0x%02X, ", 571 (unsigned int) (byte & 0xff)); 572 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 573 count++; 574 if (count == 12) 575 { 576 (void) CopyMagickString(buffer,"\n ",MagickPathExtent); 577 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 578 count=0; 579 }; 580 bit=0; 581 byte=0; 582 } 583 p+=GetPixelChannels(image); 584 } 585 if (bit != 0) 586 { 587 /* 588 Write a bitmap byte to the image file. 589 */ 590 byte>>=(8-bit); 591 (void) FormatLocaleString(buffer,MagickPathExtent,"0x%02X, ", 592 (unsigned int) (byte & 0xff)); 593 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 594 count++; 595 if (count == 12) 596 { 597 (void) CopyMagickString(buffer,"\n ",MagickPathExtent); 598 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 599 count=0; 600 }; 601 bit=0; 602 byte=0; 603 }; 604 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 605 image->rows); 606 if (status == MagickFalse) 607 break; 608 } 609 (void) CopyMagickString(buffer,"};\n",MagickPathExtent); 610 (void) WriteBlob(image,strlen(buffer),(unsigned char *) buffer); 611 (void) CloseBlob(image); 612 return(MagickTrue); 613 } 614