1 /* 2 * Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 // ToDo: This definitely needs to be worked over for endian issues 7 8 #include "ICO.h" 9 #include "ICOTranslator.h" 10 11 #include <ByteOrder.h> 12 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 17 18 //#define TRACE_ICO 19 #ifdef TRACE_ICO 20 # define TRACE(x) printf x 21 #else 22 # define TRACE(x) ; 23 #endif 24 25 26 using namespace ICO; 27 28 29 static const rgba32_color kMagicTransparentColor = *(rgba32_color *)&B_TRANSPARENT_MAGIC_RGBA32; 30 31 32 class TempAllocator { 33 public: 34 TempAllocator() : fMemory(NULL) {} 35 ~TempAllocator() { free(fMemory); } 36 37 void *Allocate(size_t size) { return fMemory = malloc(size); } 38 39 private: 40 void *fMemory; 41 }; 42 43 44 bool 45 ico_header::IsValid() const 46 { 47 return reserved == 0 48 && (type == kTypeIcon || type == kTypeCursor) 49 && entry_count < 32; 50 } 51 52 53 void 54 ico_header::SwapToHost() 55 { 56 swap_data(B_UINT16_TYPE, this, sizeof(ico_header), B_SWAP_LENDIAN_TO_HOST); 57 } 58 59 60 void 61 ico_header::SwapFromHost() 62 { 63 swap_data(B_UINT16_TYPE, this, sizeof(ico_header), B_SWAP_HOST_TO_LENDIAN); 64 } 65 66 67 // #pragma mark - 68 69 70 void 71 ico_dir_entry::SwapToHost() 72 { 73 swap_data(B_UINT16_TYPE, &planes, sizeof(uint16) * 2, B_SWAP_LENDIAN_TO_HOST); 74 swap_data(B_UINT32_TYPE, &size, sizeof(uint32) * 2, B_SWAP_LENDIAN_TO_HOST); 75 } 76 77 78 void 79 ico_dir_entry::SwapFromHost() 80 { 81 swap_data(B_UINT16_TYPE, &planes, sizeof(uint16) * 2, B_SWAP_HOST_TO_LENDIAN); 82 swap_data(B_UINT32_TYPE, &size, sizeof(uint32) * 2, B_SWAP_HOST_TO_LENDIAN); 83 } 84 85 86 // #pragma mark - 87 88 89 bool 90 ico_bitmap_header::IsValid() const 91 { 92 return size == sizeof(ico_bitmap_header) && compression == 0 93 && (bits_per_pixel == 1 || bits_per_pixel == 4 || bits_per_pixel == 8 94 || bits_per_pixel == 16 || bits_per_pixel == 24 || bits_per_pixel == 32); 95 } 96 97 98 void 99 ico_bitmap_header::SwapToHost() 100 { 101 swap_data(B_UINT32_TYPE, &size, sizeof(uint32) * 3, B_SWAP_LENDIAN_TO_HOST); 102 swap_data(B_UINT16_TYPE, &planes, sizeof(uint16) * 2, B_SWAP_LENDIAN_TO_HOST); 103 swap_data(B_UINT32_TYPE, &compression, sizeof(uint32) * 6, B_SWAP_LENDIAN_TO_HOST); 104 } 105 106 107 void 108 ico_bitmap_header::SwapFromHost() 109 { 110 swap_data(B_UINT32_TYPE, &size, sizeof(uint32) * 3, B_SWAP_HOST_TO_LENDIAN); 111 swap_data(B_UINT16_TYPE, &planes, sizeof(uint16) * 2, B_SWAP_HOST_TO_LENDIAN); 112 swap_data(B_UINT32_TYPE, &compression, sizeof(uint32) * 6, B_SWAP_HOST_TO_LENDIAN); 113 } 114 115 116 // #pragma mark - 117 118 119 static inline uint8 * 120 get_data_row(uint8 *data, int32 dataSize, int32 rowBytes, int32 row) 121 { 122 return data + dataSize - (row + 1) * rowBytes; 123 } 124 125 126 static inline int32 127 get_bytes_per_row(int32 width, int32 bitsPerPixel) 128 { 129 return (((bitsPerPixel * width + 7) / 8) + 3) & ~3; 130 } 131 132 133 static inline void 134 set_1_bit_per_pixel(uint8 *line, int32 x, int32 value) 135 { 136 int32 mask = 1 << (7 - (x & 7)); 137 if (value) 138 line[x / 8] |= mask; 139 else 140 line[x / 8] &= ~mask; 141 } 142 143 144 static inline void 145 set_4_bits_per_pixel(uint8 *line, int32 x, int32 value) 146 { 147 int32 shift = x & 1 ? 0 : 4; 148 int32 mask = ~0L & (0xf0 >> shift); 149 150 line[x / 2] &= mask; 151 line[x / 2] |= value << shift; 152 } 153 154 155 static inline int32 156 get_1_bit_per_pixel(uint8 *line, int32 x) 157 { 158 return (line[x / 8] >> (7 - (x & 7))) & 1; 159 } 160 161 162 static inline int32 163 get_4_bits_per_pixel(uint8 *line, int32 x) 164 { 165 return (line[x / 2] >> (4 * ((x + 1) & 1))) & 0xf; 166 } 167 168 169 static uint8 170 get_alpha_value(color_space space, uint32 value) 171 { 172 // ToDo: support more color spaces 173 if (space == B_RGBA32) 174 return value >> 24; 175 176 return 0; 177 } 178 179 180 static uint16 181 rgba32_color_to_16_bit_color(rgba32_color &color) 182 { 183 return ((color.blue >> 3) << 11) | ((color.green >> 2) << 5) | (color.red >> 3); 184 } 185 186 187 static int32 188 find_rgba32_color(rgba32_color *palette, int32 numColors, rgba32_color &color) 189 { 190 // ToDo: sorting and binary search? 191 for (int32 i = 0; i < numColors; i++) { 192 if (palette[i] == color) 193 return i; 194 } 195 196 return -1; 197 } 198 199 200 static inline rgba32_color 201 get_rgba32_color_from_bits(TranslatorBitmap &bitsHeader, uint8 *data, int32 x, int32 y) 202 { 203 data += bitsHeader.rowBytes * y; 204 205 switch (bitsHeader.colors) { 206 case B_RGBA32: 207 return *(rgba32_color *)(data + 4 * x); 208 case B_RGB32: 209 default: 210 // stupid applications like ArtPaint use the alpha channel in B_RGB32 images... 211 rgba32_color color = *(rgba32_color *)(data + 4 * x); 212 if (color.alpha >= 128) 213 color.alpha = 255; 214 else 215 color.alpha = 0; 216 return color; 217 // ToDo: support some more color spaces... 218 } 219 } 220 221 222 static int32 223 fill_palette(TranslatorBitmap &bitsHeader, uint8 *data, rgba32_color *palette) 224 { 225 int32 numColors = 0; 226 227 for (int32 y = 0; y < bitsHeader.bounds.IntegerHeight() + 1; y++) { 228 for (int32 x = 0; x < bitsHeader.bounds.IntegerWidth() + 1; x++) { 229 rgba32_color color = get_rgba32_color_from_bits(bitsHeader, data, x, y); 230 231 int32 index = find_rgba32_color(palette, numColors, color); 232 if (index == -1) { 233 // add this color if there is space left 234 if (numColors == 256) 235 return -1; 236 237 color.alpha = 0; 238 // the alpha channel is actually unused 239 palette[numColors++] = color; 240 } 241 } 242 } 243 244 return numColors; 245 } 246 247 248 /** This function is used to determine, if a true alpha channel has to 249 * be used in order to preserve all information. 250 */ 251 252 static bool 253 has_true_alpha_channel(color_space space, uint8 *data, 254 int32 width, int32 height, int32 bytesPerRow) 255 { 256 for (int32 y = 0; y < height; y++) { 257 for (int32 x = 0; x < width; x++) { 258 uint8 value = get_alpha_value(space, ((uint32 *)data)[x]); 259 if (value != 0 && value != 255) 260 return true; 261 } 262 263 data += bytesPerRow; 264 } 265 266 return false; 267 } 268 269 270 static status_t 271 convert_data_to_bits(ico_dir_entry &entry, ico_bitmap_header &header, 272 const rgba32_color *palette, BPositionIO &source, 273 BPositionIO &target) 274 { 275 uint16 bitsPerPixel = header.bits_per_pixel; 276 277 // round row bytes to next 4 byte boundary 278 int32 xorRowBytes = get_bytes_per_row(entry.width, header.bits_per_pixel); 279 int32 andRowBytes = 0; 280 if (bitsPerPixel != 32) 281 andRowBytes = get_bytes_per_row(entry.width, 1); 282 int32 outRowBytes = entry.width * 4; 283 284 // allocate buffers 285 TempAllocator xorAllocator, andAllocator, rowAllocator; 286 287 int32 xorDataSize = xorRowBytes * entry.height; 288 uint8 *xorData = (uint8 *)xorAllocator.Allocate(xorDataSize); 289 if (xorData == NULL) 290 return B_NO_MEMORY; 291 292 int32 andDataSize = andRowBytes * entry.height; 293 uint8 *andData = NULL; 294 if (bitsPerPixel != 32) { 295 andData = (uint8 *)andAllocator.Allocate(andDataSize); 296 if (andData == NULL) 297 return B_NO_MEMORY; 298 } 299 300 rgba32_color *outRowData = (rgba32_color *)rowAllocator.Allocate(outRowBytes); 301 if (outRowData == NULL) 302 return B_NO_MEMORY; 303 304 ssize_t bytesRead = source.Read(xorData, xorDataSize); 305 if (bytesRead != xorDataSize) 306 return B_BAD_DATA; 307 308 if (bitsPerPixel != 32) { 309 bytesRead = source.Read(andData, andDataSize); 310 if (bytesRead != andDataSize) { 311 // reading the alpha channel failed, so we're ignoring it 312 // (but we're still able to show the image data) 313 andData = NULL; 314 } 315 } 316 317 for (uint32 row = 0; row < entry.height; row++) { 318 for (uint32 x = 0; x < entry.width; x++) { 319 uint8 *line = get_data_row(xorData, xorDataSize, xorRowBytes, row); 320 321 if (palette != NULL) { 322 uint8 index; 323 324 switch (bitsPerPixel) { 325 case 1: 326 index = get_1_bit_per_pixel(line, x); 327 break; 328 case 4: 329 index = get_4_bits_per_pixel(line, x); 330 break; 331 case 8: 332 default: 333 index = line[x]; 334 break; 335 } 336 337 outRowData[x] = palette[index]; 338 } else { 339 switch (bitsPerPixel) { 340 case 16: 341 { 342 uint16 color = ((uint16 *)line)[x]; 343 outRowData[x].blue = (color >> 11) << 3; 344 outRowData[x].green = ((color >> 5) & 0x3f) << 3; 345 outRowData[x].red = (color & 0x1f) << 3; 346 break; 347 } 348 349 case 24: 350 outRowData[x].blue = line[x * 3 + 0]; 351 outRowData[x].green = line[x * 3 + 1]; 352 outRowData[x].red = line[x * 3 + 2]; 353 break; 354 355 case 32: 356 outRowData[x] = ((rgba32_color *)line)[x]; 357 break; 358 } 359 } 360 361 if (bitsPerPixel != 32) { 362 // set alpha channel 363 if (andData != NULL 364 && get_1_bit_per_pixel(get_data_row(andData, andDataSize, andRowBytes, row), x)) 365 outRowData[x] = kMagicTransparentColor; 366 else 367 outRowData[x].alpha = 255; 368 } else if (outRowData[x].alpha == 0) 369 outRowData[x] = kMagicTransparentColor; 370 } 371 372 ssize_t bytesWritten = target.Write(outRowData, outRowBytes); 373 if (bytesWritten < B_OK) 374 return bytesWritten; 375 if (bytesWritten != outRowBytes) 376 return B_IO_ERROR; 377 } 378 379 return B_OK; 380 } 381 382 383 static status_t 384 convert_bits_to_data(TranslatorBitmap &bitsHeader, uint8 *bitsData, ico_dir_entry &entry, 385 ico_bitmap_header &header, rgba32_color *palette, BPositionIO &target) 386 { 387 int32 bitsPerPixel = header.bits_per_pixel; 388 389 // round row bytes to next 4 byte boundary 390 int32 xorRowBytes = get_bytes_per_row(entry.width, bitsPerPixel); 391 int32 andRowBytes = get_bytes_per_row(entry.width, 1); 392 393 TempAllocator xorAllocator, andAllocator; 394 395 uint8 *xorRowData = (uint8 *)xorAllocator.Allocate(xorRowBytes); 396 if (xorRowData == NULL) 397 return B_NO_MEMORY; 398 399 uint8 *andRowData = (uint8 *)andAllocator.Allocate(andRowBytes); 400 if (andRowData == NULL) 401 return B_NO_MEMORY; 402 403 int32 numColors = 1 << bitsPerPixel; 404 405 // write XOR data (the actual image data) 406 // (ICO data is upside down, so we're starting at the last line) 407 408 for (uint32 row = entry.height; row-- > 0;) { 409 for (uint32 x = 0; x < entry.width; x++) { 410 rgba32_color color = get_rgba32_color_from_bits(bitsHeader, bitsData, x, row); 411 412 if (palette != NULL) { 413 uint8 index = find_rgba32_color(palette, numColors, color); 414 415 switch (bitsPerPixel) { 416 case 1: 417 set_1_bit_per_pixel(xorRowData, x, index); 418 break; 419 case 4: 420 set_4_bits_per_pixel(xorRowData, x, index); 421 break; 422 case 8: 423 default: 424 xorRowData[x] = index; 425 break; 426 } 427 } else { 428 switch (bitsPerPixel) { 429 default: 430 case 16: 431 { 432 uint16 *data = (uint16 *)xorRowData; 433 data[x] = rgba32_color_to_16_bit_color(color); 434 break; 435 } 436 437 case 24: 438 { 439 xorRowData[x * 3 + 0] = color.blue; 440 xorRowData[x * 3 + 1] = color.green; 441 xorRowData[x * 3 + 2] = color.red; 442 break; 443 } 444 445 case 32: 446 { 447 rgba32_color *data = (rgba32_color *)xorRowData; 448 data[x] = color; 449 break; 450 } 451 } 452 } 453 } 454 455 ssize_t bytesWritten = target.Write(xorRowData, xorRowBytes); 456 if (bytesWritten < B_OK) 457 return bytesWritten; 458 if (bytesWritten != xorRowBytes) 459 return B_IO_ERROR; 460 } 461 462 if (bitsPerPixel == 32) { 463 // the alpha channel has already been written with the image data 464 return B_OK; 465 } 466 467 // write AND data (the transparency bit) 468 469 for (uint32 row = entry.height; row-- > 0;) { 470 for (uint32 x = 0; x < entry.width; x++) { 471 rgba32_color color = get_rgba32_color_from_bits(bitsHeader, bitsData, x, row); 472 bool transparent = *(uint32 *)&color == B_TRANSPARENT_MAGIC_RGBA32 || color.alpha == 0; 473 474 set_1_bit_per_pixel(andRowData, x, transparent ? 1 : 0); 475 } 476 477 ssize_t bytesWritten = target.Write(andRowData, andRowBytes); 478 if (bytesWritten < B_OK) 479 return bytesWritten; 480 if (bytesWritten != andRowBytes) 481 return B_IO_ERROR; 482 } 483 484 return B_OK; 485 } 486 487 488 // #pragma mark - 489 490 491 bool 492 ICO::is_valid_size(int32 size) 493 { 494 return size == 16 || size == 32 || size == 48; 495 } 496 497 498 status_t 499 ICO::identify(BMessage *settings, BPositionIO &stream, uint8 &type, int32 &bitsPerPixel) 500 { 501 // read in the header 502 503 ico_header header; 504 if (stream.Read(&header, sizeof(ico_header)) != (ssize_t)sizeof(ico_header)) 505 return B_BAD_VALUE; 506 507 header.SwapToHost(); 508 509 // check header 510 511 if (!header.IsValid()) 512 return B_BAD_VALUE; 513 514 int32 iconIndex = 0; 515 type = header.type; 516 517 if (settings) { 518 // Add page count to ioExtension 519 settings->RemoveName(kDocumentCount); 520 settings->AddInt32(kDocumentCount, header.entry_count); 521 522 // Check if a document index has been specified 523 if (settings->FindInt32(kDocumentIndex, &iconIndex) == B_OK) 524 iconIndex--; 525 else 526 iconIndex = 0; 527 528 if (iconIndex < 0 || iconIndex >= header.entry_count) 529 return B_NO_TRANSLATOR; 530 } 531 532 TRACE(("iconIndex = %ld, count = %ld\n", iconIndex, header.entry_count)); 533 534 // read in directory entries 535 536 for (uint32 i = 0; i < header.entry_count; i++) { 537 ico_dir_entry entry; 538 if (stream.Read(&entry, sizeof(ico_dir_entry)) != (ssize_t)sizeof(ico_dir_entry)) 539 return B_BAD_VALUE; 540 541 entry.SwapToHost(); 542 TRACE(("width: %d, height: %d, planes: %d, color_count: %d, bits_per_pixel: %d, size: %ld, offset: %ld\n", 543 entry.width, entry.height, entry.planes, entry.color_count, entry.bits_per_pixel, 544 entry.size, entry.offset)); 545 546 ico_bitmap_header bitmapHeader; 547 if (stream.ReadAt(entry.offset, &bitmapHeader, sizeof(ico_bitmap_header)) != (ssize_t)sizeof(ico_bitmap_header)) 548 return B_BAD_VALUE; 549 550 bitmapHeader.SwapToHost(); 551 TRACE(("size: %ld, width: %ld, height: %ld, bits_per_pixel: %d, x/y per meter: %ld:%ld, compression: %ld, image_size: %ld, colors used: %ld, important colors: %ld\n", 552 bitmapHeader.size, bitmapHeader.width, bitmapHeader.height, bitmapHeader.bits_per_pixel, 553 bitmapHeader.x_pixels_per_meter, bitmapHeader.y_pixels_per_meter, 554 bitmapHeader.compression, bitmapHeader.image_size, bitmapHeader.colors_used, 555 bitmapHeader.important_colors)); 556 557 if (!bitmapHeader.IsValid()) 558 return B_BAD_VALUE; 559 560 if ((uint32)iconIndex == i) 561 bitsPerPixel = bitmapHeader.bits_per_pixel; 562 } 563 564 return B_OK; 565 } 566 567 568 /** Converts an ICO image of any type into a B_RGBA32 B_TRANSLATOR_BITMAP. 569 */ 570 571 status_t 572 ICO::convert_ico_to_bits(BMessage *settings, BPositionIO &source, BPositionIO &target) 573 { 574 ico_header header; 575 if (source.Read(&header, sizeof(ico_header)) != (ssize_t)sizeof(ico_header)) 576 return B_BAD_VALUE; 577 578 header.SwapToHost(); 579 580 // check header 581 582 if (!header.IsValid()) 583 return B_BAD_VALUE; 584 585 int32 iconIndex = 0; 586 587 if (settings) { 588 // Check if a document index has been specified 589 if (settings->FindInt32(kDocumentIndex, &iconIndex) == B_OK) 590 iconIndex--; 591 else 592 iconIndex = 0; 593 594 if (iconIndex < 0 || iconIndex >= header.entry_count) 595 return B_BAD_VALUE; 596 } 597 598 // read in selected entry 599 600 ico_dir_entry entry; 601 if (source.ReadAt(sizeof(ico_header) + sizeof(ico_dir_entry) * iconIndex, 602 &entry, sizeof(ico_dir_entry)) != (ssize_t)sizeof(ico_dir_entry)) 603 return B_BAD_VALUE; 604 605 entry.SwapToHost(); 606 source.Seek(entry.offset, SEEK_SET); 607 608 ico_bitmap_header bitmapHeader; 609 if (source.Read(&bitmapHeader, sizeof(ico_bitmap_header)) != (ssize_t)sizeof(ico_bitmap_header)) 610 return B_BAD_VALUE; 611 612 bitmapHeader.SwapToHost(); 613 614 if (!bitmapHeader.IsValid()) 615 return B_BAD_VALUE; 616 617 if (bitmapHeader.compression != 0) 618 return EOPNOTSUPP; 619 620 int32 numColors = 0; 621 if (bitmapHeader.bits_per_pixel <= 8) 622 numColors = 1L << bitmapHeader.bits_per_pixel; 623 624 // This is a work-around for a broken ICO file writer that publishes 625 // a wrong image height in the ico_dir_entry structure 626 if (entry.size != 0 && 2 * entry.width == entry.height && numColors != 0 627 && sizeof(rgba32_color) * numColors + entry.width * entry.height > entry.size) 628 entry.height = entry.width; 629 630 TranslatorBitmap bitsHeader; 631 bitsHeader.magic = B_TRANSLATOR_BITMAP; 632 bitsHeader.bounds.left = 0; 633 bitsHeader.bounds.top = 0; 634 bitsHeader.bounds.right = entry.width - 1; 635 bitsHeader.bounds.bottom = entry.height - 1; 636 bitsHeader.bounds.Set(0, 0, entry.width - 1, entry.height - 1); 637 bitsHeader.rowBytes = entry.width * 4; 638 bitsHeader.colors = B_RGBA32; 639 bitsHeader.dataSize = bitsHeader.rowBytes * entry.height; 640 641 // read in palette 642 643 rgba32_color palette[256]; 644 if (numColors > 0) { 645 if (source.Read(palette, numColors * 4) != numColors * 4) 646 return B_BAD_VALUE; 647 648 // clear alpha channel (it's not used in ICO color information) 649 for (int32 i = 0; i < numColors; i++) 650 palette[i].alpha = 0; 651 } 652 653 // write out Be's Bitmap header 654 swap_data(B_UINT32_TYPE, &bitsHeader, sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN); 655 target.Write(&bitsHeader, sizeof(TranslatorBitmap)); 656 657 return convert_data_to_bits(entry, bitmapHeader, numColors > 0 ? palette : NULL, source, target); 658 } 659 660 661 status_t 662 ICO::convert_bits_to_ico(BMessage *settings, BPositionIO &source, 663 TranslatorBitmap &bitsHeader, BPositionIO &target) 664 { 665 int32 width = bitsHeader.bounds.IntegerWidth() + 1; 666 int32 height = bitsHeader.bounds.IntegerHeight() + 1; 667 if (!is_valid_size(width) || !is_valid_size(height)) 668 return B_BAD_VALUE; 669 670 int32 bitsPerPixel; 671 switch (bitsHeader.colors) { 672 case B_RGBA32: 673 bitsPerPixel = 32; 674 break; 675 case B_RGB32: 676 bitsPerPixel = 24; 677 break; 678 case B_RGB16: 679 bitsPerPixel = 16; 680 break; 681 case B_CMAP8: 682 case B_GRAY8: 683 bitsPerPixel = 8; 684 break; 685 case B_GRAY1: 686 bitsPerPixel = 1; 687 break; 688 default: 689 fprintf(stderr, "unsupported color space.\n"); 690 return B_BAD_VALUE; 691 } 692 693 TempAllocator dataAllocator; 694 uint8 *bitsData = (uint8 *)dataAllocator.Allocate(bitsHeader.rowBytes * height); 695 if (bitsData == NULL) 696 return B_NO_MEMORY; 697 698 ssize_t bytesRead = source.Read(bitsData, bitsHeader.rowBytes * height); 699 if (bytesRead < B_OK) 700 return bytesRead; 701 702 rgba32_color palette[256]; 703 if (bitsPerPixel > 8) { 704 // it's a non-palette mode - but does it have to be? 705 if (bitsHeader.colors != B_RGBA32 706 || !has_true_alpha_channel(bitsHeader.colors, bitsData, 707 width, height, bitsHeader.rowBytes)) { 708 memset(palette, 0, sizeof(palette)); 709 710 // count colors 711 int32 colors = fill_palette(bitsHeader, bitsData, palette); 712 if (colors != -1) { 713 // we fit into a palette mode 714 if (colors > 16) 715 bitsPerPixel = 8; 716 else if (colors > 2) 717 bitsPerPixel = 4; 718 else 719 bitsPerPixel = 1; 720 } 721 } 722 } 723 int32 numColors = 1 << bitsPerPixel; 724 725 ico_header header; 726 header.type = B_HOST_TO_LENDIAN_INT16(1); 727 header.entry_count = B_HOST_TO_LENDIAN_INT16(1); 728 header.reserved = 0; 729 730 ssize_t bytesWritten = target.Write(&header, sizeof(ico_header)); 731 if (bytesWritten < B_OK) 732 return bytesWritten; 733 734 ico_dir_entry entry; 735 entry.width = width; 736 entry.height = height; 737 entry.planes = 1; 738 entry.bits_per_pixel = bitsPerPixel; 739 entry.color_count = 0; 740 if (bitsPerPixel <= 8) 741 entry.color_count = numColors; 742 743 // When bits_per_pixel == 32, the data already contains the alpha channel 744 745 int32 xorRowBytes = get_bytes_per_row(width, bitsPerPixel); 746 int32 andRowBytes = 0; 747 if (bitsPerPixel != 32) 748 andRowBytes = get_bytes_per_row(width, 1); 749 750 entry.size = sizeof(ico_bitmap_header) + width * (xorRowBytes + andRowBytes); 751 if (bitsPerPixel <= 8) 752 entry.size += numColors * sizeof(rgba32_color); 753 entry.offset = sizeof(ico_header) + sizeof(ico_dir_entry); 754 entry.reserved = 0; 755 756 ico_bitmap_header bitmapHeader; 757 memset(&bitmapHeader, 0, sizeof(ico_bitmap_header)); 758 bitmapHeader.size = sizeof(ico_bitmap_header); 759 bitmapHeader.width = width; 760 bitmapHeader.height = height + (bitsPerPixel == 32 ? 0 : height); 761 bitmapHeader.bits_per_pixel = bitsPerPixel; 762 bitmapHeader.planes = 1; 763 bitmapHeader.image_size = 0; 764 if (bitsPerPixel <= 8) 765 bitmapHeader.colors_used = numColors; 766 767 entry.SwapFromHost(); 768 bitmapHeader.SwapFromHost(); 769 770 bytesWritten = target.Write(&entry, sizeof(ico_dir_entry)); 771 if (bytesWritten < B_OK) 772 return bytesWritten; 773 774 bytesWritten = target.Write(&bitmapHeader, sizeof(ico_bitmap_header)); 775 if (bytesWritten < B_OK) 776 return bytesWritten; 777 778 // we'll need them in convert_bits_to_data() 779 entry.SwapToHost(); 780 bitmapHeader.SwapToHost(); 781 782 if (bitsPerPixel <= 8) { 783 bytesWritten = target.Write(palette, numColors * sizeof(rgba32_color)); 784 if (bytesWritten < B_OK) 785 return bytesWritten; 786 } 787 788 return convert_bits_to_data(bitsHeader, bitsData, entry, bitmapHeader, 789 bitsPerPixel <= 8 ? palette : NULL, target); 790 } 791 792