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;
38using std::nothrow;
39
40
41//	#pragma mark - Scaling functions
42
43
44static void
45scale_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
110static void
111scale_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
173static void
174scale2x(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
217static void
218scale3x(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
283static void
284scale4x(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
303status_t
304BIconUtils::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
380status_t
381BIconUtils::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
442status_t
443BIconUtils::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
507status_t
508BIconUtils::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
623status_t
624BIconUtils::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
646status_t
647BIconUtils::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
671status_t
672BIconUtils::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
796status_t
797BIconUtils::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
860BIconUtils::BIconUtils() {}
861BIconUtils::~BIconUtils() {}
862BIconUtils::BIconUtils(const BIconUtils&) {}
863BIconUtils& BIconUtils::operator=(const BIconUtils&) { return *this; }
864