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