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