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