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