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**)a;
13	icns_type_t **typeItemB = (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		icns_element_t	 iconElement;
84		icns_size_t      iconDataSize;
85
86		memcpy(&iconElement, (dataPtr + dataOffset), 8);
87		iconDataSize = iconElement.elementSize - 8;
88
89		if (IS_SPUPPORTED_TYPE(iconElement.elementType)) {
90				icns_type_t* newTypeItem = new icns_type_t;
91				*newTypeItem = iconElement.elementType;
92				fFormatList.AddItem(newTypeItem);
93				fIconsCount++;
94		}
95		dataOffset += iconElement.elementSize;
96	}
97
98	fFormatList.SortItems(compareTypes);
99
100	delete[] icnsDataBuffer;
101
102	fLoaded = true;
103}
104
105
106ICNSLoader::~ICNSLoader()
107{
108	if (fIconFamily != NULL)
109		free(fIconFamily);
110
111	icns_type_t* item;
112	for (int32 i = 0; (item = (icns_type_t*)fFormatList.ItemAt(i)) != NULL; i++)
113   		delete item;
114   	fFormatList.MakeEmpty();
115}
116
117
118bool
119ICNSLoader::IsLoaded(void)
120{
121	return fLoaded;
122}
123
124
125int
126ICNSLoader::IconsCount(void)
127{
128	return fIconsCount;
129}
130
131
132int
133ICNSLoader::GetIcon(BPositionIO *target, int index)
134{
135	if (index < 1 || index > fIconsCount || !fLoaded)
136		return B_NO_TRANSLATOR;
137
138	icns_image_t iconImage;
139	memset(&iconImage, 0, sizeof(icns_image_t));
140
141	icns_type_t typeItem = *((icns_type_t*)fFormatList.ItemAt(index - 1));
142	int status = icns_get_image32_with_mask_from_family(fIconFamily,
143		typeItem, &iconImage);
144
145	if (status != 0)
146		return B_NO_TRANSLATOR;
147
148	TranslatorBitmap bitsHeader;
149	bitsHeader.magic = B_TRANSLATOR_BITMAP;
150	bitsHeader.bounds.left = 0;
151	bitsHeader.bounds.top = 0;
152	bitsHeader.bounds.right = iconImage.imageWidth - 1;
153	bitsHeader.bounds.bottom = iconImage.imageHeight - 1;
154	bitsHeader.rowBytes = sizeof(uint32) * iconImage.imageWidth;
155	bitsHeader.colors = B_RGBA32;
156	bitsHeader.dataSize = bitsHeader.rowBytes * iconImage.imageHeight;
157	if (swap_data(B_UINT32_TYPE, &bitsHeader,
158		sizeof(TranslatorBitmap), B_SWAP_HOST_TO_BENDIAN) != B_OK) {
159		icns_free_image(&iconImage);
160		return B_NO_TRANSLATOR;
161	}
162	target->Write(&bitsHeader, sizeof(TranslatorBitmap));
163
164	uint8 *rowBuff = new uint8[iconImage.imageWidth * sizeof(uint32)];
165	for (uint32 i = 0; i < iconImage.imageHeight; i++) {
166		uint8 *rowData = iconImage.imageData
167			+ (i * iconImage.imageWidth * sizeof(uint32));
168		uint8 *rowBuffPtr = rowBuff;
169		for (uint32 j=0; j < iconImage.imageWidth; j++) {
170			rowBuffPtr[0] = rowData[2];
171			rowBuffPtr[1] = rowData[1];
172			rowBuffPtr[2] = rowData[0];
173			rowBuffPtr[3] = rowData[3];
174			rowBuffPtr += sizeof(uint32);
175			rowData += sizeof(uint32);
176		}
177		target->Write(rowBuff, iconImage.imageWidth * sizeof(uint32));
178	}
179	delete[] rowBuff;
180	icns_free_image(&iconImage);
181
182	return B_OK;
183}
184
185
186ICNSSaver::ICNSSaver(BPositionIO *stream, uint32 rowBytes, icns_type_t type)
187{
188	fCreated = false;
189
190	icns_icon_info_t imageTypeInfo = icns_get_image_info_for_type(type);
191	int iconWidth = imageTypeInfo.iconWidth;
192	int iconHeight = imageTypeInfo.iconHeight;
193	int bpp = 32;
194
195	uint8 *bits = new uint8[iconWidth * iconHeight * sizeof(uint32)];
196
197	uint8 *rowPtr = bits;
198	for (int i = 0; i < iconHeight; i++) {
199		stream->Read(rowPtr, rowBytes);
200		uint8 *bytePtr = rowPtr;
201		for (int j=0; j < iconWidth; j++) {
202			uint8 temp = bytePtr[0];
203			bytePtr[0] = bytePtr[2];
204			bytePtr[2] = temp;
205			bytePtr += sizeof(uint32);
206		}
207		rowPtr += iconWidth * sizeof(uint32);
208	}
209
210	icns_create_family(&fIconFamily);
211
212	icns_image_t icnsImage;
213	icnsImage.imageWidth = iconWidth;
214	icnsImage.imageHeight = iconHeight;
215	icnsImage.imageChannels = 4;
216	icnsImage.imagePixelDepth = 8;
217	icnsImage.imageDataSize = iconWidth * iconHeight * 4;
218	icnsImage.imageData = bits;
219
220	icns_icon_info_t iconInfo;
221	iconInfo.isImage = 1;
222	iconInfo.iconWidth = icnsImage.imageWidth;
223	iconInfo.iconHeight = icnsImage.imageHeight;
224	iconInfo.iconBitDepth = bpp;
225	iconInfo.iconChannels = (bpp == 32 ? 4 : 1);
226	iconInfo.iconPixelDepth = bpp / iconInfo.iconChannels;
227
228	icns_type_t iconType = icns_get_type_from_image_info(iconInfo);
229
230	if (iconType == ICNS_NULL_TYPE) {
231		delete[] bits;
232		free(fIconFamily);
233		fIconFamily = NULL;
234		return;
235	}
236
237	icns_element_t *iconElement = NULL;
238	int icnsErr = icns_new_element_from_image(&icnsImage, iconType,
239		&iconElement);
240
241	if (iconElement != NULL) {
242		if (icnsErr == ICNS_STATUS_OK) {
243			icns_set_element_in_family(&fIconFamily, iconElement);
244			fCreated = true;
245		}
246		free(iconElement);
247	}
248
249	if (iconType != ICNS_1024x1024_32BIT_ARGB_DATA
250		&& iconType != ICNS_512x512_32BIT_ARGB_DATA
251		&& iconType != ICNS_256x256_32BIT_ARGB_DATA) {
252		icns_type_t maskType =
253			icns_get_mask_type_for_icon_type(iconType);
254
255		icns_image_t icnsMask;
256		icns_init_image_for_type(maskType, &icnsMask);
257
258		uint32 iconDataOffset = 0;
259		uint32 maskDataOffset = 0;
260
261		while (iconDataOffset < icnsImage.imageDataSize
262			&& maskDataOffset < icnsMask.imageDataSize) {
263			icnsMask.imageData[maskDataOffset] =
264				icnsImage.imageData[iconDataOffset + 3];
265			iconDataOffset += 4;
266			maskDataOffset += 1;
267		}
268
269		icns_element_t *maskElement = NULL;
270		icnsErr = icns_new_element_from_mask(&icnsMask, maskType,
271			&maskElement);
272
273		if (maskElement != NULL) {
274			if (icnsErr == ICNS_STATUS_OK) {
275				icns_set_element_in_family(&fIconFamily,
276					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