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 printf("translate_from_bits_to_bmp24() bmppixrow %" B_PRId32 "\n", 652 bmppixrow); 653 654 for (int32 i = 0; i < msheader.width; i++) { 655 uint8 *bitspixel, *bmppixel; 656 uint16 val; 657 switch (fromspace) { 658 case B_RGB32: 659 case B_RGBA32: 660 case B_RGB24: 661 memcpy(bmpRowData + (i * 3), 662 bitsRowData + (i * bitsBytesPerPixel), 3); 663 break; 664 665 case B_RGB16: 666 case B_RGB16_BIG: 667 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 668 bmppixel = bmpRowData + (i * 3); 669 if (fromspace == B_RGB16) 670 val = bitspixel[0] + (bitspixel[1] << 8); 671 else 672 val = bitspixel[1] + (bitspixel[0] << 8); 673 bmppixel[0] = 674 ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); 675 bmppixel[1] = 676 ((val & 0x7e0) >> 3) | ((val & 0x7e0) >> 9); 677 bmppixel[2] = 678 ((val & 0xf800) >> 8) | ((val & 0xf800) >> 13); 679 break; 680 681 case B_RGB15: 682 case B_RGB15_BIG: 683 case B_RGBA15: 684 case B_RGBA15_BIG: 685 // NOTE: the alpha data for B_RGBA15* is not used 686 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 687 bmppixel = bmpRowData + (i * 3); 688 if (fromspace == B_RGB15 || fromspace == B_RGBA15) 689 val = bitspixel[0] + (bitspixel[1] << 8); 690 else 691 val = bitspixel[1] + (bitspixel[0] << 8); 692 bmppixel[0] = 693 ((val & 0x1f) << 3) | ((val & 0x1f) >> 2); 694 bmppixel[1] = 695 ((val & 0x3e0) >> 2) | ((val & 0x3e0) >> 7); 696 bmppixel[2] = 697 ((val & 0x7c00) >> 7) | ((val & 0x7c00) >> 12); 698 break; 699 700 case B_RGB32_BIG: 701 case B_RGBA32_BIG: 702 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 703 bmppixel = bmpRowData + (i * 3); 704 bmppixel[0] = bitspixel[3]; 705 bmppixel[1] = bitspixel[2]; 706 bmppixel[2] = bitspixel[1]; 707 break; 708 709 case B_RGB24_BIG: 710 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 711 bmppixel = bmpRowData + (i * 3); 712 bmppixel[0] = bitspixel[2]; 713 bmppixel[1] = bitspixel[1]; 714 bmppixel[2] = bitspixel[0]; 715 break; 716 717 case B_CMAP8: 718 { 719 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 720 bmppixel = bmpRowData + (i * 3); 721 rgb_color c = pmap->color_list[bitspixel[0]]; 722 bmppixel[0] = c.blue; 723 bmppixel[1] = c.green; 724 bmppixel[2] = c.red; 725 break; 726 } 727 728 case B_GRAY8: 729 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 730 bmppixel = bmpRowData + (i * 3); 731 bmppixel[0] = bitspixel[0]; 732 bmppixel[1] = bitspixel[0]; 733 bmppixel[2] = bitspixel[0]; 734 break; 735 736 case B_CMYK32: 737 { 738 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 739 bmppixel = bmpRowData + (i * 3); 740 741 int32 comp = 255 - bitspixel[2] - bitspixel[3]; 742 bmppixel[0] = (comp < 0) ? 0 : comp; 743 744 comp = 255 - bitspixel[1] - bitspixel[3]; 745 bmppixel[1] = (comp < 0) ? 0 : comp; 746 747 comp = 255 - bitspixel[0] - bitspixel[3]; 748 bmppixel[2] = (comp < 0) ? 0 : comp; 749 break; 750 } 751 752 case B_CMY32: 753 case B_CMYA32: 754 case B_CMY24: 755 bitspixel = bitsRowData + (i * bitsBytesPerPixel); 756 bmppixel = bmpRowData + (i * 3); 757 bmppixel[0] = 255 - bitspixel[2]; 758 bmppixel[1] = 255 - bitspixel[1]; 759 bmppixel[2] = 255 - bitspixel[0]; 760 break; 761 762 default: 763 break; 764 } // switch (fromspace) 765 } // for for (uint32 i = 0; i < msheader.width; i++) 766 767 outDestination->Write(bmpRowData, bmpRowBytes); 768 bmppixrow++; 769 // if I've read all of the pixel data, break 770 // out of the loop so I don't try to read 771 // non-pixel data 772 if (bmppixrow == abs(msheader.height)) 773 break; 774 775 if (msheader.height > 0) 776 inSource->Seek(bitsRowBytes * -2, SEEK_CUR); 777 rd = inSource->Read(bitsRowData, bitsRowBytes); 778 } // while (rd == bitsRowBytes) 779 780 delete[] bmpRowData; 781 delete[] bitsRowData; 782 783 return B_OK; 784 } 785 786 // --------------------------------------------------------------- 787 // translate_from_bits8_to_bmp8 788 // 789 // Converts 8-bit Be Bitmaps ('bits') to the MS 8-bit BMP format 790 // 791 // Preconditions: 792 // 793 // Parameters: inSource, contains the bits data to convert 794 // 795 // outDestination, where the BMP data will be written 796 // 797 // bitsRowBytes, number of bytes in one row of 798 // bits data 799 // 800 // msheader, contains information about the BMP 801 // dimensions and filesize 802 // 803 // Postconditions: 804 // 805 // Returns: B_ERROR, if memory couldn't be allocated or another 806 // error occured 807 // 808 // B_OK, if no errors occurred 809 // --------------------------------------------------------------- 810 status_t 811 translate_from_bits8_to_bmp8(BPositionIO *inSource, 812 BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader) 813 { 814 int32 padding = get_padding(msheader.width, msheader.bitsperpixel); 815 int32 bmpRowBytes = 816 get_rowbytes(msheader.width, msheader.bitsperpixel); 817 int32 bmppixrow = 0; 818 if (msheader.height > 0) 819 inSource->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 820 uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes]; 821 if (!bmpRowData) 822 return B_NO_MEMORY; 823 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 824 if (!bitsRowData) { 825 delete[] bmpRowData; 826 return B_NO_MEMORY; 827 } 828 memset(bmpRowData + (bmpRowBytes - padding), 0, padding); 829 ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes); 830 while (rd == bitsRowBytes) { 831 memcpy(bmpRowData, bitsRowData, msheader.width); 832 outDestination->Write(bmpRowData, bmpRowBytes); 833 bmppixrow++; 834 // if I've read all of the pixel data, break 835 // out of the loop so I don't try to read 836 // non-pixel data 837 if (bmppixrow == abs(msheader.height)) 838 break; 839 840 if (msheader.height > 0) 841 inSource->Seek(bitsRowBytes * -2, SEEK_CUR); 842 rd = inSource->Read(bitsRowData, bitsRowBytes); 843 } // while (rd == bitsRowBytes) 844 845 delete[] bmpRowData; 846 delete[] bitsRowData; 847 848 return B_OK; 849 } 850 851 // --------------------------------------------------------------- 852 // translate_from_bits1_to_bmp1 853 // 854 // Converts 1-bit Be Bitmaps ('bits') to the MS 1-bit BMP format 855 // 856 // Preconditions: 857 // 858 // Parameters: inSource, contains the bits data to convert 859 // 860 // outDestination, where the BMP data will be written 861 // 862 // bitsRowBytes, number of bytes in one row of 863 // bits data 864 // 865 // msheader, contains information about the BMP 866 // dimensions and filesize 867 // 868 // Postconditions: 869 // 870 // Returns: B_ERROR, if memory couldn't be allocated or another 871 // error occured 872 // 873 // B_OK, if no errors occurred 874 // --------------------------------------------------------------- 875 status_t 876 translate_from_bits1_to_bmp1(BPositionIO *inSource, 877 BPositionIO *outDestination, int32 bitsRowBytes, MSInfoHeader &msheader) 878 { 879 uint8 pixelsPerByte = 8 / msheader.bitsperpixel; 880 int32 bmpRowBytes = 881 get_rowbytes(msheader.width, msheader.bitsperpixel); 882 int32 bmppixrow = 0; 883 if (msheader.height > 0) 884 inSource->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 885 uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes]; 886 if (!bmpRowData) 887 return B_NO_MEMORY; 888 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 889 if (!bitsRowData) { 890 delete[] bmpRowData; 891 return B_NO_MEMORY; 892 } 893 ssize_t rd = inSource->Read(bitsRowData, bitsRowBytes); 894 while (rd == bitsRowBytes) { 895 int32 bmppixcol = 0; 896 memset(bmpRowData, 0, bmpRowBytes); 897 for (int32 i = 0; (bmppixcol < msheader.width) && 898 (i < bitsRowBytes); i++) { 899 // process each byte in the row 900 uint8 pixels = bitsRowData[i]; 901 for (uint8 compbit = 128; (bmppixcol < msheader.width) && 902 compbit; compbit >>= 1) { 903 // for each bit in the current byte, convert to a BMP palette 904 // index and store that in the bmpRowData 905 uint8 index; 906 if (pixels & compbit) 907 // 1 == black 908 index = 1; 909 else 910 // 0 == white 911 index = 0; 912 bmpRowData[bmppixcol / pixelsPerByte] |= 913 index << (7 - (bmppixcol % pixelsPerByte)); 914 bmppixcol++; 915 } 916 } 917 918 outDestination->Write(bmpRowData, bmpRowBytes); 919 bmppixrow++; 920 // if I've read all of the pixel data, break 921 // out of the loop so I don't try to read 922 // non-pixel data 923 if (bmppixrow == abs(msheader.height)) 924 break; 925 926 if (msheader.height > 0) 927 inSource->Seek(bitsRowBytes * -2, SEEK_CUR); 928 rd = inSource->Read(bitsRowData, bitsRowBytes); 929 } // while (rd == bitsRowBytes) 930 931 delete[] bmpRowData; 932 delete[] bitsRowData; 933 934 return B_OK; 935 } 936 937 // --------------------------------------------------------------- 938 // write_bmp_headers 939 // 940 // Writes the MS BMP headers (fileHeader and msheader) 941 // to outDestination. 942 // 943 // Preconditions: 944 // 945 // Parameters: outDestination, where the headers are written to 946 // 947 // fileHeader, BMP file header data 948 // 949 // msheader, BMP info header data 950 // 951 // Postconditions: 952 // 953 // Returns: B_ERROR, if something went wrong 954 // 955 // B_OK, if there were no problems writing out the headers 956 // --------------------------------------------------------------- 957 status_t 958 write_bmp_headers(BPositionIO *outDestination, BMPFileHeader &fileHeader, 959 MSInfoHeader &msheader) 960 { 961 uint8 bmpheaders[54]; 962 memcpy(bmpheaders, &fileHeader.magic, sizeof(uint16)); 963 memcpy(bmpheaders + 2, &fileHeader.fileSize, sizeof(uint32)); 964 memcpy(bmpheaders + 6, &fileHeader.reserved, sizeof(uint32)); 965 memcpy(bmpheaders + 10, &fileHeader.dataOffset, sizeof(uint32)); 966 memcpy(bmpheaders + 14, &msheader, sizeof(msheader)); 967 if (swap_data(B_UINT16_TYPE, bmpheaders, 2, 968 B_SWAP_HOST_TO_LENDIAN) != B_OK) 969 return B_ERROR; 970 if (swap_data(B_UINT32_TYPE, bmpheaders + 2, 12, 971 B_SWAP_HOST_TO_LENDIAN) != B_OK) 972 return B_ERROR; 973 if (swap_data(B_UINT32_TYPE, bmpheaders + 14, 974 sizeof(MSInfoHeader), B_SWAP_HOST_TO_LENDIAN) != B_OK) 975 return B_ERROR; 976 if (outDestination->Write(bmpheaders, 54) != 54) 977 return B_ERROR; 978 979 return B_OK; 980 } 981 982 // --------------------------------------------------------------- 983 // translate_from_bits 984 // 985 // Convert the data in inSource from the Be Bitmap format ('bits') 986 // to the format specified in outType (either bits or BMP). 987 // 988 // Preconditions: 989 // 990 // Parameters: inSource, the bits data to translate 991 // 992 // outType, the type of data to convert to 993 // 994 // outDestination, where the output is written to 995 // 996 // Postconditions: 997 // 998 // Returns: B_NO_TRANSLATOR, if the data is not in a supported 999 // format 1000 // 1001 // B_ERROR, if there was an error allocating memory or some other 1002 // error 1003 // 1004 // B_OK, if successfully translated the data from the bits format 1005 // --------------------------------------------------------------- 1006 status_t 1007 BMPTranslator::translate_from_bits(BPositionIO *inSource, uint32 outType, 1008 BPositionIO *outDestination) 1009 { 1010 bool bheaderonly, bdataonly; 1011 bheaderonly = bdataonly = false; 1012 1013 TranslatorBitmap bitsHeader; 1014 status_t result; 1015 result = identify_bits_header(inSource, NULL, &bitsHeader); 1016 if (result != B_OK) 1017 return result; 1018 1019 // Translate B_TRANSLATOR_BITMAP to B_BMP_FORMAT 1020 if (outType == B_BMP_FORMAT) { 1021 // Set up BMP header 1022 BMPFileHeader fileHeader; 1023 fileHeader.magic = 'MB'; 1024 fileHeader.reserved = 0; 1025 1026 MSInfoHeader msheader; 1027 msheader.size = 40; 1028 msheader.width = 1029 static_cast<uint32> (bitsHeader.bounds.Width() + 1); 1030 msheader.height = 1031 static_cast<int32> (bitsHeader.bounds.Height() + 1); 1032 msheader.planes = 1; 1033 msheader.xpixperm = 2835; // 72 dpi horizontal 1034 msheader.ypixperm = 2835; // 72 dpi vertical 1035 msheader.colorsused = 0; 1036 msheader.colorsimportant = 0; 1037 1038 // determine fileSize / imagesize 1039 switch (bitsHeader.colors) { 1040 case B_RGB32: 1041 case B_RGB32_BIG: 1042 case B_RGBA32: 1043 case B_RGBA32_BIG: 1044 case B_RGB24: 1045 case B_RGB24_BIG: 1046 case B_RGB16: 1047 case B_RGB16_BIG: 1048 case B_RGB15: 1049 case B_RGB15_BIG: 1050 case B_RGBA15: 1051 case B_RGBA15_BIG: 1052 case B_CMYK32: 1053 case B_CMY32: 1054 case B_CMYA32: 1055 case B_CMY24: 1056 1057 fileHeader.dataOffset = 54; 1058 msheader.bitsperpixel = 24; 1059 msheader.compression = BMP_NO_COMPRESS; 1060 msheader.imagesize = get_rowbytes(msheader.width, 24) * 1061 msheader.height; 1062 fileHeader.fileSize = fileHeader.dataOffset + 1063 msheader.imagesize; 1064 1065 break; 1066 1067 case B_CMAP8: 1068 case B_GRAY8: 1069 1070 msheader.colorsused = 256; 1071 msheader.colorsimportant = 256; 1072 fileHeader.dataOffset = 54 + (4 * 256); 1073 msheader.bitsperpixel = 8; 1074 msheader.compression = BMP_NO_COMPRESS; 1075 msheader.imagesize = get_rowbytes(msheader.width, 1076 msheader.bitsperpixel) * msheader.height; 1077 fileHeader.fileSize = fileHeader.dataOffset + 1078 msheader.imagesize; 1079 1080 break; 1081 1082 case B_GRAY1: 1083 1084 msheader.colorsused = 2; 1085 msheader.colorsimportant = 2; 1086 fileHeader.dataOffset = 62; 1087 msheader.bitsperpixel = 1; 1088 msheader.compression = BMP_NO_COMPRESS; 1089 msheader.imagesize = get_rowbytes(msheader.width, 1090 msheader.bitsperpixel) * msheader.height; 1091 fileHeader.fileSize = fileHeader.dataOffset + 1092 msheader.imagesize; 1093 1094 break; 1095 1096 default: 1097 return B_NO_TRANSLATOR; 1098 } 1099 1100 // write out the BMP headers 1101 if (bheaderonly || (!bheaderonly && !bdataonly)) { 1102 result = write_bmp_headers(outDestination, fileHeader, msheader); 1103 if (result != B_OK) 1104 return result; 1105 } 1106 if (bheaderonly) 1107 // if user only wants the header, bail out 1108 // before the data is written 1109 return result; 1110 1111 // write out the BMP pixel data 1112 switch (bitsHeader.colors) { 1113 case B_RGB32: 1114 case B_RGB32_BIG: 1115 case B_RGBA32: 1116 case B_RGBA32_BIG: 1117 case B_RGB24: 1118 case B_RGB24_BIG: 1119 case B_RGB16: 1120 case B_RGB16_BIG: 1121 case B_RGB15: 1122 case B_RGB15_BIG: 1123 case B_RGBA15: 1124 case B_RGBA15_BIG: 1125 case B_CMYK32: 1126 case B_CMY32: 1127 case B_CMYA32: 1128 case B_CMY24: 1129 return translate_from_bits_to_bmp24(inSource, outDestination, 1130 bitsHeader.colors, msheader); 1131 1132 case B_CMAP8: 1133 case B_GRAY8: 1134 { 1135 // write palette to BMP file 1136 uint8 pal[1024]; 1137 uint8* palHandle = pal; 1138 if (bitsHeader.colors == B_CMAP8) { 1139 // write system palette 1140 const color_map *pmap = system_colors(); 1141 if (!pmap) 1142 return B_ERROR; 1143 for (int32 i = 0; i < 256; i++) { 1144 rgb_color c = pmap->color_list[i]; 1145 palHandle[0] = c.blue; 1146 palHandle[1] = c.green; 1147 palHandle[2] = c.red; 1148 palHandle[3] = c.alpha; 1149 palHandle += 4; 1150 } 1151 } else { 1152 // write gray palette 1153 for (int32 i = 0; i < 256; i++) { 1154 palHandle[0] = i; 1155 palHandle[1] = i; 1156 palHandle[2] = i; 1157 palHandle[3] = 255; 1158 palHandle += 4; 1159 } 1160 } 1161 ssize_t written = outDestination->Write(pal, 1024); 1162 if (written < 0) 1163 return written; 1164 if (written != 1024) 1165 return B_ERROR; 1166 1167 return translate_from_bits8_to_bmp8(inSource, outDestination, 1168 bitsHeader.rowBytes, msheader); 1169 } 1170 1171 case B_GRAY1: 1172 { 1173 // write monochrome palette to the BMP file 1174 const uint32 monopal[] = { 0x00ffffff, 0x00000000 }; 1175 ssize_t written = outDestination->Write(monopal, 8); 1176 if (written < 0) 1177 return written; 1178 if (written != 8) 1179 return B_ERROR; 1180 1181 return translate_from_bits1_to_bmp1(inSource, outDestination, 1182 bitsHeader.rowBytes, msheader); 1183 } 1184 1185 default: 1186 return B_NO_TRANSLATOR; 1187 } 1188 } else 1189 return B_NO_TRANSLATOR; 1190 } 1191 1192 // --------------------------------------------------------------- 1193 // translate_from_bmpnpal_to_bits 1194 // 1195 // Translates a non-palette BMP from inSource to the B_RGB32 1196 // bits format. 1197 // 1198 // Preconditions: 1199 // 1200 // Parameters: inSource, the BMP data to be translated 1201 // 1202 // outDestination, where the bits data will be written to 1203 // 1204 // msheader, header information about the BMP to be written 1205 // 1206 // Postconditions: 1207 // 1208 // Returns: B_ERROR, if there is an error allocating memory 1209 // 1210 // B_OK, if all went well 1211 // --------------------------------------------------------------- 1212 status_t 1213 translate_from_bmpnpal_to_bits(BPositionIO *inSource, 1214 BPositionIO *outDestination, MSInfoHeader &msheader) 1215 { 1216 int32 bitsRowBytes = msheader.width * 4; 1217 int32 bmpBytesPerPixel = msheader.bitsperpixel / 8; 1218 int32 bmpRowBytes = 1219 get_rowbytes(msheader.width, msheader.bitsperpixel); 1220 1221 // Setup outDestination so that it can be written to 1222 // from the end of the file to the beginning instead of 1223 // the other way around 1224 off_t bitsFileSize = (bitsRowBytes * abs(msheader.height)) + 1225 sizeof(TranslatorBitmap); 1226 if (outDestination->SetSize(bitsFileSize) != B_OK) { 1227 // This call should work for BFile and BMallocIO objects, 1228 // but may not work for other BPositionIO based types 1229 ERROR("BMPTranslator::translate_from_bmpnpal_to_bits() - " 1230 "failed to SetSize()\n"); 1231 return B_ERROR; 1232 } 1233 if (msheader.height > 0) 1234 outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 1235 1236 // allocate row buffers 1237 uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes]; 1238 if (!bmpRowData) 1239 return B_NO_MEMORY; 1240 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 1241 if (!bitsRowData) { 1242 delete[] bmpRowData; 1243 return B_NO_MEMORY; 1244 } 1245 1246 // perform the actual translation 1247 if (bmpBytesPerPixel != 4) { 1248 // clean out buffer so that we don't have to write 1249 // alpha for each row 1250 memset(bitsRowData, 0xff, bitsRowBytes); 1251 } 1252 1253 status_t ret = B_OK; 1254 1255 uint32 rowCount = abs(msheader.height); 1256 for (uint32 y = 0; y < rowCount; y++) { 1257 ssize_t read = inSource->Read(bmpRowData, bmpRowBytes); 1258 if (read != bmpRowBytes) { 1259 // break on read error 1260 if (read >= 0) 1261 ret = B_ERROR; 1262 else 1263 ret = read; 1264 break; 1265 } 1266 1267 if (bmpBytesPerPixel == 4) { 1268 memcpy(bitsRowData, bmpRowData, bmpRowBytes); 1269 } else { 1270 uint8 *pBitsPixel = bitsRowData; 1271 uint8 *pBmpPixel = bmpRowData; 1272 for (int32 i = 0; i < msheader.width; i++) { 1273 pBitsPixel[0] = pBmpPixel[0]; 1274 pBitsPixel[1] = pBmpPixel[1]; 1275 pBitsPixel[2] = pBmpPixel[2]; 1276 pBitsPixel += 4; 1277 pBmpPixel += bmpBytesPerPixel; 1278 } 1279 } 1280 // write row and seek backward by two rows 1281 ssize_t written = outDestination->Write(bitsRowData, bitsRowBytes); 1282 if (msheader.height > 0) 1283 outDestination->Seek(bitsRowBytes * -2, SEEK_CUR); 1284 1285 if (written != bitsRowBytes) { 1286 // break on write error 1287 if (written >= 0) 1288 ret = B_ERROR; 1289 else 1290 ret = read; 1291 break; 1292 } 1293 } 1294 1295 delete[] bmpRowData; 1296 delete[] bitsRowData; 1297 1298 return ret; 1299 } 1300 1301 // --------------------------------------------------------------- 1302 // translate_from_bmppal_to_bits 1303 // 1304 // Translates an uncompressed, palette BMP from inSource to 1305 // the B_RGB32 bits format. 1306 // 1307 // Preconditions: 1308 // 1309 // Parameters: inSource, the BMP data to be translated 1310 // 1311 // outDestination, where the bits data will be written to 1312 // 1313 // msheader, header information about the BMP to be written 1314 // 1315 // palette, BMP palette for the BMP image 1316 // 1317 // frommsformat, true if BMP in inSource is in MS format, 1318 // false if it is in OS/2 format 1319 // 1320 // Postconditions: 1321 // 1322 // Returns: B_NO_MEMORY, if there is an error allocating memory 1323 // 1324 // B_OK, if all went well 1325 // --------------------------------------------------------------- 1326 status_t 1327 translate_from_bmppal_to_bits(BPositionIO *inSource, 1328 BPositionIO *outDestination, MSInfoHeader &msheader, 1329 const uint8 *palette, bool frommsformat) 1330 { 1331 uint16 pixelsPerByte = 8 / msheader.bitsperpixel; 1332 uint16 bitsPerPixel = msheader.bitsperpixel; 1333 uint8 palBytesPerPixel; 1334 if (frommsformat) 1335 palBytesPerPixel = 4; 1336 else 1337 palBytesPerPixel = 3; 1338 1339 uint8 mask = 1; 1340 mask = (mask << bitsPerPixel) - 1; 1341 1342 int32 bmpRowBytes = 1343 get_rowbytes(msheader.width, msheader.bitsperpixel); 1344 int32 bmppixrow = 0; 1345 1346 // Setup outDestination so that it can be written to 1347 // from the end of the file to the beginning instead of 1348 // the other way around 1349 int32 bitsRowBytes = msheader.width * 4; 1350 off_t bitsFileSize = (bitsRowBytes * abs(msheader.height)) + 1351 sizeof(TranslatorBitmap); 1352 if (outDestination->SetSize(bitsFileSize) != B_OK) 1353 // This call should work for BFile and BMallocIO objects, 1354 // but may not work for other BPositionIO based types 1355 return B_ERROR; 1356 if (msheader.height > 0) 1357 outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 1358 1359 // allocate row buffers 1360 uint8 *bmpRowData = new (nothrow) uint8[bmpRowBytes]; 1361 if (!bmpRowData) 1362 return B_NO_MEMORY; 1363 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 1364 if (!bitsRowData) { 1365 delete[] bmpRowData; 1366 return B_NO_MEMORY; 1367 } 1368 memset(bitsRowData, 0xff, bitsRowBytes); 1369 ssize_t rd = inSource->Read(bmpRowData, bmpRowBytes); 1370 while (rd == static_cast<ssize_t>(bmpRowBytes)) { 1371 for (int32 i = 0; i < msheader.width; i++) { 1372 uint8 indices = (bmpRowData + (i / pixelsPerByte))[0]; 1373 uint8 index; 1374 index = (indices >> 1375 (bitsPerPixel * ((pixelsPerByte - 1) - 1376 (i % pixelsPerByte)))) & mask; 1377 memcpy(bitsRowData + (i * 4), 1378 palette + (index * palBytesPerPixel), 3); 1379 } 1380 1381 outDestination->Write(bitsRowData, bitsRowBytes); 1382 bmppixrow++; 1383 // if I've read all of the pixel data, break 1384 // out of the loop so I don't try to read 1385 // non-pixel data 1386 if (bmppixrow == abs(msheader.height)) 1387 break; 1388 1389 if (msheader.height > 0) 1390 outDestination->Seek(bitsRowBytes * -2, SEEK_CUR); 1391 rd = inSource->Read(bmpRowData, bmpRowBytes); 1392 } 1393 1394 delete[] bmpRowData; 1395 delete[] bitsRowData; 1396 1397 return B_OK; 1398 } 1399 1400 1401 // --------------------------------------------------------------- 1402 // pixelcpy 1403 // 1404 // Copies count 32-bit pixels with a color value of pixel to dest. 1405 // 1406 // Preconditions: 1407 // 1408 // Parameters: dest, where the pixel data will be copied to 1409 // 1410 // pixel, the 32-bit color value to copy to dest 1411 // count times 1412 // 1413 // count, the number of times pixel is copied to 1414 // dest 1415 // 1416 // Postconditions: 1417 // 1418 // Returns: 1419 // --------------------------------------------------------------- 1420 void 1421 pixelcpy(uint8 *dest, uint32 pixel, uint32 count) 1422 { 1423 for (uint32 i = 0; i < count; i++) { 1424 memcpy(dest, &pixel, 3); 1425 dest += 4; 1426 } 1427 } 1428 1429 // --------------------------------------------------------------- 1430 // translate_from_bmppalr_to_bits 1431 // 1432 // Translates an RLE compressed, palette BMP from inSource to 1433 // the B_RGB32 bits format. Currently, this code is not as 1434 // memory effcient as it could be. It assumes that the BMP 1435 // from inSource is relatively small. 1436 // 1437 // Preconditions: 1438 // 1439 // Parameters: inSource, the BMP data to be translated 1440 // 1441 // outDestination, where the bits data will be written to 1442 // 1443 // datasize, number of bytes of data needed for the bits output 1444 // 1445 // msheader, header information about the BMP to be written 1446 // 1447 // palette, BMP palette for data in inSource 1448 // 1449 // Postconditions: 1450 // 1451 // Returns: B_ERROR, if there is an error allocating memory 1452 // 1453 // B_OK, if all went well 1454 // --------------------------------------------------------------- 1455 status_t 1456 translate_from_bmppalr_to_bits(BPositionIO *inSource, 1457 BPositionIO *outDestination, int32 datasize, MSInfoHeader &msheader, 1458 const uint8 *palette) 1459 { 1460 uint16 pixelsPerByte = 8 / msheader.bitsperpixel; 1461 uint16 bitsPerPixel = msheader.bitsperpixel; 1462 uint8 mask = (1 << bitsPerPixel) - 1; 1463 1464 uint8 count, indices, index; 1465 // Setup outDestination so that it can be written to 1466 // from the end of the file to the beginning instead of 1467 // the other way around 1468 int32 rowCount = abs(msheader.height); 1469 int32 bitsRowBytes = msheader.width * 4; 1470 off_t bitsFileSize = (bitsRowBytes * rowCount) + 1471 sizeof(TranslatorBitmap); 1472 if (outDestination->SetSize(bitsFileSize) != B_OK) 1473 // This call should work for BFile and BMallocIO objects, 1474 // but may not work for other BPositionIO based types 1475 return B_ERROR; 1476 uint8 *bitsRowData = new (nothrow) uint8[bitsRowBytes]; 1477 if (!bitsRowData) 1478 return B_NO_MEMORY; 1479 memset(bitsRowData, 0xff, bitsRowBytes); 1480 int32 bmppixcol = 0, bmppixrow = 0; 1481 uint32 defaultcolor = *(uint32*)palette; 1482 off_t rowOffset = msheader.height > 0 ? bitsRowBytes * -2 : 0; 1483 // set bits output to last row in the image 1484 if (msheader.height > 0) 1485 outDestination->Seek((msheader.height - 1) * bitsRowBytes, SEEK_CUR); 1486 ssize_t rd = inSource->Read(&count, 1); 1487 while (rd > 0) { 1488 // repeated color 1489 if (count) { 1490 // abort if all of the pixels in the row 1491 // have already been drawn to 1492 if (bmppixcol == msheader.width) { 1493 rd = -1; 1494 break; 1495 } 1496 // if count is greater than the number of 1497 // pixels remaining in the current row, 1498 // only process the correct number of pixels 1499 // remaining in the row 1500 if (count + bmppixcol > msheader.width) 1501 count = msheader.width - bmppixcol; 1502 1503 rd = inSource->Read(&indices, 1); 1504 if (rd != 1) { 1505 rd = -1; 1506 break; 1507 } 1508 for (uint8 i = 0; i < count; i++) { 1509 index = (indices >> (bitsPerPixel * ((pixelsPerByte - 1) - 1510 (i % pixelsPerByte)))) & mask; 1511 memcpy(bitsRowData + (bmppixcol*4), palette + (index*4), 3); 1512 bmppixcol++; 1513 } 1514 // special code 1515 } else { 1516 uint8 code; 1517 rd = inSource->Read(&code, 1); 1518 if (rd != 1) { 1519 rd = -1; 1520 break; 1521 } 1522 switch (code) { 1523 // end of line 1524 case 0: 1525 // if there are columns remaing on this 1526 // line, set them to the color at index zero 1527 if (bmppixcol < msheader.width) 1528 pixelcpy(bitsRowData + (bmppixcol * 4), 1529 defaultcolor, msheader.width - bmppixcol); 1530 outDestination->Write(bitsRowData, bitsRowBytes); 1531 bmppixcol = 0; 1532 bmppixrow++; 1533 if (bmppixrow < rowCount) 1534 outDestination->Seek(rowOffset, SEEK_CUR); 1535 break; 1536 1537 // end of bitmap 1538 case 1: 1539 // if at the end of a row 1540 if (bmppixcol == msheader.width) { 1541 outDestination->Write(bitsRowData, bitsRowBytes); 1542 bmppixcol = 0; 1543 bmppixrow++; 1544 if (bmppixrow < rowCount) 1545 outDestination->Seek(rowOffset, SEEK_CUR); 1546 } 1547 1548 while (bmppixrow < rowCount) { 1549 pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor, 1550 msheader.width - bmppixcol); 1551 outDestination->Write(bitsRowData, bitsRowBytes); 1552 bmppixcol = 0; 1553 bmppixrow++; 1554 if (bmppixrow < rowCount) 1555 outDestination->Seek(rowOffset, SEEK_CUR); 1556 } 1557 rd = 0; 1558 // break out of while loop 1559 break; 1560 1561 // delta, skip several rows and/or columns and 1562 // fill the skipped pixels with the default color 1563 case 2: 1564 { 1565 uint8 da[2], lastcol, dx, dy; 1566 rd = inSource->Read(da, 2); 1567 if (rd != 2) { 1568 rd = -1; 1569 break; 1570 } 1571 dx = da[0]; 1572 dy = da[1]; 1573 1574 // abort if dx or dy is too large 1575 if ((dx + bmppixcol >= msheader.width) || 1576 (dy + bmppixrow >= rowCount)) { 1577 rd = -1; 1578 break; 1579 } 1580 1581 lastcol = bmppixcol; 1582 1583 // set all pixels to the first entry in 1584 // the palette, for the number of rows skipped 1585 while (dy > 0) { 1586 pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor, 1587 msheader.width - bmppixcol); 1588 outDestination->Write(bitsRowData, bitsRowBytes); 1589 bmppixcol = 0; 1590 bmppixrow++; 1591 dy--; 1592 outDestination->Seek(rowOffset, SEEK_CUR); 1593 } 1594 1595 if (bmppixcol < static_cast<int32>(lastcol + dx)) { 1596 pixelcpy(bitsRowData + (bmppixcol * 4), defaultcolor, 1597 dx + lastcol - bmppixcol); 1598 bmppixcol = dx + lastcol; 1599 } 1600 1601 break; 1602 } 1603 1604 // code >= 3 1605 // read code uncompressed indices 1606 default: 1607 // abort if all of the pixels in the row 1608 // have already been drawn to 1609 if (bmppixcol == msheader.width) { 1610 rd = -1; 1611 break; 1612 } 1613 // if code is greater than the number of 1614 // pixels remaining in the current row, 1615 // only process the correct number of pixels 1616 // remaining in the row 1617 if (code + bmppixcol > msheader.width) 1618 code = msheader.width - bmppixcol; 1619 1620 uint8 uncomp[256]; 1621 int32 padding; 1622 if (!(code % pixelsPerByte)) 1623 padding = (code / pixelsPerByte) % 2; 1624 else 1625 padding = ((code + pixelsPerByte - 1626 (code % pixelsPerByte)) / pixelsPerByte) % 2; 1627 int32 uncompBytes = (code / pixelsPerByte) + 1628 ((code % pixelsPerByte) ? 1 : 0) + padding; 1629 rd = inSource->Read(uncomp, uncompBytes); 1630 if (rd != uncompBytes) { 1631 rd = -1; 1632 break; 1633 } 1634 for (uint8 i = 0; i < code; i++) { 1635 indices = (uncomp + (i / pixelsPerByte))[0]; 1636 index = (indices >> 1637 (bitsPerPixel * ((pixelsPerByte - 1) - 1638 (i % pixelsPerByte)))) & mask; 1639 memcpy(bitsRowData + (bmppixcol * 4), 1640 palette + (index * 4), 3); 1641 bmppixcol++; 1642 } 1643 1644 break; 1645 } 1646 } 1647 if (rd > 0) 1648 rd = inSource->Read(&count, 1); 1649 } 1650 1651 delete[] bitsRowData; 1652 1653 if (!rd) 1654 return B_OK; 1655 else 1656 return B_NO_TRANSLATOR; 1657 } 1658 1659 // --------------------------------------------------------------- 1660 // translate_from_bmp 1661 // 1662 // Convert the data in inSource from the BMP format 1663 // to the format specified in outType (either bits or BMP). 1664 // 1665 // Preconditions: 1666 // 1667 // Parameters: inSource, the bits data to translate 1668 // 1669 // outType, the type of data to convert to 1670 // 1671 // outDestination, where the output is written to 1672 // 1673 // Postconditions: 1674 // 1675 // Returns: B_NO_TRANSLATOR, if the data is not in a supported 1676 // format 1677 // 1678 // B_ERROR, if there was an error allocating memory or some other 1679 // error 1680 // 1681 // B_OK, if successfully translated the data from the bits format 1682 // --------------------------------------------------------------- 1683 status_t 1684 BMPTranslator::translate_from_bmp(BPositionIO *inSource, uint32 outType, 1685 BPositionIO *outDestination) 1686 { 1687 bool bheaderonly, bdataonly; 1688 bheaderonly = bdataonly = false; 1689 1690 BMPFileHeader fileHeader; 1691 MSInfoHeader msheader; 1692 bool frommsformat; 1693 off_t os2skip = 0; 1694 1695 status_t result; 1696 result = identify_bmp_header(inSource, NULL, &fileHeader, &msheader, 1697 &frommsformat, &os2skip); 1698 if (result != B_OK) { 1699 INFO("BMPTranslator::translate_from_bmp() - identify_bmp_header failed\n"); 1700 return result; 1701 } 1702 1703 // if the user wants to translate a BMP to a BMP, easy enough :) 1704 if (outType == B_BMP_FORMAT) { 1705 // write out the BMP headers 1706 if (bheaderonly || (!bheaderonly && !bdataonly)) { 1707 result = write_bmp_headers(outDestination, fileHeader, msheader); 1708 if (result != B_OK) 1709 return result; 1710 } 1711 if (bheaderonly) 1712 // if the user only wants the header, 1713 // bail before it is written 1714 return result; 1715 1716 uint8 buf[1024]; 1717 ssize_t rd; 1718 uint32 rdtotal = 54; 1719 if (!frommsformat && (msheader.bitsperpixel == 1 || 1720 msheader.bitsperpixel == 4 || msheader.bitsperpixel == 8)) { 1721 // if OS/2 paletted format, convert palette to MS format 1722 uint16 ncolors = 1 << msheader.bitsperpixel; 1723 rd = inSource->Read(buf, ncolors * 3); 1724 if (rd != ncolors * 3) 1725 return B_NO_TRANSLATOR; 1726 uint8 mspalent[4] = {0, 0, 0, 0}; 1727 for (uint16 i = 0; i < ncolors; i++) { 1728 memcpy(mspalent, buf + (i * 3), 3); 1729 outDestination->Write(mspalent, 4); 1730 } 1731 rdtotal = fileHeader.dataOffset; 1732 } 1733 // if there is junk between the OS/2 headers and 1734 // the actual data, skip it 1735 if (!frommsformat && os2skip) 1736 inSource->Seek(os2skip, SEEK_CUR); 1737 1738 rd = min((uint32)1024, fileHeader.fileSize - rdtotal); 1739 rd = inSource->Read(buf, rd); 1740 while (rd > 0) { 1741 outDestination->Write(buf, rd); 1742 rdtotal += rd; 1743 rd = min((uint32)1024, fileHeader.fileSize - rdtotal); 1744 rd = inSource->Read(buf, rd); 1745 } 1746 if (rd == 0) 1747 return B_OK; 1748 else 1749 return B_ERROR; 1750 1751 // if translating a BMP to a Be Bitmap 1752 } else if (outType == B_TRANSLATOR_BITMAP) { 1753 TranslatorBitmap bitsHeader; 1754 bitsHeader.magic = B_TRANSLATOR_BITMAP; 1755 bitsHeader.bounds.left = 0; 1756 bitsHeader.bounds.top = 0; 1757 bitsHeader.bounds.right = msheader.width - 1; 1758 bitsHeader.bounds.bottom = abs(msheader.height) - 1; 1759 1760 // read in palette and/or skip non-BMP data 1761 uint8 bmppalette[1024]; 1762 off_t nskip = 0; 1763 if (msheader.bitsperpixel == 1 || 1764 msheader.bitsperpixel == 4 || 1765 msheader.bitsperpixel == 8) { 1766 1767 uint8 palBytesPerPixel; 1768 if (frommsformat) 1769 palBytesPerPixel = 4; 1770 else 1771 palBytesPerPixel = 3; 1772 1773 if (!msheader.colorsused) 1774 msheader.colorsused = 1 << msheader.bitsperpixel; 1775 1776 if (inSource->Read(bmppalette, msheader.colorsused * 1777 palBytesPerPixel) != 1778 (off_t) msheader.colorsused * palBytesPerPixel) 1779 return B_NO_TRANSLATOR; 1780 1781 // skip over non-BMP data 1782 if (frommsformat) { 1783 if (fileHeader.dataOffset > (msheader.colorsused * 1784 palBytesPerPixel) + 54) 1785 nskip = fileHeader.dataOffset - 1786 ((msheader.colorsused * palBytesPerPixel) + 54); 1787 } else 1788 nskip = os2skip; 1789 } else if (fileHeader.dataOffset > 54) 1790 // skip over non-BMP data 1791 nskip = fileHeader.dataOffset - 54; 1792 1793 if (nskip > 0 && inSource->Seek(nskip, SEEK_CUR) < 0) 1794 return B_NO_TRANSLATOR; 1795 1796 bitsHeader.rowBytes = msheader.width * 4; 1797 bitsHeader.colors = B_RGB32; 1798 int32 datasize = bitsHeader.rowBytes * abs(msheader.height); 1799 bitsHeader.dataSize = datasize; 1800 1801 // write out Be's Bitmap header 1802 if (bheaderonly || (!bheaderonly && !bdataonly)) { 1803 if (swap_data(B_UINT32_TYPE, &bitsHeader, 1804 sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) 1805 return B_ERROR; 1806 outDestination->Write(&bitsHeader, sizeof(TranslatorBitmap)); 1807 } 1808 if (bheaderonly) 1809 // if the user only wants the header, 1810 // bail before the data is written 1811 return B_OK; 1812 1813 // write out the actual image data 1814 switch (msheader.bitsperpixel) { 1815 case 32: 1816 case 24: 1817 return translate_from_bmpnpal_to_bits(inSource, 1818 outDestination, msheader); 1819 1820 case 8: 1821 // 8 bit BMP with NO compression 1822 if (msheader.compression == BMP_NO_COMPRESS) 1823 return translate_from_bmppal_to_bits(inSource, 1824 outDestination, msheader, bmppalette, frommsformat); 1825 1826 // 8 bit RLE compressed BMP 1827 else if (msheader.compression == BMP_RLE8_COMPRESS) 1828 return translate_from_bmppalr_to_bits(inSource, 1829 outDestination, datasize, msheader, bmppalette); 1830 else 1831 return B_NO_TRANSLATOR; 1832 1833 case 4: 1834 // 4 bit BMP with NO compression 1835 if (!msheader.compression) 1836 return translate_from_bmppal_to_bits(inSource, 1837 outDestination, msheader, bmppalette, frommsformat); 1838 1839 // 4 bit RLE compressed BMP 1840 else if (msheader.compression == BMP_RLE4_COMPRESS) 1841 return translate_from_bmppalr_to_bits(inSource, 1842 outDestination, datasize, msheader, bmppalette); 1843 else 1844 return B_NO_TRANSLATOR; 1845 1846 case 1: 1847 return translate_from_bmppal_to_bits(inSource, 1848 outDestination, msheader, bmppalette, frommsformat); 1849 1850 default: 1851 return B_NO_TRANSLATOR; 1852 } 1853 1854 } else 1855 return B_NO_TRANSLATOR; 1856 } 1857 1858 // --------------------------------------------------------------- 1859 // DerivedTranslate 1860 // 1861 // Translates the data in inSource to the type outType and stores 1862 // the translated data in outDestination. 1863 // 1864 // Preconditions: 1865 // 1866 // Parameters: inSource, the data to be translated 1867 // 1868 // inInfo, hint about the data in inSource (not used) 1869 // 1870 // ioExtension, configuration options for the 1871 // translator 1872 // 1873 // outType, the type to convert inSource to 1874 // 1875 // outDestination, where the translated data is 1876 // put 1877 // 1878 // baseType, indicates whether inSource is in the 1879 // bits format, not in the bits format or 1880 // is unknown 1881 // 1882 // Postconditions: 1883 // 1884 // Returns: B_BAD_VALUE, if the options in ioExtension are bad 1885 // 1886 // B_NO_TRANSLATOR, if this translator doesn't understand the data 1887 // 1888 // B_ERROR, if there was an error allocating memory or converting 1889 // data 1890 // 1891 // B_OK, if all went well 1892 // --------------------------------------------------------------- 1893 status_t 1894 BMPTranslator::DerivedTranslate(BPositionIO *inSource, 1895 const translator_info *inInfo, BMessage *ioExtension, 1896 uint32 outType, BPositionIO *outDestination, int32 baseType) 1897 { 1898 if (baseType == 1) 1899 // if inSource is in bits format 1900 return translate_from_bits(inSource, outType, outDestination); 1901 else if (baseType == 0) 1902 // if inSource is NOT in bits format 1903 return translate_from_bmp(inSource, outType, outDestination); 1904 else 1905 return B_NO_TRANSLATOR; 1906 } 1907 1908 BView * 1909 BMPTranslator::NewConfigView(TranslatorSettings *settings) 1910 { 1911 return new BMPView(BRect(0, 0, 225, 175), 1912 B_TRANSLATE("BMPTranslator Settings"), B_FOLLOW_ALL, B_WILL_DRAW, 1913 settings); 1914 } 1915