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