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