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