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