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