1 /* 2 * Copyright 2006-2013, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus, superstippi@gmx.de 7 * Ingo Weinhold, ingo_weinhold@gmx.de 8 */ 9 10 11 #include <Icon.h> 12 13 #include <string.h> 14 15 #include <new> 16 17 #include <Bitmap.h> 18 19 #include <AutoDeleter.h> 20 21 22 namespace BPrivate { 23 24 25 BIcon::BIcon() 26 : 27 fEnabledBitmaps(8, true), 28 fDisabledBitmaps(8, true) 29 { 30 } 31 32 33 BIcon::~BIcon() 34 { 35 } 36 37 38 status_t 39 BIcon::SetTo(const BBitmap* bitmap, uint32 flags) 40 { 41 if (!bitmap->IsValid()) 42 return B_BAD_VALUE; 43 44 DeleteBitmaps(); 45 46 // check the color space 47 bool hasAlpha = false; 48 bool canUseForMakeBitmaps = false; 49 50 switch (bitmap->ColorSpace()) { 51 case B_RGBA32: 52 case B_RGBA32_BIG: 53 hasAlpha = true; 54 // fall through 55 case B_RGB32: 56 case B_RGB32_BIG: 57 canUseForMakeBitmaps = true; 58 break; 59 60 case B_UVLA32: 61 case B_LABA32: 62 case B_HSIA32: 63 case B_HSVA32: 64 case B_HLSA32: 65 case B_CMYA32: 66 case B_RGBA15: 67 case B_RGBA15_BIG: 68 case B_CMAP8: 69 hasAlpha = true; 70 break; 71 72 default: 73 break; 74 } 75 76 BBitmap* trimmedBitmap = NULL; 77 78 // trim the bitmap, if requested and the bitmap actually has alpha 79 status_t error; 80 if ((flags & (B_TRIM_ICON_BITMAP | B_TRIM_ICON_BITMAP_KEEP_ASPECT)) != 0 81 && hasAlpha) { 82 if (bitmap->ColorSpace() == B_RGBA32) { 83 error = _TrimBitmap(bitmap, 84 (flags & B_TRIM_ICON_BITMAP_KEEP_ASPECT) != 0, trimmedBitmap); 85 } else { 86 BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap, true); 87 if (rgb32Bitmap != NULL) { 88 error = _TrimBitmap(rgb32Bitmap, 89 (flags & B_TRIM_ICON_BITMAP_KEEP_ASPECT) != 0, 90 trimmedBitmap); 91 delete rgb32Bitmap; 92 } else 93 error = B_NO_MEMORY; 94 } 95 96 if (error != B_OK) 97 return error; 98 99 bitmap = trimmedBitmap; 100 canUseForMakeBitmaps = true; 101 } 102 103 // create the bitmaps 104 if (canUseForMakeBitmaps) { 105 error = _MakeBitmaps(bitmap, flags); 106 } else { 107 if (BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap, true)) { 108 error = _MakeBitmaps(rgb32Bitmap, flags); 109 delete rgb32Bitmap; 110 } else 111 error = B_NO_MEMORY; 112 } 113 114 delete trimmedBitmap; 115 116 return error; 117 } 118 119 120 bool 121 BIcon::SetBitmap(BBitmap* bitmap, uint32 which) 122 { 123 BitmapList& list = (which & B_DISABLED_ICON_BITMAP) == 0 124 ? fEnabledBitmaps : fDisabledBitmaps; 125 which &= ~uint32(B_DISABLED_ICON_BITMAP); 126 127 int32 count = list.CountItems(); 128 if ((int32)which < count) { 129 list.ReplaceItem(which, bitmap); 130 return true; 131 } 132 133 while (list.CountItems() < (int32)which) { 134 if (!list.AddItem((BBitmap*)NULL)) 135 return false; 136 } 137 138 return list.AddItem(bitmap); 139 } 140 141 142 BBitmap* 143 BIcon::Bitmap(uint32 which) const 144 { 145 const BitmapList& list = (which & B_DISABLED_ICON_BITMAP) == 0 146 ? fEnabledBitmaps : fDisabledBitmaps; 147 return list.ItemAt(which & ~uint32(B_DISABLED_ICON_BITMAP)); 148 } 149 150 151 BBitmap* 152 BIcon::CreateBitmap(const BRect& bounds, color_space colorSpace, uint32 which) 153 { 154 BBitmap* bitmap = new(std::nothrow) BBitmap(bounds, colorSpace); 155 if (bitmap == NULL || !bitmap->IsValid() || !SetBitmap(bitmap, which)) { 156 delete bitmap; 157 return NULL; 158 } 159 160 return bitmap; 161 } 162 163 164 status_t 165 BIcon::SetExternalBitmap(const BBitmap* bitmap, uint32 which, uint32 flags) 166 { 167 BBitmap* ourBitmap = NULL; 168 if (bitmap != NULL) { 169 if (!bitmap->IsValid()) 170 return B_BAD_VALUE; 171 172 if ((flags & B_KEEP_ICON_BITMAP) != 0) { 173 ourBitmap = const_cast<BBitmap*>(bitmap); 174 } else { 175 ourBitmap = _ConvertToRGB32(bitmap); 176 if (ourBitmap == NULL) 177 return B_NO_MEMORY; 178 } 179 } 180 181 if (!SetBitmap(ourBitmap, which)) { 182 if (ourBitmap != bitmap) 183 delete ourBitmap; 184 return B_NO_MEMORY; 185 } 186 187 return B_OK; 188 } 189 190 191 BBitmap* 192 BIcon::CopyBitmap(const BBitmap& bitmapToClone, uint32 which) 193 { 194 BBitmap* bitmap = new(std::nothrow) BBitmap(bitmapToClone); 195 if (bitmap == NULL || !bitmap->IsValid() || !SetBitmap(bitmap, which)) { 196 delete bitmap; 197 return NULL; 198 } 199 200 return bitmap; 201 } 202 203 204 void 205 BIcon::DeleteBitmaps() 206 { 207 fEnabledBitmaps.MakeEmpty(true); 208 fDisabledBitmaps.MakeEmpty(true); 209 } 210 211 212 /*static*/ status_t 213 BIcon::UpdateIcon(const BBitmap* bitmap, uint32 flags, BIcon*& _icon) 214 { 215 if (bitmap == NULL) { 216 delete _icon; 217 _icon = NULL; 218 return B_OK; 219 } 220 221 BIcon* icon = new(std::nothrow) BIcon; 222 if (icon == NULL) 223 return B_NO_MEMORY; 224 225 status_t error = icon->SetTo(bitmap, flags); 226 if (error != B_OK) { 227 delete icon; 228 return error; 229 } 230 231 _icon = icon; 232 return B_OK; 233 } 234 235 236 /*static*/ status_t 237 BIcon::SetIconBitmap(const BBitmap* bitmap, uint32 which, uint32 flags, 238 BIcon*& _icon) 239 { 240 bool newIcon = false; 241 if (_icon == NULL) { 242 if (bitmap == NULL) 243 return B_OK; 244 245 _icon = new(std::nothrow) BIcon; 246 if (_icon == NULL) 247 return B_NO_MEMORY; 248 newIcon = true; 249 } 250 251 status_t error = _icon->SetExternalBitmap(bitmap, which, flags); 252 if (error != B_OK) { 253 if (newIcon) { 254 delete _icon; 255 _icon = NULL; 256 } 257 return error; 258 } 259 260 return B_OK; 261 } 262 263 264 /*static*/ BBitmap* 265 BIcon::_ConvertToRGB32(const BBitmap* bitmap, bool noAppServerLink) 266 { 267 BBitmap* rgb32Bitmap = new(std::nothrow) BBitmap(bitmap->Bounds(), 268 noAppServerLink ? B_BITMAP_NO_SERVER_LINK : 0, B_RGBA32); 269 if (rgb32Bitmap == NULL) 270 return NULL; 271 272 if (!rgb32Bitmap->IsValid() || rgb32Bitmap->ImportBits(bitmap)!= B_OK) { 273 delete rgb32Bitmap; 274 return NULL; 275 } 276 277 return rgb32Bitmap; 278 } 279 280 281 /*static*/ status_t 282 BIcon::_TrimBitmap(const BBitmap* bitmap, bool keepAspect, 283 BBitmap*& _trimmedBitmap) 284 { 285 if (bitmap == NULL || !bitmap->IsValid() 286 || bitmap->ColorSpace() != B_RGBA32) { 287 return B_BAD_VALUE; 288 } 289 290 uint8* bits = (uint8*)bitmap->Bits(); 291 uint32 bpr = bitmap->BytesPerRow(); 292 uint32 width = bitmap->Bounds().IntegerWidth() + 1; 293 uint32 height = bitmap->Bounds().IntegerHeight() + 1; 294 BRect trimmed(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN); 295 296 for (uint32 y = 0; y < height; y++) { 297 uint8* b = bits + 3; 298 bool rowHasAlpha = false; 299 for (uint32 x = 0; x < width; x++) { 300 if (*b) { 301 rowHasAlpha = true; 302 if (x < trimmed.left) 303 trimmed.left = x; 304 if (x > trimmed.right) 305 trimmed.right = x; 306 } 307 b += 4; 308 } 309 if (rowHasAlpha) { 310 if (y < trimmed.top) 311 trimmed.top = y; 312 if (y > trimmed.bottom) 313 trimmed.bottom = y; 314 } 315 bits += bpr; 316 } 317 318 if (!trimmed.IsValid()) 319 return B_BAD_VALUE; 320 321 if (keepAspect) { 322 float minInset = trimmed.left; 323 minInset = min_c(minInset, trimmed.top); 324 minInset = min_c(minInset, bitmap->Bounds().right - trimmed.right); 325 minInset = min_c(minInset, bitmap->Bounds().bottom - trimmed.bottom); 326 trimmed = bitmap->Bounds().InsetByCopy(minInset, minInset); 327 } 328 trimmed = trimmed & bitmap->Bounds(); 329 330 BBitmap* trimmedBitmap = new(std::nothrow) BBitmap( 331 trimmed.OffsetToCopy(B_ORIGIN), B_BITMAP_NO_SERVER_LINK, B_RGBA32); 332 if (trimmedBitmap == NULL) 333 return B_NO_MEMORY; 334 335 bits = (uint8*)bitmap->Bits(); 336 bits += 4 * (int32)trimmed.left + bpr * (int32)trimmed.top; 337 uint8* dst = (uint8*)trimmedBitmap->Bits(); 338 uint32 trimmedWidth = trimmedBitmap->Bounds().IntegerWidth() + 1; 339 uint32 trimmedHeight = trimmedBitmap->Bounds().IntegerHeight() + 1; 340 uint32 trimmedBPR = trimmedBitmap->BytesPerRow(); 341 for (uint32 y = 0; y < trimmedHeight; y++) { 342 memcpy(dst, bits, trimmedWidth * 4); 343 dst += trimmedBPR; 344 bits += bpr; 345 } 346 347 _trimmedBitmap = trimmedBitmap; 348 return B_OK; 349 } 350 351 352 status_t 353 BIcon::_MakeBitmaps(const BBitmap* bitmap, uint32 flags) 354 { 355 // make our own versions of the bitmap 356 BRect b(bitmap->Bounds()); 357 358 color_space format = bitmap->ColorSpace(); 359 BBitmap* normalBitmap = CreateBitmap(b, format, B_INACTIVE_ICON_BITMAP); 360 if (normalBitmap == NULL) 361 return B_NO_MEMORY; 362 363 BBitmap* disabledBitmap = NULL; 364 if ((flags & B_CREATE_DISABLED_ICON_BITMAPS) != 0) { 365 disabledBitmap = CreateBitmap(b, format, 366 B_INACTIVE_ICON_BITMAP | B_DISABLED_ICON_BITMAP); 367 if (disabledBitmap == NULL) 368 return B_NO_MEMORY; 369 } 370 371 BBitmap* clickedBitmap = NULL; 372 if ((flags & (B_CREATE_ACTIVE_ICON_BITMAP 373 | B_CREATE_PARTIALLY_ACTIVE_ICON_BITMAP)) != 0) { 374 clickedBitmap = CreateBitmap(b, format, B_ACTIVE_ICON_BITMAP); 375 if (clickedBitmap == NULL) 376 return B_NO_MEMORY; 377 } 378 379 BBitmap* disabledClickedBitmap = NULL; 380 if (disabledBitmap != NULL && clickedBitmap != NULL) { 381 disabledClickedBitmap = CreateBitmap(b, format, 382 B_ACTIVE_ICON_BITMAP | B_DISABLED_ICON_BITMAP); 383 if (disabledClickedBitmap == NULL) 384 return B_NO_MEMORY; 385 } 386 387 // copy bitmaps from file bitmap 388 uint8* nBits = normalBitmap != NULL ? (uint8*)normalBitmap->Bits() : NULL; 389 uint8* dBits = disabledBitmap != NULL 390 ? (uint8*)disabledBitmap->Bits() : NULL; 391 uint8* cBits = clickedBitmap != NULL ? (uint8*)clickedBitmap->Bits() : NULL; 392 uint8* dcBits = disabledClickedBitmap != NULL 393 ? (uint8*)disabledClickedBitmap->Bits() : NULL; 394 uint8* fBits = (uint8*)bitmap->Bits(); 395 int32 nbpr = normalBitmap->BytesPerRow(); 396 int32 fbpr = bitmap->BytesPerRow(); 397 int32 pixels = b.IntegerWidth() + 1; 398 int32 lines = b.IntegerHeight() + 1; 399 if (format == B_RGB32) { 400 // nontransparent version 401 402 // iterate over color components 403 for (int32 y = 0; y < lines; y++) { 404 for (int32 x = 0; x < pixels; x++) { 405 int32 nOffset = 4 * x; 406 int32 fOffset = 4 * x; 407 nBits[nOffset + 0] = fBits[fOffset + 0]; 408 nBits[nOffset + 1] = fBits[fOffset + 1]; 409 nBits[nOffset + 2] = fBits[fOffset + 2]; 410 nBits[nOffset + 3] = 255; 411 412 // clicked bits are darker (lame method...) 413 if (cBits != NULL) { 414 cBits[nOffset + 0] = uint8((float)nBits[nOffset + 0] * 0.8); 415 cBits[nOffset + 1] = uint8((float)nBits[nOffset + 1] * 0.8); 416 cBits[nOffset + 2] = uint8((float)nBits[nOffset + 2] * 0.8); 417 cBits[nOffset + 3] = 255; 418 } 419 420 // disabled bits have less contrast (lame method...) 421 if (dBits != NULL) { 422 uint8 grey = 216; 423 float dist = (nBits[nOffset + 0] - grey) * 0.4; 424 dBits[nOffset + 0] = (uint8)(grey + dist); 425 dist = (nBits[nOffset + 1] - grey) * 0.4; 426 dBits[nOffset + 1] = (uint8)(grey + dist); 427 dist = (nBits[nOffset + 2] - grey) * 0.4; 428 dBits[nOffset + 2] = (uint8)(grey + dist); 429 dBits[nOffset + 3] = 255; 430 } 431 432 // disabled bits have less contrast (lame method...) 433 if (dcBits != NULL) { 434 uint8 grey = 188; 435 float dist = (nBits[nOffset + 0] - grey) * 0.4; 436 dcBits[nOffset + 0] = (uint8)(grey + dist); 437 dist = (nBits[nOffset + 1] - grey) * 0.4; 438 dcBits[nOffset + 1] = (uint8)(grey + dist); 439 dist = (nBits[nOffset + 2] - grey) * 0.4; 440 dcBits[nOffset + 2] = (uint8)(grey + dist); 441 dcBits[nOffset + 3] = 255; 442 } 443 } 444 fBits += fbpr; 445 nBits += nbpr; 446 if (cBits != NULL) 447 cBits += nbpr; 448 if (dBits != NULL) 449 dBits += nbpr; 450 if (dcBits != NULL) 451 dcBits += nbpr; 452 } 453 } else if (format == B_RGB32_BIG) { 454 // nontransparent version 455 456 // iterate over color components 457 for (int32 y = 0; y < lines; y++) { 458 for (int32 x = 0; x < pixels; x++) { 459 int32 nOffset = 4 * x; 460 int32 fOffset = 4 * x; 461 nBits[nOffset + 3] = fBits[fOffset + 3]; 462 nBits[nOffset + 2] = fBits[fOffset + 2]; 463 nBits[nOffset + 1] = fBits[fOffset + 1]; 464 nBits[nOffset + 0] = 255; 465 466 // clicked bits are darker (lame method...) 467 if (cBits != NULL) { 468 cBits[nOffset + 3] = uint8((float)nBits[nOffset + 3] * 0.8); 469 cBits[nOffset + 2] = uint8((float)nBits[nOffset + 2] * 0.8); 470 cBits[nOffset + 1] = uint8((float)nBits[nOffset + 1] * 0.8); 471 cBits[nOffset + 0] = 255; 472 } 473 474 // disabled bits have less contrast (lame method...) 475 if (dBits != NULL) { 476 uint8 grey = 216; 477 float dist = (nBits[nOffset + 3] - grey) * 0.4; 478 dBits[nOffset + 3] = (uint8)(grey + dist); 479 dist = (nBits[nOffset + 2] - grey) * 0.4; 480 dBits[nOffset + 2] = (uint8)(grey + dist); 481 dist = (nBits[nOffset + 1] - grey) * 0.4; 482 dBits[nOffset + 1] = (uint8)(grey + dist); 483 dBits[nOffset + 0] = 255; 484 } 485 486 // disabled bits have less contrast (lame method...) 487 if (dcBits != NULL) { 488 uint8 grey = 188; 489 float dist = (nBits[nOffset + 3] - grey) * 0.4; 490 dcBits[nOffset + 3] = (uint8)(grey + dist); 491 dist = (nBits[nOffset + 2] - grey) * 0.4; 492 dcBits[nOffset + 2] = (uint8)(grey + dist); 493 dist = (nBits[nOffset + 1] - grey) * 0.4; 494 dcBits[nOffset + 1] = (uint8)(grey + dist); 495 dcBits[nOffset + 0] = 255; 496 } 497 } 498 fBits += fbpr; 499 nBits += nbpr; 500 if (cBits != NULL) 501 cBits += nbpr; 502 if (dBits != NULL) 503 dBits += nbpr; 504 if (dcBits != NULL) 505 dcBits += nbpr; 506 } 507 } else if (format == B_RGBA32) { 508 // transparent version 509 510 // iterate over color components 511 for (int32 y = 0; y < lines; y++) { 512 for (int32 x = 0; x < pixels; x++) { 513 int32 nOffset = 4 * x; 514 int32 fOffset = 4 * x; 515 nBits[nOffset + 0] = fBits[fOffset + 0]; 516 nBits[nOffset + 1] = fBits[fOffset + 1]; 517 nBits[nOffset + 2] = fBits[fOffset + 2]; 518 nBits[nOffset + 3] = fBits[fOffset + 3]; 519 520 // clicked bits are darker (lame method...) 521 if (cBits != NULL) { 522 cBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8); 523 cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8); 524 cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8); 525 cBits[nOffset + 3] = fBits[fOffset + 3]; 526 } 527 528 // disabled bits have less opacity 529 if (dBits != NULL) { 530 uint8 grey = ((uint16)nBits[nOffset + 0] * 10 531 + nBits[nOffset + 1] * 60 532 + nBits[nOffset + 2] * 30) / 100; 533 float dist = (nBits[nOffset + 0] - grey) * 0.3; 534 dBits[nOffset + 0] = (uint8)(grey + dist); 535 dist = (nBits[nOffset + 1] - grey) * 0.3; 536 dBits[nOffset + 1] = (uint8)(grey + dist); 537 dist = (nBits[nOffset + 2] - grey) * 0.3; 538 dBits[nOffset + 2] = (uint8)(grey + dist); 539 dBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3); 540 } 541 542 // disabled bits have less contrast (lame method...) 543 if (dcBits != NULL) { 544 dcBits[nOffset + 0] = (uint8)(dBits[nOffset + 0] * 0.8); 545 dcBits[nOffset + 1] = (uint8)(dBits[nOffset + 1] * 0.8); 546 dcBits[nOffset + 2] = (uint8)(dBits[nOffset + 2] * 0.8); 547 dcBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.3); 548 } 549 } 550 fBits += fbpr; 551 nBits += nbpr; 552 if (cBits != NULL) 553 cBits += nbpr; 554 if (dBits != NULL) 555 dBits += nbpr; 556 if (dcBits != NULL) 557 dcBits += nbpr; 558 } 559 } else if (format == B_RGBA32_BIG) { 560 // transparent version 561 562 // iterate over color components 563 for (int32 y = 0; y < lines; y++) { 564 for (int32 x = 0; x < pixels; x++) { 565 int32 nOffset = 4 * x; 566 int32 fOffset = 4 * x; 567 nBits[nOffset + 3] = fBits[fOffset + 3]; 568 nBits[nOffset + 2] = fBits[fOffset + 2]; 569 nBits[nOffset + 1] = fBits[fOffset + 1]; 570 nBits[nOffset + 0] = fBits[fOffset + 0]; 571 572 // clicked bits are darker (lame method...) 573 if (cBits != NULL) { 574 cBits[nOffset + 3] = (uint8)(nBits[nOffset + 3] * 0.8); 575 cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8); 576 cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8); 577 cBits[nOffset + 0] = fBits[fOffset + 0]; 578 } 579 580 // disabled bits have less opacity 581 if (dBits != NULL) { 582 uint8 grey = ((uint16)nBits[nOffset + 3] * 10 583 + nBits[nOffset + 2] * 60 584 + nBits[nOffset + 1] * 30) / 100; 585 float dist = (nBits[nOffset + 3] - grey) * 0.3; 586 dBits[nOffset + 3] = (uint8)(grey + dist); 587 dist = (nBits[nOffset + 2] - grey) * 0.3; 588 dBits[nOffset + 2] = (uint8)(grey + dist); 589 dist = (nBits[nOffset + 1] - grey) * 0.3; 590 dBits[nOffset + 1] = (uint8)(grey + dist); 591 dBits[nOffset + 0] = (uint8)(fBits[fOffset + 0] * 0.3); 592 } 593 594 // disabled bits have less contrast (lame method...) 595 if (dcBits != NULL) { 596 dcBits[nOffset + 3] = (uint8)(dBits[nOffset + 3] * 0.8); 597 dcBits[nOffset + 2] = (uint8)(dBits[nOffset + 2] * 0.8); 598 dcBits[nOffset + 1] = (uint8)(dBits[nOffset + 1] * 0.8); 599 dcBits[nOffset + 0] = (uint8)(fBits[fOffset + 0] * 0.3); 600 } 601 } 602 fBits += fbpr; 603 nBits += nbpr; 604 if (cBits != NULL) 605 cBits += nbpr; 606 if (dBits != NULL) 607 dBits += nbpr; 608 if (dcBits != NULL) 609 dcBits += nbpr; 610 } 611 } else { 612 // unsupported format 613 return B_BAD_VALUE; 614 } 615 616 // make the partially-on bitmaps a copy of the on bitmaps 617 if ((flags & B_CREATE_PARTIALLY_ACTIVE_ICON_BITMAP) != 0) { 618 if (CopyBitmap(clickedBitmap, B_PARTIALLY_ACTIVATE_ICON_BITMAP) == NULL) 619 return B_NO_MEMORY; 620 if ((flags & B_CREATE_DISABLED_ICON_BITMAPS) != 0) { 621 if (CopyBitmap(disabledClickedBitmap, 622 B_PARTIALLY_ACTIVATE_ICON_BITMAP | B_DISABLED_ICON_BITMAP) 623 == NULL) { 624 return B_NO_MEMORY; 625 } 626 } 627 } 628 629 return B_OK; 630 } 631 632 633 } // namespace BPrivate 634