1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2008 by Denton Woods
5 // Last modified: 12/17/2008
6 //
7 // Filename: src-IL/src/il_stack.c
8 //
9 // Description: The main image stack
10 //
11 //-----------------------------------------------------------------------------
12
13 // Credit goes to John Villar (johnny@reliaschev.com) for making the suggestion
14 // of not letting the user use ILimage structs but instead binding images
15 // like OpenGL.
16
17 #include "il_internal.h"
18 #include "il_stack.h"
19
20 //! Creates Num images and puts their index in Images - similar to glGenTextures().
ilGenImages(ILsizei Num,ILuint * Images)21 void ILAPIENTRY ilGenImages(ILsizei Num, ILuint *Images)
22 {
23 ILsizei Index = 0;
24 iFree *TempFree = FreeNames;
25
26 if (Num < 1 || Images == NULL) {
27 ilSetError(IL_INVALID_VALUE);
28 return;
29 }
30
31 // No images have been generated yet, so create the image stack.
32 if (ImageStack == NULL)
33 if (!iEnlargeStack())
34 return;
35
36 do {
37 if (FreeNames != NULL) { // If any have been deleted, then reuse their image names.
38 TempFree = (iFree*)FreeNames->Next;
39 Images[Index] = FreeNames->Name;
40 ImageStack[FreeNames->Name] = ilNewImage(1, 1, 1, 1, 1);
41 ifree(FreeNames);
42 FreeNames = TempFree;
43 } else {
44 if (LastUsed >= StackSize)
45 if (!iEnlargeStack())
46 return;
47 Images[Index] = LastUsed;
48 // Must be all 1's instead of 0's, because some functions would divide by 0.
49 ImageStack[LastUsed] = ilNewImage(1, 1, 1, 1, 1);
50 LastUsed++;
51 }
52 } while (++Index < Num);
53
54 return;
55 }
56
ilGenImage()57 ILuint ILAPIENTRY ilGenImage()
58 {
59 ILuint i;
60 ilGenImages(1,&i);
61 return i;
62 }
63
64 //! Makes Image the current active image - similar to glBindTexture().
ilBindImage(ILuint Image)65 void ILAPIENTRY ilBindImage(ILuint Image)
66 {
67 if (ImageStack == NULL || StackSize == 0) {
68 if (!iEnlargeStack()) {
69 return;
70 }
71 }
72
73 // If the user requests a high image name.
74 while (Image >= StackSize) {
75 if (!iEnlargeStack()) {
76 return;
77 }
78 }
79
80 if (ImageStack[Image] == NULL) {
81 ImageStack[Image] = ilNewImage(1, 1, 1, 1, 1);
82 if (Image >= LastUsed) // >= ?
83 LastUsed = Image + 1;
84 }
85
86 iCurImage = ImageStack[Image];
87 CurName = Image;
88
89 ParentImage = IL_TRUE;
90
91 return;
92 }
93
94
95 //! Deletes Num images from the image stack - similar to glDeleteTextures().
ilDeleteImages(ILsizei Num,const ILuint * Images)96 void ILAPIENTRY ilDeleteImages(ILsizei Num, const ILuint *Images)
97 {
98 iFree *Temp = FreeNames;
99 ILuint Index = 0;
100
101 if (Num < 1) {
102 //ilSetError(IL_INVALID_VALUE);
103 return;
104 }
105 if (StackSize == 0)
106 return;
107
108 do {
109 if (Images[Index] > 0 && Images[Index] < LastUsed) { // <= ?
110 /*if (FreeNames != NULL) { // Terribly inefficient
111 Temp = FreeNames;
112 do {
113 if (Temp->Name == Images[Index]) {
114 continue; // Sufficient?
115 }
116 } while ((Temp = Temp->Next));
117 }*/
118
119 // Already has been deleted or was never used.
120 if (ImageStack[Images[Index]] == NULL)
121 continue;
122
123 // Find out if current image - if so, set to default image zero.
124 if (Images[Index] == CurName || Images[Index] == 0) {
125 iCurImage = ImageStack[0];
126 CurName = 0;
127 }
128
129 // Should *NOT* be NULL here!
130 ilCloseImage(ImageStack[Images[Index]]);
131 ImageStack[Images[Index]] = NULL;
132
133 // Add to head of list - works for empty and non-empty lists
134 Temp = (iFree*)ialloc(sizeof(iFree));
135 if (!Temp) {
136 return;
137 }
138 Temp->Name = Images[Index];
139 Temp->Next = FreeNames;
140 FreeNames = Temp;
141 }
142 /*else { // Shouldn't set an error...just continue onward.
143 ilSetError(IL_ILLEGAL_OPERATION);
144 }*/
145 } while (++Index < (ILuint)Num);
146 }
147
148
ilDeleteImage(const ILuint Num)149 void ILAPIENTRY ilDeleteImage(const ILuint Num) {
150 ilDeleteImages(1,&Num);
151 }
152
153 //! Checks if Image is a valid ilGenImages-generated image (like glIsTexture()).
ilIsImage(ILuint Image)154 ILboolean ILAPIENTRY ilIsImage(ILuint Image)
155 {
156 //iFree *Temp = FreeNames;
157
158 if (ImageStack == NULL)
159 return IL_FALSE;
160 if (Image >= LastUsed || Image == 0)
161 return IL_FALSE;
162
163 /*do {
164 if (Temp->Name == Image)
165 return IL_FALSE;
166 } while ((Temp = Temp->Next));*/
167
168 if (ImageStack[Image] == NULL) // Easier check.
169 return IL_FALSE;
170
171 return IL_TRUE;
172 }
173
174
175 //! Closes Image and frees all memory associated with it.
ilCloseImage(ILimage * Image)176 ILAPI void ILAPIENTRY ilCloseImage(ILimage *Image)
177 {
178 if (Image == NULL)
179 return;
180
181 if (Image->Data != NULL) {
182 ifree(Image->Data);
183 Image->Data = NULL;
184 }
185
186 if (Image->Pal.Palette != NULL && Image->Pal.PalSize > 0 && Image->Pal.PalType != IL_PAL_NONE) {
187 ifree(Image->Pal.Palette);
188 Image->Pal.Palette = NULL;
189 }
190
191 if (Image->Next != NULL) {
192 ilCloseImage(Image->Next);
193 Image->Next = NULL;
194 }
195
196 if (Image->Faces != NULL) {
197 ilCloseImage(Image->Faces);
198 Image->Mipmaps = NULL;
199 }
200
201 if (Image->Mipmaps != NULL) {
202 ilCloseImage(Image->Mipmaps);
203 Image->Mipmaps = NULL;
204 }
205
206 if (Image->Layers != NULL) {
207 ilCloseImage(Image->Layers);
208 Image->Layers = NULL;
209 }
210
211 if (Image->AnimList != NULL && Image->AnimSize != 0) {
212 ifree(Image->AnimList);
213 Image->AnimList = NULL;
214 }
215
216 if (Image->Profile != NULL && Image->ProfileSize != 0) {
217 ifree(Image->Profile);
218 Image->Profile = NULL;
219 Image->ProfileSize = 0;
220 }
221
222 if (Image->DxtcData != NULL && Image->DxtcFormat != IL_DXT_NO_COMP) {
223 ifree(Image->DxtcData);
224 Image->DxtcData = NULL;
225 Image->DxtcFormat = IL_DXT_NO_COMP;
226 Image->DxtcSize = 0;
227 }
228
229 ifree(Image);
230 Image = NULL;
231
232 return;
233 }
234
235
ilIsValidPal(ILpal * Palette)236 ILAPI ILboolean ILAPIENTRY ilIsValidPal(ILpal *Palette)
237 {
238 if (Palette == NULL)
239 return IL_FALSE;
240 if (Palette->PalSize == 0 || Palette->Palette == NULL)
241 return IL_FALSE;
242 switch (Palette->PalType)
243 {
244 case IL_PAL_RGB24:
245 case IL_PAL_RGB32:
246 case IL_PAL_RGBA32:
247 case IL_PAL_BGR24:
248 case IL_PAL_BGR32:
249 case IL_PAL_BGRA32:
250 return IL_TRUE;
251 }
252 return IL_FALSE;
253 }
254
255
256 //! Closes Palette and frees all memory associated with it.
ilClosePal(ILpal * Palette)257 ILAPI void ILAPIENTRY ilClosePal(ILpal *Palette)
258 {
259 if (Palette == NULL)
260 return;
261 if (!ilIsValidPal(Palette))
262 return;
263 ifree(Palette->Palette);
264 ifree(Palette);
265 return;
266 }
267
268
iGetBaseImage()269 ILimage *iGetBaseImage()
270 {
271 return ImageStack[ilGetCurName()];
272 }
273
274
275 //! Sets the current mipmap level
ilActiveMipmap(ILuint Number)276 ILboolean ILAPIENTRY ilActiveMipmap(ILuint Number)
277 {
278 ILuint Current;
279 ILimage *iTempImage;
280
281 if (iCurImage == NULL) {
282 ilSetError(IL_ILLEGAL_OPERATION);
283 return IL_FALSE;
284 }
285
286 if (Number == 0) {
287 return IL_TRUE;
288 }
289
290 iTempImage = iCurImage;
291 iCurImage = iCurImage->Mipmaps;
292 if (iCurImage == NULL) {
293 iCurImage = iTempImage;
294 ilSetError(IL_ILLEGAL_OPERATION);
295 return IL_FALSE;
296 }
297
298 for (Current = 1; Current < Number; Current++) {
299 iCurImage = iCurImage->Mipmaps;
300 if (iCurImage == NULL) {
301 ilSetError(IL_ILLEGAL_OPERATION);
302 iCurImage = iTempImage;
303 return IL_FALSE;
304 }
305 }
306
307 ParentImage = IL_FALSE;
308
309 return IL_TRUE;
310 }
311
312
313 //! Used for setting the current image if it is an animation.
ilActiveImage(ILuint Number)314 ILboolean ILAPIENTRY ilActiveImage(ILuint Number)
315 {
316 ILuint Current;
317 ILimage *iTempImage;
318
319 if (iCurImage == NULL) {
320 ilSetError(IL_ILLEGAL_OPERATION);
321 return IL_FALSE;
322 }
323
324 if (Number == 0) {
325 return IL_TRUE;
326 }
327
328 iTempImage = iCurImage;
329 iCurImage = iCurImage->Next;
330 if (iCurImage == NULL) {
331 iCurImage = iTempImage;
332 ilSetError(IL_ILLEGAL_OPERATION);
333 return IL_FALSE;
334 }
335
336 Number--; // Skip 0 (parent image)
337 for (Current = 0; Current < Number; Current++) {
338 iCurImage = iCurImage->Next;
339 if (iCurImage == NULL) {
340 ilSetError(IL_ILLEGAL_OPERATION);
341 iCurImage = iTempImage;
342 return IL_FALSE;
343 }
344 }
345
346 ParentImage = IL_FALSE;
347
348 return IL_TRUE;
349 }
350
351
352 //! Used for setting the current face if it is a cubemap.
ilActiveFace(ILuint Number)353 ILboolean ILAPIENTRY ilActiveFace(ILuint Number)
354 {
355 ILuint Current;
356 ILimage *iTempImage;
357
358 if (iCurImage == NULL) {
359 ilSetError(IL_ILLEGAL_OPERATION);
360 return IL_FALSE;
361 }
362
363 if (Number == 0) {
364 return IL_TRUE;
365 }
366
367 iTempImage = iCurImage;
368 iCurImage = iCurImage->Faces;
369 if (iCurImage == NULL) {
370 iCurImage = iTempImage;
371 ilSetError(IL_ILLEGAL_OPERATION);
372 return IL_FALSE;
373 }
374
375 //Number--; // Skip 0 (parent image)
376 for (Current = 1; Current < Number; Current++) {
377 iCurImage = iCurImage->Faces;
378 if (iCurImage == NULL) {
379 ilSetError(IL_ILLEGAL_OPERATION);
380 iCurImage = iTempImage;
381 return IL_FALSE;
382 }
383 }
384
385 ParentImage = IL_FALSE;
386
387 return IL_TRUE;
388 }
389
390
391
392 //! Used for setting the current layer if layers exist.
ilActiveLayer(ILuint Number)393 ILboolean ILAPIENTRY ilActiveLayer(ILuint Number)
394 {
395 ILuint Current;
396 ILimage *iTempImage;
397
398 if (iCurImage == NULL) {
399 ilSetError(IL_ILLEGAL_OPERATION);
400 return IL_FALSE;
401 }
402
403 if (Number == 0) {
404 return IL_TRUE;
405 }
406
407 iTempImage = iCurImage;
408 iCurImage = iCurImage->Layers;
409 if (iCurImage == NULL) {
410 iCurImage = iTempImage;
411 ilSetError(IL_ILLEGAL_OPERATION);
412 return IL_FALSE;
413 }
414
415 //Number--; // Skip 0 (parent image)
416 for (Current = 1; Current < Number; Current++) {
417 iCurImage = iCurImage->Layers;
418 if (iCurImage == NULL) {
419 ilSetError(IL_ILLEGAL_OPERATION);
420 iCurImage = iTempImage;
421 return IL_FALSE;
422 }
423 }
424
425 ParentImage = IL_FALSE;
426
427 return IL_TRUE;
428 }
429
430
ilCreateSubImage(ILenum Type,ILuint Num)431 ILuint ILAPIENTRY ilCreateSubImage(ILenum Type, ILuint Num)
432 {
433 ILimage *SubImage;
434 ILuint Count ; // Create one before we go in the loop.
435
436 if (iCurImage == NULL) {
437 ilSetError(IL_ILLEGAL_OPERATION);
438 return 0;
439 }
440 if (Num == 0) {
441 return 0;
442 }
443
444 switch (Type)
445 {
446 case IL_SUB_NEXT:
447 if (iCurImage->Next)
448 ilCloseImage(iCurImage->Next);
449 iCurImage->Next = ilNewImage(1, 1, 1, 1, 1);
450 SubImage = iCurImage->Next;
451 break;
452
453 case IL_SUB_MIPMAP:
454 if (iCurImage->Mipmaps)
455 ilCloseImage(iCurImage->Mipmaps);
456 iCurImage->Mipmaps = ilNewImage(1, 1, 1, 1, 1);
457 SubImage = iCurImage->Mipmaps;
458 break;
459
460 case IL_SUB_LAYER:
461 if (iCurImage->Layers)
462 ilCloseImage(iCurImage->Layers);
463 iCurImage->Layers = ilNewImage(1, 1, 1, 1, 1);
464 SubImage = iCurImage->Layers;
465 break;
466
467 default:
468 ilSetError(IL_INVALID_ENUM);
469 return IL_FALSE;
470 }
471
472 if (SubImage == NULL) {
473 return 0;
474 }
475
476 for (Count = 1; Count < Num; Count++) {
477 SubImage->Next = ilNewImage(1, 1, 1, 1, 1);
478 SubImage = SubImage->Next;
479 if (SubImage == NULL)
480 return Count;
481 }
482
483 return Count;
484 }
485
486
487 // Returns the current index.
ilGetCurName()488 ILAPI ILuint ILAPIENTRY ilGetCurName()
489 {
490 if (iCurImage == NULL || ImageStack == NULL || StackSize == 0)
491 return 0;
492 return CurName;
493 }
494
495
496 // Returns the current image.
ilGetCurImage()497 ILAPI ILimage* ILAPIENTRY ilGetCurImage()
498 {
499 return iCurImage;
500 }
501
502
503 // To be only used when the original image is going to be set back almost immediately.
ilSetCurImage(ILimage * Image)504 ILAPI void ILAPIENTRY ilSetCurImage(ILimage *Image)
505 {
506 iCurImage = Image;
507 return;
508 }
509
510
511 // Completely replaces the current image and the version in the image stack.
ilReplaceCurImage(ILimage * Image)512 ILAPI void ILAPIENTRY ilReplaceCurImage(ILimage *Image)
513 {
514 if (iCurImage) {
515 ilActiveImage(0);
516 ilCloseImage(iCurImage);
517 }
518 ImageStack[ilGetCurName()] = Image;
519 iCurImage = Image;
520 ParentImage = IL_TRUE;
521 return;
522 }
523
524
525 // Like realloc but sets new memory to 0.
ilRecalloc(void * Ptr,ILuint OldSize,ILuint NewSize)526 void* ILAPIENTRY ilRecalloc(void *Ptr, ILuint OldSize, ILuint NewSize)
527 {
528 void *Temp = ialloc(NewSize);
529 ILuint CopySize = (OldSize < NewSize) ? OldSize : NewSize;
530
531 if (Temp != NULL) {
532 if (Ptr != NULL) {
533 memcpy(Temp, Ptr, CopySize);
534 ifree(Ptr);
535 }
536
537 Ptr = Temp;
538
539 if (OldSize < NewSize)
540 imemclear((ILubyte*)Temp + OldSize, NewSize - OldSize);
541 }
542
543 return Temp;
544 }
545
546
547 // Internal function to enlarge the image stack by I_STACK_INCREMENT members.
iEnlargeStack()548 ILboolean iEnlargeStack()
549 {
550 // 02-05-2001: Moved from ilGenImages().
551 // Puts the cleanup function on the exit handler once.
552 if (!OnExit) {
553 #ifdef _MEM_DEBUG
554 AddToAtexit(); // So iFreeMem doesn't get called after unfreed information.
555 #endif//_MEM_DEBUG
556 #if (!defined(_WIN32_WCE)) && (!defined(IL_STATIC_LIB))
557 atexit((void*)ilShutDown);
558 #endif
559 OnExit = IL_TRUE;
560 }
561
562 if (!(ImageStack = (ILimage**)ilRecalloc(ImageStack, StackSize * sizeof(ILimage*), (StackSize + I_STACK_INCREMENT) * sizeof(ILimage*)))) {
563 return IL_FALSE;
564 }
565 StackSize += I_STACK_INCREMENT;
566 return IL_TRUE;
567 }
568
569
570 static ILboolean IsInit = IL_FALSE;
571
572 // ONLY call at startup.
ilInit()573 void ILAPIENTRY ilInit()
574 {
575 // if it is already initialized skip initialization
576 if (IsInit == IL_TRUE )
577 return;
578
579 //ilSetMemory(NULL, NULL); Now useless 3/4/2006 (due to modification in il_alloc.c)
580 ilSetError(IL_NO_ERROR);
581 ilDefaultStates(); // Set states to their defaults.
582 // Sets default file-reading callbacks.
583 ilResetRead();
584 ilResetWrite();
585 #if (!defined(_WIN32_WCE)) && (!defined(IL_STATIC_LIB))
586 atexit((void*)ilRemoveRegistered);
587 #endif
588 //_WIN32_WCE
589 //ilShutDown();
590 iSetImage0(); // Beware! Clears all existing textures!
591 iBindImageTemp(); // Go ahead and create the temporary image.
592 IsInit = IL_TRUE;
593 return;
594 }
595
596
597 // Frees any extra memory in the stack.
598 // - Called on exit
ilShutDown()599 void ILAPIENTRY ilShutDown()
600 {
601 // if it is not initialized do not shutdown
602 iFree* TempFree = (iFree*)FreeNames;
603 ILuint i;
604
605 if (!IsInit)
606 return;
607
608 if (!IsInit) { // Prevent from being called when not initialized.
609 ilSetError(IL_ILLEGAL_OPERATION);
610 return;
611 }
612
613 while (TempFree != NULL) {
614 FreeNames = (iFree*)TempFree->Next;
615 ifree(TempFree);
616 TempFree = FreeNames;
617 }
618
619 //for (i = 0; i < LastUsed; i++) {
620 for (i = 0; i < StackSize; i++) {
621 if (ImageStack[i] != NULL)
622 ilCloseImage(ImageStack[i]);
623 }
624
625 if (ImageStack)
626 ifree(ImageStack);
627 ImageStack = NULL;
628 LastUsed = 0;
629 StackSize = 0;
630 IsInit = IL_FALSE;
631 return;
632 }
633
634
635 // Initializes the image stack's first entry (default image) -- ONLY CALL ONCE!
iSetImage0()636 void iSetImage0()
637 {
638 if (ImageStack == NULL)
639 if (!iEnlargeStack())
640 return;
641
642 LastUsed = 1;
643 CurName = 0;
644 ParentImage = IL_TRUE;
645 if (!ImageStack[0])
646 ImageStack[0] = ilNewImage(1, 1, 1, 1, 1);
647 iCurImage = ImageStack[0];
648 ilDefaultImage();
649
650 return;
651 }
652
653
iBindImageTemp()654 ILAPI void ILAPIENTRY iBindImageTemp()
655 {
656 if (ImageStack == NULL || StackSize <= 1)
657 if (!iEnlargeStack())
658 return;
659
660 if (LastUsed < 2)
661 LastUsed = 2;
662 CurName = 1;
663 ParentImage = IL_TRUE;
664 if (!ImageStack[1])
665 ImageStack[1] = ilNewImage(1, 1, 1, 1, 1);
666 iCurImage = ImageStack[1];
667
668 return;
669 }
670