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