1 /*
2 * Copyright 2006-2023 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Stephan Aßmus, superstippi@gmx.de
7 * Axel Dörfler, axeld@pinc-software.de
8 * John Scipione, jscipione@gmail.com
9 * Ingo Weinhold, bonefish@cs.tu-berlin.de
10 */
11
12
13 #include "IconUtils.h"
14
15 #include <new>
16 #include <fs_attr.h>
17 #include <stdio.h>
18 #include <string.h>
19
20 #include <Bitmap.h>
21 #include <FindDirectory.h>
22 #include <Node.h>
23 #include <NodeInfo.h>
24 #include <Path.h>
25 #include <Resources.h>
26 #include <String.h>
27 #include <TypeConstants.h>
28
29 #include "AutoDeleter.h"
30 #include "Icon.h"
31 #include "IconRenderer.h"
32 #include "FlatIconImporter.h"
33 #include "MessageImporter.h"
34
35
36 #define B_MINI_ICON_TYPE 'MICN'
37 #define B_LARGE_ICON_TYPE 'ICON'
38
39
40 _USING_ICON_NAMESPACE;
41
42
43 // #pragma mark - Scaling functions
44
45
46 static void
scale_bilinear(uint8 * bits,int32 srcWidth,int32 srcHeight,int32 dstWidth,int32 dstHeight,uint32 bpr)47 scale_bilinear(uint8* bits, int32 srcWidth, int32 srcHeight, int32 dstWidth,
48 int32 dstHeight, uint32 bpr)
49 {
50 // first pass: scale bottom to top
51
52 uint8* dst = bits + (dstHeight - 1) * bpr;
53 // offset to bottom left pixel in target size
54 for (int32 x = 0; x < srcWidth; x++) {
55 uint8* d = dst;
56 for (int32 y = dstHeight - 1; y >= 0; y--) {
57 int32 lineF = (y << 8) * (srcHeight - 1) / (dstHeight - 1);
58 int32 lineI = lineF >> 8;
59 uint8 weight = (uint8)(lineF & 0xff);
60 uint8* s1 = bits + lineI * bpr + 4 * x;
61 if (weight == 0) {
62 d[0] = s1[0];
63 d[1] = s1[1];
64 d[2] = s1[2];
65 d[3] = s1[3];
66 } else {
67 uint8* s2 = s1 + bpr;
68
69 d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8;
70 d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8;
71 d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8;
72 d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8;
73 }
74
75 d -= bpr;
76 }
77 dst += 4;
78 }
79
80 // second pass: scale right to left
81
82 dst = bits + (dstWidth - 1) * 4;
83 // offset to top left pixel in target size
84 for (int32 y = 0; y < dstWidth; y++) {
85 uint8* d = dst;
86 for (int32 x = dstWidth - 1; x >= 0; x--) {
87 int32 columnF = (x << 8) * (srcWidth - 1) / (dstWidth - 1);
88 int32 columnI = columnF >> 8;
89 uint8 weight = (uint8)(columnF & 0xff);
90 uint8* s1 = bits + y * bpr + 4 * columnI;
91 if (weight == 0) {
92 d[0] = s1[0];
93 d[1] = s1[1];
94 d[2] = s1[2];
95 d[3] = s1[3];
96 } else {
97 uint8* s2 = s1 + 4;
98
99 d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8;
100 d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8;
101 d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8;
102 d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8;
103 }
104
105 d -= 4;
106 }
107 dst += bpr;
108 }
109 }
110
111
112 static void
scale_down(const uint8 * srcBits,uint8 * dstBits,int32 srcWidth,int32 srcHeight,int32 dstWidth,int32 dstHeight)113 scale_down(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight,
114 int32 dstWidth, int32 dstHeight)
115 {
116 int32 l;
117 int32 c;
118 float t;
119 float u;
120 float tmp;
121 float d1, d2, d3, d4;
122 // coefficients
123 rgb_color p1, p2, p3, p4;
124 // nearby pixels
125 rgb_color out;
126 // color components
127
128 for (int32 i = 0; i < dstHeight; i++) {
129 for (int32 j = 0; j < dstWidth; j++) {
130 tmp = (float)(i) / (float)(dstHeight - 1) * (srcHeight - 1);
131 l = (int32)floorf(tmp);
132 if (l < 0)
133 l = 0;
134 else if (l >= srcHeight - 1)
135 l = srcHeight - 2;
136 u = tmp - l;
137
138 tmp = (float)(j) / (float)(dstWidth - 1) * (srcWidth - 1);
139 c = (int32)floorf(tmp);
140 if (c < 0)
141 c = 0;
142 else if (c >= srcWidth - 1)
143 c = srcWidth - 2;
144 t = tmp - c;
145
146 // coefficients
147 d1 = (1 - t) * (1 - u);
148 d2 = t * (1 - u);
149 d3 = t * u;
150 d4 = (1 - t) * u;
151
152 // nearby pixels
153 p1 = *((rgb_color*)srcBits + (l * srcWidth) + c);
154 p2 = *((rgb_color*)srcBits + (l * srcWidth) + c + 1);
155 p3 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c + 1);
156 p4 = *((rgb_color*)srcBits + ((l + 1) * srcWidth) + c);
157
158 // color components
159 out.blue = (uint8)(p1.blue * d1 + p2.blue * d2 + p3.blue * d3
160 + p4.blue * d4);
161 out.green = (uint8)(p1.green * d1 + p2.green * d2 + p3.green * d3
162 + p4.green * d4);
163 out.red = (uint8)(p1.red * d1 + p2.red * d2 + p3.red * d3
164 + p4.red * d4);
165 out.alpha = (uint8)(p1.alpha * d1 + p2.alpha * d2 + p3.alpha * d3
166 + p4.alpha * d4);
167
168 // destination RGBA pixel
169 *((rgb_color*)dstBits + (i * dstWidth) + j) = out;
170 }
171 }
172 }
173
174
175 static void
scale2x(const uint8 * srcBits,uint8 * dstBits,int32 srcWidth,int32 srcHeight,int32 srcBPR,int32 dstBPR)176 scale2x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight,
177 int32 srcBPR, int32 dstBPR)
178 {
179 /*
180 * This implements the AdvanceMAME Scale2x algorithm found on:
181 * http://scale2x.sourceforge.net/
182 *
183 * It is an incredibly simple and powerful image doubling routine that does
184 * an astonishing job of doubling game graphic data while interpolating out
185 * the jaggies.
186 *
187 * Derived from the (public domain) SDL version of the library by Pete
188 * Shinners.
189 */
190
191 // Assume that both src and dst are 4 BPP (B_RGBA32)
192 for (int32 y = 0; y < srcHeight; ++y) {
193 for (int32 x = 0; x < srcWidth; ++x) {
194 uint32 b = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR)
195 + (4 * x));
196 uint32 d = *(uint32*)(srcBits + (y * srcBPR)
197 + (4 * MAX(0, x - 1)));
198 uint32 e = *(uint32*)(srcBits + (y * srcBPR)
199 + (4 * x));
200 uint32 f = *(uint32*)(srcBits + (y * srcBPR)
201 + (4 * MIN(srcWidth - 1, x + 1)));
202 uint32 h = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1)
203 * srcBPR) + (4 * x));
204
205 uint32 e0 = d == b && b != f && d != h ? d : e;
206 uint32 e1 = b == f && b != d && f != h ? f : e;
207 uint32 e2 = d == h && d != b && h != f ? d : e;
208 uint32 e3 = h == f && d != h && b != f ? f : e;
209
210 *(uint32*)(dstBits + y * 2 * dstBPR + x * 2 * 4) = e0;
211 *(uint32*)(dstBits + y * 2 * dstBPR + (x * 2 + 1) * 4) = e1;
212 *(uint32*)(dstBits + (y * 2 + 1) * dstBPR + x * 2 * 4) = e2;
213 *(uint32*)(dstBits + (y * 2 + 1) * dstBPR + (x * 2 + 1) * 4) = e3;
214 }
215 }
216 }
217
218
219 static void
scale3x(const uint8 * srcBits,uint8 * dstBits,int32 srcWidth,int32 srcHeight,int32 srcBPR,int32 dstBPR)220 scale3x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight,
221 int32 srcBPR, int32 dstBPR)
222 {
223 /*
224 * This implements the AdvanceMAME Scale3x algorithm found on:
225 * http://scale2x.sourceforge.net/
226 *
227 * It is an incredibly simple and powerful image tripling routine that does
228 * an astonishing job of tripling game graphic data while interpolating out
229 * the jaggies.
230 *
231 * Derived from the (public domain) SDL version of the library by Pete
232 * Shinners.
233 */
234
235 // Assume that both src and dst are 4 BPP (B_RGBA32)
236 for (int32 y = 0; y < srcHeight; ++y) {
237 for (int32 x = 0; x < srcWidth; ++x) {
238 uint32 a = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR)
239 + (4 * MAX(0, x - 1)));
240 uint32 b = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR)
241 + (4 * x));
242 uint32 c = *(uint32*)(srcBits + (MAX(0, y - 1) * srcBPR)
243 + (4 * MIN(srcWidth - 1, x + 1)));
244 uint32 d = *(uint32*)(srcBits + (y * srcBPR)
245 + (4 * MAX(0, x - 1)));
246 uint32 e = *(uint32*)(srcBits + (y * srcBPR)
247 + (4 * x));
248 uint32 f = *(uint32*)(srcBits + (y * srcBPR)
249 + (4 * MIN(srcWidth - 1,x + 1)));
250 uint32 g = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1)
251 * srcBPR) + (4 * MAX(0, x - 1)));
252 uint32 h = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1)
253 * srcBPR) + (4 * x));
254 uint32 i = *(uint32*)(srcBits + (MIN(srcHeight - 1, y + 1)
255 * srcBPR) + (4 * MIN(srcWidth - 1, x + 1)));
256
257 uint32 e0 = d == b && b != f && d != h ? d : e;
258 uint32 e1 = (d == b && b != f && d != h && e != c)
259 || (b == f && b != d && f != h && e != a) ? b : e;
260 uint32 e2 = b == f && b != d && f != h ? f : e;
261 uint32 e3 = (d == b && b != f && d != h && e != g)
262 || (d == b && b != f && d != h && e != a) ? d : e;
263 uint32 e4 = e;
264 uint32 e5 = (b == f && b != d && f != h && e != i)
265 || (h == f && d != h && b != f && e != c) ? f : e;
266 uint32 e6 = d == h && d != b && h != f ? d : e;
267 uint32 e7 = (d == h && d != b && h != f && e != i)
268 || (h == f && d != h && b != f && e != g) ? h : e;
269 uint32 e8 = h == f && d != h && b != f ? f : e;
270
271 *(uint32*)(dstBits + y * 3 * dstBPR + x * 3 * 4) = e0;
272 *(uint32*)(dstBits + y * 3 * dstBPR + (x * 3 + 1) * 4) = e1;
273 *(uint32*)(dstBits + y * 3 * dstBPR + (x * 3 + 2) * 4) = e2;
274 *(uint32*)(dstBits + (y * 3 + 1) * dstBPR + x * 3 * 4) = e3;
275 *(uint32*)(dstBits + (y * 3 + 1) * dstBPR + (x * 3 + 1) * 4) = e4;
276 *(uint32*)(dstBits + (y * 3 + 1) * dstBPR + (x * 3 + 2) * 4) = e5;
277 *(uint32*)(dstBits + (y * 3 + 2) * dstBPR + x * 3 * 4) = e6;
278 *(uint32*)(dstBits + (y * 3 + 2) * dstBPR + (x * 3 + 1) * 4) = e7;
279 *(uint32*)(dstBits + (y * 3 + 2) * dstBPR + (x * 3 + 2) * 4) = e8;
280 }
281 }
282 }
283
284
285 static void
scale4x(const uint8 * srcBits,uint8 * dstBits,int32 srcWidth,int32 srcHeight,int32 srcBPR,int32 dstBPR)286 scale4x(const uint8* srcBits, uint8* dstBits, int32 srcWidth, int32 srcHeight,
287 int32 srcBPR, int32 dstBPR)
288 {
289 // scale4x is just scale2x twice
290 BRect rect = BRect(0, 0, srcWidth * 2 - 1, srcHeight * 2 - 1);
291 BBitmap* tmp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK, B_RGBA32);
292 uint8* tmpBits = (uint8*)tmp->Bits();
293 int32 tmpBPR = tmp->BytesPerRow();
294
295 scale2x(srcBits, tmpBits, srcWidth, srcHeight, srcBPR, tmpBPR);
296 scale2x(tmpBits, dstBits, srcWidth * 2, srcHeight * 2, tmpBPR, dstBPR);
297
298 delete tmp;
299 }
300
301
302 // #pragma mark - GetIcon()
303
304
305 status_t
GetIcon(BNode * node,const char * vectorIconAttrName,const char * smallIconAttrName,const char * largeIconAttrName,icon_size which,BBitmap * icon)306 BIconUtils::GetIcon(BNode* node, const char* vectorIconAttrName,
307 const char* smallIconAttrName, const char* largeIconAttrName,
308 icon_size which, BBitmap* icon)
309 {
310 if (node == NULL || icon == NULL)
311 return B_BAD_VALUE;
312
313 status_t result = node->InitCheck();
314 if (result != B_OK)
315 return result;
316
317 result = icon->InitCheck();
318 if (result != B_OK)
319 return result;
320
321 switch (icon->ColorSpace()) {
322 case B_RGBA32:
323 case B_RGB32:
324 // prefer vector icon
325 result = GetVectorIcon(node, vectorIconAttrName, icon);
326 if (result != B_OK) {
327 // try to fallback to B_CMAP8 icons
328 // (converting to B_RGBA32 is handled)
329
330 // override size
331 if (icon->Bounds().IntegerWidth() + 1 >= B_LARGE_ICON)
332 which = B_LARGE_ICON;
333 else
334 which = B_MINI_ICON;
335
336 result = GetCMAP8Icon(node, smallIconAttrName,
337 largeIconAttrName, which, icon);
338 }
339 break;
340
341 case B_CMAP8:
342 // prefer old B_CMAP8 icons
343 result = GetCMAP8Icon(node, smallIconAttrName, largeIconAttrName,
344 which, icon);
345 if (result != B_OK) {
346 // try to fallback to vector icon
347 BBitmap temp(icon->Bounds(), B_BITMAP_NO_SERVER_LINK,
348 B_RGBA32);
349 result = temp.InitCheck();
350 if (result != B_OK)
351 break;
352
353 result = GetVectorIcon(node, vectorIconAttrName, &temp);
354 if (result != B_OK)
355 break;
356
357 uint32 width = temp.Bounds().IntegerWidth() + 1;
358 uint32 height = temp.Bounds().IntegerHeight() + 1;
359 uint32 bytesPerRow = temp.BytesPerRow();
360 result = ConvertToCMAP8((uint8*)temp.Bits(), width, height,
361 bytesPerRow, icon);
362 }
363 break;
364
365 default:
366 printf("BIconUtils::GetIcon() - unsupported colorspace\n");
367 result = B_ERROR;
368 break;
369 }
370
371 return result;
372 }
373
374
375 // #pragma mark - GetVectorIcon()
376
377
378 status_t
GetVectorIcon(BNode * node,const char * attrName,BBitmap * icon)379 BIconUtils::GetVectorIcon(BNode* node, const char* attrName, BBitmap* icon)
380 {
381 if (node == NULL || attrName == NULL || *attrName == '\0' || icon == NULL)
382 return B_BAD_VALUE;
383
384 status_t result = node->InitCheck();
385 if (result != B_OK)
386 return result;
387
388 result = icon->InitCheck();
389 if (result != B_OK)
390 return result;
391
392 #if TIME_VECTOR_ICONS
393 bigtime_t startTime = system_time();
394 #endif
395
396 // get the attribute info and check type and size of the attr contents
397 attr_info attrInfo;
398 result = node->GetAttrInfo(attrName, &attrInfo);
399 if (result != B_OK)
400 return result;
401
402 type_code attrType = B_VECTOR_ICON_TYPE;
403
404 if (attrInfo.type != attrType)
405 return B_BAD_TYPE;
406
407 // chicken out on unrealisticly large attributes
408 if (attrInfo.size > 512 * 1024)
409 return B_BAD_VALUE;
410
411 uint8* buffer = new(std::nothrow) uint8[attrInfo.size];
412 if (buffer == NULL)
413 return B_NO_MEMORY;
414
415 ArrayDeleter<uint8> deleter(buffer);
416
417 ssize_t bytesRead = node->ReadAttr(attrName, attrType, 0, buffer,
418 attrInfo.size);
419 if (bytesRead != attrInfo.size)
420 return B_ERROR;
421
422 #if TIME_VECTOR_ICONS
423 bigtime_t importTime = system_time();
424 #endif
425
426 result = GetVectorIcon(buffer, attrInfo.size, icon);
427 if (result != B_OK)
428 return result;
429
430 #if TIME_VECTOR_ICONS
431 bigtime_t finishTime = system_time();
432 printf("read: %lld, import: %lld\n", importTime - startTime,
433 finishTime - importTime);
434 #endif
435
436 return B_OK;
437 }
438
439
440 status_t
GetVectorIcon(const uint8 * buffer,size_t size,BBitmap * icon)441 BIconUtils::GetVectorIcon(const uint8* buffer, size_t size, BBitmap* icon)
442 {
443 if (buffer == NULL || size <= 0 || icon == NULL)
444 return B_BAD_VALUE;
445
446 status_t result = icon->InitCheck();
447 if (result != B_OK)
448 return result;
449
450 BBitmap* temp = icon;
451 ObjectDeleter<BBitmap> deleter;
452
453 if (icon->ColorSpace() != B_RGBA32 && icon->ColorSpace() != B_RGB32) {
454 temp = new(std::nothrow) BBitmap(icon->Bounds(),
455 B_BITMAP_NO_SERVER_LINK, B_RGBA32);
456 deleter.SetTo(temp);
457 if (temp == NULL || temp->InitCheck() != B_OK)
458 return B_NO_MEMORY;
459 }
460
461 Icon vector;
462 result = vector.InitCheck();
463 if (result != B_OK)
464 return result;
465
466 FlatIconImporter importer;
467 result = importer.Import(&vector, const_cast<uint8*>(buffer), size);
468 if (result != B_OK) {
469 // try the message based format used by Icon-O-Matic
470 MessageImporter messageImporter;
471 BMemoryIO memoryIO(const_cast<uint8*>(buffer), size);
472 result = messageImporter.Import(&vector, &memoryIO);
473 if (result != B_OK)
474 return result;
475 }
476
477 IconRenderer renderer(temp);
478 renderer.SetIcon(&vector);
479 renderer.SetScale((temp->Bounds().Width() + 1.0) / 64.0);
480 renderer.Render();
481
482 if (temp != icon) {
483 uint8* src = (uint8*)temp->Bits();
484 uint32 width = temp->Bounds().IntegerWidth() + 1;
485 uint32 height = temp->Bounds().IntegerHeight() + 1;
486 uint32 srcBPR = temp->BytesPerRow();
487 result = ConvertToCMAP8(src, width, height, srcBPR, icon);
488 }
489
490 // TODO: would be nice to get rid of this
491 // (B_RGBA32_PREMULTIPLIED or better yet, new blending_mode)
492 // NOTE: probably not necessary only because
493 // transparent colors are "black" in all existing icons
494 // lighter transparent colors should be too dark if
495 // app_server uses correct blending
496 //renderer.Demultiply();
497
498 return result;
499 }
500
501
502 // #pragma mark - GetCMAP8Icon()
503
504
505 status_t
GetCMAP8Icon(BNode * node,const char * smallIconAttrName,const char * largeIconAttrName,icon_size which,BBitmap * icon)506 BIconUtils::GetCMAP8Icon(BNode* node, const char* smallIconAttrName,
507 const char* largeIconAttrName, icon_size which, BBitmap* icon)
508 {
509 // NOTE: this might be changed if other icon
510 // sizes are supported in B_CMAP8 attributes,
511 // but this is currently not the case, so we
512 // relax the requirement to pass an icon
513 // of just the right size
514 if (which < B_LARGE_ICON)
515 which = B_MINI_ICON;
516 else
517 which = B_LARGE_ICON;
518
519 // check parameters and initialization
520 if (node == NULL || icon == NULL
521 || (which == B_MINI_ICON
522 && (smallIconAttrName == NULL || *smallIconAttrName == '\0'))
523 || (which == B_LARGE_ICON
524 && (largeIconAttrName == NULL || *largeIconAttrName == '\0'))) {
525 return B_BAD_VALUE;
526 }
527
528 status_t result;
529 result = node->InitCheck();
530 if (result != B_OK)
531 return result;
532
533 result = icon->InitCheck();
534 if (result != B_OK)
535 return result;
536
537 // set some icon size related variables
538 const char* attribute = NULL;
539 BRect bounds;
540 uint32 attrType = 0;
541 off_t attrSize = 0;
542 switch (which) {
543 case B_MINI_ICON:
544 attribute = smallIconAttrName;
545 bounds.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
546 attrType = B_MINI_ICON_TYPE;
547 attrSize = B_MINI_ICON * B_MINI_ICON;
548 break;
549
550 case B_LARGE_ICON:
551 attribute = largeIconAttrName;
552 bounds.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1);
553 attrType = B_LARGE_ICON_TYPE;
554 attrSize = B_LARGE_ICON * B_LARGE_ICON;
555 break;
556
557 default:
558 // can not happen, see above
559 result = B_BAD_VALUE;
560 break;
561 }
562
563 // get the attribute info and check type and size of the attr contents
564 attr_info attrInfo;
565 if (result == B_OK)
566 result = node->GetAttrInfo(attribute, &attrInfo);
567
568 if (result == B_OK && attrInfo.type != attrType)
569 result = B_BAD_TYPE;
570
571 if (result == B_OK && attrInfo.size != attrSize)
572 result = B_BAD_DATA;
573
574 // check parameters
575 // currently, scaling B_CMAP8 icons is not supported
576 if (icon->ColorSpace() == B_CMAP8 && icon->Bounds() != bounds)
577 return B_BAD_VALUE;
578
579 // read the attribute
580 if (result == B_OK) {
581 bool useBuffer = (icon->ColorSpace() != B_CMAP8
582 || icon->Bounds() != bounds);
583 uint8* buffer = NULL;
584 ssize_t bytesRead;
585 if (useBuffer) {
586 // other color space or bitmap size than stored in attribute
587 buffer = new(std::nothrow) uint8[attrSize];
588 if (buffer == NULL)
589 bytesRead = result = B_NO_MEMORY;
590 else {
591 bytesRead = node->ReadAttr(attribute, attrType, 0, buffer,
592 attrSize);
593 }
594 } else {
595 bytesRead = node->ReadAttr(attribute, attrType, 0, icon->Bits(),
596 attrSize);
597 }
598
599 if (result == B_OK) {
600 if (bytesRead < 0)
601 result = (status_t)bytesRead;
602 else if (bytesRead != (ssize_t)attrSize)
603 result = B_ERROR;
604 }
605
606 if (useBuffer) {
607 // other color space than stored in attribute
608 if (result == B_OK) {
609 result = ConvertFromCMAP8(buffer, (uint32)which, (uint32)which,
610 (uint32)which, icon);
611 }
612 delete[] buffer;
613 }
614 }
615
616 return result;
617 }
618
619
620 status_t
GetSystemIcon(const char * iconName,BBitmap * icon)621 BIconUtils::GetSystemIcon(const char* iconName, BBitmap* icon)
622 {
623 static BResources resources;
624 static bool resourcesAreLoaded = false;
625
626 if (!resourcesAreLoaded) {
627 BPath path;
628 status_t status = find_directory(B_SYSTEM_LIB_DIRECTORY, &path);
629 if (status != B_OK) {
630 return status;
631 }
632
633 path.Append("libbe.so");
634 BFile file;
635 status = file.SetTo(path.Path(), B_READ_ONLY);
636 if (status != B_OK) {
637 return status;
638 }
639
640 status = resources.SetTo(&file);
641 if (status != B_OK) {
642 return status;
643 }
644
645 resourcesAreLoaded = true;
646 }
647
648 // Check the icon bitmap
649 if (icon == NULL || icon->InitCheck() < B_OK) {
650 return B_BAD_DATA;
651 }
652
653 // Load the raw icon data
654 size_t size = 0;
655 const uint8* rawIcon;
656
657 // Try to load vector icon
658 rawIcon = (const uint8*)resources.LoadResource(B_VECTOR_ICON_TYPE,
659 iconName, &size);
660 if (rawIcon != NULL
661 && BIconUtils::GetVectorIcon(rawIcon, size, icon) == B_OK) {
662 return B_OK;
663 }
664
665 // Fall back to bitmap icon
666 rawIcon = (const uint8*)resources.LoadResource(B_LARGE_ICON_TYPE,
667 iconName, &size);
668 if (rawIcon == NULL) {
669 delete icon;
670 return B_ENTRY_NOT_FOUND;
671 }
672
673 // Handle color space conversion
674 if (icon->ColorSpace() != B_CMAP8) {
675 BIconUtils::ConvertFromCMAP8(rawIcon, B_LARGE_ICON, B_LARGE_ICON,
676 B_LARGE_ICON, icon);
677 }
678
679 return B_OK;
680 }
681
682
683 // #pragma mark - ConvertFromCMAP8() and ConvertToCMAP8()
684
685
686 status_t
ConvertFromCMAP8(BBitmap * source,BBitmap * destination)687 BIconUtils::ConvertFromCMAP8(BBitmap* source, BBitmap* destination)
688 {
689 if (source == NULL || source->ColorSpace() != B_CMAP8)
690 return B_BAD_VALUE;
691
692 status_t result = source->InitCheck();
693 if (result != B_OK)
694 return result;
695
696 result = destination->InitCheck();
697 if (result != B_OK)
698 return result;
699
700 uint8* src = (uint8*)source->Bits();
701 uint32 srcBPR = source->BytesPerRow();
702 uint32 width = source->Bounds().IntegerWidth() + 1;
703 uint32 height = source->Bounds().IntegerHeight() + 1;
704
705 return ConvertFromCMAP8(src, width, height, srcBPR, destination);
706 }
707
708
709 status_t
ConvertToCMAP8(BBitmap * source,BBitmap * destination)710 BIconUtils::ConvertToCMAP8(BBitmap* source, BBitmap* destination)
711 {
712 if (source == NULL || source->ColorSpace() != B_RGBA32
713 || destination->ColorSpace() != B_CMAP8) {
714 return B_BAD_VALUE;
715 }
716
717 status_t result = source->InitCheck();
718 if (result != B_OK)
719 return result;
720
721 result = destination->InitCheck();
722 if (result != B_OK)
723 return result;
724
725 uint8* src = (uint8*)source->Bits();
726 uint32 srcBPR = source->BytesPerRow();
727 uint32 width = source->Bounds().IntegerWidth() + 1;
728 uint32 height = source->Bounds().IntegerHeight() + 1;
729
730 return ConvertToCMAP8(src, width, height, srcBPR, destination);
731 }
732
733
734 status_t
ConvertFromCMAP8(const uint8 * src,uint32 width,uint32 height,uint32 srcBPR,BBitmap * icon)735 BIconUtils::ConvertFromCMAP8(const uint8* src, uint32 width, uint32 height,
736 uint32 srcBPR, BBitmap* icon)
737 {
738 if (src == NULL || icon == NULL || srcBPR == 0)
739 return B_BAD_VALUE;
740
741 status_t result = icon->InitCheck();
742 if (result != B_OK)
743 return result;
744
745 if (icon->ColorSpace() != B_RGBA32 && icon->ColorSpace() != B_RGB32) {
746 // TODO: support other color spaces
747 return B_BAD_VALUE;
748 }
749
750 uint32 dstWidth = icon->Bounds().IntegerWidth() + 1;
751 uint32 dstHeight = icon->Bounds().IntegerHeight() + 1;
752
753 uint8* dst = (uint8*)icon->Bits();
754 uint32 dstBPR = icon->BytesPerRow();
755
756 // check for downscaling or integer multiple scaling
757 if (dstWidth < width || dstHeight < height
758 || (dstWidth == 2 * width && dstHeight == 2 * height)
759 || (dstWidth == 3 * width && dstHeight == 3 * height)
760 || (dstWidth == 4 * width && dstHeight == 4 * height)) {
761 BRect rect = BRect(0, 0, width - 1, height - 1);
762 BBitmap* converted = new(std::nothrow) BBitmap(rect,
763 B_BITMAP_NO_SERVER_LINK, icon->ColorSpace());
764 if (converted == NULL)
765 return B_NO_MEMORY;
766
767 converted->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8);
768 uint8* convertedBits = (uint8*)converted->Bits();
769 int32 convertedBPR = converted->BytesPerRow();
770
771 if (dstWidth < width || dstHeight < height)
772 scale_down(convertedBits, dst, width, height, dstWidth, dstHeight);
773 else if (dstWidth == 2 * width && dstHeight == 2 * height)
774 scale2x(convertedBits, dst, width, height, convertedBPR, dstBPR);
775 else if (dstWidth == 3 * width && dstHeight == 3 * height)
776 scale3x(convertedBits, dst, width, height, convertedBPR, dstBPR);
777 else if (dstWidth == 4 * width && dstHeight == 4 * height)
778 scale4x(convertedBits, dst, width, height, convertedBPR, dstBPR);
779
780 delete converted;
781 return B_OK;
782 }
783
784 const rgb_color* colorMap = system_colors()->color_list;
785 if (colorMap == NULL)
786 return B_NO_INIT;
787
788 const uint8* srcStart = src;
789 uint8* dstStart = dst;
790
791 // convert from B_CMAP8 to B_RGB(A)32 without scaling
792 for (uint32 y = 0; y < height; y++) {
793 uint32* d = (uint32*)dst;
794 const uint8* s = src;
795 for (uint32 x = 0; x < width; x++, s++, d++) {
796 const rgb_color c = colorMap[*s];
797 uint8 alpha = 0xff;
798 if (*s == B_TRANSPARENT_MAGIC_CMAP8)
799 alpha = 0;
800 *d = (alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
801 }
802 src += srcBPR;
803 dst += dstBPR;
804 }
805
806 if (width == dstWidth && height == dstHeight)
807 return B_OK;
808
809 // reset src and dst back to their original locations
810 src = srcStart;
811 dst = dstStart;
812
813 if (dstWidth > width && dstHeight > height
814 && dstWidth < 2 * width && dstHeight < 2 * height) {
815 // scale2x then downscale
816 BRect rect = BRect(0, 0, width * 2 - 1, height * 2 - 1);
817 BBitmap* temp = new(std::nothrow) BBitmap(rect,
818 B_BITMAP_NO_SERVER_LINK, icon->ColorSpace());
819 if (temp == NULL)
820 return B_NO_MEMORY;
821
822 uint8* tempBits = (uint8*)temp->Bits();
823 uint32 tempBPR = temp->BytesPerRow();
824 scale2x(dst, tempBits, width, height, dstBPR, tempBPR);
825 scale_down(tempBits, dst, width * 2, height * 2, dstWidth, dstHeight);
826 delete temp;
827 } else if (dstWidth > 2 * width && dstHeight > 2 * height
828 && dstWidth < 3 * width && dstHeight < 3 * height) {
829 // scale3x then downscale
830 BRect rect = BRect(0, 0, width * 3 - 1, height * 3 - 1);
831 BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK,
832 icon->ColorSpace());
833 if (temp == NULL)
834 return B_NO_MEMORY;
835
836 uint8* tempBits = (uint8*)temp->Bits();
837 uint32 tempBPR = temp->BytesPerRow();
838 scale3x(dst, tempBits, width, height, dstBPR, tempBPR);
839 scale_down(tempBits, dst, width * 3, height * 3, dstWidth, dstHeight);
840 delete temp;
841 } else if (dstWidth > 3 * width && dstHeight > 3 * height
842 && dstWidth < 4 * width && dstHeight < 4 * height) {
843 // scale4x then downscale
844 BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1);
845 BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK,
846 icon->ColorSpace());
847 if (temp == NULL)
848 return B_NO_MEMORY;
849
850 uint8* tempBits = (uint8*)temp->Bits();
851 uint32 tempBPR = temp->BytesPerRow();
852 scale4x(dst, tempBits, width, height, dstBPR, tempBPR);
853 scale_down(tempBits, dst, width * 3, height * 3, dstWidth, dstHeight);
854 delete temp;
855 } else if (dstWidth > 4 * width && dstHeight > 4 * height) {
856 // scale4x then bilinear
857 BRect rect = BRect(0, 0, width * 4 - 1, height * 4 - 1);
858 BBitmap* temp = new BBitmap(rect, B_BITMAP_NO_SERVER_LINK,
859 icon->ColorSpace());
860 if (temp == NULL)
861 return B_NO_MEMORY;
862
863 uint8* tempBits = (uint8*)temp->Bits();
864 uint32 tempBPR = temp->BytesPerRow();
865 scale4x(dst, tempBits, width, height, dstBPR, tempBPR);
866 icon->ImportBits(tempBits, height * tempBPR, tempBPR, 0,
867 temp->ColorSpace());
868 scale_bilinear(dst, width, height, dstWidth, dstHeight, dstBPR);
869 delete temp;
870 } else {
871 // fall back to bilinear scaling
872 scale_bilinear(dst, width, height, dstWidth, dstHeight, dstBPR);
873 }
874
875 return B_OK;
876 }
877
878
879 status_t
ConvertToCMAP8(const uint8 * src,uint32 width,uint32 height,uint32 srcBPR,BBitmap * icon)880 BIconUtils::ConvertToCMAP8(const uint8* src, uint32 width, uint32 height,
881 uint32 srcBPR, BBitmap* icon)
882 {
883 if (src == NULL || icon == NULL || srcBPR == 0)
884 return B_BAD_VALUE;
885
886 status_t result = icon->InitCheck();
887 if (result != B_OK)
888 return result;
889
890 if (icon->ColorSpace() != B_CMAP8)
891 return B_BAD_VALUE;
892
893 uint32 dstWidth = icon->Bounds().IntegerWidth() + 1;
894 uint32 dstHeight = icon->Bounds().IntegerHeight() + 1;
895
896 if (dstWidth < width || dstHeight < height) {
897 // TODO: down scaling
898 return B_ERROR;
899 } else if (dstWidth > width || dstHeight > height) {
900 // TODO: up scaling
901 // (currently copies bitmap into icon at left-top)
902 memset(icon->Bits(), 255, icon->BitsLength());
903 }
904
905 //#if __HAIKU__
906 // return icon->ImportBits(src, height * srcBPR, srcBPR, 0, B_RGBA32);
907 //#else
908 uint8* dst = (uint8*)icon->Bits();
909 uint32 dstBPR = icon->BytesPerRow();
910
911 const color_map* colorMap = system_colors();
912 if (colorMap == NULL)
913 return B_NO_INIT;
914
915 uint16 index;
916
917 for (uint32 y = 0; y < height; y++) {
918 uint8* d = dst;
919 const uint8* s = src;
920 for (uint32 x = 0; x < width; x++) {
921 if (s[3] < 128) {
922 *d = B_TRANSPARENT_MAGIC_CMAP8;
923 } else {
924 index = ((s[2] & 0xf8) << 7) | ((s[1] & 0xf8) << 2)
925 | (s[0] >> 3);
926 *d = colorMap->index_map[index];
927 }
928 s += 4;
929 d += 1;
930 }
931 src += srcBPR;
932 dst += dstBPR;
933 }
934
935 return B_OK;
936 //#endif // __HAIKU__
937 }
938
939
940 // #pragma mark - Forbidden
941
942
BIconUtils()943 BIconUtils::BIconUtils() {}
~BIconUtils()944 BIconUtils::~BIconUtils() {}
BIconUtils(const BIconUtils &)945 BIconUtils::BIconUtils(const BIconUtils&) {}
operator =(const BIconUtils &)946 BIconUtils& BIconUtils::operator=(const BIconUtils&) { return *this; }
947