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