1/*
2 * Copyright 2012, Gerasim Troeglazov, 3dEyes@gmail.com. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "ICNSLoader.h"
7#include "BaseTranslator.h"
8
9
10static int compareTypes(const void *a, const void *b)
11{
12	icns_type_t typeItemA = *((icns_type_t*)(*((icns_type_t*)a)));
13	icns_type_t typeItemB = *((icns_type_t*)(*((icns_type_t*)b)));
14
15	icns_icon_info_t imageInfoA = icns_get_image_info_for_type(typeItemA);
16	icns_icon_info_t imageInfoB = icns_get_image_info_for_type(typeItemB);
17
18	return imageInfoB.iconWidth - imageInfoA.iconWidth;
19}
20
21
22icns_type_t
23ICNSFormat(float width, float height, color_space colors)
24{
25	int imageWidth = (int)ceil(width);
26	int imageHeight = (int)ceil(height);
27
28	if (imageWidth != imageHeight)
29		return ICNS_NULL_TYPE;
30
31	//other colors depth not supported now
32	if (colors != B_RGB32 && colors != B_RGBA32)
33		return ICNS_NULL_TYPE;
34
35	switch (imageWidth) {
36		case 16:
37			return ICNS_16x16_32BIT_DATA;
38		case 32:
39			return ICNS_32x32_32BIT_DATA;
40		case 48:
41			return ICNS_48x48_32BIT_DATA;
42		case 128:
43			return ICNS_128X128_32BIT_DATA;
44		case 256:
45			return ICNS_256x256_32BIT_ARGB_DATA;
46		case 512:
47			return ICNS_512x512_32BIT_ARGB_DATA;
48		case 1024:
49			return ICNS_1024x1024_32BIT_ARGB_DATA;
50	}
51	return ICNS_NULL_TYPE;
52}
53
54
55ICNSLoader::ICNSLoader(BPositionIO *stream)
56{
57	fLoaded = false;
58	fIconsCount = 0;
59
60	stream->Seek(0, SEEK_END);
61	fStreamSize = stream->Position();
62	stream->Seek(0, SEEK_SET);
63
64	if (fStreamSize <= 0)
65		return;
66
67	uint8* icnsDataBuffer = new uint8[fStreamSize];
68	size_t readedBytes = stream->Read(icnsDataBuffer,fStreamSize);
69
70	fIconFamily = NULL;
71	int status = icns_import_family_data(readedBytes, icnsDataBuffer,
72		&fIconFamily);
73
74	if (status != 0) {
75		delete icnsDataBuffer;
76		return;
77	}
78
79	icns_byte_t *dataPtr = (icns_byte_t*)fIconFamily;
80	off_t dataOffset = sizeof(icns_type_t) + sizeof(icns_size_t);
81
82	while ((dataOffset+8) < fIconFamily->resourceSize)
83	{
84		icns_element_t	 iconElement;
85		icns_size_t      iconDataSize;
86
87		memcpy(&iconElement, (dataPtr + dataOffset), 8);
88		iconDataSize = iconElement.elementSize - 8;
89
90		if(IS_SPUPPORTED_TYPE(iconElement.elementType)) {
91				icns_type_t* newTypeItem = new icns_type_t;
92				*newTypeItem = iconElement.elementType;
93				fFormatList.AddItem(newTypeItem);
94				fIconsCount++;
95		}
96		dataOffset += iconElement.elementSize;
97	}
98
99	fFormatList.SortItems(compareTypes);
100
101	delete icnsDataBuffer;
102
103	fLoaded = true;
104}
105
106
107ICNSLoader::~ICNSLoader()
108{
109	if (fIconFamily != NULL)
110		free(fIconFamily);
111
112	icns_type_t* item;
113	for (int32 i = 0; (item = (icns_type_t*)fFormatList.ItemAt(i)) != NULL; i++)
114   		delete item;
115   	fFormatList.MakeEmpty();
116}
117
118
119bool
120ICNSLoader::IsLoaded(void)
121{
122	return fLoaded;
123}
124
125
126int
127ICNSLoader::IconsCount(void)
128{
129	return fIconsCount;
130}
131
132
133int
134ICNSLoader::GetIcon(BPositionIO *target, int index)
135{
136	if (index < 1 || index > fIconsCount || !fLoaded)
137		return B_NO_TRANSLATOR;
138
139	icns_image_t iconImage;
140	memset(&iconImage, 0, sizeof(icns_image_t));
141
142	icns_type_t typeItem = *((icns_type_t*)fFormatList.ItemAt(index - 1));
143	int status = icns_get_image32_with_mask_from_family(fIconFamily,
144		typeItem, &iconImage);
145
146	if (status != 0)
147		return B_NO_TRANSLATOR;
148
149	TranslatorBitmap bitsHeader;
150	bitsHeader.magic = B_TRANSLATOR_BITMAP;
151	bitsHeader.bounds.left = 0;
152	bitsHeader.bounds.top = 0;
153	bitsHeader.bounds.right = iconImage.imageWidth - 1;
154	bitsHeader.bounds.bottom = iconImage.imageHeight - 1;
155	bitsHeader.rowBytes = sizeof(uint32) * iconImage.imageWidth;
156	bitsHeader.colors = B_RGBA32;
157	bitsHeader.dataSize = bitsHeader.rowBytes * iconImage.imageHeight;
158	if (swap_data(B_UINT32_TYPE, &bitsHeader,
159		sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
160		icns_free_image(&iconImage);
161		return B_NO_TRANSLATOR;
162	}
163	target->Write(&bitsHeader, sizeof(TranslatorBitmap));
164
165	uint8 *rowBuff = new uint8[iconImage.imageWidth * sizeof(uint32)];
166	for (uint32 i = 0; i < iconImage.imageHeight; i++) {
167		uint8 *rowData = iconImage.imageData
168			+ (i * iconImage.imageWidth * sizeof(uint32));
169		uint8 *rowBuffPtr = rowBuff;
170		for (uint32 j=0; j < iconImage.imageWidth; j++) {
171			rowBuffPtr[0] = rowData[2];
172			rowBuffPtr[1] = rowData[1];
173			rowBuffPtr[2] = rowData[0];
174			rowBuffPtr[3] = rowData[3];
175			rowBuffPtr += sizeof(uint32);
176			rowData += sizeof(uint32);
177		}
178		target->Write(rowBuff, iconImage.imageWidth * sizeof(uint32));
179	}
180	delete rowBuff;
181	icns_free_image(&iconImage);
182
183	return B_OK;
184}
185
186
187ICNSSaver::ICNSSaver(BPositionIO *stream, uint32 rowBytes, icns_type_t type)
188{
189	fCreated = false;
190
191	icns_icon_info_t imageTypeInfo = icns_get_image_info_for_type(type);
192	int iconWidth = imageTypeInfo.iconWidth;
193	int iconHeight = imageTypeInfo.iconWidth;
194	int bpp = 32;
195
196	uint8 *bits = new uint8[iconWidth * iconHeight * sizeof(uint32)];
197
198	uint8 *rowPtr = bits;
199	for (int i = 0; i < iconHeight; i++) {
200		stream->Read(rowPtr, rowBytes);
201		uint8 *bytePtr = rowPtr;
202		for (int j=0; j < iconWidth; j++) {
203			uint8 temp = bytePtr[0];
204			bytePtr[0] = bytePtr[2];
205			bytePtr[2] = temp;
206			bytePtr += sizeof(uint32);
207		}
208		rowPtr += iconWidth * sizeof(uint32);
209	}
210
211	icns_create_family(&fIconFamily);
212
213	icns_image_t icnsImage;
214	icnsImage.imageWidth = iconWidth;
215	icnsImage.imageHeight = iconHeight;
216	icnsImage.imageChannels = 4;
217	icnsImage.imagePixelDepth = 8;
218	icnsImage.imageDataSize = iconWidth * iconHeight * 4;
219	icnsImage.imageData = bits;
220
221	icns_icon_info_t iconInfo;
222	iconInfo.isImage = 1;
223	iconInfo.iconWidth = icnsImage.imageWidth;
224	iconInfo.iconHeight = icnsImage.imageHeight;
225	iconInfo.iconBitDepth = bpp;
226	iconInfo.iconChannels = (bpp == 32 ? 4 : 1);
227	iconInfo.iconPixelDepth = bpp / iconInfo.iconChannels;
228
229	icns_type_t iconType = icns_get_type_from_image_info(iconInfo);
230
231	if (iconType == ICNS_NULL_TYPE) {
232		delete bits;
233		free(fIconFamily);
234		fIconFamily = NULL;
235		return;
236	}
237
238	icns_element_t *iconElement = NULL;
239	int icnsErr = icns_new_element_from_image(&icnsImage, iconType,
240		&iconElement);
241
242	if (iconElement != NULL) {
243		if (icnsErr == ICNS_STATUS_OK) {
244			icns_set_element_in_family(&fIconFamily, iconElement);
245			fCreated = true;
246		}
247		free(iconElement);
248	}
249
250	if (iconType != ICNS_1024x1024_32BIT_ARGB_DATA
251		&& iconType != ICNS_512x512_32BIT_ARGB_DATA
252		&& iconType != ICNS_256x256_32BIT_ARGB_DATA)
253	{
254		icns_type_t maskType = icns_get_mask_type_for_icon_type(iconType);
255
256		icns_image_t icnsMask;
257		icns_init_image_for_type(maskType, &icnsMask);
258
259		uint32 iconDataOffset = 0;
260		uint32 maskDataOffset = 0;
261
262		while (iconDataOffset < icnsImage.imageDataSize
263			&& maskDataOffset < icnsMask.imageDataSize)	{
264			icnsMask.imageData[maskDataOffset] =
265				icnsImage.imageData[iconDataOffset + 3];
266			iconDataOffset += 4;
267			maskDataOffset += 1;
268		}
269
270		icns_element_t *maskElement = NULL;
271		icnsErr = icns_new_element_from_mask(&icnsMask, maskType,
272			&maskElement);
273
274		if (maskElement != NULL) {
275			if (icnsErr == ICNS_STATUS_OK)
276				icns_set_element_in_family(&fIconFamily, maskElement);
277			else
278				fCreated = false;
279			free(maskElement);
280		}
281		icns_free_image(&icnsMask);
282	}
283
284	if (!fCreated) {
285		free(fIconFamily);
286		fIconFamily = NULL;
287	}
288
289	delete bits;
290}
291
292
293ICNSSaver::~ICNSSaver()
294{
295	if (fIconFamily != NULL)
296		free(fIconFamily);
297}
298
299
300int
301ICNSSaver::SaveData(BPositionIO *target)
302{
303	icns_size_t dataSize;
304	icns_byte_t *dataPtrOut;
305	icns_export_family_data(fIconFamily, &dataSize, &dataPtrOut);
306	if (dataSize != 0 && dataPtrOut != NULL) {
307		if (target->Write(dataPtrOut, dataSize) == dataSize);
308			return B_OK;
309	}
310	return B_ERROR;
311}
312
313
314bool
315ICNSSaver::IsCreated(void)
316{
317	return fCreated;
318}
319