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