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