1 /* 2 * Copyright 2002-2009, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Michael Wilber <mwilber@users.berlios.de> 7 */ 8 9 #include "BMPTranslator.h" 10 11 #include <algorithm> 12 #include <new> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include <Catalog.h> 18 19 #include "BMPView.h" 20 21 22 using std::nothrow; 23 using std::min; 24 25 26 //#define INFO(x) printf(x); 27 #define INFO(x) 28 //#define ERROR(x) printf(x); 29 #define ERROR(x) 30 31 #undef B_TRANSLATION_CONTEXT 32 #define B_TRANSLATION_CONTEXT "BMPTranslator" 33 34 35 // The input formats that this translator supports. 36 static const translation_format sInputFormats[] = { 37 { 38 B_TRANSLATOR_BITMAP, 39 B_TRANSLATOR_BITMAP, 40 BBT_IN_QUALITY, 41 BBT_IN_CAPABILITY, 42 "image/x-be-bitmap", 43 "Be Bitmap Format (BMPTranslator)" 44 }, 45 { 46 B_BMP_FORMAT, 47 B_TRANSLATOR_BITMAP, 48 BMP_IN_QUALITY, 49 BMP_IN_CAPABILITY, 50 "image/bmp", 51 "BMP image" 52 } 53 }; 54 55 // The output formats that this translator supports. 56 static const translation_format sOutputFormats[] = { 57 { 58 B_TRANSLATOR_BITMAP, 59 B_TRANSLATOR_BITMAP, 60 BBT_OUT_QUALITY, 61 BBT_OUT_CAPABILITY, 62 "image/x-be-bitmap", 63 "Be Bitmap Format (BMPTranslator)" 64 }, 65 { 66 B_BMP_FORMAT, 67 B_TRANSLATOR_BITMAP, 68 BMP_OUT_QUALITY, 69 BMP_OUT_CAPABILITY, 70 "image/bmp", 71 "BMP image" 72 } 73 }; 74 75 // Default settings for the Translator 76 static const TranSetting sDefaultSettings[] = { 77 {B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false}, 78 {B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false} 79 }; 80 81 const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format); 82 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format); 83 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting); 84 85 86 // --------------------------------------------------------------- 87 // make_nth_translator 88 // 89 // Creates a BMPTranslator object to be used by BTranslatorRoster 90 // 91 // Preconditions: 92 // 93 // Parameters: n, The translator to return. Since 94 // BMPTranslator only publishes one 95 // translator, it only returns a 96 // BMPTranslator if n == 0 97 // 98 // you, The image_id of the add-on that 99 // contains code (not used). 100 // 101 // flags, Has no meaning yet, should be 0. 102 // 103 // Postconditions: 104 // 105 // Returns: NULL if n is not zero, 106 // a new BMPTranslator if n is zero 107 // --------------------------------------------------------------- 108 BTranslator * 109 make_nth_translator(int32 n, image_id you, uint32 flags, ...) 110 { 111 if (!n) 112 return new BMPTranslator(); 113 else 114 return NULL; 115 } 116 117 // --------------------------------------------------------------- 118 // Constructor 119 // 120 // Sets up the version info and the name of the translator so that 121 // these values can be returned when they are requested. 122 // 123 // Preconditions: 124 // 125 // Parameters: 126 // 127 // Postconditions: 128 // 129 // Returns: 130 // --------------------------------------------------------------- 131 BMPTranslator::BMPTranslator() 132 : BaseTranslator(B_TRANSLATE("BMP images"), 133 B_TRANSLATE("BMP image translator"), 134 BMP_TRANSLATOR_VERSION, 135 sInputFormats, kNumInputFormats, 136 sOutputFormats, kNumOutputFormats, 137 "BMPTranslator_Settings", 138 sDefaultSettings, kNumDefaultSettings, 139 B_TRANSLATOR_BITMAP, B_BMP_FORMAT) 140 { 141 } 142 143 // --------------------------------------------------------------- 144 // Destructor 145 // 146 // Does nothing 147 // 148 // Preconditions: 149 // 150 // Parameters: 151 // 152 // Postconditions: 153 // 154 // Returns: 155 // --------------------------------------------------------------- 156 BMPTranslator::~BMPTranslator() 157 { 158 } 159 160 // --------------------------------------------------------------- 161 // get_padding 162 // 163 // Returns number of bytes of padding required at the end of 164 // the row by the BMP format 165 // 166 // 167 // Preconditions: If bitsperpixel is zero, a division by zero 168 // will occur, which is bad 169 // 170 // Parameters: width, width of the row, in pixels 171 // 172 // bitsperpixel, bitdepth of the image 173 // 174 // Postconditions: 175 // 176 // Returns: 177 // --------------------------------------------------------------- 178 int32 179 get_padding(uint32 width, uint16 bitsperpixel) 180 { 181 int32 padding = 0; 182 183 if (bitsperpixel > 8) { 184 uint8 bytesPerPixel = bitsperpixel / 8; 185 padding = (width * bytesPerPixel) % 4; 186 } else { 187 uint8 pixelsPerByte = 8 / bitsperpixel; 188 if (!(width % pixelsPerByte)) 189 padding = (width / pixelsPerByte) % 4; 190 else 191 padding = ((width + pixelsPerByte - 192 (width % pixelsPerByte)) / 193 pixelsPerByte) % 4; 194 } 195 196 if (padding) 197 padding = 4 - padding; 198 199 return padding; 200 } 201 202 // --------------------------------------------------------------- 203 // get_rowbytes 204 // 205 // Returns number of bytes required to store a row of BMP pixels 206 // with a width of width and a bit depth of bitsperpixel. 207 // 208 // 209 // Preconditions: If bitsperpixel is zero, a division by zero 210 // will occur, which is bad 211 // 212 // Parameters: width, width of the row, in pixels 213 // 214 // bitsperpixel, bitdepth of the image 215 // 216 // Postconditions: 217 // 218 // Returns: 219 // --------------------------------------------------------------- 220 int32 221 get_rowbytes(uint32 width, uint16 bitsperpixel) 222 { 223 int32 rowbytes = 0; 224 int32 padding = get_padding(width, bitsperpixel); 225 226 if (bitsperpixel > 8) { 227 uint8 bytesPerPixel = bitsperpixel / 8; 228 rowbytes = (width * bytesPerPixel) + padding; 229 } else { 230 uint8 pixelsPerByte = 8 / bitsperpixel; 231 rowbytes = (width / pixelsPerByte) + 232 ((width % pixelsPerByte) ? 1 : 0) + padding; 233 } 234 235 return rowbytes; 236 } 237 238 // --------------------------------------------------------------- 239 // identify_bmp_header 240 // 241 // Determines if the data in inSource is in the MS or OS/2 BMP 242 // format. If it is, it returns info about the data in inSource 243 // to outInfo, pfileheader, pmsheader, pfrommsformat and os2skip. 244 // 245 // Preconditions: 246 // 247 // Parameters: inSource, The source of the image data 248 // 249 // outInfo, Information about the translator 250 // is copied here 251 // 252 // amtread, Amount of data read from inSource 253 // before this function was called 254 // 255 // read, Pointer to the data that was read 256 // in before this function was called 257 // 258 // pfileheader, File header info for the BMP is 259 // copied here after it is read from 260 // the file. 261 // 262 // pmsheader, BMP header info read in from the 263 // BMP file 264 // 265 // pfrommsformat, Set to true if BMP data is BMP 266 // format, false if BMP data is OS/2 267 // format. 268 // 269 // pos2skip, If data is in OS/2 format, the number 270 // of bytes to skip between the header 271 // data and image data is stored here 272 // 273 // Postconditions: 274 // 275 // Returns: B_NO_TRANSLATOR, if the data does not look like 276 // BMP format data 277 // 278 // B_ERROR, if the header data could not be converted to host 279 // format 280 // 281 // B_OK, if the data looks like bits data and no errors were 282 // encountered 283 // --------------------------------------------------------------- 284 status_t 285 identify_bmp_header(BPositionIO *inSource, translator_info *outInfo, 286 BMPFileHeader *pfileheader = NULL, MSInfoHeader *pmsheader = NULL, 287 bool *pfrommsformat = NULL, off_t *pos2skip = NULL) 288 { 289 // read in the fileHeader 290 uint8 buf[40]; 291 BMPFileHeader fileHeader; 292 ssize_t size = 14; 293 if (inSource->Read(buf, size) != size) 294 return B_NO_TRANSLATOR; 295 296 // check BMP magic number 297 const uint16 kBmpMagic = B_HOST_TO_LENDIAN_INT16('MB'); 298 uint16 sourceMagic; 299 memcpy(&sourceMagic, buf, sizeof(uint16)); 300 if (sourceMagic != kBmpMagic) 301 return B_NO_TRANSLATOR; 302 303 // convert fileHeader to host byte order 304 memcpy(&fileHeader.magic, buf, 2); 305 memcpy(&fileHeader.fileSize, buf + 2, 4); 306 memcpy(&fileHeader.reserved, buf + 6, 4); 307 memcpy(&fileHeader.dataOffset, buf + 10, 4); 308 if (swap_data(B_UINT16_TYPE, &fileHeader.magic, sizeof(uint16), 309 B_SWAP_LENDIAN_TO_HOST) != B_OK) 310 return B_ERROR; 311 if (swap_data(B_UINT32_TYPE, 312 (reinterpret_cast<uint8 *> (&fileHeader)) + 2, 12, 313 B_SWAP_LENDIAN_TO_HOST) != B_OK) 314 return B_ERROR; 315 316 if (fileHeader.reserved != 0) 317 return B_NO_TRANSLATOR; 318 319 uint32 headersize = 0; 320 if (inSource->Read(&headersize, 4) != 4) 321 return B_NO_TRANSLATOR; 322 if (swap_data(B_UINT32_TYPE, &headersize, 4, 323 B_SWAP_LENDIAN_TO_HOST) != B_OK) 324 return B_ERROR; 325 326 if (headersize == sizeof(MSInfoHeader)) { 327 // MS format 328 329 if (fileHeader.dataOffset < 54) 330 return B_NO_TRANSLATOR; 331 332 MSInfoHeader msheader; 333 msheader.size = headersize; 334 if (inSource->Read( 335 reinterpret_cast<uint8 *> (&msheader) + 4, 36) != 36) 336 return B_NO_TRANSLATOR; 337 338 // convert msheader to host byte order 339 if (swap_data(B_UINT32_TYPE, 340 reinterpret_cast<uint8 *> (&msheader) + 4, 36, 341 B_SWAP_LENDIAN_TO_HOST) != B_OK) 342 return B_ERROR; 343 344 // check if msheader is valid 345 if (msheader.width == 0 || msheader.height == 0) 346 return B_NO_TRANSLATOR; 347 if (msheader.planes != 1) 348 return B_NO_TRANSLATOR; 349 if ((msheader.bitsperpixel != 1 || 350 msheader.compression != BMP_NO_COMPRESS) && 351 (msheader.bitsperpixel != 4 || 352 msheader.compression != BMP_NO_COMPRESS) && 353 (msheader.bitsperpixel != 4 || 354 msheader.compression != BMP_RLE4_COMPRESS) && 355 (msheader.bitsperpixel != 8 || 356 msheader.compression != BMP_NO_COMPRESS) && 357 (msheader.bitsperpixel != 8 || 358 msheader.compression != BMP_RLE8_COMPRESS) && 359 (msheader.bitsperpixel != 24 || 360 msheader.compression != BMP_NO_COMPRESS) && 361 (msheader.bitsperpixel != 32 || 362 msheader.compression != BMP_NO_COMPRESS)) 363 return B_NO_TRANSLATOR; 364 if (!msheader.imagesize && msheader.compression) 365 return B_NO_TRANSLATOR; 366 if (msheader.colorsimportant > msheader.colorsused) 367 return B_NO_TRANSLATOR; 368 369 if (outInfo) { 370 outInfo->type = B_BMP_FORMAT; 371 outInfo->group = B_TRANSLATOR_BITMAP; 372 outInfo->quality = BMP_IN_QUALITY; 373 outInfo->capability = BMP_IN_CAPABILITY; 374 sprintf(outInfo->name, 375 B_TRANSLATE_COMMENT("BMP image (MS format, %d bits", 376 "Ignore missing closing round bracket"), 377 msheader.bitsperpixel); 378 if (msheader.compression) 379 strcat(outInfo->name, ", RLE)"); 380 else 381 strcat(outInfo->name, ")"); 382 strcpy(outInfo->MIME, "image/x-bmp"); 383 } 384 385 if (pfileheader) { 386 pfileheader->magic = fileHeader.magic; 387 pfileheader->fileSize = fileHeader.fileSize; 388 pfileheader->reserved = fileHeader.reserved; 389 pfileheader->dataOffset = fileHeader.dataOffset; 390 } 391 if (pmsheader) { 392 pmsheader->size = msheader.size; 393 pmsheader->width = abs(msheader.width); 394 pmsheader->height = msheader.height; 395 pmsheader->planes = msheader.planes; 396 pmsheader->bitsperpixel = msheader.bitsperpixel; 397 pmsheader->compression = msheader.compression; 398 pmsheader->imagesize = msheader.imagesize; 399 pmsheader->xpixperm = msheader.xpixperm; 400 pmsheader->ypixperm = msheader.ypixperm; 401 pmsheader->colorsused = msheader.colorsused; 402 pmsheader->colorsimportant = msheader.colorsimportant; 403 } 404 if (pfrommsformat) 405 (*pfrommsformat) = true; 406 407 return B_OK; 408 409 } else if (headersize == sizeof(OS2InfoHeader)) { 410 // OS/2 format 411 412 if (fileHeader.dataOffset < 26) 413 return B_NO_TRANSLATOR; 414 415 OS2InfoHeader os2header; 416 os2header.size = headersize; 417 if (inSource->Read( 418 reinterpret_cast<uint8 *> (&os2header) + 4, 8) != 8) 419 return B_NO_TRANSLATOR; 420 421 // convert msheader to host byte order 422 if (swap_data(B_UINT32_TYPE, 423 reinterpret_cast<uint8 *> (&os2header) + 4, 8, 424 B_SWAP_LENDIAN_TO_HOST) != B_OK) 425 return B_ERROR; 426 427 // check if msheader is valid 428 if (os2header.width == 0 || os2header.height == 0) 429 return B_NO_TRANSLATOR; 430 if (os2header.planes != 1) 431 return B_NO_TRANSLATOR; 432 if (os2header.bitsperpixel != 1 && 433 os2header.bitsperpixel != 4 && 434 os2header.bitsperpixel != 8 && 435 os2header.bitsperpixel != 24) 436 return B_NO_TRANSLATOR; 437 438 if (outInfo) { 439 outInfo->type = B_BMP_FORMAT; 440 outInfo->group = B_TRANSLATOR_BITMAP; 441 outInfo->quality = BMP_IN_QUALITY; 442 outInfo->capability = BMP_IN_CAPABILITY; 443 sprintf(outInfo->name, B_TRANSLATE("BMP image (OS/2 format, " 444 "%d bits)"), os2header.bitsperpixel); 445 strcpy(outInfo->MIME, "image/x-bmp"); 446 } 447 if (pfileheader && pmsheader) { 448 pfileheader->magic = 'MB'; 449 pfileheader->fileSize = 0; 450 pfileheader->reserved = 0; 451 pfileheader->dataOffset = 0; 452 453 pmsheader->size = 40; 454 pmsheader->width = os2header.width; 455 pmsheader->height = os2header.height; 456 pmsheader->planes = 1; 457 pmsheader->bitsperpixel = os2header.bitsperpixel; 458 pmsheader->compression = BMP_NO_COMPRESS; 459 pmsheader->imagesize = 0; 460 pmsheader->xpixperm = 2835; // 72 dpi horizontal 461 pmsheader->ypixperm = 2835; // 72 dpi vertical 462 pmsheader->colorsused = 0; 463 pmsheader->colorsimportant = 0; 464 465 // determine fileSize / imagesize 466 switch (pmsheader->bitsperpixel) { 467 case 24: 468 if (pos2skip && fileHeader.dataOffset > 26) 469 (*pos2skip) = fileHeader.dataOffset - 26; 470 471 pfileheader->dataOffset = 54; 472 pmsheader->imagesize = get_rowbytes(pmsheader->width, 473 pmsheader->bitsperpixel) * abs(pmsheader->height); 474 pfileheader->fileSize = pfileheader->dataOffset + 475 pmsheader->imagesize; 476 477 break; 478 479 case 8: 480 case 4: 481 case 1: 482 { 483 uint16 ncolors = 1 << pmsheader->bitsperpixel; 484 pmsheader->colorsused = ncolors; 485 pmsheader->colorsimportant = ncolors; 486 if (pos2skip && fileHeader.dataOffset > 487 static_cast<uint32> (26 + (ncolors * 3))) 488 (*pos2skip) = fileHeader.dataOffset - 489 (26 + (ncolors * 3)); 490 491 pfileheader->dataOffset = 54 + (ncolors * 4); 492 pmsheader->imagesize = get_rowbytes(pmsheader->width, 493 pmsheader->bitsperpixel) * abs(pmsheader->height); 494 pfileheader->fileSize = pfileheader->dataOffset + 495 pmsheader->imagesize; 496 497 break; 498 } 499 500 default: 501 break; 502 } 503 } 504 if (pfrommsformat) 505 (*pfrommsformat) = false; 506 507 return B_OK; 508 509 } else 510 return B_NO_TRANSLATOR; 511 } 512 513 // --------------------------------------------------------------- 514 // DerivedIdentify 515 // 516 // Examines the data from inSource and determines if it is in a 517 // format that this translator knows how to work with. 518 // 519 // Preconditions: 520 // 521 // Parameters: inSource, where the data to examine is 522 // 523 // inFormat, a hint about the data in inSource, 524 // it is ignored since it is only a hint 525 // 526 // ioExtension, configuration settings for the 527 // translator 528 // 529 // outInfo, information about what data is in 530 // inSource and how well this translator 531 // can handle that data is stored here 532 // 533 // outType, The format that the user wants 534 // the data in inSource to be 535 // converted to 536 // 537 // Postconditions: 538 // 539 // Returns: B_NO_TRANSLATOR, if this translator can't handle 540 // the data in inSource 541 // 542 // B_ERROR, if there was an error converting the data to the host 543 // format 544 // 545 // B_BAD_VALUE, if the settings in ioExtension are bad 546 // 547 // B_OK, if this translator understand the data and there were 548 // no errors found 549 // --------------------------------------------------------------- 550 status_t 551 BMPTranslator::DerivedIdentify(BPositionIO *inSource, 552 const translation_format *inFormat, BMessage *ioExtension, 553 translator_info *outInfo, uint32 outType) 554 { 555 return identify_bmp_header(inSource, outInfo); 556 } 557 558 // --------------------------------------------------------------- 559 // translate_from_bits_to_bmp24 560 // 561 // Converts various varieties of the Be Bitmap format ('bits') to 562 // the MS BMP 24-bit format. 563 // 564 // Preconditions: 565 // 566 // Parameters: inSource, contains the bits data to convert 567 // 568 // outDestination, where the BMP data will be written 569 // 570 // fromspace, the format of the data in inSource 571 // 572 // msheader, contains information about the BMP 573 // dimensions and filesize 574 // 575 // Postconditions: 576 // 577 // Returns: B_ERROR, if memory couldn't be allocated or another 578 // error occured 579 // 580 // B_OK, if no errors occurred 581 // --------------------------------------------------------------- 582 status_t 583 translate_from_bits_to_bmp24(BPositionIO *inSource, 584 BPositionIO *outDestination, color_space fromspace, MSInfoHeader &msheader) 585 { 586 // TODO: WHOHA! big switch statement for the innermost loop! 587 // make a loop per colorspace and put the switch outside!!! 588 // remove memcpy() to copy 3 bytes 589 int32 bitsBytesPerPixel = 0; 590 switch (fromspace) { 591 case B_RGB32: 592 case B_RGB32_BIG: 593 case B_RGBA32: 594 case B_RGBA32_BIG: 595 case B_CMY32: 596 case B_CMYA32: 597 case B_CMYK32: 598 bitsBytesPerPixel = 4; 599 break; 600 601 case B_RGB24: 602 case B_RGB24_BIG: 603 case B_CMY24: 604 bitsBytesPerPixel = 3; 605 break; 606 607 case B_RGB16: 608 case B_RGB16_BIG: 609 case B_RGBA15: 610 case B_RGBA15_BIG: 611 case B_RGB15: 612 case B_RGB15_BIG: 613 bitsBytesPerPixel = 2; 614 break; 615 616 case B_CMAP8: 617 case B_GRAY8: 618 bitsBytesPerPixel = 1; 619 break; 620 621 default: 622 return B_ERROR; 623 } 624 int32 bitsRowBytes = msheader.width * bitsBytesPerPixel; 625 int32 padding = get_padding(msheader.width, msheader.bitsperpixel); 626 int32 bmpRowBytes = 627 get_rowbytes(msheader.width, msheader.bitsperpixel); 628 int32 bmppixrow = 0; 629 if (msheader.height > 0) 630 inSource->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 631 uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes]; 632 if (!bmpRowData) 633 return B_NO_MEMORY; 634 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 635 if (!bitsRowData) { 636 delete[] bmpRowData; 637 return B_NO_MEMORY; 638 } 639 memset(bmpRowData + (bmpRowBytes - padding), 0, padding); 640 ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes); 641 const color_map *pmap = NULL; 642 if (fromspace == B_CMAP8) { 643 pmap = system_colors(); 644 if (!pmap) { 645 delete [] bmpRowData; 646 delete [] bitsRowData; 647 return B_ERROR; 648 } 649 } 650 while (rd == static_cast<ssize_t>(bitsRowBytes)) { 651 for (int32 i = 0; i < msheader.width; i++) { 652 uint8 *bitspixel, *bmppixel; 653 uint16 val; 654 switch (fromspace) { 655 case B_RGB32: 656 case B_RGBA32: 657 case B_RGB24: 658 memcpy(bmpRowData + (i * 3), 659 bitsRowData + (i * bitsBytesPerPixel), 3); 660 break; 661 662 case B_RGB16: 663 case B_RGB16_BIG: 664 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 665 bmppixel = bmpRowData + (i * 3); 666 if (fromspace == B_RGB16) 667 val = bitspixel[0] + (bitspixel[1] << 8); 668 else 669 val = bitspixel[1] + (bitspixel[0] << 8); 670 bmppixel[0] = 671 ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); 672 bmppixel[1] = 673 ((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9); 674 bmppixel[2] = 675 ((val & 0xf800) >> 8) | ((val & 0xf800) >> 13); 676 break; 677 678 case B_RGB15: 679 case B_RGB15_BIG: 680 case B_RGBA15: 681 case B_RGBA15_BIG: 682 // NOTE: the alpha data for B_RGBA15* is not used 683 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 684 bmppixel = bmpRowData + (i * 3); 685 if (fromspace == B_RGB15 || fromspace == B_RGBA15) 686 val = bitspixel[0] + (bitspixel[1] << 8); 687 else 688 val = bitspixel[1] + (bitspixel[0] << 8); 689 bmppixel[0] = 690 ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); 691 bmppixel[1] = 692 ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7); 693 bmppixel[2] = 694 ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12); 695 break; 696 697 case B_RGB32_BIG: 698 case B_RGBA32_BIG: 699 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 700 bmppixel = bmpRowData + (i * 3); 701 bmppixel[0] = bitspixel[3]; 702 bmppixel[1] = bitspixel[2]; 703 bmppixel[2] = bitspixel[1]; 704 break; 705 706 case B_RGB24_BIG: 707 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 708 bmppixel = bmpRowData + (i * 3); 709 bmppixel[0] = bitspixel[2]; 710 bmppixel[1] = bitspixel[1]; 711 bmppixel[2] = bitspixel[0]; 712 break; 713 714 case B_CMAP8: 715 { 716 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 717 bmppixel = bmpRowData + (i * 3); 718 rgb_color c = pmap->color_list[bitspixel[0]]; 719 bmppixel[0] = c.blue; 720 bmppixel[1] = c.green; 721 bmppixel[2] = c.red; 722 break; 723 } 724 725 case B_GRAY8: 726 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 727 bmppixel = bmpRowData + (i * 3); 728 bmppixel[0] = bitspixel[0]; 729 bmppixel[1] = bitspixel[0]; 730 bmppixel[2] = bitspixel[0]; 731 break; 732 733 case B_CMYK32: 734 { 735 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 736 bmppixel = bmpRowData + (i * 3); 737 738 int32 comp = 255 - bitspixel[2] - bitspixel[3]; 739 bmppixel[0] = (comp < 0) ? 0 : comp; 740 741 comp = 255 - bitspixel[1] - bitspixel[3]; 742 bmppixel[1] = (comp < 0) ? 0 : comp; 743 744 comp = 255 - bitspixel[0] - bitspixel[3]; 745 bmppixel[2] = (comp < 0) ? 0 : comp; 746 break; 747 } 748 749 case B_CMY32: 750 case B_CMYA32: 751 case B_CMY24: 752 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 753 bmppixel = bmpRowData + (i * 3); 754 bmppixel[0] = 255 - bitspixel[2]; 755 bmppixel[1] = 255 - bitspixel[1]; 756 bmppixel[2] = 255 - bitspixel[0]; 757 break; 758 759 default: 760 break; 761 } // switch (fromspace) 762 } // for for (uint32 i = 0; i < msheader.width; i++) 763 764 outDestination->Write(bmpRowData, bmpRowBytes); 765 bmppixrow++; 766 // if I've read all of the pixel data, break 767 // out of the loop so I don't try to read 768 // non-pixel data 769 if (bmppixrow == abs(msheader.height)) 770 break; 771 772 if (msheader.height > 0) 773 inSource->Seek(bitsRowBytes * -2, SEEK_CUR); 774 rd = inSource->Read(bitsRowData, bitsRowBytes); 775 } // while (rd == bitsRowBytes) 776 777 delete[] bmpRowData; 778 delete[] bitsRowData; 779 780 return B_OK; 781 } 782 783 // --------------------------------------------------------------- 784 // translate_from_bits8_to_bmp8 785 // 786 // Converts 8-bit Be Bitmaps ('bits') to the MS 8-bit BMP format 787 // 788 // Preconditions: 789 // 790 // Parameters: inSource, contains the bits data to convert 791 // 792 // outDestination, where the BMP data will be written 793 // 794 // bitsRowBytes, number of bytes in one row of 795 // bits data 796 // 797 // msheader, contains information about the BMP 798 // dimensions and filesize 799 // 800 // Postconditions: 801 // 802 // Returns: B_ERROR, if memory couldn't be allocated or another 803 // error occured 804 // 805 // B_OK, if no errors occurred 806 // --------------------------------------------------------------- 807 status_t 808 translate_from_bits8_to_bmp8(BPositionIO *inSource, 809 BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader) 810 { 811 int32 padding = get_padding(msheader.width, msheader.bitsperpixel); 812 int32 bmpRowBytes = 813 get_rowbytes(msheader.width, msheader.bitsperpixel); 814 int32 bmppixrow = 0; 815 if (msheader.height > 0) 816 inSource->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 817 uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes]; 818 if (!bmpRowData) 819 return B_NO_MEMORY; 820 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 821 if (!bitsRowData) { 822 delete[] bmpRowData; 823 return B_NO_MEMORY; 824 } 825 memset(bmpRowData + (bmpRowBytes - padding), 0, padding); 826 ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes); 827 while (rd == bitsRowBytes) { 828 memcpy(bmpRowData, bitsRowData, msheader.width); 829 outDestination->Write(bmpRowData, bmpRowBytes); 830 bmppixrow++; 831 // if I've read all of the pixel data, break 832 // out of the loop so I don't try to read 833 // non-pixel data 834 if (bmppixrow == abs(msheader.height)) 835 break; 836 837 if (msheader.height > 0) 838 inSource->Seek(bitsRowBytes * -2, SEEK_CUR); 839 rd = inSource->Read(bitsRowData, bitsRowBytes); 840 } // while (rd == bitsRowBytes) 841 842 delete[] bmpRowData; 843 delete[] bitsRowData; 844 845 return B_OK; 846 } 847 848 // --------------------------------------------------------------- 849 // translate_from_bits1_to_bmp1 850 // 851 // Converts 1-bit Be Bitmaps ('bits') to the MS 1-bit BMP format 852 // 853 // Preconditions: 854 // 855 // Parameters: inSource, contains the bits data to convert 856 // 857 // outDestination, where the BMP data will be written 858 // 859 // bitsRowBytes, number of bytes in one row of 860 // bits data 861 // 862 // msheader, contains information about the BMP 863 // dimensions and filesize 864 // 865 // Postconditions: 866 // 867 // Returns: B_ERROR, if memory couldn't be allocated or another 868 // error occured 869 // 870 // B_OK, if no errors occurred 871 // --------------------------------------------------------------- 872 status_t 873 translate_from_bits1_to_bmp1(BPositionIO *inSource, 874 BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader) 875 { 876 uint8 pixelsPerByte = 8 / msheader.bitsperpixel; 877 int32 bmpRowBytes = 878 get_rowbytes(msheader.width, msheader.bitsperpixel); 879 int32 bmppixrow = 0; 880 if (msheader.height > 0) 881 inSource->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 882 uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes]; 883 if (!bmpRowData) 884 return B_NO_MEMORY; 885 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 886 if (!bitsRowData) { 887 delete[] bmpRowData; 888 return B_NO_MEMORY; 889 } 890 ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes); 891 while (rd == bitsRowBytes) { 892 int32 bmppixcol = 0; 893 memset(bmpRowData, 0, bmpRowBytes); 894 for (int32 i = 0; (bmppixcol < msheader.width) && 895 (i < bitsRowBytes); i++) { 896 // process each byte in the row 897 uint8 pixels = bitsRowData[i]; 898 for (uint8 compbit = 128; (bmppixcol < msheader.width) && 899 compbit; compbit >>= 1) { 900 // for each bit in the current byte, convert to a BMP palette 901 // index and store that in the bmpRowData 902 uint8 index; 903 if (pixels & compbit) 904 // 1 == black 905 index = 1; 906 else 907 // 0 == white 908 index = 0; 909 bmpRowData[bmppixcol / pixelsPerByte] |= 910 index << (7 - (bmppixcol % pixelsPerByte)); 911 bmppixcol++; 912 } 913 } 914 915 outDestination->Write(bmpRowData, bmpRowBytes); 916 bmppixrow++; 917 // if I've read all of the pixel data, break 918 // out of the loop so I don't try to read 919 // non-pixel data 920 if (bmppixrow == abs(msheader.height)) 921 break; 922 923 if (msheader.height > 0) 924 inSource->Seek(bitsRowBytes * -2, SEEK_CUR); 925 rd = inSource->Read(bitsRowData, bitsRowBytes); 926 } // while (rd == bitsRowBytes) 927 928 delete[] bmpRowData; 929 delete[] bitsRowData; 930 931 return B_OK; 932 } 933 934 // --------------------------------------------------------------- 935 // write_bmp_headers 936 // 937 // Writes the MS BMP headers (fileHeader and msheader) 938 // to outDestination. 939 // 940 // Preconditions: 941 // 942 // Parameters: outDestination, where the headers are written to 943 // 944 // fileHeader, BMP file header data 945 // 946 // msheader, BMP info header data 947 // 948 // Postconditions: 949 // 950 // Returns: B_ERROR, if something went wrong 951 // 952 // B_OK, if there were no problems writing out the headers 953 // --------------------------------------------------------------- 954 status_t 955 write_bmp_headers(BPositionIO *outDestination, BMPFileHeader &fileHeader, 956 MSInfoHeader &msheader) 957 { 958 uint8 bmpheaders[54]; 959 memcpy(bmpheaders, &fileHeader.magic, sizeof(uint16)); 960 memcpy(bmpheaders + 2, &fileHeader.fileSize, sizeof(uint32)); 961 memcpy(bmpheaders + 6, &fileHeader.reserved, sizeof(uint32)); 962 memcpy(bmpheaders + 10, &fileHeader.dataOffset, sizeof(uint32)); 963 memcpy(bmpheaders + 14, &msheader, sizeof(msheader)); 964 if (swap_data(B_UINT16_TYPE, bmpheaders, 2, 965 B_SWAP_HOST_TO_LENDIAN) != B_OK) 966 return B_ERROR; 967 if (swap_data(B_UINT32_TYPE, bmpheaders + 2, 12, 968 B_SWAP_HOST_TO_LENDIAN) != B_OK) 969 return B_ERROR; 970 if (swap_data(B_UINT32_TYPE, bmpheaders + 14, 971 sizeof(MSInfoHeader), B_SWAP_HOST_TO_LENDIAN) != B_OK) 972 return B_ERROR; 973 if (outDestination->Write(bmpheaders, 54) != 54) 974 return B_ERROR; 975 976 return B_OK; 977 } 978 979 // --------------------------------------------------------------- 980 // translate_from_bits 981 // 982 // Convert the data in inSource from the Be Bitmap format ('bits') 983 // to the format specified in outType (either bits or BMP). 984 // 985 // Preconditions: 986 // 987 // Parameters: inSource, the bits data to translate 988 // 989 // outType, the type of data to convert to 990 // 991 // outDestination, where the output is written to 992 // 993 // Postconditions: 994 // 995 // Returns: B_NO_TRANSLATOR, if the data is not in a supported 996 // format 997 // 998 // B_ERROR, if there was an error allocating memory or some other 999 // error 1000 // 1001 // B_OK, if successfully translated the data from the bits format 1002 // --------------------------------------------------------------- 1003 status_t 1004 BMPTranslator::translate_from_bits(BPositionIO *inSource, uint32 outType, 1005 BPositionIO *outDestination) 1006 { 1007 bool bheaderonly, bdataonly; 1008 bheaderonly = bdataonly = false; 1009 1010 TranslatorBitmap bitsHeader; 1011 status_t result; 1012 result = identify_bits_header(inSource, NULL, &bitsHeader); 1013 if (result != B_OK) 1014 return result; 1015 1016 // Translate B_TRANSLATOR_BITMAP to B_BMP_FORMAT 1017 if (outType == B_BMP_FORMAT) { 1018 // Set up BMP header 1019 BMPFileHeader fileHeader; 1020 fileHeader.magic = 'MB'; 1021 fileHeader.reserved = 0; 1022 1023 MSInfoHeader msheader; 1024 msheader.size = 40; 1025 msheader.width = 1026 static_cast<uint32> (bitsHeader.bounds.Width() + 1); 1027 msheader.height = 1028 static_cast<int32> (bitsHeader.bounds.Height() + 1); 1029 msheader.planes = 1; 1030 msheader.xpixperm = 2835; // 72 dpi horizontal 1031 msheader.ypixperm = 2835; // 72 dpi vertical 1032 msheader.colorsused = 0; 1033 msheader.colorsimportant = 0; 1034 1035 // determine fileSize / imagesize 1036 switch (bitsHeader.colors) { 1037 case B_RGB32: 1038 case B_RGB32_BIG: 1039 case B_RGBA32: 1040 case B_RGBA32_BIG: 1041 case B_RGB24: 1042 case B_RGB24_BIG: 1043 case B_RGB16: 1044 case B_RGB16_BIG: 1045 case B_RGB15: 1046 case B_RGB15_BIG: 1047 case B_RGBA15: 1048 case B_RGBA15_BIG: 1049 case B_CMYK32: 1050 case B_CMY32: 1051 case B_CMYA32: 1052 case B_CMY24: 1053 1054 fileHeader.dataOffset = 54; 1055 msheader.bitsperpixel = 24; 1056 msheader.compression = BMP_NO_COMPRESS; 1057 msheader.imagesize = get_rowbytes(msheader.width, 24) * 1058 msheader.height; 1059 fileHeader.fileSize = fileHeader.dataOffset + 1060 msheader.imagesize; 1061 1062 break; 1063 1064 case B_CMAP8: 1065 case B_GRAY8: 1066 1067 msheader.colorsused = 256; 1068 msheader.colorsimportant = 256; 1069 fileHeader.dataOffset = 54 + (4 * 256); 1070 msheader.bitsperpixel = 8; 1071 msheader.compression = BMP_NO_COMPRESS; 1072 msheader.imagesize = get_rowbytes(msheader.width, 1073 msheader.bitsperpixel) * msheader.height; 1074 fileHeader.fileSize = fileHeader.dataOffset + 1075 msheader.imagesize; 1076 1077 break; 1078 1079 case B_GRAY1: 1080 1081 msheader.colorsused = 2; 1082 msheader.colorsimportant = 2; 1083 fileHeader.dataOffset = 62; 1084 msheader.bitsperpixel = 1; 1085 msheader.compression = BMP_NO_COMPRESS; 1086 msheader.imagesize = get_rowbytes(msheader.width, 1087 msheader.bitsperpixel) * msheader.height; 1088 fileHeader.fileSize = fileHeader.dataOffset + 1089 msheader.imagesize; 1090 1091 break; 1092 1093 default: 1094 return B_NO_TRANSLATOR; 1095 } 1096 1097 // write out the BMP headers 1098 if (bheaderonly || (!bheaderonly && !bdataonly)) { 1099 result = write_bmp_headers(outDestination, fileHeader, msheader); 1100 if (result != B_OK) 1101 return result; 1102 } 1103 if (bheaderonly) 1104 // if user only wants the header, bail out 1105 // before the data is written 1106 return result; 1107 1108 // write out the BMP pixel data 1109 switch (bitsHeader.colors) { 1110 case B_RGB32: 1111 case B_RGB32_BIG: 1112 case B_RGBA32: 1113 case B_RGBA32_BIG: 1114 case B_RGB24: 1115 case B_RGB24_BIG: 1116 case B_RGB16: 1117 case B_RGB16_BIG: 1118 case B_RGB15: 1119 case B_RGB15_BIG: 1120 case B_RGBA15: 1121 case B_RGBA15_BIG: 1122 case B_CMYK32: 1123 case B_CMY32: 1124 case B_CMYA32: 1125 case B_CMY24: 1126 return translate_from_bits_to_bmp24(inSource, outDestination, 1127 bitsHeader.colors, msheader); 1128 1129 case B_CMAP8: 1130 case B_GRAY8: 1131 { 1132 // write palette to BMP file 1133 uint8 pal[1024]; 1134 uint8* palHandle = pal; 1135 if (bitsHeader.colors == B_CMAP8) { 1136 // write system palette 1137 const color_map *pmap = system_colors(); 1138 if (!pmap) 1139 return B_ERROR; 1140 for (int32 i = 0; i < 256; i++) { 1141 rgb_color c = pmap->color_list[i]; 1142 palHandle[0] = c.blue; 1143 palHandle[1] = c.green; 1144 palHandle[2] = c.red; 1145 palHandle[3] = c.alpha; 1146 palHandle += 4; 1147 } 1148 } else { 1149 // write gray palette 1150 for (int32 i = 0; i < 256; i++) { 1151 palHandle[0] = i; 1152 palHandle[1] = i; 1153 palHandle[2] = i; 1154 palHandle[3] = 255; 1155 palHandle += 4; 1156 } 1157 } 1158 ssize_t written = outDestination->Write(pal, 1024); 1159 if (written < 0) 1160 return written; 1161 if (written != 1024) 1162 return B_ERROR; 1163 1164 return translate_from_bits8_to_bmp8(inSource, outDestination, 1165 bitsHeader.rowBytes, msheader); 1166 } 1167 1168 case B_GRAY1: 1169 { 1170 // write monochrome palette to the BMP file 1171 const uint32 monopal[] = { 0x00ffffff, 0x00000000 }; 1172 ssize_t written = outDestination->Write(monopal, 8); 1173 if (written < 0) 1174 return written; 1175 if (written != 8) 1176 return B_ERROR; 1177 1178 return translate_from_bits1_to_bmp1(inSource, outDestination, 1179 bitsHeader.rowBytes, msheader); 1180 } 1181 1182 default: 1183 return B_NO_TRANSLATOR; 1184 } 1185 } else 1186 return B_NO_TRANSLATOR; 1187 } 1188 1189 // --------------------------------------------------------------- 1190 // translate_from_bmpnpal_to_bits 1191 // 1192 // Translates a non-palette BMP from inSource to the B_RGB32 1193 // bits format. 1194 // 1195 // Preconditions: 1196 // 1197 // Parameters: inSource, the BMP data to be translated 1198 // 1199 // outDestination, where the bits data will be written to 1200 // 1201 // msheader, header information about the BMP to be written 1202 // 1203 // Postconditions: 1204 // 1205 // Returns: B_ERROR, if there is an error allocating memory 1206 // 1207 // B_OK, if all went well 1208 // --------------------------------------------------------------- 1209 status_t 1210 translate_from_bmpnpal_to_bits(BPositionIO *inSource, 1211 BPositionIO *outDestination, MSInfoHeader &msheader) 1212 { 1213 int32 bitsRowBytes = msheader.width * 4; 1214 int32 bmpBytesPerPixel = msheader.bitsperpixel / 8; 1215 int32 bmpRowBytes = 1216 get_rowbytes(msheader.width, msheader.bitsperpixel); 1217 1218 // Setup outDestination so that it can be written to 1219 // from the end of the file to the beginning instead of 1220 // the other way around 1221 off_t bitsFileSize = (bitsRowBytes * abs(msheader.height)) + 1222 sizeof(TranslatorBitmap); 1223 if (outDestination->SetSize(bitsFileSize) != B_OK) { 1224 // This call should work for BFile and BMallocIO objects, 1225 // but may not work for other BPositionIO based types 1226 ERROR("BMPTranslator::translate_from_bmpnpal_to_bits() - " 1227 "failed to SetSize()\n"); 1228 return B_ERROR; 1229 } 1230 if (msheader.height > 0) 1231 outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 1232 1233 // allocate row buffers 1234 uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes]; 1235 if (!bmpRowData) 1236 return B_NO_MEMORY; 1237 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 1238 if (!bitsRowData) { 1239 delete[] bmpRowData; 1240 return B_NO_MEMORY; 1241 } 1242 1243 // perform the actual translation 1244 if (bmpBytesPerPixel != 4) { 1245 // clean out buffer so that we don't have to write 1246 // alpha for each row 1247 memset(bitsRowData, 0xff, bitsRowBytes); 1248 } 1249 1250 status_t ret = B_OK; 1251 1252 uint32 rowCount = abs(msheader.height); 1253 for (uint32 y = 0; y < rowCount; y++) { 1254 ssize_t read = inSource->Read(bmpRowData, bmpRowBytes); 1255 if (read != bmpRowBytes) { 1256 // break on read error 1257 if (read >= 0) 1258 ret = B_ERROR; 1259 else 1260 ret = read; 1261 break; 1262 } 1263 1264 if (bmpBytesPerPixel == 4) { 1265 memcpy(bitsRowData, bmpRowData, bmpRowBytes); 1266 } else { 1267 uint8 *pBitsPixel = bitsRowData; 1268 uint8 *pBmpPixel = bmpRowData; 1269 for (int32 i = 0; i < msheader.width; i++) { 1270 pBitsPixel[0] = pBmpPixel[0]; 1271 pBitsPixel[1] = pBmpPixel[1]; 1272 pBitsPixel[2] = pBmpPixel[2]; 1273 pBitsPixel += 4; 1274 pBmpPixel += bmpBytesPerPixel; 1275 } 1276 } 1277 // write row and seek backward by two rows 1278 ssize_t written = outDestination->Write(bitsRowData, bitsRowBytes); 1279 if (msheader.height > 0) 1280 outDestination->Seek(bitsRowBytes * -2, SEEK_CUR); 1281 1282 if (written != bitsRowBytes) { 1283 // break on write error 1284 if (written >= 0) 1285 ret = B_ERROR; 1286 else 1287 ret = read; 1288 break; 1289 } 1290 } 1291 1292 delete[] bmpRowData; 1293 delete[] bitsRowData; 1294 1295 return ret; 1296 } 1297 1298 // --------------------------------------------------------------- 1299 // translate_from_bmppal_to_bits 1300 // 1301 // Translates an uncompressed, palette BMP from inSource to 1302 // the B_RGB32 bits format. 1303 // 1304 // Preconditions: 1305 // 1306 // Parameters: inSource, the BMP data to be translated 1307 // 1308 // outDestination, where the bits data will be written to 1309 // 1310 // msheader, header information about the BMP to be written 1311 // 1312 // palette, BMP palette for the BMP image 1313 // 1314 // frommsformat, true if BMP in inSource is in MS format, 1315 // false if it is in OS/2 format 1316 // 1317 // Postconditions: 1318 // 1319 // Returns: B_NO_MEMORY, if there is an error allocating memory 1320 // 1321 // B_OK, if all went well 1322 // --------------------------------------------------------------- 1323 status_t 1324 translate_from_bmppal_to_bits(BPositionIO *inSource, 1325 BPositionIO *outDestination, MSInfoHeader &msheader, 1326 const uint8 *palette, bool frommsformat) 1327 { 1328 uint16 pixelsPerByte = 8 / msheader.bitsperpixel; 1329 uint16 bitsPerPixel = msheader.bitsperpixel; 1330 uint8 palBytesPerPixel; 1331 if (frommsformat) 1332 palBytesPerPixel = 4; 1333 else 1334 palBytesPerPixel = 3; 1335 1336 uint8 mask = 1; 1337 mask = (mask << bitsPerPixel) - 1; 1338 1339 int32 bmpRowBytes = 1340 get_rowbytes(msheader.width, msheader.bitsperpixel); 1341 int32 bmppixrow = 0; 1342 1343 // Setup outDestination so that it can be written to 1344 // from the end of the file to the beginning instead of 1345 // the other way around 1346 int32 bitsRowBytes = msheader.width * 4; 1347 off_t bitsFileSize = (bitsRowBytes * abs(msheader.height)) + 1348 sizeof(TranslatorBitmap); 1349 if (outDestination->SetSize(bitsFileSize) != B_OK) 1350 // This call should work for BFile and BMallocIO objects, 1351 // but may not work for other BPositionIO based types 1352 return B_ERROR; 1353 if (msheader.height > 0) 1354 outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 1355 1356 // allocate row buffers 1357 uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes]; 1358 if (!bmpRowData) 1359 return B_NO_MEMORY; 1360 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 1361 if (!bitsRowData) { 1362 delete[] bmpRowData; 1363 return B_NO_MEMORY; 1364 } 1365 memset(bitsRowData, 0xff, bitsRowBytes); 1366 ssize_t rd = inSource->Read(bmpRowData, bmpRowBytes); 1367 while (rd == static_cast<ssize_t>(bmpRowBytes)) { 1368 for (int32 i = 0; i < msheader.width; i++) { 1369 uint8 indices = (bmpRowData + (i / pixelsPerByte))[0]; 1370 uint8 index; 1371 index = (indices >> 1372 (bitsPerPixel * ((pixelsPerByte - 1) - 1373 (i % pixelsPerByte)))) & mask; 1374 memcpy(bitsRowData + (i * 4), 1375 palette + (index * palBytesPerPixel), 3); 1376 } 1377 1378 outDestination->Write(bitsRowData, bitsRowBytes); 1379 bmppixrow++; 1380 // if I've read all of the pixel data, break 1381 // out of the loop so I don't try to read 1382 // non-pixel data 1383 if (bmppixrow == abs(msheader.height)) 1384 break; 1385 1386 if (msheader.height > 0) 1387 outDestination->Seek(bitsRowBytes * -2, SEEK_CUR); 1388 rd = inSource->Read(bmpRowData, bmpRowBytes); 1389 } 1390 1391 delete[] bmpRowData; 1392 delete[] bitsRowData; 1393 1394 return B_OK; 1395 } 1396 1397 1398 // --------------------------------------------------------------- 1399 // pixelcpy 1400 // 1401 // Copies count 32-bit pixels with a color value of pixel to dest. 1402 // 1403 // Preconditions: 1404 // 1405 // Parameters: dest, where the pixel data will be copied to 1406 // 1407 // pixel, the 32-bit color value to copy to dest 1408 // count times 1409 // 1410 // count, the number of times pixel is copied to 1411 // dest 1412 // 1413 // Postconditions: 1414 // 1415 // Returns: 1416 // --------------------------------------------------------------- 1417 void 1418 pixelcpy(uint8 *dest, uint32 pixel, uint32 count) 1419 { 1420 for (uint32 i = 0; i < count; i++) { 1421 memcpy(dest, &pixel, 3); 1422 dest += 4; 1423 } 1424 } 1425 1426 // --------------------------------------------------------------- 1427 // translate_from_bmppalr_to_bits 1428 // 1429 // Translates an RLE compressed, palette BMP from inSource to 1430 // the B_RGB32 bits format. Currently, this code is not as 1431 // memory effcient as it could be. It assumes that the BMP 1432 // from inSource is relatively small. 1433 // 1434 // Preconditions: 1435 // 1436 // Parameters: inSource, the BMP data to be translated 1437 // 1438 // outDestination, where the bits data will be written to 1439 // 1440 // datasize, number of bytes of data needed for the bits output 1441 // 1442 // msheader, header information about the BMP to be written 1443 // 1444 // palette, BMP palette for data in inSource 1445 // 1446 // Postconditions: 1447 // 1448 // Returns: B_ERROR, if there is an error allocating memory 1449 // 1450 // B_OK, if all went well 1451 // --------------------------------------------------------------- 1452 status_t 1453 translate_from_bmppalr_to_bits(BPositionIO *inSource, 1454 BPositionIO *outDestination, int32 datasize, MSInfoHeader &msheader, 1455 const uint8 *palette) 1456 { 1457 uint16 pixelsPerByte = 8 / msheader.bitsperpixel; 1458 uint16 bitsPerPixel = msheader.bitsperpixel; 1459 uint8 mask = (1 << bitsPerPixel) - 1; 1460 1461 uint8 count, indices, index; 1462 // Setup outDestination so that it can be written to 1463 // from the end of the file to the beginning instead of 1464 // the other way around 1465 int32 rowCount = abs(msheader.height); 1466 int32 bitsRowBytes = msheader.width * 4; 1467 off_t bitsFileSize = (bitsRowBytes * rowCount) + 1468 sizeof(TranslatorBitmap); 1469 if (outDestination->SetSize(bitsFileSize) != B_OK) 1470 // This call should work for BFile and BMallocIO objects, 1471 // but may not work for other BPositionIO based types 1472 return B_ERROR; 1473 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 1474 if (!bitsRowData) 1475 return B_NO_MEMORY; 1476 memset(bitsRowData, 0xff, bitsRowBytes); 1477 int32 bmppixcol = 0, bmppixrow = 0; 1478 uint32 defaultcolor = *(uint32*)palette; 1479 off_t rowOffset = msheader.height > 0 ? bitsRowBytes * -2 : 0; 1480 // set bits output to last row in the image 1481 if (msheader.height > 0) 1482 outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 1483 ssize_t rd = inSource->Read(&count, 1); 1484 while (rd > 0) { 1485 // repeated color 1486 if (count) { 1487 // abort if all of the pixels in the row 1488 // have already been drawn to 1489 if (bmppixcol == msheader.width) { 1490 rd = -1; 1491 break; 1492 } 1493 // if count is greater than the number of 1494 // pixels remaining in the current row, 1495 // only process the correct number of pixels 1496 // remaining in the row 1497 if (count + bmppixcol > msheader.width) 1498 count = msheader.width - bmppixcol; 1499 1500 rd = inSource->Read(&indices, 1); 1501 if (rd != 1) { 1502 rd = -1; 1503 break; 1504 } 1505 for (uint8 i = 0; i < count; i++) { 1506 index = (indices >> (bitsPerPixel * ((pixelsPerByte - 1) - 1507 (i % pixelsPerByte)))) & mask; 1508 memcpy(bitsRowData + (bmppixcol*4), palette + (index*4), 3); 1509 bmppixcol++; 1510 } 1511 // special code 1512 } else { 1513 uint8 code; 1514 rd = inSource->Read(&code, 1); 1515 if (rd != 1) { 1516 rd = -1; 1517 break; 1518 } 1519 switch (code) { 1520 // end of line 1521 case 0: 1522 // if there are columns remaing on this 1523 // line, set them to the color at index zero 1524 if (bmppixcol < msheader.width) 1525 pixelcpy(bitsRowData + (bmppixcol * 4), 1526 defaultcolor, msheader.width - bmppixcol); 1527 outDestination->Write(bitsRowData, bitsRowBytes); 1528 bmppixcol = 0; 1529 bmppixrow++; 1530 if (bmppixrow < rowCount) 1531 outDestination->Seek(rowOffset, SEEK_CUR); 1532 break; 1533 1534 // end of bitmap 1535 case 1: 1536 // if at the end of a row 1537 if (bmppixcol == msheader.width) { 1538 outDestination->Write(bitsRowData, bitsRowBytes); 1539 bmppixcol = 0; 1540 bmppixrow++; 1541 if (bmppixrow < rowCount) 1542 outDestination->Seek(rowOffset, SEEK_CUR); 1543 } 1544 1545 while (bmppixrow < rowCount) { 1546 pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor, 1547 msheader.width - bmppixcol); 1548 outDestination->Write(bitsRowData, bitsRowBytes); 1549 bmppixcol = 0; 1550 bmppixrow++; 1551 if (bmppixrow < rowCount) 1552 outDestination->Seek(rowOffset, SEEK_CUR); 1553 } 1554 rd = 0; 1555 // break out of while loop 1556 break; 1557 1558 // delta, skip several rows and/or columns and 1559 // fill the skipped pixels with the default color 1560 case 2: 1561 { 1562 uint8 da[2], lastcol, dx, dy; 1563 rd = inSource->Read(da, 2); 1564 if (rd != 2) { 1565 rd = -1; 1566 break; 1567 } 1568 dx = da[0]; 1569 dy = da[1]; 1570 1571 // abort if dx or dy is too large 1572 if ((dx + bmppixcol >= msheader.width) || 1573 (dy + bmppixrow >= rowCount)) { 1574 rd = -1; 1575 break; 1576 } 1577 1578 lastcol = bmppixcol; 1579 1580 // set all pixels to the first entry in 1581 // the palette, for the number of rows skipped 1582 while (dy > 0) { 1583 pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor, 1584 msheader.width - bmppixcol); 1585 outDestination->Write(bitsRowData, bitsRowBytes); 1586 bmppixcol = 0; 1587 bmppixrow++; 1588 dy--; 1589 outDestination->Seek(rowOffset, SEEK_CUR); 1590 } 1591 1592 if (bmppixcol < static_cast<int32>(lastcol + dx)) { 1593 pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor, 1594 dx + lastcol - bmppixcol); 1595 bmppixcol = dx + lastcol; 1596 } 1597 1598 break; 1599 } 1600 1601 // code >= 3 1602 // read code uncompressed indices 1603 default: 1604 // abort if all of the pixels in the row 1605 // have already been drawn to 1606 if (bmppixcol == msheader.width) { 1607 rd = -1; 1608 break; 1609 } 1610 // if code is greater than the number of 1611 // pixels remaining in the current row, 1612 // only process the correct number of pixels 1613 // remaining in the row 1614 if (code + bmppixcol > msheader.width) 1615 code = msheader.width - bmppixcol; 1616 1617 uint8 uncomp[256]; 1618 int32 padding; 1619 if (!(code % pixelsPerByte)) 1620 padding = (code / pixelsPerByte) % 2; 1621 else 1622 padding = ((code + pixelsPerByte - 1623 (code % pixelsPerByte)) / pixelsPerByte) % 2; 1624 int32 uncompBytes = (code / pixelsPerByte) + 1625 ((code % pixelsPerByte) ? 1 : 0) + padding; 1626 rd = inSource->Read(uncomp, uncompBytes); 1627 if (rd != uncompBytes) { 1628 rd = -1; 1629 break; 1630 } 1631 for (uint8 i = 0; i < code; i++) { 1632 indices = (uncomp + (i / pixelsPerByte))[0]; 1633 index = (indices >> 1634 (bitsPerPixel * ((pixelsPerByte - 1) - 1635 (i % pixelsPerByte)))) & mask; 1636 memcpy(bitsRowData + (bmppixcol * 4), 1637 palette + (index * 4), 3); 1638 bmppixcol++; 1639 } 1640 1641 break; 1642 } 1643 } 1644 if (rd > 0) 1645 rd = inSource->Read(&count, 1); 1646 } 1647 1648 delete[] bitsRowData; 1649 1650 if (!rd) 1651 return B_OK; 1652 else 1653 return B_NO_TRANSLATOR; 1654 } 1655 1656 // --------------------------------------------------------------- 1657 // translate_from_bmp 1658 // 1659 // Convert the data in inSource from the BMP format 1660 // to the format specified in outType (either bits or BMP). 1661 // 1662 // Preconditions: 1663 // 1664 // Parameters: inSource, the bits data to translate 1665 // 1666 // outType, the type of data to convert to 1667 // 1668 // outDestination, where the output is written to 1669 // 1670 // Postconditions: 1671 // 1672 // Returns: B_NO_TRANSLATOR, if the data is not in a supported 1673 // format 1674 // 1675 // B_ERROR, if there was an error allocating memory or some other 1676 // error 1677 // 1678 // B_OK, if successfully translated the data from the bits format 1679 // --------------------------------------------------------------- 1680 status_t 1681 BMPTranslator::translate_from_bmp(BPositionIO *inSource, uint32 outType, 1682 BPositionIO *outDestination) 1683 { 1684 bool bheaderonly, bdataonly; 1685 bheaderonly = bdataonly = false; 1686 1687 BMPFileHeader fileHeader; 1688 MSInfoHeader msheader; 1689 bool frommsformat; 1690 off_t os2skip = 0; 1691 1692 status_t result; 1693 result = identify_bmp_header(inSource, NULL, &fileHeader, &msheader, 1694 &frommsformat, &os2skip); 1695 if (result != B_OK) { 1696 INFO("BMPTranslator::translate_from_bmp() - identify_bmp_header failed\n"); 1697 return result; 1698 } 1699 1700 // if the user wants to translate a BMP to a BMP, easy enough :) 1701 if (outType == B_BMP_FORMAT) { 1702 // write out the BMP headers 1703 if (bheaderonly || (!bheaderonly && !bdataonly)) { 1704 result = write_bmp_headers(outDestination, fileHeader, msheader); 1705 if (result != B_OK) 1706 return result; 1707 } 1708 if (bheaderonly) 1709 // if the user only wants the header, 1710 // bail before it is written 1711 return result; 1712 1713 uint8 buf[1024]; 1714 ssize_t rd; 1715 uint32 rdtotal = 54; 1716 if (!frommsformat && (msheader.bitsperpixel == 1 || 1717 msheader.bitsperpixel == 4 || msheader.bitsperpixel == 8)) { 1718 // if OS/2 paletted format, convert palette to MS format 1719 uint16 ncolors = 1 << msheader.bitsperpixel; 1720 rd = inSource->Read(buf, ncolors * 3); 1721 if (rd != ncolors * 3) 1722 return B_NO_TRANSLATOR; 1723 uint8 mspalent[4] = {0, 0, 0, 0}; 1724 for (uint16 i = 0; i < ncolors; i++) { 1725 memcpy(mspalent, buf + (i * 3), 3); 1726 outDestination->Write(mspalent, 4); 1727 } 1728 rdtotal = fileHeader.dataOffset; 1729 } 1730 // if there is junk between the OS/2 headers and 1731 // the actual data, skip it 1732 if (!frommsformat && os2skip) 1733 inSource->Seek(os2skip, SEEK_CUR); 1734 1735 rd = min((uint32)1024, fileHeader.fileSize - rdtotal); 1736 rd = inSource->Read(buf, rd); 1737 while (rd > 0) { 1738 outDestination->Write(buf, rd); 1739 rdtotal += rd; 1740 rd = min((uint32)1024, fileHeader.fileSize - rdtotal); 1741 rd = inSource->Read(buf, rd); 1742 } 1743 if (rd == 0) 1744 return B_OK; 1745 else 1746 return B_ERROR; 1747 1748 // if translating a BMP to a Be Bitmap 1749 } else if (outType == B_TRANSLATOR_BITMAP) { 1750 TranslatorBitmap bitsHeader; 1751 bitsHeader.magic = B_TRANSLATOR_BITMAP; 1752 bitsHeader.bounds.left = 0; 1753 bitsHeader.bounds.top = 0; 1754 bitsHeader.bounds.right = msheader.width - 1; 1755 bitsHeader.bounds.bottom = abs(msheader.height) - 1; 1756 1757 // read in palette and/or skip non-BMP data 1758 uint8 bmppalette[1024]; 1759 off_t nskip = 0; 1760 if (msheader.bitsperpixel == 1 || 1761 msheader.bitsperpixel == 4 || 1762 msheader.bitsperpixel == 8) { 1763 1764 uint8 palBytesPerPixel; 1765 if (frommsformat) 1766 palBytesPerPixel = 4; 1767 else 1768 palBytesPerPixel = 3; 1769 1770 if (!msheader.colorsused) 1771 msheader.colorsused = 1 << msheader.bitsperpixel; 1772 1773 if (inSource->Read(bmppalette, msheader.colorsused * 1774 palBytesPerPixel) != 1775 (off_t) msheader.colorsused * palBytesPerPixel) 1776 return B_NO_TRANSLATOR; 1777 1778 // skip over non-BMP data 1779 if (frommsformat) { 1780 if (fileHeader.dataOffset > (msheader.colorsused * 1781 palBytesPerPixel) + 54) 1782 nskip = fileHeader.dataOffset - 1783 ((msheader.colorsused * palBytesPerPixel) + 54); 1784 } else 1785 nskip = os2skip; 1786 } else if (fileHeader.dataOffset > 54) 1787 // skip over non-BMP data 1788 nskip = fileHeader.dataOffset - 54; 1789 1790 if (nskip > 0 && inSource->Seek(nskip, SEEK_CUR) < 0) 1791 return B_NO_TRANSLATOR; 1792 1793 bitsHeader.rowBytes = msheader.width * 4; 1794 bitsHeader.colors = B_RGB32; 1795 int32 datasize = bitsHeader.rowBytes * abs(msheader.height); 1796 bitsHeader.dataSize = datasize; 1797 1798 // write out Be's Bitmap header 1799 if (bheaderonly || (!bheaderonly && !bdataonly)) { 1800 if (swap_data(B_UINT32_TYPE, &bitsHeader, 1801 sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) 1802 return B_ERROR; 1803 outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap)); 1804 } 1805 if (bheaderonly) 1806 // if the user only wants the header, 1807 // bail before the data is written 1808 return B_OK; 1809 1810 // write out the actual image data 1811 switch (msheader.bitsperpixel) { 1812 case 32: 1813 case 24: 1814 return translate_from_bmpnpal_to_bits(inSource, 1815 outDestination, msheader); 1816 1817 case 8: 1818 // 8 bit BMP with NO compression 1819 if (msheader.compression == BMP_NO_COMPRESS) 1820 return translate_from_bmppal_to_bits(inSource, 1821 outDestination, msheader, bmppalette, frommsformat); 1822 1823 // 8 bit RLE compressed BMP 1824 else if (msheader.compression == BMP_RLE8_COMPRESS) 1825 return translate_from_bmppalr_to_bits(inSource, 1826 outDestination, datasize, msheader, bmppalette); 1827 else 1828 return B_NO_TRANSLATOR; 1829 1830 case 4: 1831 // 4 bit BMP with NO compression 1832 if (!msheader.compression) 1833 return translate_from_bmppal_to_bits(inSource, 1834 outDestination, msheader, bmppalette, frommsformat); 1835 1836 // 4 bit RLE compressed BMP 1837 else if (msheader.compression == BMP_RLE4_COMPRESS) 1838 return translate_from_bmppalr_to_bits(inSource, 1839 outDestination, datasize, msheader, bmppalette); 1840 else 1841 return B_NO_TRANSLATOR; 1842 1843 case 1: 1844 return translate_from_bmppal_to_bits(inSource, 1845 outDestination, msheader, bmppalette, frommsformat); 1846 1847 default: 1848 return B_NO_TRANSLATOR; 1849 } 1850 1851 } else 1852 return B_NO_TRANSLATOR; 1853 } 1854 1855 // --------------------------------------------------------------- 1856 // DerivedTranslate 1857 // 1858 // Translates the data in inSource to the type outType and stores 1859 // the translated data in outDestination. 1860 // 1861 // Preconditions: 1862 // 1863 // Parameters: inSource, the data to be translated 1864 // 1865 // inInfo, hint about the data in inSource (not used) 1866 // 1867 // ioExtension, configuration options for the 1868 // translator 1869 // 1870 // outType, the type to convert inSource to 1871 // 1872 // outDestination, where the translated data is 1873 // put 1874 // 1875 // baseType, indicates whether inSource is in the 1876 // bits format, not in the bits format or 1877 // is unknown 1878 // 1879 // Postconditions: 1880 // 1881 // Returns: B_BAD_VALUE, if the options in ioExtension are bad 1882 // 1883 // B_NO_TRANSLATOR, if this translator doesn't understand the data 1884 // 1885 // B_ERROR, if there was an error allocating memory or converting 1886 // data 1887 // 1888 // B_OK, if all went well 1889 // --------------------------------------------------------------- 1890 status_t 1891 BMPTranslator::DerivedTranslate(BPositionIO *inSource, 1892 const translator_info *inInfo, BMessage *ioExtension, 1893 uint32 outType, BPositionIO *outDestination, int32 baseType) 1894 { 1895 if (baseType == 1) 1896 // if inSource is in bits format 1897 return translate_from_bits(inSource, outType, outDestination); 1898 else if (baseType == 0) 1899 // if inSource is NOT in bits format 1900 return translate_from_bmp(inSource, outType, outDestination); 1901 else 1902 return B_NO_TRANSLATOR; 1903 } 1904 1905 BView * 1906 BMPTranslator::NewConfigView(TranslatorSettings *settings) 1907 { 1908 return new BMPView(BRect(0, 0, 225, 175), 1909 B_TRANSLATE("BMPTranslator Settings"), B_FOLLOW_ALL, B_WILL_DRAW, 1910 settings); 1911 } 1912