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