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