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