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