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