1 /* 2 * Copyright 2006-2011, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 * Ingo Weinhold <bonefish@cs.tu-berlin.de> 8 * John Scipione <jscipione@gmail.com> 9 */ 10 11 12 #include "IconUtils.h" 13 14 #include <new> 15 #include <fs_attr.h> 16 #include <stdio.h> 17 #include <string.h> 18 19 #include <Bitmap.h> 20 #include <Node.h> 21 #include <TypeConstants.h> 22 23 #include "AutoDeleter.h" 24 #include "Icon.h" 25 #include "IconRenderer.h" 26 #include "FlatIconImporter.h" 27 #include "MessageImporter.h" 28 29 #ifndef HAIKU_TARGET_PLATFORM_HAIKU 30 # define B_MINI_ICON_TYPE 'MICN' 31 # define B_LARGE_ICON_TYPE 'ICON' 32 #endif 33 34 _USING_ICON_NAMESPACE; 35 using std::nothrow; 36 37 38 static void 39 scale_bilinear(uint8* bits, int32 srcWidth, int32 srcHeight, int32 dstWidth, 40 int32 dstHeight, uint32 bpr) 41 { 42 // first pass: scale bottom to top 43 44 uint8* dst = bits + (dstHeight - 1) * bpr; 45 // offset to bottom left pixel in target size 46 for (int32 x = 0; x < srcWidth; x++) { 47 uint8* d = dst; 48 for (int32 y = dstHeight - 1; y >= 0; y--) { 49 int32 lineF = y * 256 * (srcHeight - 1) / (dstHeight - 1); 50 int32 lineI = lineF >> 8; 51 uint8 weight = (uint8)(lineF & 0xff); 52 uint8* s1 = bits + lineI * bpr + 4 * x; 53 if (weight == 0) { 54 d[0] = s1[0]; 55 d[1] = s1[1]; 56 d[2] = s1[2]; 57 d[3] = s1[3]; 58 } else { 59 uint8* s2 = s1 + bpr; 60 61 d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8; 62 d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8; 63 d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8; 64 d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8; 65 } 66 67 d -= bpr; 68 } 69 dst += 4; 70 } 71 72 // second pass: scale right to left 73 74 dst = bits + (dstWidth - 1) * 4; 75 // offset to top left pixel in target size 76 for (int32 y = 0; y < dstWidth; y++) { 77 uint8* d = dst; 78 for (int32 x = dstWidth - 1; x >= 0; x--) { 79 int32 columnF = x * 256 * (srcWidth - 1) / (dstWidth - 1); 80 int32 columnI = columnF >> 8; 81 uint8 weight = (uint8)(columnF & 0xff); 82 uint8* s1 = bits + y * bpr + 4 * columnI; 83 if (weight == 0) { 84 d[0] = s1[0]; 85 d[1] = s1[1]; 86 d[2] = s1[2]; 87 d[3] = s1[3]; 88 } else { 89 uint8* s2 = s1 + 4; 90 91 d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8; 92 d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8; 93 d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8; 94 d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8; 95 } 96 97 d -= 4; 98 } 99 dst += bpr; 100 } 101 } 102 103 104 static void 105 scale2x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight, 106 int32 srcBPR, int32 dstBPR) 107 { 108 /* 109 * This implements the AdvanceMAME Scale2x algorithm found on: 110 * http://scale2x.sourceforge.net/ 111 * 112 * It is an incredibly simple and powerful image doubling routine that does 113 * an astonishing job of doubling game graphic data while interpolating out 114 * the jaggies. 115 * 116 * Derived from the (public domain) SDL version of the library by Pete 117 * Shinners 118 */ 119 120 // Assume that both src and dst are 4 BPP (B_RGBA32) 121 for (int32 y = 0; y < srcHeight; ++y) { 122 for (int32 x = 0; x < srcWidth; ++x) { 123 uint32 b = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR) 124 + (4 * x)); 125 uint32 d = *(uint32*)(srcBits + (y * srcBPR) 126 + (4 * MAX(0, x - 1))); 127 uint32 e = *(uint32*)(srcBits + (y * srcBPR) 128 + (4 * x)); 129 uint32 f = *(uint32*)(srcBits + (y * srcBPR) 130 + (4 * MIN(srcWidth - 1, x + 1))); 131 uint32 h = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1) 132 * srcBPR) + (4 * x)); 133 134 uint32 e0 = d == b && b != f && d != h ? d : e; 135 uint32 e1 = b == f && b != d && f != h ? f : e; 136 uint32 e2 = d == h && d != b && h != f ? d : e; 137 uint32 e3 = h == f && d != h && b != f ? f : e; 138 139 *(uint32*)(dstBits + y * 2 * dstBPR + x * 2 * 4) = e0; 140 *(uint32*)(dstBits + y * 2 * dstBPR + (x * 2 + 1) * 4) = e1; 141 *(uint32*)(dstBits + (y * 2 + 1) * dstBPR + x * 2 * 4) = e2; 142 *(uint32*)(dstBits + (y * 2 + 1) * dstBPR + (x * 2 + 1) * 4) = e3; 143 } 144 } 145 } 146 147 148 // #pragma mark - 149 150 151 status_t 152 BIconUtils::GetIcon(BNode* node, const char* vectorIconAttrName, 153 const char* smallIconAttrName, const char* largeIconAttrName, 154 icon_size size, BBitmap* result) 155 { 156 if (!result || result->InitCheck()) 157 return B_BAD_VALUE; 158 159 status_t ret = B_ERROR; 160 161 switch (result->ColorSpace()) { 162 case B_RGBA32: 163 case B_RGB32: 164 // prefer vector icon 165 ret = GetVectorIcon(node, vectorIconAttrName, result); 166 if (ret < B_OK) { 167 // try to fallback to B_CMAP8 icons 168 // (converting to B_RGBA32 is handled) 169 170 // override size 171 if (result->Bounds().IntegerWidth() + 1 >= 32) 172 size = B_LARGE_ICON; 173 else 174 size = B_MINI_ICON; 175 176 ret = GetCMAP8Icon(node, smallIconAttrName, largeIconAttrName, 177 size, result); 178 } 179 break; 180 181 case B_CMAP8: 182 // prefer old B_CMAP8 icons 183 ret = GetCMAP8Icon(node, smallIconAttrName, largeIconAttrName, 184 size, result); 185 if (ret < B_OK) { 186 // try to fallback to vector icon 187 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 188 BBitmap temp(result->Bounds(), B_BITMAP_NO_SERVER_LINK, 189 B_RGBA32); 190 #else 191 BBitmap temp(result->Bounds(), B_RGBA32); 192 #endif 193 ret = temp.InitCheck(); 194 if (ret < B_OK) 195 break; 196 ret = GetVectorIcon(node, vectorIconAttrName, &temp); 197 if (ret < B_OK) 198 break; 199 uint32 width = temp.Bounds().IntegerWidth() + 1; 200 uint32 height = temp.Bounds().IntegerHeight() + 1; 201 uint32 bytesPerRow = temp.BytesPerRow(); 202 ret = ConvertToCMAP8((uint8*)temp.Bits(), width, height, 203 bytesPerRow, result); 204 } 205 break; 206 default: 207 printf("BIconUtils::GetIcon() - unsupported colorspace\n"); 208 break; 209 } 210 211 return ret; 212 } 213 214 215 // #pragma mark - 216 217 218 status_t 219 BIconUtils::GetVectorIcon(BNode* node, const char* attrName, BBitmap* result) 220 { 221 if (!node || node->InitCheck() < B_OK || !attrName) 222 return B_BAD_VALUE; 223 224 #if TIME_VECTOR_ICONS 225 bigtime_t startTime = system_time(); 226 #endif 227 228 // get the attribute info and check type and size of the attr contents 229 attr_info attrInfo; 230 status_t ret = node->GetAttrInfo(attrName, &attrInfo); 231 if (ret < B_OK) 232 return ret; 233 234 type_code attrType = B_VECTOR_ICON_TYPE; 235 236 if (attrInfo.type != attrType) 237 return B_BAD_TYPE; 238 239 // chicken out on unrealisticly large attributes 240 if (attrInfo.size > 16 * 1024) 241 return B_BAD_VALUE; 242 243 uint8 buffer[attrInfo.size]; 244 ssize_t read = node->ReadAttr(attrName, attrType, 0, buffer, attrInfo.size); 245 if (read != attrInfo.size) 246 return B_ERROR; 247 248 #if TIME_VECTOR_ICONS 249 bigtime_t importTime = system_time(); 250 #endif 251 252 ret = GetVectorIcon(buffer, attrInfo.size, result); 253 if (ret < B_OK) 254 return ret; 255 256 #if TIME_VECTOR_ICONS 257 bigtime_t finishTime = system_time(); 258 printf("read: %lld, import: %lld\n", importTime - startTime, finishTime - importTime); 259 #endif 260 261 return B_OK; 262 } 263 264 265 status_t 266 BIconUtils::GetVectorIcon(const uint8* buffer, size_t size, BBitmap* result) 267 { 268 if (!result) 269 return B_BAD_VALUE; 270 271 status_t ret = result->InitCheck(); 272 if (ret < B_OK) 273 return ret; 274 275 BBitmap* temp = result; 276 ObjectDeleter<BBitmap> deleter; 277 278 if (result->ColorSpace() != B_RGBA32 && result->ColorSpace() != B_RGB32) { 279 temp = new (nothrow) BBitmap(result->Bounds(), 280 B_BITMAP_NO_SERVER_LINK, B_RGBA32); 281 deleter.SetTo(temp); 282 if (!temp || temp->InitCheck() != B_OK) 283 return B_NO_MEMORY; 284 } 285 286 Icon icon; 287 ret = icon.InitCheck(); 288 if (ret < B_OK) 289 return ret; 290 291 FlatIconImporter importer; 292 ret = importer.Import(&icon, const_cast<uint8*>(buffer), size); 293 if (ret < B_OK) { 294 // try the message based format used by Icon-O-Matic 295 MessageImporter messageImporter; 296 BMemoryIO memoryIO(const_cast<uint8*>(buffer), size); 297 ret = messageImporter.Import(&icon, &memoryIO); 298 if (ret < B_OK) 299 return ret; 300 } 301 302 IconRenderer renderer(temp); 303 renderer.SetIcon(&icon); 304 renderer.SetScale((temp->Bounds().Width() + 1.0) / 64.0); 305 renderer.Render(); 306 307 if (temp != result) { 308 uint8* src = (uint8*)temp->Bits(); 309 uint32 width = temp->Bounds().IntegerWidth() + 1; 310 uint32 height = temp->Bounds().IntegerHeight() + 1; 311 uint32 srcBPR = temp->BytesPerRow(); 312 ret = ConvertToCMAP8(src, width, height, srcBPR, result); 313 } 314 315 // TODO: would be nice to get rid of this 316 // (B_RGBA32_PREMULTIPLIED or better yet, new blending_mode) 317 // NOTE: probably not necessary only because 318 // transparent colors are "black" in all existing icons 319 // lighter transparent colors should be too dark if 320 // app_server uses correct blending 321 // renderer.Demultiply(); 322 323 return ret; 324 } 325 326 327 // #pragma mark - 328 329 330 status_t 331 BIconUtils::GetCMAP8Icon(BNode* node, const char* smallIconAttrName, 332 const char* largeIconAttrName, icon_size size, BBitmap* icon) 333 { 334 // check parameters and initialization 335 if (!icon || icon->InitCheck() != B_OK 336 || !node || node->InitCheck() != B_OK 337 || !smallIconAttrName || !largeIconAttrName) 338 return B_BAD_VALUE; 339 340 status_t ret = B_OK; 341 342 // NOTE: this might be changed if other icon 343 // sizes are supported in B_CMAP8 attributes, 344 // but this is currently not the case, so we 345 // relax the requirement to pass an icon 346 // of just the right size 347 if (size < B_LARGE_ICON) 348 size = B_MINI_ICON; 349 else 350 size = B_LARGE_ICON; 351 352 // set some icon size related variables 353 const char *attribute = NULL; 354 BRect bounds; 355 uint32 attrType = 0; 356 size_t attrSize = 0; 357 switch (size) { 358 case B_MINI_ICON: 359 attribute = smallIconAttrName; 360 bounds.Set(0, 0, 15, 15); 361 attrType = B_MINI_ICON_TYPE; 362 attrSize = 16 * 16; 363 break; 364 case B_LARGE_ICON: 365 attribute = largeIconAttrName; 366 bounds.Set(0, 0, 31, 31); 367 attrType = B_LARGE_ICON_TYPE; 368 attrSize = 32 * 32; 369 break; 370 default: 371 // can not happen, see above 372 ret = B_BAD_VALUE; 373 break; 374 } 375 376 // get the attribute info and check type and size of the attr contents 377 attr_info attrInfo; 378 if (ret == B_OK) 379 ret = node->GetAttrInfo(attribute, &attrInfo); 380 if (ret == B_OK && attrInfo.type != attrType) 381 ret = B_BAD_TYPE; 382 if (ret == B_OK && attrInfo.size != attrSize) 383 ret = B_BAD_DATA; 384 385 // check parameters 386 // currently, scaling B_CMAP8 icons is not supported 387 if (icon->ColorSpace() == B_CMAP8 && icon->Bounds() != bounds) 388 return B_BAD_VALUE; 389 390 // read the attribute 391 if (ret == B_OK) { 392 bool tempBuffer = (icon->ColorSpace() != B_CMAP8 393 || icon->Bounds() != bounds); 394 uint8* buffer = NULL; 395 ssize_t read; 396 if (tempBuffer) { 397 // other color space or bitmap size than stored in attribute 398 buffer = new(nothrow) uint8[attrSize]; 399 if (!buffer) { 400 ret = B_NO_MEMORY; 401 } else { 402 read = node->ReadAttr(attribute, attrType, 0, buffer, attrSize); 403 } 404 } else { 405 read = node->ReadAttr(attribute, attrType, 0, icon->Bits(), 406 attrSize); 407 } 408 if (ret == B_OK) { 409 if (read < 0) 410 ret = read; 411 else if (read != (ssize_t)attrSize) 412 ret = B_ERROR; 413 } 414 if (tempBuffer) { 415 // other color space than stored in attribute 416 if (ret == B_OK) { 417 ret = ConvertFromCMAP8(buffer, (uint32)size, (uint32)size, 418 (uint32)size, icon); 419 } 420 delete[] buffer; 421 } 422 } 423 return ret; 424 } 425 426 427 // #pragma mark - 428 429 430 status_t 431 BIconUtils::ConvertFromCMAP8(BBitmap* source, BBitmap* result) 432 { 433 if (source == NULL || source->ColorSpace() != B_CMAP8) 434 return B_BAD_VALUE; 435 436 status_t status = source->InitCheck(); 437 if (status < B_OK) 438 return status; 439 440 status = result->InitCheck(); 441 if (status < B_OK) 442 return status; 443 444 uint8* src = (uint8*)source->Bits(); 445 uint32 srcBPR = source->BytesPerRow(); 446 uint32 width = source->Bounds().IntegerWidth() + 1; 447 uint32 height = source->Bounds().IntegerHeight() + 1; 448 449 return ConvertFromCMAP8(src, width, height, srcBPR, result); 450 } 451 452 453 status_t 454 BIconUtils::ConvertToCMAP8(BBitmap* source, BBitmap* result) 455 { 456 if (source == NULL || source->ColorSpace() != B_RGBA32 457 || result->ColorSpace() != B_CMAP8) 458 return B_BAD_VALUE; 459 460 status_t status = source->InitCheck(); 461 if (status < B_OK) 462 return status; 463 464 status = result->InitCheck(); 465 if (status < B_OK) 466 return status; 467 468 uint8* src = (uint8*)source->Bits(); 469 uint32 srcBPR = source->BytesPerRow(); 470 uint32 width = source->Bounds().IntegerWidth() + 1; 471 uint32 height = source->Bounds().IntegerHeight() + 1; 472 473 return ConvertToCMAP8(src, width, height, srcBPR, result); 474 } 475 476 477 status_t 478 BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height, 479 uint32 srcBPR, BBitmap* result) 480 { 481 if (!src || !result || srcBPR == 0) 482 return B_BAD_VALUE; 483 484 status_t ret = result->InitCheck(); 485 if (ret < B_OK) 486 return ret; 487 488 uint32 dstWidth = result->Bounds().IntegerWidth() + 1; 489 uint32 dstHeight = result->Bounds().IntegerHeight() + 1; 490 491 if (dstWidth < width || dstHeight < height) { 492 // TODO: down scaling 493 return B_ERROR; 494 } 495 496 if (result->ColorSpace() != B_RGBA32 && result->ColorSpace() != B_RGB32) { 497 // TODO: support other color spaces 498 return B_BAD_VALUE; 499 } 500 501 uint8* dst = (uint8*)result->Bits(); 502 uint32 dstBPR = result->BytesPerRow(); 503 504 const rgb_color* colorMap = system_colors()->color_list; 505 506 const uint8* srcStart = src; 507 uint8* dstStart = dst; 508 509 for (uint32 y = 0; y < height; y++) { 510 uint32* d = (uint32*)dst; 511 const uint8* s = src; 512 for (uint32 x = 0; x < width; x++) { 513 const rgb_color c = colorMap[*s]; 514 uint8 alpha = 255; 515 if (*s == B_TRANSPARENT_MAGIC_CMAP8) 516 alpha = 0; 517 *d = (alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue); 518 s++; 519 d++; 520 } 521 src += srcBPR; 522 dst += dstBPR; 523 } 524 525 // reset src and dst back to their original locations 526 src = srcStart; 527 dst = dstStart; 528 529 if (dstWidth > width || dstHeight > height) { 530 if (dstWidth == 2 * width && dstHeight == 2 * height) { 531 // scale using the scale2x algorithm 532 BBitmap* converted = new BBitmap(BRect(0, 0, width - 1, height - 1), 533 result->ColorSpace()); 534 converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8); 535 uint8* convertedBits = (uint8*)converted->Bits(); 536 int32 convertedBPR = converted->BytesPerRow(); 537 scale2x(convertedBits, dst, width, height, convertedBPR, dstBPR); 538 } else { 539 // bilinear scaling 540 scale_bilinear(dst, width, height, dstWidth, dstHeight, dstBPR); 541 } 542 } 543 544 return B_OK; 545 } 546 547 548 status_t 549 BIconUtils::ConvertToCMAP8(const uint8* src, uint32 width, uint32 height, 550 uint32 srcBPR, BBitmap* result) 551 { 552 if (!src || !result || srcBPR == 0) 553 return B_BAD_VALUE; 554 555 status_t ret = result->InitCheck(); 556 if (ret < B_OK) 557 return ret; 558 559 if (result->ColorSpace() != B_CMAP8) 560 return B_BAD_VALUE; 561 562 uint32 dstWidth = result->Bounds().IntegerWidth() + 1; 563 uint32 dstHeight = result->Bounds().IntegerHeight() + 1; 564 565 if (dstWidth < width || dstHeight < height) { 566 // TODO: down scaling 567 return B_ERROR; 568 } else if (dstWidth > width || dstHeight > height) { 569 // TODO: up scaling 570 // (currently copies bitmap into result at left-top) 571 memset(result->Bits(), 255, result->BitsLength()); 572 } 573 574 //#if __HAIKU__ 575 // 576 // return result->ImportBits(src, height * srcBPR, srcBPR, 0, B_RGBA32); 577 // 578 //#else 579 580 uint8* dst = (uint8*)result->Bits(); 581 uint32 dstBPR = result->BytesPerRow(); 582 583 const color_map* colorMap = system_colors(); 584 if (!colorMap) 585 return B_NO_INIT; 586 uint16 index; 587 588 for (uint32 y = 0; y < height; y++) { 589 uint8* d = dst; 590 const uint8* s = src; 591 for (uint32 x = 0; x < width; x++) { 592 if (s[3] < 128) { 593 *d = B_TRANSPARENT_MAGIC_CMAP8; 594 } else { 595 index = ((s[2] & 0xf8) << 7) | ((s[1] & 0xf8) << 2) 596 | (s[0] >> 3); 597 *d = colorMap->index_map[index]; 598 } 599 s += 4; 600 d += 1; 601 } 602 src += srcBPR; 603 dst += dstBPR; 604 } 605 606 return B_OK; 607 608 //#endif // __HAIKU__ 609 } 610 611 612 // #pragma mark - forbidden 613 614 615 BIconUtils::BIconUtils() {} 616 BIconUtils::~BIconUtils() {} 617 BIconUtils::BIconUtils(const BIconUtils&) {} 618 BIconUtils& BIconUtils::operator=(const BIconUtils&) { return *this; } 619 620