1 //----------------------------------------------------------------------------
2 //  EDGE Generalised Image Handling
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2009  The EDGE Team.
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //----------------------------------------------------------------------------
18 //
19 //  Based on the DOOM source code, released by Id Software under the
20 //  following copyright:
21 //
22 //    Copyright (C) 1993-1996 by id Software, Inc.
23 //
24 //----------------------------------------------------------------------------
25 //
26 // -AJA- 2000/06/25: Began this image generalisation, based on Erik
27 //       Sandberg's w_textur.c/h code.
28 //
29 // TODO HERE:
30 //   -  faster search methods.
31 //   -  do some optimisation
32 //
33 
34 #include "i_defs.h"
35 #include "i_defs_gl.h"
36 
37 #include <limits.h>
38 #include <list>
39 
40 #include "epi/endianess.h"
41 #include "epi/file.h"
42 #include "epi/filesystem.h"
43 #include "epi/file_memory.h"
44 
45 #include "epi/image_data.h"
46 #include "epi/image_hq2x.h"
47 #include "epi/image_png.h"
48 #include "epi/image_jpeg.h"
49 #include "epi/image_tga.h"
50 
51 #include "dm_data.h"
52 #include "dm_defs.h"
53 #include "dm_state.h"
54 #include "e_search.h"
55 #include "e_main.h"
56 #include "m_argv.h"
57 #include "m_misc.h"
58 #include "p_local.h"
59 #include "r_defs.h"
60 #include "r_gldefs.h"
61 #include "r_image.h"
62 #include "r_sky.h"
63 #include "r_texgl.h"
64 #include "r_colormap.h"
65 #include "w_texture.h"
66 #include "w_wad.h"
67 #include "z_zone.h"
68 
69 
70 // LIGHTING DEBUGGING
71 // #define MAKE_TEXTURES_WHITE  1
72 
73 extern epi::image_data_c *ReadAsEpiBlock(image_c *rim);
74 
75 extern epi::file_c *OpenUserFileOrLump(imagedef_c *def);
76 
77 extern void CloseUserFileOrLump(imagedef_c *def, epi::file_c *f);
78 
79 // FIXME: duplicated in r_doomtex
80 #define DUMMY_X  16
81 #define DUMMY_Y  16
82 
83 
84 extern void DeleteSkyTextures(void);
85 extern void DeleteColourmapTextures(void);
86 
87 
88 //
89 // This structure is for "cached" images (i.e. ready to be used for
90 // rendering), and is the non-opaque version of cached_image_t.  A
91 // single structure is used for all image modes (Block and OGL).
92 //
93 // Note: multiple modes and/or multiple mips of the same image_c can
94 // indeed be present in the cache list at any one time.
95 //
96 typedef struct cached_image_s
97 {
98 	// parent image
99 	image_c *parent;
100 
101 	// colormap used for translated image, normally NULL
102 	const colourmap_c *trans_map;
103 
104 	// general hue of image (skewed towards pure colors)
105 	rgbcol_t hue;
106 
107 	// texture identifier within GL
108 	GLuint tex_id;
109 }
110 cached_image_t;
111 
112 
113 typedef std::list<image_c *> real_image_container_c;
114 
115 
do_Lookup(real_image_container_c & bucket,const char * name,int source_type=-1)116 static image_c *do_Lookup(real_image_container_c& bucket, const char *name,
117                           int source_type = -1
118 						  /* use -2 to prevent USER override */)
119 {
120 	// for a normal lookup, we want USER images to override
121 	if (source_type == -1)
122 	{
123 		image_c *rim = do_Lookup(bucket, name, IMSRC_User);  // recursion
124 		if (rim)
125 			return rim;
126 	}
127 
128 	real_image_container_c::reverse_iterator it;
129 
130 	// search backwards, we want newer image to override older ones
131 	for (it = bucket.rbegin(); it != bucket.rend(); it++)
132 	{
133 		image_c *rim = *it;
134 
135 		if (source_type >= 0 && source_type != (int)rim->source_type)
136 			continue;
137 
138 		if (stricmp(name, rim->name) == 0)
139 			return rim;
140 	}
141 
142 	return NULL;  // not found
143 }
144 
do_Animate(real_image_container_c & bucket)145 static void do_Animate(real_image_container_c& bucket)
146 {
147 	real_image_container_c::iterator it;
148 
149 	for (it = bucket.begin(); it != bucket.end(); it++)
150 	{
151 		image_c *rim = *it;
152 
153 		if (rim->anim.speed == 0)  // not animated ?
154 			continue;
155 
156 		SYS_ASSERT(rim->anim.count > 0);
157 
158 		rim->anim.count--;
159 
160 		if (rim->anim.count == 0 && rim->anim.cur->anim.next)
161 		{
162 			rim->anim.cur   = rim->anim.cur->anim.next;
163 			rim->anim.count = rim->anim.speed;
164 		}
165 	}
166 }
167 
168 #if 0
169 static void do_DebugDump(real_image_container_c& bucket)
170 {
171 	L_WriteDebug("{\n");
172 
173 	real_image_container_c::iterator it;
174 
175 	for (it = bucket.begin(); it != bucket.end(); it++)
176 	{
177 		image_c *rim = *it;
178 
179 		L_WriteDebug("   [%s] type %d: %dx%d < %dx%d\n",
180 			rim->name, rim->source_type,
181 			rim->actual_w, rim->actual_h,
182 			rim->total_w, rim->total_h);
183 	}
184 
185 	L_WriteDebug("}\n");
186 }
187 #endif
188 
189 // mipmapping enabled ?
190 // 0 off, 1 bilinear, 2 trilinear
191 int var_mipmapping = 1;
192 
193 int var_smoothing  = 1;
194 
195 bool var_dithering = false;
196 
197 int hq2x_scaling = 1;
198 
199 
200 // total set of images
201 static real_image_container_c real_graphics;
202 static real_image_container_c real_textures;
203 static real_image_container_c real_flats;
204 static real_image_container_c real_sprites;
205 
206 
207 const image_c *skyflatimage;
208 
209 static const image_c *dummy_sprite;
210 static const image_c *dummy_skin;
211 static const image_c *dummy_hom[2];
212 
213 
214 // image cache (actually a ring structure)
215 static std::list<cached_image_t *> image_cache;
216 
217 
218 // tiny ring helpers
InsertAtTail(cached_image_t * rc)219 static inline void InsertAtTail(cached_image_t *rc)
220 {
221 	image_cache.push_back(rc);
222 
223 #if 0  // OLD WAY
224 	SYS_ASSERT(rc != &imagecachehead);
225 
226 	rc->prev =  imagecachehead.prev;
227 	rc->next = &imagecachehead;
228 
229 	rc->prev->next = rc;
230 	rc->next->prev = rc;
231 #endif
232 }
Unlink(cached_image_t * rc)233 static inline void Unlink(cached_image_t *rc)
234 {
235 	// FIXME: Unlink
236 #if 0
237 	SYS_ASSERT(rc != &imagecachehead);
238 
239 	rc->prev->next = rc->next;
240 	rc->next->prev = rc->prev;
241 #endif
242 }
243 
244 
245 
246 //----------------------------------------------------------------------------
247 //
248 //  IMAGE CREATION
249 //
250 
image_c()251 image_c::image_c() : actual_w(0), actual_h(0), total_w(0), total_h(0),
252 					 source_type(IMSRC_Dummy),
253 					 source_palette(-1),
254 					 cache()
255 {
256 	strcpy(name, "_UNINIT_");
257 
258 	memset(&source, 0, sizeof(source));
259 	memset(&anim,   0, sizeof(anim));
260 }
261 
~image_c()262 image_c::~image_c()
263 {
264   /* TODO: image_c destructor */
265 }
266 
267 
NewImage(int width,int height,int opacity=OPAC_Unknown)268 static image_c *NewImage(int width, int height, int opacity = OPAC_Unknown)
269 {
270 	image_c *rim = new image_c;
271 
272 	rim->actual_w = width;
273 	rim->actual_h = height;
274 	rim->total_w  = W_MakeValidSize(width);
275 	rim->total_h  = W_MakeValidSize(height);
276 	rim->offset_x = rim->offset_y = 0;
277 	rim->scale_x  = rim->scale_y = 1.0f;
278 	rim->opacity  = opacity;
279 
280 	// set initial animation info
281 	rim->anim.cur = rim;
282 	rim->anim.next = NULL;
283 	rim->anim.count = rim->anim.speed = 0;
284 
285 	return rim;
286 }
287 
288 
CreateDummyImage(const char * name,rgbcol_t fg,rgbcol_t bg)289 static image_c *CreateDummyImage(const char *name, rgbcol_t fg, rgbcol_t bg)
290 {
291 	image_c *rim;
292 
293 	rim = NewImage(DUMMY_X, DUMMY_Y, (bg == TRANS_PIXEL) ? OPAC_Masked : OPAC_Solid);
294 
295  	strcpy(rim->name, name);
296 
297 	rim->source_type = IMSRC_Dummy;
298 	rim->source_palette = -1;
299 
300 	rim->source.dummy.fg = fg;
301 	rim->source.dummy.bg = bg;
302 
303 	return rim;
304 }
305 
306 
AddImageGraphic(const char * name,image_source_e type,int lump,real_image_container_c & container,const image_c * replaces=NULL)307 static image_c *AddImageGraphic(const char *name, image_source_e type, int lump,
308 								real_image_container_c& container,
309 								const image_c *replaces = NULL)
310 {
311 	/* also used for Sprites and TX/HI stuff */
312 
313 	int lump_len = W_LumpLength(lump);
314 
315 	epi::file_c *f = W_OpenLump(lump);
316 	SYS_ASSERT(f);
317 
318 	byte buffer[32];
319 
320 	f->Read(buffer, sizeof(buffer));
321 
322 	f->Seek(0, epi::file_c::SEEKPOINT_START);
323 
324 	// determine info, and whether it is PNG or DOOM_PATCH
325 	int width=0, height=0;
326 	int offset_x=0, offset_y=0;
327 
328 	bool is_png = false;
329 	bool solid  = false;
330 
331 	if (epi::PNG_IsDataPNG(buffer, lump_len))
332 	{
333 		is_png = true;
334 
335 		if (! PNG_GetInfo(f, &width, &height, &solid) ||
336 		    width <= 0 or height <= 0)
337 		{
338 			I_Error("Error scanning PNG image in '%s' lump\n", W_GetLumpName(lump));
339 		}
340 
341 		// close it
342 		delete f;
343 	}
344 	else  // DOOM PATCH format
345 	{
346 		patch_t *pat = (patch_t *) buffer;
347 
348 		width    = EPI_LE_S16(pat->width);
349 		height   = EPI_LE_S16(pat->height);
350 		offset_x = EPI_LE_S16(pat->leftoffset);
351 		offset_y = EPI_LE_S16(pat->topoffset);
352 
353 		delete f;
354 
355 		// do some basic checks
356 		// !!! FIXME: identify lump types in wad code.
357 		if (width  <= 0 || width > 2048 ||
358 		    height <= 0 || height > 512 ||
359 			ABS(offset_x) > 2048 || ABS(offset_y) > 1024)
360 		{
361 			// check for Heretic/Hexen images, which are raw 320x200
362 			if (lump_len == 320*200 && type == IMSRC_Graphic)
363 			{
364 				image_c *rim = NewImage(320, 200, OPAC_Solid);
365 				strcpy(rim->name, name);
366 
367 				rim->source_type = IMSRC_Raw320x200;
368 				rim->source.flat.lump = lump;
369 				rim->source_palette = W_GetPaletteForLump(lump);
370 				return rim;
371 			}
372 
373 			if (lump_len == 64*64 || lump_len == 64*65 || lump_len == 64*128)
374 				I_Warning("Graphic '%s' seems to be a flat.\n", name);
375 			else
376 				I_Warning("Graphic '%s' does not seem to be a graphic.\n", name);
377 
378 			return NULL;
379 		}
380 	}
381 
382 	// create new image
383 	image_c *rim = NewImage(width, height, solid ? OPAC_Solid : OPAC_Unknown);
384 
385 	rim->offset_x = offset_x;
386 	rim->offset_y = offset_y;
387 
388 	strcpy(rim->name, name);
389 
390 	rim->source_type = type;
391 	rim->source.graphic.lump = lump;
392 	rim->source.graphic.is_png = is_png;
393 	rim->source_palette = W_GetPaletteForLump(lump);
394 
395 	if (replaces)
396 	{
397 		rim->scale_x = replaces->actual_w / (float)width;
398 		rim->scale_y = replaces->actual_h / (float)height;
399 
400 		if (is_png && replaces->source_type == IMSRC_Sprite)
401 		{
402 			rim->offset_x = replaces->offset_x;
403 			rim->offset_y = replaces->offset_y;
404 		}
405 	}
406 
407 	container.push_back(rim);
408 
409 	return rim;
410 }
411 
412 
AddImageTexture(const char * name,texturedef_t * tdef)413 static image_c *AddImageTexture(const char *name, texturedef_t *tdef)
414 {
415 	image_c *rim;
416 
417 	rim = NewImage(tdef->width, tdef->height);
418 
419 	strcpy(rim->name, name);
420 
421 	if (tdef->scale_x) rim->scale_x = 8.0 / tdef->scale_x;
422 	if (tdef->scale_y) rim->scale_y = 8.0 / tdef->scale_y;
423 
424 	rim->source_type = IMSRC_Texture;
425 	rim->source.texture.tdef = tdef;
426 	rim->source_palette = tdef->palette_lump;
427 
428 	real_textures.push_back(rim);
429 
430 	return rim;
431 }
432 
AddImageFlat(const char * name,int lump)433 static image_c *AddImageFlat(const char *name, int lump)
434 {
435 	image_c *rim;
436 	int len, size;
437 
438 	len = W_LumpLength(lump);
439 
440 	switch (len)
441 	{
442 		case 64 * 64: size = 64; break;
443 
444 		// support for odd-size Heretic flats
445 		case 64 * 65: size = 64; break;
446 
447 		// support for odd-size Hexen flats
448 		case 64 * 128: size = 64; break;
449 
450 		// -- EDGE feature: bigger than normal flats --
451 		case 128 * 128: size = 128; break;
452 		case 256 * 256: size = 256; break;
453 		case 512 * 512: size = 512; break;
454 		case 1024 * 1024: size = 1024; break;
455 
456 		default:
457 			return NULL;
458 	}
459 
460 	rim = NewImage(size, size, OPAC_Solid);
461 
462 	strcpy(rim->name, name);
463 
464 	rim->source_type = IMSRC_Flat;
465 	rim->source.flat.lump = lump;
466 	rim->source_palette = W_GetPaletteForLump(lump);
467 
468 	real_flats.push_back(rim);
469 
470 	return rim;
471 }
472 
473 
AddImageUser(imagedef_c * def)474 static image_c *AddImageUser(imagedef_c *def)
475 {
476 	int w, h;
477 	bool solid;
478 
479 	switch (def->type)
480 	{
481 		case IMGDT_Colour:
482 			w = h = 8;
483 			solid = true;
484 			break;
485 
486 		case IMGDT_Builtin:
487 			//!!!!! (detail_level == 2) ? 512 : 256;
488 			w = 256;
489 			h = 256;
490 			solid = false;
491 			break;
492 
493 		case IMGDT_File:
494 		case IMGDT_Lump:
495 		{
496 			const char *basename = def->info.c_str();
497 
498 			epi::file_c *f = OpenUserFileOrLump(def);
499 
500 			if (! f)
501 			{
502 				I_Warning("Unable to add image %s: %s\n",
503 					(def->type == IMGDT_Lump) ? "lump" : "file", basename);
504 				return NULL;
505 			}
506 
507 			bool got_info;
508 
509 			if (def->format == LIF_JPEG)
510 				got_info = epi::JPEG_GetInfo(f, &w, &h, &solid);
511 			else if (def->format == LIF_TGA)
512 				got_info = epi::TGA_GetInfo(f, &w, &h, &solid);
513 			else
514 				got_info = epi::PNG_GetInfo(f, &w, &h, &solid);
515 
516 			if (! got_info)
517 				I_Error("Error occurred scanning image: %s\n", basename);
518 
519 			CloseUserFileOrLump(def, f);
520 #if 1
521 			L_WriteDebug("GETINFO [%s] : size %dx%d\n", def->name.c_str(), w, h);
522 #endif
523 		}
524 		break;
525 
526 		default:
527 			I_Error("AddImageUser: Coding error, unknown type %d\n", def->type);
528 			return NULL; /* NOT REACHED */
529 	}
530 
531 	image_c *rim = NewImage(w, h, solid ? OPAC_Solid : OPAC_Unknown);
532 
533 	rim->offset_x = def->x_offset;
534 	rim->offset_y = def->y_offset;
535 
536 	rim->scale_x = def->scale * def->aspect;
537 	rim->scale_y = def->scale;
538 
539 	strcpy(rim->name, def->name.c_str());
540 
541 	/* FIX NAME : replace space with '_' */
542 	for (int i = 0; rim->name[i]; i++)
543 		if (rim->name[i] == ' ')
544 			rim->name[i] = '_';
545 
546 	rim->source_type = IMSRC_User;
547 	rim->source.user.def = def;
548 
549 	if (def->special & IMGSP_Crosshair)
550 	{
551 		float dy = (200.0f - rim->actual_h * rim->scale_y) / 2.0f - WEAPONTOP;
552 
553 		rim->offset_y += int(dy / rim->scale_y);
554 	}
555 
556 	switch (def->belong)
557 	{
558 		case INS_Graphic: real_graphics.push_back(rim); break;
559 		case INS_Texture: real_textures.push_back(rim); break;
560 		case INS_Flat:    real_flats.   push_back(rim); break;
561 		case INS_Sprite:  real_sprites. push_back(rim); break;
562 
563 		default:
564 			I_Error("INTERNAL ERROR: Bad belong value: %d\n", def->belong);
565 	}
566 
567 	return rim;
568 }
569 
570 
571 //
572 // Used to fill in the image array with flats from the WAD.  The set
573 // of lumps is those that occurred between F_START and F_END in each
574 // existing wad file, with duplicates set to -1.
575 //
576 // NOTE: should only be called once, as it assumes none of the flats
577 // in the list have names colliding with existing flat images.
578 //
W_ImageCreateFlats(int * lumps,int number)579 void W_ImageCreateFlats(int *lumps, int number)
580 {
581 	int i;
582 
583 	SYS_ASSERT(lumps);
584 
585 	for (i=0; i < number; i++)
586 	{
587 		if (lumps[i] < 0)
588 			continue;
589 
590 		AddImageFlat(W_GetLumpName(lumps[i]), lumps[i]);
591 	}
592 }
593 
594 
595 //
596 // Used to fill in the image array with textures from the WAD.  The
597 // list of texture definitions comes from each TEXTURE1/2 lump in each
598 // existing wad file, with duplicates set to NULL.
599 //
600 // NOTE: should only be called once, as it assumes none of the
601 // textures in the list have names colliding with existing texture
602 // images.
603 //
W_ImageCreateTextures(struct texturedef_s ** defs,int number)604 void W_ImageCreateTextures(struct texturedef_s ** defs, int number)
605 {
606 	int i;
607 
608 	SYS_ASSERT(defs);
609 
610 	for (i=0; i < number; i++)
611 	{
612 		if (defs[i] == NULL)
613 			continue;
614 
615 		AddImageTexture(defs[i]->name, defs[i]);
616 	}
617 }
618 
619 
620 //
621 // Used to fill in the image array with sprites from the WAD.  The
622 // lumps come from those occurring between S_START and S_END markers
623 // in each existing wad.
624 //
625 // NOTE: it is assumed that each new sprite is unique i.e. the name
626 // does not collide with any existing sprite image.
627 //
W_ImageCreateSprite(const char * name,int lump,bool is_weapon)628 const image_c *W_ImageCreateSprite(const char *name, int lump, bool is_weapon)
629 {
630 	SYS_ASSERT(lump >= 0);
631 
632 	image_c *rim = AddImageGraphic(name, IMSRC_Sprite, lump, real_sprites);
633 	if (! rim)
634 		return NULL;
635 
636 	// adjust sprite offsets so that (0,0) is normal
637 	if (is_weapon)
638 	{
639 		rim->offset_x += (320 / 2 - rim->actual_w / 2);  // loss of accuracy
640 		rim->offset_y += (200 - 32 - rim->actual_h);
641 	}
642 	else
643 	{
644 		rim->offset_x -= rim->actual_w / 2;   // loss of accuracy
645 		rim->offset_y -= rim->actual_h;
646 	}
647 
648 	return rim;
649 }
650 
651 
652 //
653 // Add the images defined in IMAGES.DDF.
654 //
W_ImageCreateUser(void)655 void W_ImageCreateUser(void)
656 {
657 	for (int i = 0; i < imagedefs.GetSize(); i++)
658 	{
659 		imagedef_c* def = imagedefs[i];
660 
661 		if (def)
662 			AddImageUser(def);
663 
664 		E_LocalProgress(i, imagedefs.GetSize());
665 	}
666 
667 #if 0
668 	L_WriteDebug("Textures -----------------------------\n");
669 	do_DebugDump(real_textures);
670 
671 	L_WriteDebug("Flats ------------------------------\n");
672 	do_DebugDump(real_flats);
673 
674 	L_WriteDebug("Sprites ------------------------------\n");
675 	do_DebugDump(real_sprites);
676 
677 	L_WriteDebug("Graphics ------------------------------\n");
678 	do_DebugDump(real_graphics);
679 #endif
680 }
681 
682 
W_ImageAddTX(int lump,const char * name,bool hires)683 void W_ImageAddTX(int lump, const char *name, bool hires)
684 {
685 	if (hires)
686 	{
687 		const image_c *rim = do_Lookup(real_textures, name, -2);
688 		if (rim && rim->source_type != IMSRC_User)
689 		{
690 			AddImageGraphic(name, IMSRC_TX_HI, lump, real_textures, rim);
691 			return;
692 		}
693 
694 		rim = do_Lookup(real_flats, name, -2);
695 		if (rim && rim->source_type != IMSRC_User)
696 		{
697 			AddImageGraphic(name, IMSRC_TX_HI, lump, real_flats, rim);
698 			return;
699 		}
700 
701 		rim = do_Lookup(real_sprites, name, -2);
702 		if (rim && rim->source_type != IMSRC_User)
703 		{
704 			AddImageGraphic(name, IMSRC_TX_HI, lump, real_sprites, rim);
705 			return;
706 		}
707 
708 		// we do it this way to force the original graphic to be loaded
709 		rim = W_ImageLookup(name, INS_Graphic, ILF_Exact|ILF_Null);
710 
711 		if (rim && rim->source_type != IMSRC_User)
712 		{
713 			AddImageGraphic(name, IMSRC_TX_HI, lump, real_graphics, rim);
714 			return;
715 		}
716 
717 		I_Warning("HIRES replacement '%s' has no counterpart.\n", name);
718 	}
719 
720 	AddImageGraphic(name, IMSRC_TX_HI, lump, real_textures);
721 }
722 
723 
724 //
725 // Only used during sprite initialisation.  The returned array of
726 // images is guaranteed to be sorted by name.
727 //
728 // Use delete[] to free the returned array.
729 //
W_ImageGetUserSprites(int * count)730 const image_c ** W_ImageGetUserSprites(int *count)
731 {
732 	// count number of user sprites
733 	(*count) = 0;
734 
735 	real_image_container_c::iterator it;
736 
737 	for (it = real_sprites.begin(); it != real_sprites.end(); it++)
738 	{
739 		image_c *rim = *it;
740 
741 		if (rim->source_type == IMSRC_User)
742 			(*count) += 1;
743 	}
744 
745 	if (*count == 0)
746 	{
747 		L_WriteDebug("W_ImageGetUserSprites(count = %d)\n", *count);
748 		return NULL;
749 	}
750 
751 	const image_c ** array = new const image_c *[*count];
752 	int pos = 0;
753 
754 	for (it = real_sprites.begin(); it != real_sprites.end(); it++)
755 	{
756 		image_c *rim = *it;
757 
758 		if (rim->source_type == IMSRC_User)
759 			array[pos++] = rim;
760 	}
761 
762 #define CMP(a, b)  (strcmp(W_ImageGetName(a), W_ImageGetName(b)) < 0)
763 	QSORT(const image_c *, array, (*count), CUTOFF);
764 #undef CMP
765 
766 #if 0  // DEBUGGING
767 	L_WriteDebug("W_ImageGetUserSprites(count = %d)\n", *count);
768 	L_WriteDebug("{\n");
769 
770 	for (pos = 0; pos < *count; pos++)
771 		L_WriteDebug("   %p = [%s] %dx%d\n", array[pos], W_ImageGetName(array[pos]),
772 			array[pos]->actual_w, array[pos]->actual_h);
773 
774 	L_WriteDebug("}\n");
775 #endif
776 
777 	return array;
778 }
779 
780 
781 //----------------------------------------------------------------------------
782 //
783 //  IMAGE LOADING / UNLOADING
784 //
785 
786 // TODO: make methods of image_c class
IM_ShouldClamp(const image_c * rim)787 static bool IM_ShouldClamp(const image_c *rim)
788 {
789 	switch (rim->source_type)
790 	{
791 		case IMSRC_Graphic:
792 		case IMSRC_Raw320x200:
793 		case IMSRC_Sprite:
794 			return true;
795 
796 		case IMSRC_User:
797 			switch (rim->source.user.def->belong)
798 			{
799 				case INS_Graphic:
800 				case INS_Sprite:
801 					return true;
802 
803 				default:
804 					return false;
805 			}
806 
807 		default:
808 			return false;
809 	}
810 }
811 
IM_ShouldMipmap(image_c * rim)812 static bool IM_ShouldMipmap(image_c *rim)
813 {
814    	// the "SKY" check here is a hack...
815    	if (strnicmp(rim->name, "SKY", 3) == 0)
816 		return false;
817 
818 	switch (rim->source_type)
819 	{
820 		case IMSRC_Texture:
821 		case IMSRC_Flat:
822 		case IMSRC_TX_HI:
823 			return true;
824 
825 		case IMSRC_User:
826 			switch (rim->source.user.def->belong)
827 			{
828 				case INS_Texture:
829 				case INS_Flat:
830 					return true;
831 
832 				default:
833 					return false;
834 			}
835 
836 		default:
837 			return false;
838 	}
839 }
840 
IM_ShouldSmooth(image_c * rim)841 static bool IM_ShouldSmooth(image_c *rim)
842 {
843    	// the "SKY" check here is a hack...
844    	if (strnicmp(rim->name, "SKY", 3) == 0)
845 		return true;
846 
847 	// TODO: more smooth options
848 
849 	return var_smoothing ? true : false;
850 }
851 
IM_ShouldHQ2X(image_c * rim)852 static bool IM_ShouldHQ2X(image_c *rim)
853 {
854 	// Note: no need to check IMSRC_User, since those images are
855 	//       always PNG or JPEG (etc) and never palettised, hence
856 	//       the Hq2x scaling would never apply.
857 
858 	if (hq2x_scaling == 0)
859 		return false;
860 
861 	if (hq2x_scaling >= 3)
862 		return true;
863 
864 	switch (rim->source_type)
865 	{
866 		case IMSRC_Graphic:
867 		case IMSRC_Raw320x200:
868 			// UI elements
869 			return true;
870 #if 0
871 		case IMSRC_Texture:
872 			// the "SKY" check here is a hack...
873 			if (strnicmp(rim->name, "SKY", 3) == 0)
874 				return true;
875 			break;
876 #endif
877 		case IMSRC_Sprite:
878 			if (hq2x_scaling >= 2)
879 				return true;
880 			break;
881 
882 		default:
883 			break;
884 	}
885 
886 	return false;
887 }
888 
IM_PixelLimit(image_c * rim)889 static int IM_PixelLimit(image_c *rim)
890 {
891 	if (IM_ShouldMipmap(rim))
892 		return 65536 * (1 << (2 * detail_level));
893 
894 	return (1 << 24);
895 }
896 
897 
LoadImageOGL(image_c * rim,const colourmap_c * trans)898 static GLuint LoadImageOGL(image_c *rim, const colourmap_c *trans)
899 {
900 	bool clamp  = IM_ShouldClamp(rim);
901 	bool mip    = IM_ShouldMipmap(rim);
902 	bool smooth = IM_ShouldSmooth(rim);
903 
904  	int max_pix = IM_PixelLimit(rim);
905 
906 	if (rim->source_type == IMSRC_User)
907 	{
908 		if (rim->source.user.def->special & IMGSP_Clamp)
909 			clamp = true;
910 
911 		if (rim->source.user.def->special & IMGSP_Mip)
912 			mip = true;
913 		else if (rim->source.user.def->special & IMGSP_NoMip)
914 			mip = false;
915 
916 		if (rim->source.user.def->special & IMGSP_Smooth)
917 			smooth = true;
918 		else if (rim->source.user.def->special & IMGSP_NoSmooth)
919 			smooth = false;
920 	}
921 
922 
923 	const byte *what_palette = (const byte *) &playpal_data[0];
924 	bool what_pal_cached = false;
925 
926 	static byte trans_pal[256 * 3];
927 
928 	if (trans != NULL)
929 	{
930 		// Note: we don't care about source_palette here. It's likely that
931 		// the translation table itself would not match the other palette,
932 		// and so we would still end up with messed up colours.
933 
934 		R_TranslatePalette(trans_pal, what_palette, trans);
935 		what_palette = trans_pal;
936 	}
937 	else if (rim->source_palette >= 0)
938 	{
939 		what_palette = (const byte *) W_CacheLumpNum(rim->source_palette);
940 		what_pal_cached = true;
941 	}
942 
943 
944 	epi::image_data_c *tmp_img = ReadAsEpiBlock(rim);
945 
946 	if (rim->opacity == OPAC_Unknown)
947 		rim->opacity = R_DetermineOpacity(tmp_img);
948 
949 	if ((tmp_img->bpp == 1) && IM_ShouldHQ2X(rim))
950 	{
951 		bool solid = (rim->opacity == OPAC_Solid);
952 
953 		epi::Hq2x::Setup(what_palette, solid ? -1 : TRANS_PIXEL);
954 
955 		epi::image_data_c *scaled_img =
956 			epi::Hq2x::Convert(tmp_img, solid, false /* invert */);
957 
958 		delete tmp_img;
959 		tmp_img = scaled_img;
960 	}
961 	else if (tmp_img->bpp == 1)
962 	{
963 		epi::image_data_c *rgb_img =
964 				R_PalettisedToRGB(tmp_img, what_palette, rim->opacity);
965 
966 		delete tmp_img;
967 		tmp_img = rgb_img;
968 	}
969 	else if (tmp_img->bpp >= 3 && trans != NULL)
970 	{
971 		if (trans == font_whiten_map)
972 			tmp_img->Whiten();
973 		else
974 			R_PaletteRemapRGBA(tmp_img, what_palette, (const byte *) &playpal_data[0]);
975 	}
976 
977 
978 	GLuint tex_id = R_UploadTexture(tmp_img,
979 		(clamp  ? UPL_Clamp  : 0) |
980 		(mip    ? UPL_MipMap : 0) |
981 		(smooth ? UPL_Smooth : 0) |
982 		((rim->opacity == OPAC_Masked) ? UPL_Thresh : 0), max_pix);
983 
984 	delete tmp_img;
985 
986 	if (what_pal_cached)
987 		W_DoneWithLump(what_palette);
988 
989 	return tex_id;
990 }
991 
992 
993 
994 
995 #if 0
996 static
997 void UnloadImageOGL(cached_image_t *rc, image_c *rim)
998 {
999 	glDeleteTextures(1, &rc->tex_id);
1000 
1001 	for (unsigned int i = 0; i < rim->cache.size(); i++)
1002 	{
1003 		if (rim->cache[i] == rc)
1004 		{
1005 			rim->cache[i] = NULL;
1006 			return;
1007 		}
1008 	}
1009 
1010 	I_Error("INTERNAL ERROR: UnloadImageOGL: no such RC in cache !\n");
1011 }
1012 
1013 
1014 //
1015 // UnloadImage
1016 //
1017 // Unloads a cached image from the cache list and frees all resources.
1018 // Mainly just a switch to more specialised image unloaders.
1019 //
1020 static void UnloadImage(cached_image_t *rc)
1021 {
1022 	image_c *rim = rc->parent;
1023 
1024 	SYS_ASSERT(rc);
1025 	SYS_ASSERT(rc != &imagecachehead);
1026 	SYS_ASSERT(rim);
1027 
1028 	// unlink from the cache list
1029 	Unlink(rc);
1030 
1031 	UnloadImageOGL(rc, rim);
1032 
1033 	delete rc;
1034 }
1035 #endif
1036 
1037 
1038 //----------------------------------------------------------------------------
1039 //  IMAGE LOOKUP
1040 //----------------------------------------------------------------------------
1041 
1042 //
1043 // BackupTexture
1044 //
BackupTexture(const char * tex_name,int flags)1045 static const image_c *BackupTexture(const char *tex_name, int flags)
1046 {
1047 	const image_c *rim;
1048 
1049 	// backup plan: try a flat with the same name
1050 	if (! (flags & ILF_Exact))
1051 	{
1052 		rim = do_Lookup(real_flats, tex_name);
1053 		if (rim)
1054 			return rim;
1055 	}
1056 
1057 	if (flags & ILF_Null)
1058 		return NULL;
1059 
1060 	M_WarnError("Unknown texture found in level: '%s'\n", tex_name);
1061 
1062 	image_c *dummy;
1063 
1064    	if (strnicmp(tex_name, "SKY", 3) == 0)
1065 		dummy = CreateDummyImage(tex_name, 0x0000AA, 0x55AADD);
1066 	else
1067 		dummy = CreateDummyImage(tex_name, 0xAA5511, 0x663300);
1068 
1069 	// keep dummy texture so that future lookups will succeed
1070 	real_textures.push_back(dummy);
1071 	return dummy;
1072 }
1073 
1074 //
1075 // BackupFlat
1076 //
BackupFlat(const char * flat_name,int flags)1077 static const image_c *BackupFlat(const char *flat_name, int flags)
1078 {
1079 	const image_c *rim;
1080 
1081 	// backup plan 1: if lump exists and is right size, add it.
1082 	if (! (flags & ILF_NoNew))
1083 	{
1084 		int i = W_CheckNumForName(flat_name);
1085 
1086 		if (i >= 0)
1087 		{
1088 			rim = AddImageFlat(flat_name, i);
1089 			if (rim)
1090 				return rim;
1091 		}
1092 	}
1093 
1094 	// backup plan 2: Texture with the same name ?
1095 	if (! (flags & ILF_Exact))
1096 	{
1097 		rim = do_Lookup(real_textures, flat_name);
1098 		if (rim)
1099 			return rim;
1100 	}
1101 
1102 	if (flags & ILF_Null)
1103 		return NULL;
1104 
1105 	M_WarnError("Unknown flat found in level: '%s'\n", flat_name);
1106 
1107     image_c *dummy = CreateDummyImage(flat_name, 0x11AA11, 0x115511);
1108 
1109     // keep dummy flat so that future lookups will succeed
1110     real_flats.push_back(dummy);
1111     return dummy;
1112 }
1113 
1114 //
1115 // BackupGraphic
1116 //
BackupGraphic(const char * gfx_name,int flags)1117 static const image_c *BackupGraphic(const char *gfx_name, int flags)
1118 {
1119 	const image_c *rim;
1120 
1121 	// backup plan 1: look for sprites and heretic-background
1122 	if ((flags & (ILF_Exact | ILF_Font)) == 0)
1123 	{
1124 		rim = do_Lookup(real_graphics, gfx_name, IMSRC_Raw320x200);
1125 		if (rim)
1126 			return rim;
1127 
1128 		rim = do_Lookup(real_sprites, gfx_name);
1129 		if (rim)
1130 			return rim;
1131 	}
1132 
1133 	// not already loaded ?  Check if lump exists in wad, if so add it.
1134 	if (! (flags & ILF_NoNew))
1135 	{
1136 		int i = W_CheckNumForName_GFX(gfx_name);
1137 
1138 		if (i >= 0)
1139 		{
1140 			rim = AddImageGraphic(gfx_name, IMSRC_Graphic, i, real_graphics);
1141 			if (rim)
1142 				return rim;
1143 		}
1144 	}
1145 
1146 	if (flags & ILF_Null)
1147 		return NULL;
1148 
1149 	M_WarnError("Unknown graphic: '%s'\n", gfx_name);
1150 
1151 	image_c *dummy;
1152 
1153 	if (flags & ILF_Font)
1154 		dummy = CreateDummyImage(gfx_name, 0xFFFFFF, TRANS_PIXEL);
1155 	else
1156 		dummy = CreateDummyImage(gfx_name, 0xFF0000, TRANS_PIXEL);
1157 
1158 	// keep dummy graphic so that future lookups will succeed
1159 	real_graphics.push_back(dummy);
1160 	return dummy;
1161 }
1162 
1163 
BackupSprite(const char * spr_name,int flags)1164 static const image_c *BackupSprite(const char *spr_name, int flags)
1165 {
1166 	if (flags & ILF_Null)
1167 		return NULL;
1168 
1169 	return W_ImageForDummySprite();
1170 }
1171 
1172 
W_ImageLookup(const char * name,image_namespace_e type,int flags)1173 const image_c *W_ImageLookup(const char *name, image_namespace_e type, int flags)
1174 {
1175 	//
1176 	// Note: search must be case insensitive.
1177 	//
1178 
1179 	// "NoTexture" marker.
1180 	if (!name || !name[0] || name[0] == '-')
1181 		return NULL;
1182 
1183 	// "Sky" marker.
1184 	if (type == INS_Flat &&
1185 		(stricmp(name, "F_SKY1") == 0 ||
1186 		 stricmp(name, "F_SKY")  == 0))
1187 	{
1188 		return skyflatimage;
1189 	}
1190 
1191 	// compatibility hack (first texture in IWAD is a dummy)
1192 	if (type == INS_Texture &&
1193 		( (stricmp(name, "AASTINKY") == 0) ||
1194 		  (stricmp(name, "AASHITTY") == 0) ||
1195 		  (stricmp(name, "BADPATCH") == 0) ||
1196 		  (stricmp(name, "ABADONE")  == 0)))
1197 	{
1198 	    return NULL;
1199 	}
1200 
1201 	const image_c *rim;
1202 
1203 	if (type == INS_Texture)
1204 	{
1205 		rim = do_Lookup(real_textures, name);
1206 		return rim ? rim : BackupTexture(name, flags);
1207 	}
1208 	if (type == INS_Flat)
1209 	{
1210 		rim = do_Lookup(real_flats, name);
1211 		return rim ? rim : BackupFlat(name, flags);
1212 	}
1213 	if (type == INS_Sprite)
1214 	{
1215 		rim = do_Lookup(real_sprites, name);
1216 		return rim ? rim : BackupSprite(name, flags);
1217 	}
1218 
1219 	/* INS_Graphic */
1220 
1221 	rim = do_Lookup(real_graphics, name);
1222 	return rim ? rim : BackupGraphic(name, flags);
1223 }
1224 
1225 
W_ImageForDummySprite(void)1226 const image_c *W_ImageForDummySprite(void)
1227 {
1228 	return dummy_sprite;
1229 }
1230 
W_ImageForDummySkin(void)1231 const image_c *W_ImageForDummySkin(void)
1232 {
1233 	return dummy_skin;
1234 }
1235 
W_ImageForHOMDetect(void)1236 const image_c *W_ImageForHOMDetect(void)
1237 {
1238 	return dummy_hom[(framecount & 0x10) ? 1 : 0];
1239 }
1240 
1241 
W_ImageParseSaveString(char type,const char * name)1242 const image_c *W_ImageParseSaveString(char type, const char *name)
1243 {
1244 	// Used by the savegame code.
1245 
1246 	// this name represents the sky (historical reasons)
1247 	if (type == 'd' && stricmp(name, "DUMMY__2") == 0)
1248 	{
1249 		return skyflatimage;
1250 	}
1251 
1252 	switch (type)
1253 	{
1254 		case 'K':
1255 			return skyflatimage;
1256 
1257 		case 'F':
1258 			return W_ImageLookup(name, INS_Flat);
1259 
1260 		case 'P':
1261 			return W_ImageLookup(name, INS_Graphic);
1262 
1263 		case 'S':
1264 			return W_ImageLookup(name, INS_Sprite);
1265 
1266 		default:
1267 			I_Warning("W_ImageParseSaveString: unknown type '%c'\n", type);
1268 			/* FALL THROUGH */
1269 
1270 		case 'd': /* dummy */
1271 		case 'T':
1272 			return W_ImageLookup(name, INS_Texture);
1273 	}
1274 
1275 	return NULL; /* NOT REACHED */
1276 }
1277 
1278 
W_ImageMakeSaveString(const image_c * image,char * type,char * namebuf)1279 void W_ImageMakeSaveString(const image_c *image, char *type, char *namebuf)
1280 {
1281 	// Used by the savegame code
1282 
1283     if (image == skyflatimage)
1284 	{
1285 		*type = 'K';
1286 		strcpy(namebuf, "F_SKY1");
1287 		return;
1288 	}
1289 
1290 	const image_c *rim = (const image_c *) image;
1291 
1292 	strcpy(namebuf, rim->name);
1293 
1294 	/* handle User images (convert to a more general type) */
1295 	if (rim->source_type == IMSRC_User)
1296 	{
1297 		switch (rim->source.user.def->belong)
1298 		{
1299 			case INS_Texture: (*type) = 'T'; return;
1300 			case INS_Flat:    (*type) = 'F'; return;
1301 			case INS_Sprite:  (*type) = 'S'; return;
1302 
1303 			default:  /* INS_Graphic */
1304 				(*type) = 'P';
1305 				return;
1306 		}
1307 	}
1308 
1309 	switch (rim->source_type)
1310 	{
1311 		case IMSRC_Raw320x200:
1312 		case IMSRC_Graphic: (*type) = 'P'; return;
1313 
1314 		case IMSRC_TX_HI:
1315 		case IMSRC_Texture: (*type) = 'T'; return;
1316 
1317 		case IMSRC_Flat:    (*type) = 'F'; return;
1318 
1319 		case IMSRC_Sprite:  (*type) = 'S'; return;
1320 
1321 		case IMSRC_Dummy:   (*type) = 'd'; return;
1322 
1323 		default:
1324 			I_Error("W_ImageMakeSaveString: bad type %d\n", rim->source_type);
1325 			break;
1326 	}
1327 }
1328 
1329 
W_ImageGetName(const image_c * image)1330 const char *W_ImageGetName(const image_c *image)
1331 {
1332 	const image_c *rim;
1333 
1334 	rim = (const image_c *) image;
1335 
1336 	return rim->name;
1337 }
1338 
1339 
1340 //----------------------------------------------------------------------------
1341 //
1342 //  IMAGE USAGE
1343 //
1344 
ImageCacheOGL(image_c * rim,const colourmap_c * trans)1345 static cached_image_t *ImageCacheOGL(image_c *rim,
1346 	const colourmap_c *trans)
1347 {
1348 	// check if image + translation is already cached
1349 
1350 	int free_slot = -1;
1351 
1352 	cached_image_t *rc = NULL;
1353 
1354 	for (int i = 0; i < (int)rim->cache.size(); i++)
1355 	{
1356 		rc = rim->cache[i];
1357 
1358 		if (! rc)
1359 		{
1360 			free_slot = i;
1361 			continue;
1362 		}
1363 
1364 		if (rc->trans_map == trans)
1365 			break;
1366 
1367 		rc = NULL;
1368 	}
1369 
1370 	if (! rc)
1371 	{
1372 		// add entry into cache
1373 		rc = new cached_image_t;
1374 
1375 		rc->parent = rim;
1376 		rc->trans_map = trans;
1377 		rc->hue = RGB_NO_VALUE;
1378 		rc->tex_id = 0;
1379 
1380 		InsertAtTail(rc);
1381 
1382 		if (free_slot >= 0)
1383 			rim->cache[free_slot] = rc;
1384 		else
1385 			rim->cache.push_back(rc);
1386 	}
1387 
1388 	SYS_ASSERT(rc);
1389 
1390 #if 0  // REMOVE
1391 	if (rc->invalidated)
1392 	{
1393 		SYS_ASSERT(rc->tex_id != 0);
1394 
1395 		glDeleteTextures(1, &rc->tex_id);
1396 
1397 		rc->tex_id = 0;
1398 		rc->invalidated = false;
1399 	}
1400 #endif
1401 
1402 	if (rc->tex_id == 0)
1403 	{
1404 		// load image into cache
1405 		rc->tex_id = LoadImageOGL(rim, trans);
1406 	}
1407 
1408 	return rc;
1409 }
1410 
1411 
1412 //
1413 // The top-level routine for caching in an image.  Mainly just a
1414 // switch to more specialised routines.
1415 //
W_ImageCache(const image_c * image,bool anim,const colourmap_c * trans)1416 GLuint W_ImageCache(const image_c *image, bool anim,
1417 				    const colourmap_c *trans)
1418 {
1419 	// Intentional Const Override
1420 	image_c *rim = (image_c *) image;
1421 
1422 	// handle animations
1423 	if (anim)
1424 		rim = rim->anim.cur;
1425 
1426 	cached_image_t *rc = ImageCacheOGL(rim, trans);
1427 
1428 	SYS_ASSERT(rc->parent);
1429 
1430 	return rc->tex_id;
1431 }
1432 
1433 
1434 #if 0
1435 rgbcol_t W_ImageGetHue(const image_c *img)
1436 {
1437 	SYS_ASSERT(c);
1438 
1439 	// Intentional Const Override
1440 	cached_image_t *rc = ((cached_image_t *) c) - 1;
1441 
1442 	SYS_ASSERT(rc->parent);
1443 
1444 	return rc->hue;
1445 }
1446 #endif
1447 
1448 
W_ImagePreCache(const image_c * image)1449 void W_ImagePreCache(const image_c *image)
1450 {
1451 	W_ImageCache(image, false);
1452 
1453 	// Intentional Const Override
1454 	image_c *rim = (image_c *) image;
1455 
1456 	// pre-cache alternative images for switches too
1457 	if (strlen(rim->name) >= 4 &&
1458 		(strnicmp(rim->name, "SW1", 3) == 0 ||
1459 		 strnicmp(rim->name, "SW2", 3) == 0 ))
1460 	{
1461 		char alt_name[16];
1462 
1463 		strcpy(alt_name, rim->name);
1464 		alt_name[2] = (alt_name[2] == '1') ? '2' : '1';
1465 
1466 		image_c *alt = do_Lookup(real_textures, alt_name);
1467 
1468 		if (alt) W_ImageCache(alt, false);
1469 	}
1470 }
1471 
1472 
1473 //----------------------------------------------------------------------------
1474 
W_CreateDummyImages(void)1475 static void W_CreateDummyImages(void)
1476 {
1477 	dummy_sprite = CreateDummyImage("DUMMY_SPRITE", 0xFFFF00, TRANS_PIXEL);
1478 	dummy_skin   = CreateDummyImage("DUMMY_SKIN",   0xFF77FF, 0x993399);
1479 
1480 	skyflatimage = CreateDummyImage("DUMMY_SKY",    0x0000AA, 0x55AADD);
1481 
1482 	dummy_hom[0] = CreateDummyImage("DUMMY_HOM1", 0xFF3333, 0x000000);
1483 	dummy_hom[1] = CreateDummyImage("DUMMY_HOM2", 0x000000, 0xFF3333);
1484 
1485 	// make the dummy sprite easier to see
1486 	{
1487 		// Intentional Const Override
1488 		image_c *dsp = (image_c *) dummy_sprite;
1489 
1490 		dsp->scale_x = 3.0f;
1491 		dsp->scale_y = 3.0f;
1492 	}
1493 }
1494 
1495 
1496 //
1497 // Initialises the image system.
1498 //
W_InitImages(void)1499 bool W_InitImages(void)
1500 {
1501     // check options
1502 	if (M_CheckParm("-nosmoothing"))
1503 		var_smoothing = 0;
1504 	else if (M_CheckParm("-smoothing"))
1505 		var_smoothing = 1;
1506 
1507 	if (M_CheckParm("-nomipmap"))
1508 		var_mipmapping = 0;
1509 	else if (M_CheckParm("-mipmap"))
1510 		var_mipmapping = 1;
1511 	else if (M_CheckParm("-trilinear"))
1512 		var_mipmapping = 2;
1513 
1514 	M_CheckBooleanParm("dither", &var_dithering, false);
1515 
1516 	W_CreateDummyImages();
1517 
1518 	return true;
1519 }
1520 
1521 
1522 //
1523 // Animate all the images.
1524 //
W_UpdateImageAnims(void)1525 void W_UpdateImageAnims(void)
1526 {
1527 	do_Animate(real_graphics);
1528 	do_Animate(real_textures);
1529 	do_Animate(real_flats);
1530 }
1531 
1532 
W_DeleteAllImages(void)1533 void W_DeleteAllImages(void)
1534 {
1535 	std::list<cached_image_t *>::iterator CI;
1536 
1537 	for (CI = image_cache.begin(); CI != image_cache.end(); CI++)
1538 	{
1539 		cached_image_t *rc = *CI;
1540 		SYS_ASSERT(rc);
1541 
1542 		if (rc->tex_id != 0)
1543 		{
1544 			glDeleteTextures(1, &rc->tex_id);
1545 			rc->tex_id = 0;
1546 		}
1547 	}
1548 
1549 	DeleteSkyTextures();
1550 	DeleteColourmapTextures();
1551 }
1552 
1553 
1554 //
1555 // W_AnimateImageSet
1556 //
1557 // Sets up the images so they will animate properly.  Array is
1558 // allowed to contain NULL entries.
1559 //
1560 // NOTE: modifies the input array of images.
1561 //
W_AnimateImageSet(const image_c ** images,int number,int speed)1562 void W_AnimateImageSet(const image_c ** images, int number, int speed)
1563 {
1564 	int i, total;
1565 	image_c *rim, *other;
1566 
1567 	SYS_ASSERT(images);
1568 	SYS_ASSERT(speed > 0);
1569 
1570 	// ignore images that are already animating
1571 	for (i=0, total=0; i < number; i++)
1572 	{
1573 		// Intentional Const Override
1574 		rim = (image_c *) images[i];
1575 
1576 		if (! rim)
1577 			continue;
1578 
1579 		if (rim->anim.speed > 0)
1580 			continue;
1581 
1582 		images[total++] = images[i];
1583 	}
1584 
1585 	// anything left to animate ?
1586 	if (total < 2)
1587 		return;
1588 
1589 	for (i=0; i < total; i++)
1590 	{
1591 		// Intentional Const Override
1592 		rim   = (image_c *) images[i];
1593 		other = (image_c *) images[(i+1) % total];
1594 
1595 		rim->anim.next = other;
1596 		rim->anim.speed = rim->anim.count = speed;
1597 	}
1598 }
1599 
1600 //--- editor settings ---
1601 // vi:ts=4:sw=4:noexpandtab
1602