1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2001-2009 by Denton Woods
5 // Last modified: 03/07/2009
6 //
7 // Filename: src-IL/src/il_icns.c
8 //
9 // Description: Reads from a Mac OS X icon (.icns) file.
10 //		Credit for the format of .icns files goes to
11 //		http://www.macdisk.com/maciconen.php3 and
12 //		http://ezix.org/project/wiki/MacOSXIcons
13 //
14 //-----------------------------------------------------------------------------
15 
16 //@TODO: Put ilSetError calls in when errors occur.
17 //@TODO: Should we clear the alpha channel just in case there isn't one in the file?
18 //@TODO: Checks on iread
19 
20 #include "il_internal.h"
21 #ifndef IL_NO_ICNS
22 #include "il_icns.h"
23 
24 #ifndef IL_NO_JP2
25 	#if defined(_WIN32) && defined(IL_USE_PRAGMA_LIBS)
26 		#if defined(_MSC_VER) || defined(__BORLANDC__)
27 			#ifndef _DEBUG
28 				#pragma comment(lib, "libjasper.lib")
29 			#else
30 				#pragma comment(lib, "libjasper-d.lib")
31 			#endif
32 		#endif
33 	#endif
34 #endif//IL_NO_JP2
35 
36 
37 //! Checks if the file specified in FileName is a valid .icns file.
ilIsValidIcns(ILconst_string FileName)38 ILboolean ilIsValidIcns(ILconst_string FileName)
39 {
40 	ILHANDLE	IcnsFile;
41 	ILboolean	bIcns = IL_FALSE;
42 
43 	if (!iCheckExtension(FileName, IL_TEXT("icns"))) {
44 		ilSetError(IL_INVALID_EXTENSION);
45 		return bIcns;
46 	}
47 
48 	IcnsFile = iopenr(FileName);
49 	if (IcnsFile == NULL) {
50 		ilSetError(IL_COULD_NOT_OPEN_FILE);
51 		return bIcns;
52 	}
53 
54 	bIcns = ilIsValidIcnsF(IcnsFile);
55 	icloser(IcnsFile);
56 
57 	return bIcns;
58 }
59 
60 
61 //! Checks if the ILHANDLE contains a valid .icns file at the current position.
ilIsValidIcnsF(ILHANDLE File)62 ILboolean ilIsValidIcnsF(ILHANDLE File)
63 {
64 	ILuint		FirstPos;
65 	ILboolean	bRet;
66 
67 	iSetInputFile(File);
68 	FirstPos = itell();
69 	bRet = iIsValidIcns();
70 	iseek(FirstPos, IL_SEEK_SET);
71 
72 	return bRet;
73 }
74 
75 
76 //! Checks if Lump is a valid .icns lump.
ilIsValidIcnsL(const void * Lump,ILuint Size)77 ILboolean ilIsValidIcnsL(const void *Lump, ILuint Size)
78 {
79 	iSetInputLump(Lump, Size);
80 	return iIsValidIcns();
81 }
82 
83 
84 // Internal function to get the header and check it.
iIsValidIcns()85 ILboolean iIsValidIcns()
86 {
87 	ICNSHEAD	Header;
88 
89 	iread(Header.Head, 1, 4);
90 	iseek(-4, IL_SEEK_CUR);  // Go ahead and restore to previous state
91 
92 	if (strncmp(Header.Head, "icns", 4))  // First 4 bytes have to be 'icns'.
93 		return IL_FALSE;
94 
95 	return IL_TRUE;
96 }
97 
98 
99 //! Reads an icon file.
ilLoadIcns(ILconst_string FileName)100 ILboolean ilLoadIcns(ILconst_string FileName)
101 {
102 	ILHANDLE	IcnsFile;
103 	ILboolean	bIcns = IL_FALSE;
104 
105 	IcnsFile = iopenr(FileName);
106 	if (IcnsFile == NULL) {
107 		ilSetError(IL_COULD_NOT_OPEN_FILE);
108 		return bIcns;
109 	}
110 
111 	bIcns = ilLoadIcnsF(IcnsFile);
112 	icloser(IcnsFile);
113 
114 	return bIcns;
115 }
116 
117 
118 //! Reads an already-opened icon file.
ilLoadIcnsF(ILHANDLE File)119 ILboolean ilLoadIcnsF(ILHANDLE File)
120 {
121 	ILuint		FirstPos;
122 	ILboolean	bRet;
123 
124 	iSetInputFile(File);
125 	FirstPos = itell();
126 	bRet = iLoadIcnsInternal();
127 	iseek(FirstPos, IL_SEEK_SET);
128 
129 	return bRet;
130 }
131 
132 
133 //! Reads from a memory "lump" that contains an icon.
ilLoadIcnsL(const void * Lump,ILuint Size)134 ILboolean ilLoadIcnsL(const void *Lump, ILuint Size)
135 {
136 	iSetInputLump(Lump, Size);
137 	return iLoadIcnsInternal();
138 }
139 
140 
141 // Internal function used to load the icon.
iLoadIcnsInternal()142 ILboolean iLoadIcnsInternal()
143 {
144 	ICNSHEAD	Header;
145 	ICNSDATA	Entry;
146 	ILimage		*Image = NULL;
147 	ILboolean	BaseCreated = IL_FALSE;
148 
149 
150 	if (iCurImage == NULL)
151 	{
152 		ilSetError(IL_ILLEGAL_OPERATION);
153 		return IL_FALSE;
154 	}
155 
156 	iread(Header.Head, 4, 1);
157 	Header.Size = GetBigInt();
158 
159 	if (strncmp(Header.Head, "icns", 4))  // First 4 bytes have to be 'icns'.
160 		return IL_FALSE;
161 
162 	while ((ILint)itell() < Header.Size && !ieof())
163 	{
164 		iread(Entry.ID, 4, 1);
165 		Entry.Size = GetBigInt();
166 
167 		if (!strncmp(Entry.ID, "it32", 4))  // 128x128 24-bit
168 		{
169 			if (iIcnsReadData(&BaseCreated, IL_FALSE, 128, &Entry, &Image) == IL_FALSE)
170 				goto icns_error;
171 		}
172 		else if (!strncmp(Entry.ID, "t8mk", 4))  // 128x128 alpha mask
173 		{
174 			if (iIcnsReadData(&BaseCreated, IL_TRUE, 128, &Entry, &Image) == IL_FALSE)
175 				goto icns_error;
176 		}
177 		else if (!strncmp(Entry.ID, "ih32", 4))  // 48x48 24-bit
178 		{
179 			if (iIcnsReadData(&BaseCreated, IL_FALSE, 48, &Entry, &Image) == IL_FALSE)
180 				goto icns_error;
181 		}
182 		else if (!strncmp(Entry.ID, "h8mk", 4))  // 48x48 alpha mask
183 		{
184 			if (iIcnsReadData(&BaseCreated, IL_TRUE, 48, &Entry, &Image) == IL_FALSE)
185 				goto icns_error;
186 		}
187 		else if (!strncmp(Entry.ID, "il32", 4))  // 32x32 24-bit
188 		{
189 			if (iIcnsReadData(&BaseCreated, IL_FALSE, 32, &Entry, &Image) == IL_FALSE)
190 				goto icns_error;
191 		}
192 		else if (!strncmp(Entry.ID, "l8mk", 4))  // 32x32 alpha mask
193 		{
194 			if (iIcnsReadData(&BaseCreated, IL_TRUE, 32, &Entry, &Image) == IL_FALSE)
195 				goto icns_error;
196 		}
197 		else if (!strncmp(Entry.ID, "is32", 4))  // 16x16 24-bit
198 		{
199 			if (iIcnsReadData(&BaseCreated, IL_FALSE, 16, &Entry, &Image) == IL_FALSE)
200 				goto icns_error;
201 		}
202 		else if (!strncmp(Entry.ID, "s8mk", 4))  // 16x16 alpha mask
203 		{
204 			if (iIcnsReadData(&BaseCreated, IL_TRUE, 16, &Entry, &Image) == IL_FALSE)
205 				goto icns_error;
206 		}
207 #ifndef IL_NO_JP2
208 		else if (!strncmp(Entry.ID, "ic09", 4))  // 512x512 JPEG2000 encoded - Uses JasPer
209 		{
210 			if (iIcnsReadData(&BaseCreated, IL_FALSE, 512, &Entry, &Image) == IL_FALSE)
211 				goto icns_error;
212 		}
213 		else if (!strncmp(Entry.ID, "ic08", 4))  // 256x256 JPEG2000 encoded - Uses JasPer
214 		{
215 			if (iIcnsReadData(&BaseCreated, IL_FALSE, 256, &Entry, &Image) == IL_FALSE)
216 				goto icns_error;
217 		}
218 #endif//IL_NO_JP2
219 		else  // Not a valid format or one that we can use
220 		{
221 			iseek(Entry.Size - 8, IL_SEEK_CUR);
222 		}
223 	}
224 
225 	return ilFixImage();
226 
227 icns_error:
228 	return IL_FALSE;
229 }
230 
iIcnsReadData(ILboolean * BaseCreated,ILboolean IsAlpha,ILint Width,ICNSDATA * Entry,ILimage ** Image)231 ILboolean iIcnsReadData(ILboolean *BaseCreated, ILboolean IsAlpha, ILint Width, ICNSDATA *Entry, ILimage **Image)
232 {
233 	ILint		Position = 0, RLEPos = 0, Channel, i;
234 	ILubyte		RLERead, *Data = NULL;
235 	ILimage		*TempImage = NULL;
236 	ILboolean	ImageAlreadyExists = IL_FALSE;
237 
238 	// The .icns format stores the alpha and RGB as two separate images, so this
239 	//  checks to see if one exists for that particular size.  Unfortunately,
240 	//	there is no guarantee that they are in any particular order.
241 	if (*BaseCreated && iCurImage != NULL)
242 	{
243 		TempImage = iCurImage;
244 		while (TempImage != NULL)
245 		{
246 			if ((ILuint)Width == TempImage->Width)
247 			{
248 				ImageAlreadyExists = IL_TRUE;
249 				break;
250 			}
251 			TempImage = TempImage->Next;
252 		}
253 	}
254 
255 	Data = ialloc(Entry->Size - 8);
256 	if (Data == NULL)
257 		return IL_FALSE;
258 
259 	if (!ImageAlreadyExists)
260 	{
261 		if (!*BaseCreated)  // Create base image
262 		{
263 			ilTexImage(Width, Width, 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL);
264 			iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
265 			*Image = iCurImage;
266 			*BaseCreated = IL_TRUE;
267 		}
268 		else  // Create next image in list
269 		{
270 			(*Image)->Next = ilNewImage(Width, Width, 1, 4, 1);
271 			*Image = (*Image)->Next;
272 			(*Image)->Format = IL_RGBA;
273 			(*Image)->Origin = IL_ORIGIN_UPPER_LEFT;
274 		}
275 
276 		TempImage = *Image;
277 	}
278 
279 	if (IsAlpha)  // Alpha is never compressed.
280 	{
281 		iread(Data, Entry->Size - 8, 1);  // Size includes the header
282 		if (Entry->Size - 8 != Width * Width)
283 		{
284 			ifree(Data);
285 			return IL_FALSE;
286 		}
287 
288 		for (i = 0; i < Width * Width; i++)
289 		{
290 			TempImage->Data[(i * 4) + 3] = Data[i];
291 		}
292 	}
293 	else if (Width == 256 || Width == 512)  // JPEG2000 encoded - uses JasPer
294 	{
295 #ifndef IL_NO_JP2
296 		iread(Data, Entry->Size - 8, 1);  // Size includes the header
297 		if (ilLoadJp2LInternal(Data, Entry->Size - 8, TempImage) == IL_FALSE)
298 		{
299 			ifree(Data);
300 			ilSetError(IL_LIB_JP2_ERROR);
301 			return IL_TRUE;
302 		}
303 #else  // Cannot handle this size.
304 		ilSetError(IL_LIB_JP2_ERROR);  //@TODO: Handle this better...just skip the data.
305 		return IL_FALSE;
306 #endif//IL_NO_JP2
307 	}
308 	else  // RGB data
309 	{
310 		iread(Data, Entry->Size - 8, 1);  // Size includes the header
311 		if (Width == 128)
312 			RLEPos += 4;  // There are an extra 4 bytes here of zeros.
313 		//@TODO: Should we check to make sure they are all 0?
314 
315 		if (Entry->Size - 8 == Width * Width * 4) // Uncompressed
316 		{
317 			//memcpy(TempImage->Data, Data, Entry->Size);
318 			for (i = 0; i < Width * Width; i++, Position += 4)
319 			{
320 				TempImage->Data[i * 4 + 0] = Data[Position+1];
321 				TempImage->Data[i * 4 + 1] = Data[Position+2];
322 				TempImage->Data[i * 4 + 2] = Data[Position+3];
323 			}
324 		}
325 		else  // RLE decoding
326 		{
327 			for (Channel = 0; Channel < 3; Channel++)
328 			{
329 				Position = 0;
330 				while (Position < Width * Width)
331 				{
332 					RLERead = Data[RLEPos];
333 					RLEPos++;
334 
335 					if (RLERead >= 128)
336 					{
337 						for (i = 0; i < RLERead - 125 && (Position + i) < Width * Width; i++)
338 						{
339 							TempImage->Data[Channel + (Position + i) * 4] = Data[RLEPos];
340 						}
341 						RLEPos++;
342 						Position += RLERead - 125;
343 					}
344 					else
345 					{
346 						for (i = 0; i < RLERead + 1 && (Position + i) < Width * Width; i++)
347 						{
348 							TempImage->Data[Channel + (Position + i) * 4] = Data[RLEPos + i];
349 						}
350 						RLEPos += RLERead + 1;
351 						Position += RLERead + 1;
352 					}
353 				}
354 			}
355 		}
356 	}
357 
358 	ifree(Data);
359 	return IL_TRUE;
360 }
361 
362 #endif//IL_NO_ICNS
363