xref: /haiku/src/libs/icon/IconUtils.cpp (revision 4dbd474753bd0d7392dc9d6f350ff5b7ec9f43f2)
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