xref: /haiku/src/libs/icon/IconUtils.cpp (revision 6dcd0ccf238263a3e5eb2e2a44e2ed0da1617a42)
1 /*
2  * Copyright 2006-2007, 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  */
9 
10 
11 #include "IconUtils.h"
12 
13 #include <new>
14 #include <fs_attr.h>
15 #include <stdio.h>
16 #include <string.h>
17 
18 #include <Bitmap.h>
19 #include <Node.h>
20 #include <TypeConstants.h>
21 
22 #include "Icon.h"
23 #include "IconRenderer.h"
24 #include "FlatIconImporter.h"
25 
26 #ifndef HAIKU_TARGET_PLATFORM_HAIKU
27 #	define B_MINI_ICON_TYPE		'MICN'
28 #	define B_LARGE_ICON_TYPE	'ICON'
29 #endif
30 
31 using namespace BPrivate::Icon;
32 using std::nothrow;
33 
34 
35 // GetIcon
36 status_t
37 BIconUtils::GetIcon(BNode* node,
38 					const char* vectorIconAttrName,
39 					const char* smallIconAttrName,
40 					const char* largeIconAttrName,
41 					icon_size size,
42 					BBitmap* result)
43 {
44 	if (!result || result->InitCheck())
45 		return B_BAD_VALUE;
46 
47 	status_t ret = B_ERROR;
48 
49 	switch (result->ColorSpace()) {
50 		case B_RGBA32:
51 		case B_RGB32:
52 			// prefer vector icon
53 			ret = GetVectorIcon(node, vectorIconAttrName, result);
54 			if (ret < B_OK) {
55 				// try to fallback to B_CMAP8 icons
56 				// (converting to B_RGBA32 is handled)
57 
58 				// override size
59 				if (result->Bounds().IntegerWidth() + 1 >= 32)
60 					size = B_LARGE_ICON;
61 				else
62 					size = B_MINI_ICON;
63 
64 				ret = GetCMAP8Icon(node,
65 								   smallIconAttrName,
66 								   largeIconAttrName,
67 								   size, result);
68 			}
69 			break;
70 
71 		case B_CMAP8:
72 			// prefer old B_CMAP8 icons
73 			ret = GetCMAP8Icon(node,
74 							   smallIconAttrName,
75 							   largeIconAttrName,
76 							   size, result);
77 			if (ret < B_OK) {
78 				// try to fallback to vector icon
79 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
80 				BBitmap temp(result->Bounds(),
81 							 B_BITMAP_NO_SERVER_LINK, B_RGBA32);
82 #else
83 				BBitmap temp(result->Bounds(), B_RGBA32);
84 #endif
85 				ret = temp.InitCheck();
86 				if (ret < B_OK)
87 					break;
88 				ret = GetVectorIcon(node, vectorIconAttrName, &temp);
89 				if (ret < B_OK)
90 					break;
91 				uint32 width = temp.Bounds().IntegerWidth() + 1;
92 				uint32 height = temp.Bounds().IntegerHeight() + 1;
93 				uint32 bytesPerRow = temp.BytesPerRow();
94 				ret = ConvertToCMAP8((uint8*)temp.Bits(),
95 									 width, height, bytesPerRow, result);
96 			}
97 			break;
98 		default:
99 			printf("BIconUtils::GetIcon() - unsupported colorspace\n");
100 			break;
101 	}
102 
103 	return ret;
104 }
105 
106 // #pragma mark -
107 
108 // GetVectorIcon
109 status_t
110 BIconUtils::GetVectorIcon(BNode* node, const char* attrName,
111 						  BBitmap* result)
112 {
113 	if (!node || node->InitCheck() < B_OK || !attrName)
114 		return B_BAD_VALUE;
115 
116 #if TIME_VECTOR_ICONS
117 bigtime_t startTime = system_time();
118 #endif
119 
120 	// get the attribute info and check type and size of the attr contents
121 	attr_info attrInfo;
122 	status_t ret = node->GetAttrInfo(attrName, &attrInfo);
123 	if (ret < B_OK)
124 		return ret;
125 
126 	type_code attrType = B_VECTOR_ICON_TYPE;
127 
128 	if (attrInfo.type != attrType)
129 		return B_BAD_TYPE;
130 
131 	// chicken out on unrealisticly large attributes
132 	if (attrInfo.size > 16 * 1024)
133 		return B_BAD_VALUE;
134 
135 	uint8 buffer[attrInfo.size];
136 	ssize_t read = node->ReadAttr(attrName, attrType, 0, buffer, attrInfo.size);
137 	if (read != attrInfo.size)
138 		return B_ERROR;
139 
140 #if TIME_VECTOR_ICONS
141 bigtime_t importTime = system_time();
142 #endif
143 
144 	ret = GetVectorIcon(buffer, attrInfo.size, result);
145 	if (ret < B_OK)
146 		return ret;
147 
148 #if TIME_VECTOR_ICONS
149 bigtime_t finishTime = system_time();
150 printf("read: %lld, import: %lld\n", importTime - startTime, finishTime - importTime);
151 #endif
152 
153 	return B_OK;
154 }
155 
156 // GetVectorIcon
157 status_t
158 BIconUtils::GetVectorIcon(const uint8* buffer, size_t size,
159 						  BBitmap* result)
160 {
161 	if (!result)
162 		return B_BAD_VALUE;
163 
164 	status_t ret = result->InitCheck();
165 	if (ret < B_OK)
166 		return ret;
167 
168 	if (result->ColorSpace() != B_RGBA32 && result->ColorSpace() != B_RGB32)
169 		return B_BAD_VALUE;
170 
171 	Icon icon;
172 	ret = icon.InitCheck();
173 	if (ret < B_OK)
174 		return ret;
175 
176 	FlatIconImporter importer;
177 	ret = importer.Import(&icon, const_cast<uint8*>(buffer), size);
178 	if (ret < B_OK)
179 		return ret;
180 
181 	IconRenderer renderer(result);
182 	renderer.SetIcon(&icon);
183 	renderer.SetScale((result->Bounds().Width() + 1.0) / 64.0);
184 	renderer.Render();
185 
186 	// TODO: would be nice to get rid of this
187 	// (B_RGBA32_PREMULTIPLIED or better yet, new blending_mode)
188 	// NOTE: probably not necessary only because
189 	// transparent colors are "black" in all existing icons
190 	// lighter transparent colors should be too dark if
191 	// app_server uses correct blending
192 //	renderer.Demultiply();
193 
194 	return B_OK;
195 }
196 
197 // #pragma mark -
198 
199 status_t
200 BIconUtils::GetCMAP8Icon(BNode* node,
201 						 const char* smallIconAttrName,
202 						 const char* largeIconAttrName,
203 						 icon_size size,
204 						 BBitmap* icon)
205 {
206 	// check parameters and initialization
207 	if (!icon || icon->InitCheck() != B_OK
208 		|| !node || node->InitCheck() != B_OK
209 		|| !smallIconAttrName || !largeIconAttrName)
210 		return B_BAD_VALUE;
211 
212 	status_t ret = B_OK;
213 
214 	// NOTE: this might be changed if other icon
215 	// sizes are supported in B_CMAP8 attributes,
216 	// but this is currently not the case, so we
217 	// relax the requirement to pass an icon
218 	// of just the right size
219 	if (size < B_LARGE_ICON)
220 		size = B_MINI_ICON;
221 	else
222 		size = B_LARGE_ICON;
223 
224 	// set some icon size related variables
225 	const char *attribute = NULL;
226 	BRect bounds;
227 	uint32 attrType = 0;
228 	size_t attrSize = 0;
229 	switch (size) {
230 		case B_MINI_ICON:
231 			attribute = smallIconAttrName;
232 			bounds.Set(0, 0, 15, 15);
233 			attrType = B_MINI_ICON_TYPE;
234 			attrSize = 16 * 16;
235 			break;
236 		case B_LARGE_ICON:
237 			attribute = largeIconAttrName;
238 			bounds.Set(0, 0, 31, 31);
239 			attrType = B_LARGE_ICON_TYPE;
240 			attrSize = 32 * 32;
241 			break;
242 		default:
243 			// can not happen, see above
244 			ret = B_BAD_VALUE;
245 			break;
246 	}
247 
248 	// get the attribute info and check type and size of the attr contents
249 	attr_info attrInfo;
250 	if (ret == B_OK)
251 		ret = node->GetAttrInfo(attribute, &attrInfo);
252 	if (ret == B_OK && attrInfo.type != attrType)
253 		ret = B_BAD_TYPE;
254 	if (ret == B_OK && attrInfo.size != attrSize)
255 		ret = B_BAD_DATA;
256 
257 	// check parameters
258 	// currently, scaling B_CMAP8 icons is not supported
259 	if (icon->ColorSpace() == B_CMAP8 && icon->Bounds() != bounds)
260 		return B_BAD_VALUE;
261 
262 	// read the attribute
263 	if (ret == B_OK) {
264 		bool tempBuffer = (icon->ColorSpace() != B_CMAP8
265 						   || icon->Bounds() != bounds);
266 		uint8* buffer = NULL;
267 		ssize_t read;
268 		if (tempBuffer) {
269 			// other color space or bitmap size than stored in attribute
270 			buffer = new(nothrow) uint8[attrSize];
271 			if (!buffer) {
272 				ret = B_NO_MEMORY;
273 			} else {
274 				read = node->ReadAttr(attribute, attrType, 0, buffer,
275 									  attrSize);
276 			}
277 		} else {
278 			read = node->ReadAttr(attribute, attrType, 0, icon->Bits(),
279 								  attrSize);
280 		}
281 		if (ret == B_OK) {
282 			if (read < 0)
283 				ret = read;
284 			else if (read != (ssize_t)attrSize)
285 				ret = B_ERROR;
286 		}
287 		if (tempBuffer) {
288 			// other color space than stored in attribute
289 			if (ret == B_OK) {
290 				ret = ConvertFromCMAP8(buffer,
291 									   (uint32)size, (uint32)size,
292 									   (uint32)size, icon);
293 			}
294 			delete[] buffer;
295 		}
296 	}
297 	return ret;
298 }
299 
300 // #pragma mark -
301 
302 // ConvertFromCMAP8
303 status_t
304 BIconUtils::ConvertFromCMAP8(BBitmap* source, BBitmap* result)
305 {
306 	if (!source)
307 		return B_BAD_VALUE;
308 
309 	status_t ret = source->InitCheck();
310 	if (ret < B_OK)
311 		return ret;
312 
313 	if (source->ColorSpace() != B_CMAP8)
314 		return B_BAD_VALUE;
315 
316 	uint8* src = (uint8*)source->Bits();
317 	uint32 srcBPR = source->BytesPerRow();
318 	uint32 width = source->Bounds().IntegerWidth() + 1;
319 	uint32 height = source->Bounds().IntegerHeight() + 1;
320 
321 	return ConvertFromCMAP8(src, width, height, srcBPR, result);
322 }
323 
324 // scale_bilinear
325 static void
326 scale_bilinear(uint8* bits, int32 srcWidth, int32 srcHeight,
327 			   int32 dstWidth, int32 dstHeight, uint32 bpr)
328 {
329 	// first pass: scale bottom to top
330 
331 	uint8* dst = bits + (dstHeight - 1) * bpr;
332 		// offset to bottom left pixel in target size
333 	for (int32 x = 0; x < srcWidth; x++) {
334 		uint8* d = dst;
335 		for (int32 y = dstHeight - 1; y >= 0; y--) {
336 			int32 lineF = y * 256 * (srcHeight - 1) / (dstHeight - 1);
337 			int32 lineI = lineF >> 8;
338 			uint8 weight = (uint8)(lineF & 0xff);
339 			uint8* s1 = bits + lineI * bpr + 4 * x;
340 			if (weight == 0) {
341 				d[0] = s1[0];
342 				d[1] = s1[1];
343 				d[2] = s1[2];
344 				d[3] = s1[3];
345 			} else {
346 				uint8* s2 = s1 + bpr;
347 
348 				d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8;
349 				d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8;
350 				d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8;
351 				d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8;
352 			}
353 
354 			d -= bpr;
355 		}
356 		dst += 4;
357 	}
358 
359 	// second pass: scale right to left
360 
361 	dst = bits + (dstWidth - 1) * 4;
362 		// offset to top left pixel in target size
363 	for (int32 y = 0; y < dstWidth; y++) {
364 		uint8* d = dst;
365 		for (int32 x = dstWidth - 1; x >= 0; x--) {
366 			int32 columnF = x * 256 * (srcWidth - 1) / (dstWidth - 1);
367 			int32 columnI = columnF >> 8;
368 			uint8 weight = (uint8)(columnF & 0xff);
369 			uint8* s1 = bits + y * bpr + 4 * columnI;
370 			if (weight == 0) {
371 				d[0] = s1[0];
372 				d[1] = s1[1];
373 				d[2] = s1[2];
374 				d[3] = s1[3];
375 			} else {
376 				uint8* s2 = s1 + 4;
377 
378 				d[0] = (((s2[0] - s1[0]) * weight) + (s1[0] << 8)) >> 8;
379 				d[1] = (((s2[1] - s1[1]) * weight) + (s1[1] << 8)) >> 8;
380 				d[2] = (((s2[2] - s1[2]) * weight) + (s1[2] << 8)) >> 8;
381 				d[3] = (((s2[3] - s1[3]) * weight) + (s1[3] << 8)) >> 8;
382 			}
383 
384 			d -= 4;
385 		}
386 		dst += bpr;
387 	}
388 }
389 
390 
391 // ConvertFromCMAP8
392 status_t
393 BIconUtils::ConvertFromCMAP8(const uint8* src,
394 							 uint32 width, uint32 height, uint32 srcBPR,
395 							 BBitmap* result)
396 {
397 	if (!src || !result || srcBPR == 0)
398 		return B_BAD_VALUE;
399 
400 	status_t ret = result->InitCheck();
401 	if (ret < B_OK)
402 		return ret;
403 
404 	uint32 dstWidth = result->Bounds().IntegerWidth() + 1;
405 	uint32 dstHeight = result->Bounds().IntegerHeight() + 1;
406 
407 	if (dstWidth < width || dstHeight < height) {
408 		// TODO: down scaling
409 		return B_ERROR;
410 	}
411 
412 //#if __HAIKU__
413 //
414 //	return result->ImportBits(src, height * srcBPR, srcBPR, 0, B_CMAP8);
415 //
416 //#else
417 
418 	if (result->ColorSpace() != B_RGBA32 && result->ColorSpace() != B_RGB32) {
419 		// TODO: support other color spaces
420 		return B_BAD_VALUE;
421 	}
422 
423 	uint8* dst = (uint8*)result->Bits();
424 	uint32 dstBPR = result->BytesPerRow();
425 
426 	const rgb_color* colorMap = system_colors()->color_list;
427 
428 	for (uint32 y = 0; y < height; y++) {
429 		uint32* d = (uint32*)dst;
430 		const uint8* s = src;
431 		for (uint32 x = 0; x < width; x++) {
432 			const rgb_color c = colorMap[*s];
433 			uint8 alpha = 255;
434 			if (*s == B_TRANSPARENT_MAGIC_CMAP8)
435 				alpha = 0;
436 			*d = (alpha << 24) | (c.red << 16) | (c.green << 8) | (c.blue);
437 			s++;
438 			d++;
439 		}
440 		src += srcBPR;
441 		dst += dstBPR;
442 	}
443 
444 	if (dstWidth > width || dstHeight > height) {
445 		// up scaling
446 		scale_bilinear((uint8*)result->Bits(), width, height,
447 					   dstWidth, dstHeight, dstBPR);
448 	}
449 
450 	return B_OK;
451 
452 //#endif // __HAIKU__
453 }
454 
455 // ConvertToCMAP8
456 status_t
457 BIconUtils::ConvertToCMAP8(const uint8* src,
458 						   uint32 width, uint32 height, uint32 srcBPR,
459 						   BBitmap* result)
460 {
461 	if (!src || !result || srcBPR == 0)
462 		return B_BAD_VALUE;
463 
464 	status_t ret = result->InitCheck();
465 	if (ret < B_OK)
466 		return ret;
467 
468 	uint32 dstWidth = result->Bounds().IntegerWidth() + 1;
469 	uint32 dstHeight = result->Bounds().IntegerHeight() + 1;
470 
471 	if (dstWidth < width || dstHeight < height) {
472 		// TODO: down scaling
473 		return B_ERROR;
474 	} else if (dstWidth > width || dstHeight > height) {
475 		// TODO: up scaling
476 		// (currently copies bitmap into result at left-top)
477 memset(result->Bits(), 255, result->BitsLength());
478 	}
479 
480 //#if __HAIKU__
481 //
482 //	return result->ImportBits(src, height * srcBPR, srcBPR, 0, B_RGBA32);
483 //
484 //#else
485 
486 	if (result->ColorSpace() != B_CMAP8)
487 		return B_BAD_VALUE;
488 
489 	uint8* dst = (uint8*)result->Bits();
490 	uint32 dstBPR = result->BytesPerRow();
491 
492 	const color_map* colorMap = system_colors();
493 	uint16 index;
494 
495 	for (uint32 y = 0; y < height; y++) {
496 		uint8* d = dst;
497 		const uint8* s = src;
498 		for (uint32 x = 0; x < width; x++) {
499 			if (s[3] < 128) {
500 				*d = B_TRANSPARENT_MAGIC_CMAP8;
501 			} else {
502 				index = ((s[2] & 0xf8) << 7) | ((s[1] & 0xf8) << 2)
503 						| (s[0] >> 3);
504 				*d = colorMap->index_map[index];
505 			}
506 			s += 4;
507 			d += 1;
508 		}
509 		src += srcBPR;
510 		dst += dstBPR;
511 	}
512 
513 	return B_OK;
514 
515 //#endif // __HAIKU__
516 }
517 
518 // #pragma mark - forbidden
519 
520 BIconUtils::BIconUtils() {}
521 BIconUtils::~BIconUtils() {}
522 BIconUtils::BIconUtils(const BIconUtils&) {}
523 BIconUtils& BIconUtils::operator=(const BIconUtils&) { return *this; }
524 
525