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