1 /* 2 * Copyright 2006-2023 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus, superstippi@gmx.de 7 * Axel Dörfler, axeld@pinc-software.de 8 * John Scipione, jscipione@gmail.com 9 * Ingo Weinhold, bonefish@cs.tu-berlin.de 10 */ 11 12 13 #include "IconUtils.h" 14 15 #include <new> 16 #include <fs_attr.h> 17 #include <stdio.h> 18 #include <string.h> 19 20 #include <Bitmap.h> 21 #include <FindDirectory.h> 22 #include <Node.h> 23 #include <NodeInfo.h> 24 #include <Path.h> 25 #include <Resources.h> 26 #include <String.h> 27 #include <TypeConstants.h> 28 29 #include "AutoDeleter.h" 30 #include "Icon.h" 31 #include "IconRenderer.h" 32 #include "FlatIconImporter.h" 33 #include "MessageImporter.h" 34 35 36 #define B_MINI_ICON_TYPE 'MICN' 37 #define B_LARGE_ICON_TYPE 'ICON' 38 39 40 _USING_ICON_NAMESPACE; 41 42 43 // #pragma mark - Scaling functions 44 45 46 static void 47 scale_bilinear(uint8* bits, int32 srcWidth, int32 srcHeight, int32 dstWidth, 48 int32 dstHeight, uint32 bpr) 49 { 50 // first pass: scale bottom to top 51 52 uint8* dst = bits + (dstHeight - 1) * bpr; 53 // offset to bottom left pixel in target size 54 for (int32 x = 0; x < srcWidth; x++) { 55 uint8* d = dst; 56 for (int32 y = dstHeight - 1; y >= 0; y--) { 57 int32 lineF = (y << 8) * (srcHeight - 1) / (dstHeight - 1); 58 int32 lineI = lineF >> 8; 59 uint8 weight = (uint8)(lineF & 0xff); 60 uint8* s1 = bits + lineI * bpr + 4 * x; 61 if (weight == 0) { 62 d[0] = s1[0]; 63 d[1] = s1[1]; 64 d[2] = s1[2]; 65 d[3] = s1[3]; 66 } else { 67 uint8* s2 = s1 + bpr; 68 69 d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8; 70 d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8; 71 d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8; 72 d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8; 73 } 74 75 d -= bpr; 76 } 77 dst += 4; 78 } 79 80 // second pass: scale right to left 81 82 dst = bits + (dstWidth - 1) * 4; 83 // offset to top left pixel in target size 84 for (int32 y = 0; y < dstWidth; y++) { 85 uint8* d = dst; 86 for (int32 x = dstWidth - 1; x >= 0; x--) { 87 int32 columnF = (x << 8) * (srcWidth - 1) / (dstWidth - 1); 88 int32 columnI = columnF >> 8; 89 uint8 weight = (uint8)(columnF & 0xff); 90 uint8* s1 = bits + y * bpr + 4 * columnI; 91 if (weight == 0) { 92 d[0] = s1[0]; 93 d[1] = s1[1]; 94 d[2] = s1[2]; 95 d[3] = s1[3]; 96 } else { 97 uint8* s2 = s1 + 4; 98 99 d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8; 100 d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8; 101 d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8; 102 d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8; 103 } 104 105 d -= 4; 106 } 107 dst += bpr; 108 } 109 } 110 111 112 static void 113 scale_down(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, 114 int32 dstWidth, int32 dstHeight) 115 { 116 int32 l; 117 int32 c; 118 float t; 119 float u; 120 float tmp; 121 float d1, d2, d3, d4; 122 // coefficients 123 rgb_color p1, p2, p3, p4; 124 // nearby pixels 125 rgb_color out; 126 // color components 127 128 for (int32 i = 0; i < dstHeight; i++) { 129 for (int32 j = 0; j < dstWidth; j++) { 130 tmp = (float)(i) / (float)(dstHeight - 1) * (srcHeight - 1); 131 l = (int32)floorf(tmp); 132 if (l < 0) 133 l = 0; 134 else if (l >= srcHeight - 1) 135 l = srcHeight - 2; 136 u = tmp - l; 137 138 tmp = (float)(j) / (float)(dstWidth - 1) * (srcWidth - 1); 139 c = (int32)floorf(tmp); 140 if (c < 0) 141 c = 0; 142 else if (c >= srcWidth - 1) 143 c = srcWidth - 2; 144 t = tmp - c; 145 146 // coefficients 147 d1 = (1 - t) * (1 - u); 148 d2 = t * (1 - u); 149 d3 = t * u; 150 d4 = (1 - t) * u; 151 152 // nearby pixels 153 p1 = *((rgb_color*)srcBits + (l * srcWidth) + c); 154 p2 = *((rgb_color*)srcBits + (l * srcWidth) + c + 1); 155 p3 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c + 1); 156 p4 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c); 157 158 // color components 159 out.blue = (uint8)(p1.blue * d1 + p2.blue * d2 + p3.blue * d3 160 + p4.blue * d4); 161 out.green = (uint8)(p1.green * d1 + p2.green * d2 + p3.green * d3 162 + p4.green * d4); 163 out.red = (uint8)(p1.red * d1 + p2.red * d2 + p3.red * d3 164 + p4.red * d4); 165 out.alpha = (uint8)(p1.alpha * d1 + p2.alpha * d2 + p3.alpha * d3 166 + p4.alpha * d4); 167 168 // destination RGBA pixel 169 *((rgb_color*)dstBits + (i * dstWidth) + j) = out; 170 } 171 } 172 } 173 174 175 static void 176 scale2x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, 177 int32 srcBPR, int32 dstBPR) 178 { 179 /* 180 * This implements the AdvanceMAME Scale2x algorithm found on: 181 * http://scale2x.sourceforge.net/ 182 * 183 * It is an incredibly simple and powerful image doubling routine that does 184 * an astonishing job of doubling game graphic data while interpolating out 185 * the jaggies. 186 * 187 * Derived from the (public domain) SDL version of the library by Pete 188 * Shinners. 189 */ 190 191 // Assume that both src and dst are 4 BPP (B_RGBA32) 192 for (int32 y = 0; y < srcHeight; ++y) { 193 for (int32 x = 0; x < srcWidth; ++x) { 194 uint32 b = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR) 195 + (4 * x)); 196 uint32 d = *(uint32*)(srcBits + (y * srcBPR) 197 + (4 * MAX(0, x - 1))); 198 uint32 e = *(uint32*)(srcBits + (y * srcBPR) 199 + (4 * x)); 200 uint32 f = *(uint32*)(srcBits + (y * srcBPR) 201 + (4 * MIN(srcWidth - 1, x + 1))); 202 uint32 h = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1) 203 * srcBPR) + (4 * x)); 204 205 uint32 e0 = d == b && b != f && d != h ? d : e; 206 uint32 e1 = b == f && b != d && f != h ? f : e; 207 uint32 e2 = d == h && d != b && h != f ? d : e; 208 uint32 e3 = h == f && d != h && b != f ? f : e; 209 210 *(uint32*)(dstBits + y * 2 * dstBPR + x * 2 * 4) = e0; 211 *(uint32*)(dstBits + y * 2 * dstBPR + (x * 2 + 1) * 4) = e1; 212 *(uint32*)(dstBits + (y * 2 + 1) * dstBPR + x * 2 * 4) = e2; 213 *(uint32*)(dstBits + (y * 2 + 1) * dstBPR + (x * 2 + 1) * 4) = e3; 214 } 215 } 216 } 217 218 219 static void 220 scale3x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, 221 int32 srcBPR, int32 dstBPR) 222 { 223 /* 224 * This implements the AdvanceMAME Scale3x algorithm found on: 225 * http://scale2x.sourceforge.net/ 226 * 227 * It is an incredibly simple and powerful image tripling routine that does 228 * an astonishing job of tripling game graphic data while interpolating out 229 * the jaggies. 230 * 231 * Derived from the (public domain) SDL version of the library by Pete 232 * Shinners. 233 */ 234 235 // Assume that both src and dst are 4 BPP (B_RGBA32) 236 for (int32 y = 0; y < srcHeight; ++y) { 237 for (int32 x = 0; x < srcWidth; ++x) { 238 uint32 a = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR) 239 + (4 * MAX(0, x - 1))); 240 uint32 b = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR) 241 + (4 * x)); 242 uint32 c = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR) 243 + (4 * MIN(srcWidth - 1, x + 1))); 244 uint32 d = *(uint32*)(srcBits + (y * srcBPR) 245 + (4 * MAX(0, x - 1))); 246 uint32 e = *(uint32*)(srcBits + (y * srcBPR) 247 + (4 * x)); 248 uint32 f = *(uint32*)(srcBits + (y * srcBPR) 249 + (4 * MIN(srcWidth - 1,x + 1))); 250 uint32 g = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1) 251 * srcBPR) + (4 * MAX(0, x - 1))); 252 uint32 h = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1) 253 * srcBPR) + (4 * x)); 254 uint32 i = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1) 255 * srcBPR) + (4 * MIN(srcWidth - 1, x + 1))); 256 257 uint32 e0 = d == b && b != f && d != h ? d : e; 258 uint32 e1 = (d == b && b != f && d != h && e != c) 259 || (b == f && b != d && f != h && e != a) ? b : e; 260 uint32 e2 = b == f && b != d && f != h ? f : e; 261 uint32 e3 = (d == b && b != f && d != h && e != g) 262 || (d == b && b != f && d != h && e != a) ? d : e; 263 uint32 e4 = e; 264 uint32 e5 = (b == f && b != d && f != h && e != i) 265 || (h == f && d != h && b != f && e != c) ? f : e; 266 uint32 e6 = d == h && d != b && h != f ? d : e; 267 uint32 e7 = (d == h && d != b && h != f && e != i) 268 || (h == f && d != h && b != f && e != g) ? h : e; 269 uint32 e8 = h == f && d != h && b != f ? f : e; 270 271 *(uint32*)(dstBits + y * 3 * dstBPR + x * 3 * 4) = e0; 272 *(uint32*)(dstBits + y * 3 * dstBPR + (x * 3 + 1) * 4) = e1; 273 *(uint32*)(dstBits + y * 3 * dstBPR + (x * 3 + 2) * 4) = e2; 274 *(uint32*)(dstBits + (y * 3 + 1) * dstBPR + x * 3 * 4) = e3; 275 *(uint32*)(dstBits + (y * 3 + 1) * dstBPR + (x * 3 + 1) * 4) = e4; 276 *(uint32*)(dstBits + (y * 3 + 1) * dstBPR + (x * 3 + 2) * 4) = e5; 277 *(uint32*)(dstBits + (y * 3 + 2) * dstBPR + x * 3 * 4) = e6; 278 *(uint32*)(dstBits + (y * 3 + 2) * dstBPR + (x * 3 + 1) * 4) = e7; 279 *(uint32*)(dstBits + (y * 3 + 2) * dstBPR + (x * 3 + 2) * 4) = e8; 280 } 281 } 282 } 283 284 285 static void 286 scale4x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, 287 int32 srcBPR, int32 dstBPR) 288 { 289 // scale4x is just scale2x twice 290 BRect rect = BRect(0, 0, srcWidth * 2 - 1, srcHeight * 2 - 1); 291 BBitmap* tmp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, B_RGBA32); 292 uint8* tmpBits = (uint8*)tmp->Bits(); 293 int32 tmpBPR = tmp->BytesPerRow(); 294 295 scale2x(srcBits, tmpBits, srcWidth, srcHeight, srcBPR, tmpBPR); 296 scale2x(tmpBits, dstBits, srcWidth * 2, srcHeight * 2, tmpBPR, dstBPR); 297 298 delete tmp; 299 } 300 301 302 // #pragma mark - GetIcon() 303 304 305 status_t 306 BIconUtils::GetIcon(BNode* node, const char* vectorIconAttrName, 307 const char* smallIconAttrName, const char* largeIconAttrName, 308 icon_size which, BBitmap* icon) 309 { 310 if (node == NULL || icon == NULL) 311 return B_BAD_VALUE; 312 313 status_t result = node->InitCheck(); 314 if (result != B_OK) 315 return result; 316 317 result = icon->InitCheck(); 318 if (result != B_OK) 319 return result; 320 321 switch (icon->ColorSpace()) { 322 case B_RGBA32: 323 case B_RGB32: 324 // prefer vector icon 325 result = GetVectorIcon(node, vectorIconAttrName, icon); 326 if (result != B_OK) { 327 // try to fallback to B_CMAP8 icons 328 // (converting to B_RGBA32 is handled) 329 330 // override size 331 if (icon->Bounds().IntegerWidth() + 1 >= B_LARGE_ICON) 332 which = B_LARGE_ICON; 333 else 334 which = B_MINI_ICON; 335 336 result = GetCMAP8Icon(node, smallIconAttrName, 337 largeIconAttrName, which, icon); 338 } 339 break; 340 341 case B_CMAP8: 342 // prefer old B_CMAP8 icons 343 result = GetCMAP8Icon(node, smallIconAttrName, largeIconAttrName, 344 which, icon); 345 if (result != B_OK) { 346 // try to fallback to vector icon 347 BBitmap temp(icon->Bounds(), B_BITMAP_NO_SERVER_LINK, 348 B_RGBA32); 349 result = temp.InitCheck(); 350 if (result != B_OK) 351 break; 352 353 result = GetVectorIcon(node, vectorIconAttrName, &temp); 354 if (result != B_OK) 355 break; 356 357 uint32 width = temp.Bounds().IntegerWidth() + 1; 358 uint32 height = temp.Bounds().IntegerHeight() + 1; 359 uint32 bytesPerRow = temp.BytesPerRow(); 360 result = ConvertToCMAP8((uint8*)temp.Bits(), width, height, 361 bytesPerRow, icon); 362 } 363 break; 364 365 default: 366 printf("BIconUtils::GetIcon() - unsupported colorspace\n"); 367 result = B_ERROR; 368 break; 369 } 370 371 return result; 372 } 373 374 375 // #pragma mark - GetVectorIcon() 376 377 378 status_t 379 BIconUtils::GetVectorIcon(BNode* node, const char* attrName, BBitmap* icon) 380 { 381 if (node == NULL || attrName == NULL || *attrName == '\0' || icon == NULL) 382 return B_BAD_VALUE; 383 384 status_t result = node->InitCheck(); 385 if (result != B_OK) 386 return result; 387 388 result = icon->InitCheck(); 389 if (result != B_OK) 390 return result; 391 392 #if TIME_VECTOR_ICONS 393 bigtime_t startTime = system_time(); 394 #endif 395 396 // get the attribute info and check type and size of the attr contents 397 attr_info attrInfo; 398 result = node->GetAttrInfo(attrName, &attrInfo); 399 if (result != B_OK) 400 return result; 401 402 type_code attrType = B_VECTOR_ICON_TYPE; 403 404 if (attrInfo.type != attrType) 405 return B_BAD_TYPE; 406 407 // chicken out on unrealisticly large attributes 408 if (attrInfo.size > 512 * 1024) 409 return B_BAD_VALUE; 410 411 uint8* buffer = new(std::nothrow) uint8[attrInfo.size]; 412 if (buffer == NULL) 413 return B_NO_MEMORY; 414 415 ArrayDeleter<uint8> deleter(buffer); 416 417 ssize_t bytesRead = node->ReadAttr(attrName, attrType, 0, buffer, 418 attrInfo.size); 419 if (bytesRead != attrInfo.size) 420 return B_ERROR; 421 422 #if TIME_VECTOR_ICONS 423 bigtime_t importTime = system_time(); 424 #endif 425 426 result = GetVectorIcon(buffer, attrInfo.size, icon); 427 if (result != B_OK) 428 return result; 429 430 #if TIME_VECTOR_ICONS 431 bigtime_t finishTime = system_time(); 432 printf("read: %lld, import: %lld\n", importTime - startTime, 433 finishTime - importTime); 434 #endif 435 436 return B_OK; 437 } 438 439 440 status_t 441 BIconUtils::GetVectorIcon(const uint8* buffer, size_t size, BBitmap* icon) 442 { 443 if (buffer == NULL || size <= 0 || icon == NULL) 444 return B_BAD_VALUE; 445 446 status_t result = icon->InitCheck(); 447 if (result != B_OK) 448 return result; 449 450 BBitmap* temp = icon; 451 ObjectDeleter<BBitmap> deleter; 452 453 if (icon->ColorSpace() != B_RGBA32 && icon->ColorSpace() != B_RGB32) { 454 temp = new(std::nothrow) BBitmap(icon->Bounds(), 455 B_BITMAP_NO_SERVER_LINK, B_RGBA32); 456 deleter.SetTo(temp); 457 if (temp == NULL || temp->InitCheck() != B_OK) 458 return B_NO_MEMORY; 459 } 460 461 Icon vector; 462 result = vector.InitCheck(); 463 if (result != B_OK) 464 return result; 465 466 FlatIconImporter importer; 467 result = importer.Import(&vector, const_cast<uint8*>(buffer), size); 468 if (result != B_OK) { 469 // try the message based format used by Icon-O-Matic 470 MessageImporter messageImporter; 471 BMemoryIO memoryIO(const_cast<uint8*>(buffer), size); 472 result = messageImporter.Import(&vector, &memoryIO); 473 if (result != B_OK) 474 return result; 475 } 476 477 IconRenderer renderer(temp); 478 renderer.SetIcon(&vector); 479 renderer.SetScale((temp->Bounds().Width() + 1.0) / 64.0); 480 renderer.Render(); 481 482 if (temp != icon) { 483 uint8* src = (uint8*)temp->Bits(); 484 uint32 width = temp->Bounds().IntegerWidth() + 1; 485 uint32 height = temp->Bounds().IntegerHeight() + 1; 486 uint32 srcBPR = temp->BytesPerRow(); 487 result = ConvertToCMAP8(src, width, height, srcBPR, icon); 488 } 489 490 // TODO: would be nice to get rid of this 491 // (B_RGBA32_PREMULTIPLIED or better yet, new blending_mode) 492 // NOTE: probably not necessary only because 493 // transparent colors are "black" in all existing icons 494 // lighter transparent colors should be too dark if 495 // app_server uses correct blending 496 //renderer.Demultiply(); 497 498 return result; 499 } 500 501 502 // #pragma mark - GetCMAP8Icon() 503 504 505 status_t 506 BIconUtils::GetCMAP8Icon(BNode* node, const char* smallIconAttrName, 507 const char* largeIconAttrName, icon_size which, BBitmap* icon) 508 { 509 // NOTE: this might be changed if other icon 510 // sizes are supported in B_CMAP8 attributes, 511 // but this is currently not the case, so we 512 // relax the requirement to pass an icon 513 // of just the right size 514 if (which < B_LARGE_ICON) 515 which = B_MINI_ICON; 516 else 517 which = B_LARGE_ICON; 518 519 // check parameters and initialization 520 if (node == NULL || icon == NULL 521 || (which == B_MINI_ICON 522 && (smallIconAttrName == NULL || *smallIconAttrName == '\0')) 523 || (which == B_LARGE_ICON 524 && (largeIconAttrName == NULL || *largeIconAttrName == '\0'))) { 525 return B_BAD_VALUE; 526 } 527 528 status_t result; 529 result = node->InitCheck(); 530 if (result != B_OK) 531 return result; 532 533 result = icon->InitCheck(); 534 if (result != B_OK) 535 return result; 536 537 // set some icon size related variables 538 const char* attribute = NULL; 539 BRect bounds; 540 uint32 attrType = 0; 541 off_t attrSize = 0; 542 switch (which) { 543 case B_MINI_ICON: 544 attribute = smallIconAttrName; 545 bounds.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1); 546 attrType = B_MINI_ICON_TYPE; 547 attrSize = B_MINI_ICON * B_MINI_ICON; 548 break; 549 550 case B_LARGE_ICON: 551 attribute = largeIconAttrName; 552 bounds.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1); 553 attrType = B_LARGE_ICON_TYPE; 554 attrSize = B_LARGE_ICON * B_LARGE_ICON; 555 break; 556 557 default: 558 // can not happen, see above 559 result = B_BAD_VALUE; 560 break; 561 } 562 563 // get the attribute info and check type and size of the attr contents 564 attr_info attrInfo; 565 if (result == B_OK) 566 result = node->GetAttrInfo(attribute, &attrInfo); 567 568 if (result == B_OK && attrInfo.type != attrType) 569 result = B_BAD_TYPE; 570 571 if (result == B_OK && attrInfo.size != attrSize) 572 result = B_BAD_DATA; 573 574 // check parameters 575 // currently, scaling B_CMAP8 icons is not supported 576 if (icon->ColorSpace() == B_CMAP8 && icon->Bounds() != bounds) 577 return B_BAD_VALUE; 578 579 // read the attribute 580 if (result == B_OK) { 581 bool useBuffer = (icon->ColorSpace() != B_CMAP8 582 || icon->Bounds() != bounds); 583 uint8* buffer = NULL; 584 ssize_t bytesRead; 585 if (useBuffer) { 586 // other color space or bitmap size than stored in attribute 587 buffer = new(std::nothrow) uint8[attrSize]; 588 if (buffer == NULL) 589 bytesRead = result = B_NO_MEMORY; 590 else { 591 bytesRead = node->ReadAttr(attribute, attrType, 0, buffer, 592 attrSize); 593 } 594 } else { 595 bytesRead = node->ReadAttr(attribute, attrType, 0, icon->Bits(), 596 attrSize); 597 } 598 599 if (result == B_OK) { 600 if (bytesRead < 0) 601 result = (status_t)bytesRead; 602 else if (bytesRead != (ssize_t)attrSize) 603 result = B_ERROR; 604 } 605 606 if (useBuffer) { 607 // other color space than stored in attribute 608 if (result == B_OK) { 609 result = ConvertFromCMAP8(buffer, (uint32)which, (uint32)which, 610 (uint32)which, icon); 611 } 612 delete[] buffer; 613 } 614 } 615 616 return result; 617 } 618 619 620 status_t 621 BIconUtils::GetSystemIcon(const char* iconName, BBitmap* icon) 622 { 623 static BResources resources; 624 static bool resourcesAreLoaded = false; 625 626 if (!resourcesAreLoaded) { 627 BPath path; 628 status_t status = find_directory(B_SYSTEM_LIB_DIRECTORY, &path); 629 if (status != B_OK) { 630 return status; 631 } 632 633 path.Append("libbe.so"); 634 BFile file; 635 status = file.SetTo(path.Path(), B_READ_ONLY); 636 if (status != B_OK) { 637 return status; 638 } 639 640 status = resources.SetTo(&file); 641 if (status != B_OK) { 642 return status; 643 } 644 645 resourcesAreLoaded = true; 646 } 647 648 // Check the icon bitmap 649 if (icon == NULL || icon->InitCheck() < B_OK) { 650 return B_BAD_DATA; 651 } 652 653 // Load the raw icon data 654 size_t size = 0; 655 const uint8* rawIcon; 656 657 // Try to load vector icon 658 rawIcon = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE, 659 iconName, &size); 660 if (rawIcon != NULL 661 && BIconUtils::GetVectorIcon(rawIcon, size, icon) == B_OK) { 662 return B_OK; 663 } 664 665 // Fall back to bitmap icon 666 rawIcon = (const uint8*)resources.LoadResource(B_LARGE_ICON_TYPE, 667 iconName, &size); 668 if (rawIcon == NULL) { 669 delete icon; 670 return B_ENTRY_NOT_FOUND; 671 } 672 673 // Handle color space conversion 674 if (icon->ColorSpace() != B_CMAP8) { 675 BIconUtils::ConvertFromCMAP8(rawIcon, B_LARGE_ICON, B_LARGE_ICON, 676 B_LARGE_ICON, icon); 677 } 678 679 return B_OK; 680 } 681 682 683 // #pragma mark - ConvertFromCMAP8() and ConvertToCMAP8() 684 685 686 status_t 687 BIconUtils::ConvertFromCMAP8(BBitmap* source, BBitmap* destination) 688 { 689 if (source == NULL || source->ColorSpace() != B_CMAP8) 690 return B_BAD_VALUE; 691 692 status_t result = source->InitCheck(); 693 if (result != B_OK) 694 return result; 695 696 result = destination->InitCheck(); 697 if (result != B_OK) 698 return result; 699 700 uint8* src = (uint8*)source->Bits(); 701 uint32 srcBPR = source->BytesPerRow(); 702 uint32 width = source->Bounds().IntegerWidth() + 1; 703 uint32 height = source->Bounds().IntegerHeight() + 1; 704 705 return ConvertFromCMAP8(src, width, height, srcBPR, destination); 706 } 707 708 709 status_t 710 BIconUtils::ConvertToCMAP8(BBitmap* source, BBitmap* destination) 711 { 712 if (source == NULL || source->ColorSpace() != B_RGBA32 713 || destination->ColorSpace() != B_CMAP8) { 714 return B_BAD_VALUE; 715 } 716 717 status_t result = source->InitCheck(); 718 if (result != B_OK) 719 return result; 720 721 result = destination->InitCheck(); 722 if (result != B_OK) 723 return result; 724 725 uint8* src = (uint8*)source->Bits(); 726 uint32 srcBPR = source->BytesPerRow(); 727 uint32 width = source->Bounds().IntegerWidth() + 1; 728 uint32 height = source->Bounds().IntegerHeight() + 1; 729 730 return ConvertToCMAP8(src, width, height, srcBPR, destination); 731 } 732 733 734 status_t 735 BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, 736 uint32 srcBPR, BBitmap* icon) 737 { 738 if (src == NULL || icon == NULL || srcBPR == 0) 739 return B_BAD_VALUE; 740 741 status_t result = icon->InitCheck(); 742 if (result != B_OK) 743 return result; 744 745 if (icon->ColorSpace() != B_RGBA32 && icon->ColorSpace() != B_RGB32) { 746 // TODO: support other color spaces 747 return B_BAD_VALUE; 748 } 749 750 uint32 dstWidth = icon->Bounds().IntegerWidth() + 1; 751 uint32 dstHeight = icon->Bounds().IntegerHeight() + 1; 752 753 uint8* dst = (uint8*)icon->Bits(); 754 uint32 dstBPR = icon->BytesPerRow(); 755 756 // check for downscaling or integer multiple scaling 757 if (dstWidth < width || dstHeight < height 758 || (dstWidth == 2 * width && dstHeight == 2 * height) 759 || (dstWidth == 3 * width && dstHeight == 3 * height) 760 || (dstWidth == 4 * width && dstHeight == 4 * height)) { 761 BRect rect = BRect(0, 0, width - 1, height - 1); 762 BBitmap* converted = new(std::nothrow) BBitmap(rect, 763 B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); 764 if (converted == NULL) 765 return B_NO_MEMORY; 766 767 converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8); 768 uint8* convertedBits = (uint8*)converted->Bits(); 769 int32 convertedBPR = converted->BytesPerRow(); 770 771 if (dstWidth < width || dstHeight < height) 772 scale_down(convertedBits, dst, width, height, dstWidth, dstHeight); 773 else if (dstWidth == 2 * width && dstHeight == 2 * height) 774 scale2x(convertedBits, dst, width, height, convertedBPR, dstBPR); 775 else if (dstWidth == 3 * width && dstHeight == 3 * height) 776 scale3x(convertedBits, dst, width, height, convertedBPR, dstBPR); 777 else if (dstWidth == 4 * width && dstHeight == 4 * height) 778 scale4x(convertedBits, dst, width, height, convertedBPR, dstBPR); 779 780 delete converted; 781 return B_OK; 782 } 783 784 const rgb_color* colorMap = system_colors()->color_list; 785 if (colorMap == NULL) 786 return B_NO_INIT; 787 788 const uint8* srcStart = src; 789 uint8* dstStart = dst; 790 791 // convert from B_CMAP8 to B_RGB(A)32 without scaling 792 for (uint32 y = 0; y < height; y++) { 793 uint32* d = (uint32*)dst; 794 const uint8* s = src; 795 for (uint32 x = 0; x < width; x++, s++, d++) { 796 const rgb_color c = colorMap[*s]; 797 uint8 alpha = 0xff; 798 if (*s == B_TRANSPARENT_MAGIC_CMAP8) 799 alpha = 0; 800 *d = (alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 801 } 802 src += srcBPR; 803 dst += dstBPR; 804 } 805 806 if (width == dstWidth && height == dstHeight) 807 return B_OK; 808 809 // reset src and dst back to their original locations 810 src = srcStart; 811 dst = dstStart; 812 813 if (dstWidth > width && dstHeight > height 814 && dstWidth < 2 * width && dstHeight < 2 * height) { 815 // scale2x then downscale 816 BRect rect = BRect(0, 0, width * 2 - 1, height * 2 - 1); 817 BBitmap* temp = new(std::nothrow) BBitmap(rect, 818 B_BITMAP_NO_SERVER_LINK, icon->ColorSpace()); 819 if (temp == NULL) 820 return B_NO_MEMORY; 821 822 uint8* tempBits = (uint8*)temp->Bits(); 823 uint32 tempBPR = temp->BytesPerRow(); 824 scale2x(dst, tempBits, width, height, dstBPR, tempBPR); 825 scale_down(tempBits, dst, width * 2, height * 2, dstWidth, dstHeight); 826 delete temp; 827 } else if (dstWidth > 2 * width && dstHeight > 2 * height 828 && dstWidth < 3 * width && dstHeight < 3 * height) { 829 // scale3x then downscale 830 BRect rect = BRect(0, 0, width * 3 - 1, height * 3 - 1); 831 BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, 832 icon->ColorSpace()); 833 if (temp == NULL) 834 return B_NO_MEMORY; 835 836 uint8* tempBits = (uint8*)temp->Bits(); 837 uint32 tempBPR = temp->BytesPerRow(); 838 scale3x(dst, tempBits, width, height, dstBPR, tempBPR); 839 scale_down(tempBits, dst, width * 3, height * 3, dstWidth, dstHeight); 840 delete temp; 841 } else if (dstWidth > 3 * width && dstHeight > 3 * height 842 && dstWidth < 4 * width && dstHeight < 4 * height) { 843 // scale4x then downscale 844 BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1); 845 BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, 846 icon->ColorSpace()); 847 if (temp == NULL) 848 return B_NO_MEMORY; 849 850 uint8* tempBits = (uint8*)temp->Bits(); 851 uint32 tempBPR = temp->BytesPerRow(); 852 scale4x(dst, tempBits, width, height, dstBPR, tempBPR); 853 scale_down(tempBits, dst, width * 3, height * 3, dstWidth, dstHeight); 854 delete temp; 855 } else if (dstWidth > 4 * width && dstHeight > 4 * height) { 856 // scale4x then bilinear 857 BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1); 858 BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, 859 icon->ColorSpace()); 860 if (temp == NULL) 861 return B_NO_MEMORY; 862 863 uint8* tempBits = (uint8*)temp->Bits(); 864 uint32 tempBPR = temp->BytesPerRow(); 865 scale4x(dst, tempBits, width, height, dstBPR, tempBPR); 866 icon->ImportBits(tempBits, height * tempBPR, tempBPR, 0, 867 temp->ColorSpace()); 868 scale_bilinear(dst, width, height, dstWidth, dstHeight, dstBPR); 869 delete temp; 870 } else { 871 // fall back to bilinear scaling 872 scale_bilinear(dst, width, height, dstWidth, dstHeight, dstBPR); 873 } 874 875 return B_OK; 876 } 877 878 879 status_t 880 BIconUtils::ConvertToCMAP8(const uint8* src, uint32 width, uint32 height, 881 uint32 srcBPR, BBitmap* icon) 882 { 883 if (src == NULL || icon == NULL || srcBPR == 0) 884 return B_BAD_VALUE; 885 886 status_t result = icon->InitCheck(); 887 if (result != B_OK) 888 return result; 889 890 if (icon->ColorSpace() != B_CMAP8) 891 return B_BAD_VALUE; 892 893 uint32 dstWidth = icon->Bounds().IntegerWidth() + 1; 894 uint32 dstHeight = icon->Bounds().IntegerHeight() + 1; 895 896 if (dstWidth < width || dstHeight < height) { 897 // TODO: down scaling 898 return B_ERROR; 899 } else if (dstWidth > width || dstHeight > height) { 900 // TODO: up scaling 901 // (currently copies bitmap into icon at left-top) 902 memset(icon->Bits(), 255, icon->BitsLength()); 903 } 904 905 //#if __HAIKU__ 906 // return icon->ImportBits(src, height * srcBPR, srcBPR, 0, B_RGBA32); 907 //#else 908 uint8* dst = (uint8*)icon->Bits(); 909 uint32 dstBPR = icon->BytesPerRow(); 910 911 const color_map* colorMap = system_colors(); 912 if (colorMap == NULL) 913 return B_NO_INIT; 914 915 uint16 index; 916 917 for (uint32 y = 0; y < height; y++) { 918 uint8* d = dst; 919 const uint8* s = src; 920 for (uint32 x = 0; x < width; x++) { 921 if (s[3] < 128) { 922 *d = B_TRANSPARENT_MAGIC_CMAP8; 923 } else { 924 index = ((s[2] & 0xf8) << 7) | ((s[1] & 0xf8) << 2) 925 | (s[0] >> 3); 926 *d = colorMap->index_map[index]; 927 } 928 s += 4; 929 d += 1; 930 } 931 src += srcBPR; 932 dst += dstBPR; 933 } 934 935 return B_OK; 936 //#endif // __HAIKU__ 937 } 938 939 940 // #pragma mark - Forbidden 941 942 943 BIconUtils::BIconUtils() {} 944 BIconUtils::~BIconUtils() {} 945 BIconUtils::BIconUtils(const BIconUtils&) {} 946 BIconUtils& BIconUtils::operator=(const BIconUtils&) { return *this; } 947