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