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