1 #include "build.h"
2 
3 #if USE_POLYMOST && USE_OPENGL
4 
5 #include "baselayer.h"
6 #include "glbuild.h"
7 #include "kplib.h"
8 #include "cache1d.h"
9 #include "pragmas.h"
10 #include "crc32.h"
11 #include "engine_priv.h"
12 #include "polymost_priv.h"
13 #include "hightile_priv.h"
14 #include "polymosttex_priv.h"
15 #include "polymosttexcache.h"
16 #include "polymosttexcompress.h"
17 
18 /** a texture hash entry */
19 struct PTHash_typ {
20 	struct PTHash_typ *next;
21 	PTHead head;
22 	struct PTHash_typ *deferto;	// if pt_findpthash can't find an exact match for a set of
23 				// parameters, it creates a header and defers it to another
24 				// entry that stands in its place
25 	int primecnt;	// a count of how many times the texture is touched when priming
26 };
27 typedef struct PTHash_typ PTHash;
28 
29 /** a texture manager hash entry */
30 struct PTMHash_typ {
31 	struct PTMHash_typ *next;
32 	PTMHead head;
33 	unsigned int idcrc;   // crc32 of the id below for quick comparisons
34     PTMIdent id;
35 };
36 typedef struct PTMHash_typ PTMHash;
37 
38 /** an iterator for walking the hash */
39 struct PTIter_typ {
40 	int i;
41 	PTHash *pth;
42 
43 	// criteria for doing selective matching
44 	int match;
45 	int picnum;
46 	int palnum;
47 	unsigned short flagsmask;
48 	unsigned short flags;
49 };
50 
51 /** a convenient structure for passing around texture data that is being baked */
52 struct PTTexture_typ {
53 	coltype * pic;
54 	GLsizei sizx, sizy;	// padded size
55 	GLsizei tsizx, tsizy;	// true size
56 	GLenum rawfmt;		// raw format of the data (GL_RGBA, GL_BGRA)
57 	int hasalpha;
58 };
59 typedef struct PTTexture_typ PTTexture;
60 
61 static int primecnt   = 0;	// expected number of textures to load during priming
62 static int primedone  = 0;	// running total of how many textures have been primed
63 static int primepos   = 0;	// the position in pthashhead where we are up to in priming
64 
65 int polymosttexverbosity = 1;	// 0 = none, 1 = errors, 2 = all
66 int polymosttexfullbright = 256;	// first index of the fullbright palette entries
67 
68 #define PTHASHHEADSIZ 4096
69 static PTHash * pthashhead[PTHASHHEADSIZ];	// will be initialised 0 by .bss segment
70 
71 #define PTMHASHHEADSIZ 4096
72 static PTMHash * ptmhashhead[PTMHASHHEADSIZ];	// will be initialised 0 by .bss segment
73 
74 static const char *compressfourcc[] = {
75 	"NONE",
76 	"DXT1",
77 	"DXT5",
78 	"ETC1",
79 };
80 
81 static void ptm_fixtransparency(PTTexture * tex, int clamped);
82 static void ptm_applyeffects(PTTexture * tex, int effects);
83 static void ptm_mipscale(PTTexture * tex);
84 static void ptm_uploadtexture(PTMHead * ptm, unsigned short flags, PTTexture * tex, PTCacheTile * tdef);
85 
86 
pt_gethashhead(const int picnum)87 static inline int pt_gethashhead(const int picnum)
88 {
89 	return picnum & (PTHASHHEADSIZ-1);
90 }
91 
ptm_gethashhead(const unsigned int idcrc)92 static inline int ptm_gethashhead(const unsigned int idcrc)
93 {
94 	return idcrc & (PTMHASHHEADSIZ-1);
95 }
96 
detect_texture_size()97 static void detect_texture_size()
98 {
99 	if (gltexmaxsize <= 0) {
100 		GLint siz = glinfo.maxtexsize;
101 		if (siz == 0) {
102 			gltexmaxsize = 6;   // 2^6 = 64 == default GL max texture size
103 		} else {
104 			gltexmaxsize = 0;
105 			for (; siz > 1; siz >>= 1) {
106 				gltexmaxsize++;
107 			}
108 		}
109 	}
110 }
111 
112 
113 /**
114  * Calculates a texture id from the information in a PTHead structure
115  * @param id the PTMIdent to initialise using...
116  * @param pth the PTHead structure
117  */
PTM_InitIdent(PTMIdent * id,PTHead * pth)118 void PTM_InitIdent(PTMIdent *id, PTHead *pth)
119 {
120     memset(id, 0, sizeof(PTMIdent));
121 
122     if (!pth) {
123         return;
124     }
125 
126 	if (pth->flags & PTH_HIGHTILE) {
127         id->type = PTMIDENT_HIGHTILE;
128 
129 		if (!pth->repldef) {
130 			if (polymosttexverbosity >= 1) {
131 				buildprintf("PolymostTex: cannot calculate texture id for pth with no repldef\n");
132 			}
133 			memset(id, 0, 16);
134 			return;
135 		}
136 
137 		id->flags = pth->flags & (PTH_CLAMPED | PTH_SKYBOX);
138 		id->effects = (pth->palnum != pth->repldef->palnum)
139             ? hictinting[pth->palnum].f : 0;
140 		if (pth->flags & PTH_SKYBOX) {
141 			// hightile + skybox + picnum ought to cover it
142 		    id->picnum = pth->picnum;
143 		} else {
144 		    strncpy(id->filename, pth->repldef->filename, BMAX_PATH);
145 		}
146 	} else {
147 		id->type = PTMIDENT_ART;
148 		id->flags = pth->flags & (PTH_CLAMPED);
149 		id->palnum = pth->palnum;
150 		id->picnum = pth->picnum;
151 	}
152 }
153 
154 
155 /**
156  * Returns a PTMHead pointer for the given texture id
157  * @param id the identifier of the texture
158  * @return the PTMHead item, or null if it couldn't be created
159  */
PTM_GetHead(const PTMIdent * id)160 PTMHead * PTM_GetHead(const PTMIdent *id)
161 {
162 	PTMHash * ptmh;
163 	int i;
164     unsigned int idcrc;
165 
166     idcrc = crc32once((unsigned char *)id, sizeof(PTMIdent));
167 
168 	i = ptm_gethashhead(idcrc);
169 	ptmh = ptmhashhead[i];
170 
171 	while (ptmh) {
172         if (ptmh->idcrc == idcrc &&
173             memcmp(&ptmh->id, id, sizeof(PTMIdent)) == 0) {
174 			return &ptmh->head;
175 		}
176 		ptmh = ptmh->next;
177 	}
178 
179 	ptmh = (PTMHash *) malloc(sizeof(PTMHash));
180 	if (ptmh) {
181 		memset(ptmh, 0, sizeof(PTMHash));
182 
183         ptmh->idcrc = idcrc;
184 		memcpy(&ptmh->id, id, sizeof(PTMIdent));
185 		ptmh->next = ptmhashhead[i];
186 		ptmhashhead[i] = ptmh;
187 	}
188 
189 	return &ptmh->head;
190 }
191 
192 
193 /**
194  * Loads a texture file into OpenGL from the PolymostTex cache
195  * @param filename the texture filename
196  * @param ptmh the PTMHead structure to receive the texture details
197  * @param flags PTH_* flags to tune the load process
198  * @param effects HICEFFECT_* effects
199  * @return 0 on success, <0 on error
200  */
ptm_loadcachedtexturefile(const char * filename,PTMHead * ptmh,int flags,int effects)201 static int ptm_loadcachedtexturefile(const char* filename, PTMHead* ptmh, int flags, int effects)
202 {
203 	int mipmap = 0, i = 0;
204 	PTCacheTile * tdef = 0;
205 	int compress = PTCOMPRESS_NONE;
206 
207 	tdef = PTCacheLoadTile(filename, effects, flags & (PTH_CLAMPED));
208 	if (!tdef) {
209 		return -1;
210 	}
211 
212 	switch (tdef->format) {
213 #if GL_EXT_texture_compression_dxt1 || GL_EXT_texture_compression_s3tc
214 		case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
215 			compress = PTCOMPRESS_DXT1;
216 			if (!glinfo.texcomprdxt1) goto incompatible;
217 			break;
218 #endif
219 #if GL_EXT_texture_compression_s3tc
220 		case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
221 			compress = PTCOMPRESS_DXT5;
222 			if (!glinfo.texcomprdxt5) goto incompatible;
223 			break;
224 #endif
225 #if GL_OES_compressed_ETC1_RGB8_texture
226 		case GL_ETC1_RGB8_OES:
227 			compress = PTCOMPRESS_ETC1;
228 			if (!glinfo.texcompretc1) goto incompatible;
229 			break;
230 #endif
231 		default:
232 incompatible:
233 			if (polymosttexverbosity >= 2) {
234 				buildprintf("PolymostTex: cached %s (effects %d, flags %d) has incompatible format %s (0x%x)\n",
235 						   tdef->filename, tdef->effects, tdef->flags,
236 						   compress ? compressfourcc[compress] : "?",
237 						   tdef->format);
238 			}
239 			PTCacheFreeTile(tdef);
240 			return -1;
241 	}
242 
243 	if (polymosttexverbosity >= 2) {
244 		buildprintf("PolymostTex: loaded %s (effects %d, flags %d, %s) from cache\n",
245 				   tdef->filename, tdef->effects, tdef->flags, compressfourcc[compress]);
246 	}
247 
248 	if (ptmh->glpic == 0) {
249 		glfunc.glGenTextures(1, &ptmh->glpic);
250 	}
251 	ptmh->tsizx = tdef->tsizx;
252 	ptmh->tsizy = tdef->tsizy;
253 	ptmh->sizx  = tdef->mipmap[0].sizx;
254 	ptmh->sizy  = tdef->mipmap[0].sizy;
255 	ptmh->flags = tdef->flags & PTH_HASALPHA;
256 	glfunc.glBindTexture(GL_TEXTURE_2D, ptmh->glpic);
257 
258 	mipmap = 0;
259 	if (! (flags & PTH_NOMIPLEVEL)) {
260 		// if we aren't instructed to preserve all mipmap levels,
261 		// immediately throw away gltexmiplevel mipmaps
262 		mipmap = max(0, gltexmiplevel);
263 	}
264 	while (tdef->mipmap[mipmap].sizx > (1 << gltexmaxsize) ||
265 		   tdef->mipmap[mipmap].sizy > (1 << gltexmaxsize)) {
266 		// throw away additional mipmaps until the texture fits within
267 		// the maximum size permitted by the GL driver
268 		mipmap++;
269 	}
270 
271 	for (i = 0; i + mipmap < tdef->nummipmaps; i++) {
272 		glfunc.glCompressedTexImage2D(GL_TEXTURE_2D, i,
273 								   tdef->format,
274 								   tdef->mipmap[i + mipmap].sizx,
275 								   tdef->mipmap[i + mipmap].sizy,
276 								   0, tdef->mipmap[i + mipmap].length,
277 								   (const GLvoid *) tdef->mipmap[i + mipmap].data);
278 	}
279 
280 	PTCacheFreeTile(tdef);
281 
282 	return 0;
283 }
284 
285 
286 /**
287  * Loads a texture file into OpenGL
288  * @param filename the texture filename
289  * @param ptmh the PTMHead structure to receive the texture details
290  * @param flags PTH_* flags to tune the load process
291  * @param effects HICEFFECT_* effects to apply
292  * @return 0 on success, <0 on error
293  */
PTM_LoadTextureFile(const char * filename,PTMHead * ptmh,int flags,int effects)294 int PTM_LoadTextureFile(const char* filename, PTMHead* ptmh, int flags, int effects)
295 {
296 	PTTexture tex;
297 	int filh, picdatalen;
298 	int x, y;
299 	char * picdata = 0;
300 	PTCacheTile * tdef = 0;
301 	int writetocache = 0, iscached = 0;
302 
303 	if (!(flags & PTH_NOCOMPRESS) && glusetexcache && glusetexcompr) {
304 		iscached = PTCacheHasTile(filename, effects, (flags & PTH_CLAMPED));
305 
306 		// if the texture exists in the cache but the original file is newer,
307 		// ignore what's in the cache and overwrite it
308 		/*if (iscached && filemtime(filename) > filemtime(cacheitem)) {
309 			iscached = 0;
310 		}*/
311 
312 		if (!iscached) {
313 			writetocache = 1;
314 		}
315 	}
316 
317 	if (iscached) {
318 		if (ptm_loadcachedtexturefile(filename, ptmh, flags, effects) == 0) {
319 			return 0;
320 		}
321 	}
322 
323 	filh = kopen4load((char *) filename, 0);
324 	if (filh < 0) {
325 		return -1;
326 	}
327 	picdatalen = kfilelength(filh);
328 
329 	picdata = (char *) malloc(picdatalen);
330 	if (!picdata) {
331 		kclose(filh);
332 		return -2;
333 	}
334 
335 	if (kread(filh, picdata, picdatalen) != picdatalen) {
336 		kclose(filh);
337 		return -3;
338 	}
339 
340 	kclose(filh);
341 
342 	kpgetdim(picdata, picdatalen, (int *) &tex.tsizx, (int *) &tex.tsizy);
343 	if (tex.tsizx == 0 || tex.tsizy == 0) {
344 		free(picdata);
345 		return -4;
346 	}
347 
348 	if (!glinfo.texnpot || writetocache) {
349 		for (tex.sizx = 1; tex.sizx < tex.tsizx; tex.sizx += tex.sizx) ;
350 		for (tex.sizy = 1; tex.sizy < tex.tsizy; tex.sizy += tex.sizy) ;
351 	} else {
352 		tex.sizx = tex.tsizx;
353 		tex.sizy = tex.tsizy;
354 	}
355 
356 	tex.pic = (coltype *) malloc(tex.sizx * tex.sizy * sizeof(coltype));
357 	if (!tex.pic) {
358 		return -2;
359 	}
360 	memset(tex.pic, 0, tex.sizx * tex.sizy * sizeof(coltype));
361 
362 	if (kprender(picdata, picdatalen, tex.pic, tex.sizx * sizeof(coltype), tex.sizx, tex.sizy, 0, 0)) {
363 		free(picdata);
364 		free(tex.pic);
365 		return -5;
366 	}
367 
368 	free(picdata);
369 	picdata = 0;
370 
371 	ptm_applyeffects(&tex, effects);	// updates tex.hasalpha
372 
373 	ptmh->flags = 0;
374 	if (tex.hasalpha) {
375 		ptmh->flags |= PTH_HASALPHA;
376 	}
377 
378 	if (! (flags & PTH_CLAMPED) || (flags & PTH_SKYBOX)) { //Duplicate texture pixels (wrapping tricks for non power of 2 texture sizes)
379 		if (tex.sizx > tex.tsizx) {	//Copy left to right
380 			coltype * lptr = tex.pic;
381 			for (y = 0; y < tex.tsizy; y++, lptr += tex.sizx) {
382 				memcpy(&lptr[tex.tsizx], lptr, (tex.sizx - tex.tsizx) << 2);
383 			}
384 		}
385 		if (tex.sizy > tex.tsizy) {	//Copy top to bottom
386 			memcpy(&tex.pic[tex.sizx * tex.tsizy], tex.pic, (tex.sizy - tex.tsizy) * tex.sizx << 2);
387 		}
388 	}
389 
390 	tex.rawfmt = GL_BGRA;
391 	if (!glinfo.bgra) {
392 		int j;
393 		for (j = tex.sizx * tex.sizy - 1; j >= 0; j--) {
394 			swapchar(&tex.pic[j].r, &tex.pic[j].b);
395 		}
396 		tex.rawfmt = GL_RGBA;
397 	}
398 
399 	ptmh->tsizx = tex.tsizx;
400 	ptmh->tsizy = tex.tsizy;
401 	ptmh->sizx  = tex.sizx;
402 	ptmh->sizy  = tex.sizy;
403 
404 	if (writetocache) {
405 		int nmips = 0;
406 		while (max(1, (tex.sizx >> nmips)) > 1 ||
407 			   max(1, (tex.sizy >> nmips)) > 1) {
408 			nmips++;
409 		}
410 		nmips++;
411 
412 		tdef = PTCacheAllocNewTile(nmips);
413 		tdef->filename = strdup(filename);
414 		tdef->effects = effects;
415 		tdef->flags = (flags | ptmh->flags) & (PTH_CLAMPED | PTH_HASALPHA);
416 	}
417 
418 	ptm_uploadtexture(ptmh, flags, &tex, tdef);
419 
420 	if (writetocache) {
421 		if (polymosttexverbosity >= 2) {
422 			buildprintf("PolymostTex: writing %s (effects %d, flags %d) to cache\n",
423 					   tdef->filename, tdef->effects, tdef->flags);
424 		}
425 		PTCacheWriteTile(tdef);
426 		PTCacheFreeTile(tdef);
427 		tdef = 0;
428 	}
429 
430 
431 	if (tex.pic) {
432 		free(tex.pic);
433 		tex.pic = 0;
434 	}
435 
436 	return 0;
437 }
438 
439 /**
440  * Returns a string describing the error returned by PTM_LoadTextureFile
441  * @param err the error code
442  * @return the error string
443  */
PTM_GetLoadTextureFileErrorString(int err)444 const char * PTM_GetLoadTextureFileErrorString(int err)
445 {
446 	switch (err) {
447 		case 0:
448 			return "no error";
449 		case -1:
450 			return "not found";
451 		case -2:
452 			return "out of memory";
453 		case -3:
454 			return "truncated read";
455 		case -4:
456 			return "unrecognised format";
457 		case -5:
458 			return "decode error";
459 		default:
460 			return "unknown error";
461 	}
462 }
463 
464 
465 /**
466  * Finds the pthash entry for a tile, possibly creating it if one doesn't exist
467  * @param picnum tile number
468  * @param palnum palette number
469  * @param flags PTH_HIGHTILE = try for hightile, PTH_CLAMPED
470  * @param create !0 = create if none found
471  * @return the PTHash item, or null if none was found
472  */
pt_findhash(int picnum,int palnum,unsigned short flags,int create)473 static PTHash * pt_findhash(int picnum, int palnum, unsigned short flags, int create)
474 {
475 	int i = pt_gethashhead(picnum);
476 	PTHash * pth;
477 	PTHash * basepth;	// palette 0 in case we find we need it
478 
479 	unsigned short flagmask = flags & (PTH_HIGHTILE | PTH_CLAMPED | PTH_SKYBOX);
480 
481 	// first, try and find an existing match for our parameters
482 	pth = pthashhead[i];
483 	while (pth) {
484 		if (pth->head.picnum == picnum &&
485 		    pth->head.palnum == palnum &&
486 		    (pth->head.flags & (PTH_HIGHTILE | PTH_CLAMPED | PTH_SKYBOX)) == flagmask
487 		   ) {
488 			while (pth->deferto) {
489 				pth = pth->deferto;	// find the end of the chain
490 			}
491 			return pth;
492 		}
493 
494 		pth = pth->next;
495 	}
496 
497 	if (!create) {
498 		return 0;
499 	} else {
500 		// we didn't find one, so we have to create one
501 		hicreplctyp * replc = 0;
502 
503 		if ((flags & PTH_HIGHTILE)) {
504 			replc = hicfindsubst(picnum, palnum, (flags & PTH_SKYBOX));
505 		}
506 
507 		pth = (PTHash *) malloc(sizeof(PTHash));
508 		if (!pth) {
509 			return 0;
510 		}
511 		memset(pth, 0, sizeof(PTHash));
512 
513 		pth->next = pthashhead[i];
514 		pth->head.picnum  = picnum;
515 		pth->head.palnum  = palnum;
516 		pth->head.flags   = flagmask;
517 		pth->head.repldef = replc;
518 
519 		pthashhead[i] = pth;
520 
521 		if (replc && replc->palnum != palnum) {
522 			// we were given a substitute by hightile, so
523 			if (hictinting[palnum].f & HICEFFECTMASK) {
524 				// if effects are defined for the palette we actually want
525 				// we DO NOT defer immediately to the substitute. instead
526 				// we apply effects to the replacement and treat it as a
527 				// distinct texture
528 				;
529 			} else {
530 				// we defer to the substitute
531 				pth->deferto = pt_findhash(picnum, replc->palnum, flags, create);
532 				while (pth->deferto) {
533 					pth = pth->deferto;	// find the end of the chain
534 				}
535 			}
536 		} else if ((flags & PTH_HIGHTILE) && !replc) {
537 			// there is no replacement, so defer to ART
538 			if (flags & PTH_SKYBOX) {
539 				return 0;
540 			} else {
541 				pth->deferto = pt_findhash(picnum, palnum, (flags & ~PTH_HIGHTILE), create);
542 				while (pth->deferto) {
543 					pth = pth->deferto;	// find the end of the chain
544 				}
545 			}
546 		}
547 	}
548 
549 	return pth;
550 }
551 
552 /**
553  * Unloads a texture from memory
554  * @param pth pointer to the pthash of the loaded texture
555  */
pt_unload(PTHash * pth)556 static void pt_unload(PTHash * pth)
557 {
558 	int i;
559 	for (i = PTHPIC_SIZE - 1; i>=0; i--) {
560 		if (pth->head.pic[i] && pth->head.pic[i]->glpic) {
561 			glfunc.glDeleteTextures(1, &pth->head.pic[i]->glpic);
562 			pth->head.pic[i]->glpic = 0;
563 		}
564 	}
565 }
566 
567 static int pt_load_art(PTHead * pth);
568 static int pt_load_hightile(PTHead * pth);
569 static void pt_load_applyparameters(PTHead * pth);
570 
571 /**
572  * Loads a texture into memory from disk
573  * @param pth pointer to the pthash of the texture to load
574  * @return !0 on success
575  */
pt_load(PTHash * pth)576 static int pt_load(PTHash * pth)
577 {
578 	if (pth->head.pic[PTHPIC_BASE] &&
579 		pth->head.pic[PTHPIC_BASE]->glpic != 0 &&
580 		(pth->head.pic[PTHPIC_BASE]->flags & PTH_DIRTY) == 0) {
581 		return 1;	// loaded
582 	}
583 
584 	if ((pth->head.flags & PTH_HIGHTILE)) {
585 		// try and load from a replacement
586 		if (pt_load_hightile(&pth->head)) {
587 			return 1;
588 		}
589 
590 		// if that failed, get the hash for the ART version and
591 		//   defer this to there
592 		pth->deferto = pt_findhash(
593 				pth->head.picnum, pth->head.palnum,
594 				(pth->head.flags & ~PTH_HIGHTILE),
595 				1);
596 		if (!pth->deferto) {
597 			return 0;
598 		}
599 		return pt_load(pth->deferto);
600 	}
601 
602 	if (pt_load_art(&pth->head)) {
603 		return 1;
604 	}
605 
606 	// we're SOL
607 	return 0;
608 }
609 
610 
611 /**
612  * Load an ART tile into an OpenGL texture
613  * @param pth the header to populate
614  * @return !0 on success
615  */
pt_load_art(PTHead * pth)616 static int pt_load_art(PTHead * pth)
617 {
618 	PTTexture tex, fbtex;
619 	coltype * wpptr, * fpptr;
620 	int x, y, x2, y2;
621 	int dacol;
622 	int hasalpha = 0, hasfullbright = 0;
623     PTMIdent id;
624 
625 	tex.tsizx = tilesizx[pth->picnum];
626 	tex.tsizy = tilesizy[pth->picnum];
627 	if (!glinfo.texnpot) {
628 		for (tex.sizx = 1; tex.sizx < tex.tsizx; tex.sizx += tex.sizx) ;
629 		for (tex.sizy = 1; tex.sizy < tex.tsizy; tex.sizy += tex.sizy) ;
630 	} else {
631 		if ((tex.tsizx | tex.tsizy) == 0) {
632 			tex.sizx = tex.sizy = 1;
633 		} else {
634 			tex.sizx = tex.tsizx;
635 			tex.sizy = tex.tsizy;
636 		}
637 	}
638 
639 	tex.rawfmt = GL_RGBA;
640 
641 	memcpy(&fbtex, &tex, sizeof(PTTexture));
642 
643 	if (!waloff[pth->picnum]) {
644 		loadtile(pth->picnum);
645 	}
646 
647 	tex.pic = (coltype *) malloc(tex.sizx * tex.sizy * sizeof(coltype));
648 	if (!tex.pic) {
649 		return 0;
650 	}
651 
652 	// fullbright is initialised transparent
653 	fbtex.pic = (coltype *) malloc(tex.sizx * tex.sizy * sizeof(coltype));
654 	if (!fbtex.pic) {
655 		free(tex.pic);
656 		return 0;
657 	}
658 	memset(fbtex.pic, 0, tex.sizx * tex.sizy * sizeof(coltype));
659 
660 	if (!waloff[pth->picnum]) {
661 		// Force invalid textures to draw something - an almost purely transparency texture
662 		// This allows the Z-buffer to be updated for mirrors (which are invalidated textures)
663 		tex.pic[0].r = tex.pic[0].g = tex.pic[0].b = 0; tex.pic[0].a = 1;
664 		tex.tsizx = tex.tsizy = 1;
665 		hasalpha = 1;
666 	} else {
667 		for (y = 0; y < tex.sizy; y++) {
668 			if (y < tex.tsizy) {
669 				y2 = y;
670 			} else {
671 				y2 = y-tex.tsizy;
672 			}
673 			wpptr = &tex.pic[y*tex.sizx];
674 			fpptr = &fbtex.pic[y*tex.sizx];
675 			for (x = 0; x < tex.sizx; x++, wpptr++, fpptr++) {
676 				if ((pth->flags & PTH_CLAMPED) && (x >= tex.tsizx || y >= tex.tsizy)) {
677 					// Clamp texture
678 					wpptr->r = wpptr->g = wpptr->b = wpptr->a = 0;
679 					continue;
680 				}
681 				if (x < tex.tsizx) {
682 					x2 = x;
683 				} else {
684 					// wrap around to fill the repeated region
685 					x2 = x-tex.tsizx;
686 				}
687 				dacol = (int) (*(unsigned char *)(waloff[pth->picnum]+x2*tex.tsizy+y2));
688 				if (dacol == 255) {
689 					wpptr->a = 0;
690 					hasalpha = 1;
691 				} else {
692 					wpptr->a = 255;
693 					dacol = (int) ((unsigned char)palookup[pth->palnum][dacol]);
694 				}
695 				if (gammabrightness) {
696 					wpptr->r = curpalette[dacol].r;
697 					wpptr->g = curpalette[dacol].g;
698 					wpptr->b = curpalette[dacol].b;
699 				} else {
700 					wpptr->r = britable[curbrightness][ curpalette[dacol].r ];
701 					wpptr->g = britable[curbrightness][ curpalette[dacol].g ];
702 					wpptr->b = britable[curbrightness][ curpalette[dacol].b ];
703 				}
704 
705 				if (dacol >= polymosttexfullbright && dacol < 255) {
706 					*fpptr = *wpptr;
707 					hasfullbright = 1;
708 				}
709 			}
710 		}
711 	}
712 
713 	pth->scalex = 1.0;
714 	pth->scaley = 1.0;
715 	pth->flags &= ~(PTH_HASALPHA | PTH_SKYBOX);
716 	pth->flags |= (PTH_NOCOMPRESS | PTH_NOMIPLEVEL);
717 	tex.hasalpha = hasalpha;
718 
719     PTM_InitIdent(&id, pth);
720     id.layer = PTHPIC_BASE;
721 	pth->pic[PTHPIC_BASE] = PTM_GetHead(&id);
722 	pth->pic[PTHPIC_BASE]->tsizx = tex.tsizx;
723 	pth->pic[PTHPIC_BASE]->tsizy = tex.tsizy;
724 	pth->pic[PTHPIC_BASE]->sizx  = tex.sizx;
725 	pth->pic[PTHPIC_BASE]->sizy  = tex.sizy;
726 	ptm_uploadtexture(pth->pic[PTHPIC_BASE], pth->flags, &tex, 0);
727 
728 	if (hasfullbright) {
729         id.layer = PTHPIC_GLOW;
730 		pth->pic[PTHPIC_GLOW] = PTM_GetHead(&id);
731 		pth->pic[PTHPIC_GLOW]->tsizx = tex.tsizx;
732 		pth->pic[PTHPIC_GLOW]->tsizy = tex.tsizy;
733 		pth->pic[PTHPIC_GLOW]->sizx  = tex.sizx;
734 		pth->pic[PTHPIC_GLOW]->sizy  = tex.sizy;
735 		fbtex.hasalpha = 1;
736 		ptm_uploadtexture(pth->pic[PTHPIC_GLOW], pth->flags, &fbtex, 0);
737 	} else {
738 		// it might be that after reloading an invalidated texture, the
739 		// glow map might not be needed anymore, so release it
740 		pth->pic[PTHPIC_GLOW] = 0;//FIXME should really call a disposal function
741 	}
742 	pt_load_applyparameters(pth);
743 
744 	if (tex.pic) {
745 		free(tex.pic);
746 	}
747 	if (fbtex.pic) {
748 		free(fbtex.pic);
749 	}
750 
751 	return 1;
752 }
753 
754 /**
755  * Load a Hightile texture into an OpenGL texture
756  * @param pth the header to populate
757  * @return !0 on success. Success is defined as all faces of a skybox being loaded,
758  *   or at least the base texture of a regular replacement.
759  */
pt_load_hightile(PTHead * pth)760 static int pt_load_hightile(PTHead * pth)
761 {
762 	const char *filename = 0;
763 	int hasalpha = 0;
764 	int effects = 0;
765 	int err = 0;
766 	int texture = 0, loaded[PTHPIC_SIZE] = { 0,0,0,0,0,0, };
767     PTMIdent id;
768 
769 	if (!pth->repldef) {
770 		return 0;
771 	} else if ((pth->flags & PTH_SKYBOX) && (pth->repldef->skybox == 0 || pth->repldef->skybox->ignore)) {
772 		return 0;
773 	} else if (pth->repldef->ignore) {
774 		return 0;
775 	}
776 
777 	effects = (pth->palnum != pth->repldef->palnum) ? hictinting[pth->palnum].f : 0;
778 
779 	pth->flags &= ~(PTH_NOCOMPRESS | PTH_HASALPHA);
780 	if (pth->repldef->flags & HIC_NOCOMPRESS) {
781 		pth->flags |= PTH_NOCOMPRESS;
782 	}
783 
784 	for (texture = 0; texture < PTHPIC_SIZE; texture++) {
785 		if (pth->flags & PTH_SKYBOX) {
786 			if (texture >= 6) {
787 				texture = PTHPIC_SIZE;
788 				continue;
789 			}
790 			filename = pth->repldef->skybox->face[texture];
791 		} else {
792 			switch (texture) {
793 				case PTHPIC_BASE:
794 					filename = pth->repldef->filename;
795 					break;
796 				default:
797 					// future developments may use the other indices
798 					texture = PTHPIC_SIZE;
799 					continue;
800 			}
801 		}
802 
803 		if (!filename) {
804 			continue;
805 		}
806 
807         PTM_InitIdent(&id, pth);
808         id.layer = texture;
809         pth->pic[texture] = PTM_GetHead(&id);
810 
811 		if ((err = PTM_LoadTextureFile(filename, pth->pic[texture], pth->flags, effects))) {
812 			if (polymosttexverbosity >= 1) {
813 				const char * errstr = PTM_GetLoadTextureFileErrorString(err);
814 				buildprintf("PolymostTex: %s (pic %d pal %d) %s\n",
815 						   filename, pth->picnum, pth->palnum, errstr);
816 			}
817 			continue;
818 		}
819 
820 		if (texture == 0) {
821 			if (pth->flags & PTH_SKYBOX) {
822 				pth->scalex = (float)pth->pic[texture]->tsizx / 64.0;
823 				pth->scaley = (float)pth->pic[texture]->tsizy / 64.0;
824 			} else {
825 				pth->scalex = (float)pth->pic[texture]->tsizx / (float)tilesizx[pth->picnum];
826 				pth->scaley = (float)pth->pic[texture]->tsizy / (float)tilesizy[pth->picnum];
827 			}
828 		}
829 
830 		loaded[texture] = 1;
831 	}
832 
833 	pt_load_applyparameters(pth);
834 
835 	if (pth->flags & PTH_SKYBOX) {
836 		int i = 0;
837 		for (texture = 0; texture < 6; texture++) i += loaded[texture];
838 		return (i == 6);
839 	} else {
840 		return loaded[PTHPIC_BASE];
841 	}
842 }
843 
844 /**
845  * Applies the global texture filter parameters to the given texture
846  * @param pth the cache header
847  */
pt_load_applyparameters(PTHead * pth)848 static void pt_load_applyparameters(PTHead * pth)
849 {
850 	int i;
851 
852 	for (i = 0; i < PTHPIC_SIZE; i++) {
853 		if (pth->pic[i] == 0 || pth->pic[i]->glpic == 0) {
854 			continue;
855 		}
856 
857 		glfunc.glBindTexture(GL_TEXTURE_2D, pth->pic[i]->glpic);
858 
859 		if (gltexfiltermode < 0) {
860 			gltexfiltermode = 0;
861 		} else if (gltexfiltermode >= (int)numglfiltermodes) {
862 			gltexfiltermode = numglfiltermodes-1;
863 		}
864 		glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, glfiltermodes[gltexfiltermode].mag);
865 		glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glfiltermodes[gltexfiltermode].min);
866 
867 		if (glinfo.maxanisotropy > 1.0) {
868 			if (glanisotropy <= 0 || glanisotropy > glinfo.maxanisotropy) {
869 				glanisotropy = (int)glinfo.maxanisotropy;
870 			}
871 			glfunc.glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, glanisotropy);
872 		}
873 
874 		if (! (pth->flags & PTH_CLAMPED)) {
875 			glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
876 			glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
877 		} else {     //For sprite textures, clamping looks better than wrapping
878 			GLint c = glinfo.clamptoedge ? GL_CLAMP_TO_EDGE : GL_CLAMP;
879 			glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, c);
880 			glfunc.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, c);
881 		}
882 	}
883 }
884 
885 
886 /**
887  * Applies a filter to transparent pixels to improve their appearence when bilinearly filtered
888  * @param tex the texture to process
889  * @param clamped whether the texture is to be used clamped
890  */
ptm_fixtransparency(PTTexture * tex,int clamped)891 static void ptm_fixtransparency(PTTexture * tex, int clamped)
892 {
893 	coltype *wpptr;
894 	int j, x, y, r, g, b;
895 	int dox, doy, naxsiz2;
896 	int daxsiz = tex->tsizx, daysiz = tex->tsizy;
897 	int daxsiz2 = tex->sizx, daysiz2 = tex->sizy;
898 
899 	dox = daxsiz2-1;
900 	doy = daysiz2-1;
901 	if (clamped) {
902 		dox = min(dox,daxsiz);
903 		doy = min(doy,daysiz);
904 	} else {
905 		// Make repeating textures duplicate top/left parts
906 		daxsiz = daxsiz2;
907 		daysiz = daysiz2;
908 	}
909 
910 	daxsiz--; daysiz--; naxsiz2 = -daxsiz2; // Hacks for optimization inside loop
911 
912 	// Set transparent pixels to average color of neighboring opaque pixels
913 	// Doing this makes bilinear filtering look much better for masked textures (I.E. sprites)
914 	for (y = doy; y >= 0; y--) {
915 		wpptr = &tex->pic[y*daxsiz2+dox];
916 		for (x = dox; x >= 0; x--, wpptr--) {
917 			if (wpptr->a) {
918 				continue;
919 			}
920 			r = g = b = j = 0;
921 			if ((x>     0) && (wpptr[     -1].a)) {
922 				r += (int)wpptr[     -1].r;
923 				g += (int)wpptr[     -1].g;
924 				b += (int)wpptr[     -1].b;
925 				j++;
926 			}
927 			if ((x<daxsiz) && (wpptr[     +1].a)) {
928 				r += (int)wpptr[     +1].r;
929 				g += (int)wpptr[     +1].g;
930 				b += (int)wpptr[     +1].b;
931 				j++;
932 			}
933 			if ((y>     0) && (wpptr[naxsiz2].a)) {
934 				r += (int)wpptr[naxsiz2].r;
935 				g += (int)wpptr[naxsiz2].g;
936 				b += (int)wpptr[naxsiz2].b;
937 				j++;
938 			}
939 			if ((y<daysiz) && (wpptr[daxsiz2].a)) {
940 				r += (int)wpptr[daxsiz2].r;
941 				g += (int)wpptr[daxsiz2].g;
942 				b += (int)wpptr[daxsiz2].b;
943 				j++;
944 			}
945 			switch (j) {
946 				case 1: wpptr->r =   r            ;
947 					wpptr->g =   g            ;
948 					wpptr->b =   b            ;
949 					break;
950 				case 2: wpptr->r = ((r   +  1)>>1);
951 					wpptr->g = ((g   +  1)>>1);
952 					wpptr->b = ((b   +  1)>>1);
953 					break;
954 				case 3: wpptr->r = ((r*85+128)>>8);
955 					wpptr->g = ((g*85+128)>>8);
956 					wpptr->b = ((b*85+128)>>8);
957 					break;
958 				case 4: wpptr->r = ((r   +  2)>>2);
959 					wpptr->g = ((g   +  2)>>2);
960 					wpptr->b = ((b   +  2)>>2);
961 					break;
962 				default: break;
963 			}
964 		}
965 	}
966 }
967 
968 /**
969  * Applies brightness (if no gammabrightness is available) and other hightile
970  * effects to a texture. As a bonus, it also checks if there is any transparency
971  * in the texture (and updates tex->hasalpha).
972  * @param tex the texture
973  * @param effects the effects
974  */
ptm_applyeffects(PTTexture * tex,int effects)975 static void ptm_applyeffects(PTTexture * tex, int effects)
976 {
977 	int alph = 255;
978 	int x, y;
979 	coltype * tptr = tex->pic;
980 
981 	if (effects == 0 && gammabrightness) {
982 		// use a quicker scan for alpha since we don't need
983 		// to be swizzling texel components
984 		for (y = tex->tsizy - 1; y >= 0; y--, tptr += tex->sizx) {
985 			for (x = tex->tsizx - 1; x >= 0; x--) {
986 				alph &= tptr[x].a;
987 			}
988 		}
989 	} else {
990 		unsigned char *brit = &britable[gammabrightness ? 0 : curbrightness][0];
991 		coltype tcol;
992 
993 		for (y = tex->tsizy - 1; y >= 0; y--, tptr += tex->sizx) {
994 			for (x = tex->tsizx - 1; x >= 0; x--) {
995 				tcol.b = brit[tptr[x].b];
996 				tcol.g = brit[tptr[x].g];
997 				tcol.r = brit[tptr[x].r];
998 				tcol.a = tptr[x].a;
999 				alph &= tptr[x].a;
1000 
1001 				if (effects & HICEFFECT_GREYSCALE) {
1002 					float y;
1003 					y  = 0.3  * (float)tcol.r;
1004 					y += 0.59 * (float)tcol.g;
1005 					y += 0.11 * (float)tcol.b;
1006 					tcol.b = (unsigned char)max(0.0, min(255.0, y));
1007 					tcol.g = tcol.r = tcol.b;
1008 				}
1009 				if (effects & HICEFFECT_INVERT) {
1010 					tcol.b = 255-tcol.b;
1011 					tcol.g = 255-tcol.g;
1012 					tcol.r = 255-tcol.r;
1013 				}
1014 
1015 				tptr[x] = tcol;
1016 			}
1017 		}
1018 	}
1019 
1020 	tex->hasalpha = (alph != 255);
1021 }
1022 
1023 
1024 /**
1025  * Scales down the texture by half in-place
1026  * @param tex the texture
1027  */
ptm_mipscale(PTTexture * tex)1028 static void ptm_mipscale(PTTexture * tex)
1029 {
1030 	GLsizei newx, newy;
1031 	GLsizei x, y;
1032 	coltype *wpptr, *rpptr;
1033 	int r, g, b, a, k;
1034 
1035 	newx = max(1, (tex->sizx >> 1));
1036 	newy = max(1, (tex->sizy >> 1));
1037 
1038 	for (y = 0; y < newy; y++) {
1039 		wpptr = &tex->pic[y * newx];
1040 		rpptr = &tex->pic[(y << 1) * tex->sizx];
1041 
1042 		for (x = 0; x < newx; x++, wpptr++, rpptr += 2) {
1043 			r = g = b = a = k = 0;
1044 			if (rpptr[0].a) {
1045 				r += (int)rpptr[0].r;
1046 				g += (int)rpptr[0].g;
1047 				b += (int)rpptr[0].b;
1048 				a += (int)rpptr[0].a;
1049 				k++;
1050 			}
1051 			if ((x+x+1 < tex->sizx) && (rpptr[1].a)) {
1052 				r += (int)rpptr[1].r;
1053 				g += (int)rpptr[1].g;
1054 				b += (int)rpptr[1].b;
1055 				a += (int)rpptr[1].a;
1056 				k++;
1057 			}
1058 			if (y+y+1 < tex->sizy) {
1059 				if (rpptr[tex->sizx].a) {
1060 					r += (int)rpptr[tex->sizx  ].r;
1061 					g += (int)rpptr[tex->sizx  ].g;
1062 					b += (int)rpptr[tex->sizx  ].b;
1063 					a += (int)rpptr[tex->sizx  ].a;
1064 					k++;
1065 				}
1066 				if ((x+x+1 < tex->sizx) && (rpptr[tex->sizx+1].a)) {
1067 					r += (int)rpptr[tex->sizx+1].r;
1068 					g += (int)rpptr[tex->sizx+1].g;
1069 					b += (int)rpptr[tex->sizx+1].b;
1070 					a += (int)rpptr[tex->sizx+1].a;
1071 					k++;
1072 				}
1073 			}
1074 			switch(k) {
1075 				case 0:
1076 				case 1: wpptr->r = r;
1077 					wpptr->g = g;
1078 					wpptr->b = b;
1079 					wpptr->a = a;
1080 					break;
1081 				case 2: wpptr->r = ((r+1)>>1);
1082 					wpptr->g = ((g+1)>>1);
1083 					wpptr->b = ((b+1)>>1);
1084 					wpptr->a = ((a+1)>>1);
1085 					break;
1086 				case 3: wpptr->r = ((r*85+128)>>8);
1087 					wpptr->g = ((g*85+128)>>8);
1088 					wpptr->b = ((b*85+128)>>8);
1089 					wpptr->a = ((a*85+128)>>8);
1090 					break;
1091 				case 4: wpptr->r = ((r+2)>>2);
1092 					wpptr->g = ((g+2)>>2);
1093 					wpptr->b = ((b+2)>>2);
1094 					wpptr->a = ((a+2)>>2);
1095 					break;
1096 				default: break;
1097 			}
1098 			//if (wpptr->a) wpptr->a = 255;
1099 		}
1100 	}
1101 
1102 	tex->sizx = newx;
1103 	tex->sizy = newy;
1104 }
1105 
1106 
1107 /**
1108  * Sends texture data to GL
1109  * @param ptm the texture management header
1110  * @param flags extra flags to modify how the texture is uploaded
1111  * @param tex the texture to upload
1112  * @param tdef the polymosttexcache definition to receive compressed mipmaps, or null
1113  */
ptm_uploadtexture(PTMHead * ptm,unsigned short flags,PTTexture * tex,PTCacheTile * tdef)1114 static void ptm_uploadtexture(PTMHead * ptm, unsigned short flags, PTTexture * tex, PTCacheTile * tdef)
1115 {
1116 	int i;
1117 	GLint mipmap;
1118 	GLint intexfmt;
1119 	int compress = PTCOMPRESS_NONE;
1120 	unsigned char * comprdata = 0;
1121 	int tdefmip = 0, comprsize = 0;
1122 	int starttime;
1123 
1124 	detect_texture_size();
1125 
1126 #if USE_OPENGL == USE_GLES2
1127 	// GLES permits BGRA as an internal format.
1128     intexfmt = tex->rawfmt;
1129 #else
1130     intexfmt = GL_RGBA;
1131 #endif
1132 	if (!(flags & PTH_NOCOMPRESS) && glusetexcompr) {
1133 #if GL_EXT_texture_compression_dxt1 || GL_EXT_texture_compression_s3tc
1134 		if (!compress && !tex->hasalpha && glinfo.texcomprdxt1) {
1135 			intexfmt = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1136 			compress = PTCOMPRESS_DXT1;
1137 		}
1138 #endif
1139 #if GL_OES_compressed_ETC1_RGB8_texture
1140 		if (!compress && !tex->hasalpha && glinfo.texcompretc1) {
1141 			intexfmt = GL_ETC1_RGB8_OES;
1142 			compress = PTCOMPRESS_ETC1;
1143 		}
1144 #endif
1145 #if GL_EXT_texture_compression_s3tc
1146 		if (!compress && tex->hasalpha && glinfo.texcomprdxt5) {
1147 			intexfmt = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1148 			compress = PTCOMPRESS_DXT5;
1149 		}
1150 #endif
1151 	}
1152 
1153 	if (compress && tdef) {
1154 		tdef->format = intexfmt;
1155 		tdef->tsizx  = tex->tsizx;
1156 		tdef->tsizy  = tex->tsizy;
1157 	}
1158 
1159 	if (ptm->glpic == 0) {
1160 		glfunc.glGenTextures(1, &ptm->glpic);
1161 	}
1162 	glfunc.glBindTexture(GL_TEXTURE_2D, ptm->glpic);
1163 
1164 	ptm_fixtransparency(tex, (flags & PTH_CLAMPED));
1165 
1166 	mipmap = 0;
1167 	if (! (flags & PTH_NOMIPLEVEL)) {
1168 		// if we aren't instructed to preserve all mipmap levels,
1169 		// immediately throw away gltexmiplevel mipmaps
1170 		mipmap = max(0, gltexmiplevel);
1171 	}
1172 	while ((tex->sizx >> mipmap) > (1 << gltexmaxsize) ||
1173 	       (tex->sizy >> mipmap) > (1 << gltexmaxsize)) {
1174 		// throw away additional mipmaps until the texture fits within
1175 		// the maximum size permitted by the GL driver
1176 		mipmap++;
1177 	}
1178 
1179 	for ( ;
1180 	     mipmap > 0 && (tex->sizx > 1 || tex->sizy > 1);
1181 	     mipmap--) {
1182 		if (compress && tdef) {
1183 			comprsize = ptcompress_getstorage(tex->sizx, tex->sizy, compress);
1184 			comprdata = (unsigned char *) malloc(comprsize);
1185 
1186 			starttime = getticks();
1187 			ptcompress_compress(tex->pic, tex->sizx, tex->sizy, comprdata, compress);
1188 			if (polymosttexverbosity >= 2) {
1189 				buildprintf("PolymostTex: ptcompress_compress (%dx%d, %s) took %f sec\n",
1190 					   tex->sizx, tex->sizy, compressfourcc[compress],
1191 					   (float)(getticks() - starttime) / 1000.f);
1192 			}
1193 
1194 			tdef->mipmap[tdefmip].sizx = tex->sizx;
1195 			tdef->mipmap[tdefmip].sizy = tex->sizy;
1196 			tdef->mipmap[tdefmip].length = comprsize;
1197 			tdef->mipmap[tdefmip].data = comprdata;
1198 			tdefmip++;
1199 
1200 			comprdata = 0;
1201 		}
1202 
1203 		ptm_mipscale(tex);
1204 		ptm_fixtransparency(tex, (flags & PTH_CLAMPED));
1205 	}
1206 
1207 	if (compress) {
1208 		comprsize = ptcompress_getstorage(tex->sizx, tex->sizy, compress);
1209 		comprdata = (unsigned char *) malloc(comprsize);
1210 
1211 		starttime = getticks();
1212 		ptcompress_compress(tex->pic, tex->sizx, tex->sizy, comprdata, compress);
1213 		if (polymosttexverbosity >= 2) {
1214 			buildprintf("PolymostTex: ptcompress_compress (%dx%d, %s) took %f sec\n",
1215 				   tex->sizx, tex->sizy, compressfourcc[compress],
1216 				   (float)(getticks() - starttime) / 1000.f);
1217 		}
1218 
1219 		if (tdef) {
1220 			tdef->mipmap[tdefmip].sizx = tex->sizx;
1221 			tdef->mipmap[tdefmip].sizy = tex->sizy;
1222 			tdef->mipmap[tdefmip].length = comprsize;
1223 			tdef->mipmap[tdefmip].data = comprdata;
1224 			tdefmip++;
1225 		}
1226 
1227 		glfunc.glCompressedTexImage2D(GL_TEXTURE_2D, 0,
1228 			intexfmt, tex->sizx, tex->sizy, 0,
1229 			comprsize, (const GLvoid *) comprdata);
1230 
1231 		if (tdef) {
1232 			// we need to retain each mipmap for the tdef struct, so
1233 			// force each mipmap to be allocated afresh in the loop below
1234 			comprdata = 0;
1235 		}
1236 	} else {
1237 		glfunc.glTexImage2D(GL_TEXTURE_2D, 0,
1238 			intexfmt, tex->sizx, tex->sizy, 0, tex->rawfmt,
1239 			GL_UNSIGNED_BYTE, (const GLvoid *) tex->pic);
1240 	}
1241 
1242 	for (mipmap = 1; tex->sizx > 1 || tex->sizy > 1; mipmap++) {
1243 		ptm_mipscale(tex);
1244 		ptm_fixtransparency(tex, (flags & PTH_CLAMPED));
1245 
1246 		if (compress) {
1247 			comprsize = ptcompress_getstorage(tex->sizx, tex->sizy, compress);
1248 			if (tdef) {
1249 				comprdata = (unsigned char *) malloc(comprsize);
1250 			}
1251 
1252 			starttime = getticks();
1253 			ptcompress_compress(tex->pic, tex->sizx, tex->sizy, comprdata, compress);
1254 			if (polymosttexverbosity >= 2) {
1255 				buildprintf("PolymostTex: ptcompress_compress (%dx%d, %s) took %f sec\n",
1256 					   tex->sizx, tex->sizy, compressfourcc[compress],
1257 					   (float)(getticks() - starttime) / 1000.f);
1258 			}
1259 
1260 			if (tdef) {
1261 				tdef->mipmap[tdefmip].sizx = tex->sizx;
1262 				tdef->mipmap[tdefmip].sizy = tex->sizy;
1263 				tdef->mipmap[tdefmip].length = comprsize;
1264 				tdef->mipmap[tdefmip].data = comprdata;
1265 				tdefmip++;
1266 			}
1267 
1268 			glfunc.glCompressedTexImage2D(GL_TEXTURE_2D, mipmap,
1269 						intexfmt, tex->sizx, tex->sizy, 0,
1270 						comprsize, (const GLvoid *) comprdata);
1271 
1272 			if (tdef) {
1273 				// we need to retain each mipmap for the tdef struct, so
1274 				// force each mipmap to be allocated afresh in this loop
1275 				comprdata = 0;
1276 			}
1277 		} else {
1278 			glfunc.glTexImage2D(GL_TEXTURE_2D, mipmap,
1279 				intexfmt, tex->sizx, tex->sizy, 0, tex->rawfmt,
1280 				GL_UNSIGNED_BYTE, (const GLvoid *) tex->pic);
1281 		}
1282 	}
1283 
1284 	ptm->flags = 0;
1285 	ptm->flags |= (tex->hasalpha ? PTH_HASALPHA : 0);
1286 
1287 	if (comprdata) {
1288 		free(comprdata);
1289 	}
1290 }
1291 
1292 
1293 /**
1294  * Prepare for priming by sweeping through the textures and marking them as all unused
1295  */
PTBeginPriming(void)1296 void PTBeginPriming(void)
1297 {
1298 	PTHash * pth;
1299 	int i;
1300 
1301 	for (i=PTHASHHEADSIZ-1; i>=0; i--) {
1302 		pth = pthashhead[i];
1303 		while (pth) {
1304 			pth->primecnt = 0;
1305 			pth = pth->next;
1306 		}
1307 	}
1308 
1309 	primecnt = 0;
1310 	primedone = 0;
1311 	primepos = 0;
1312 }
1313 
1314 /**
1315  * Flag a texture as required for priming
1316  */
PTMarkPrime(int picnum,int palnum,unsigned short flags)1317 void PTMarkPrime(int picnum, int palnum, unsigned short flags)
1318 {
1319 	PTHash * pth;
1320 
1321 	pth = pt_findhash(picnum, palnum, flags, 1);
1322 	if (pth) {
1323 		if (pth->primecnt == 0) {
1324 			primecnt++;
1325 		}
1326 		pth->primecnt++;
1327 	}
1328 }
1329 
1330 /**
1331  * Runs a cycle of the priming process. Call until nonzero is returned.
1332  * @param done receives the number of textures primed so far
1333  * @param total receives the total number of textures to be primed
1334  * @return 0 when priming is complete
1335  */
PTDoPrime(int * done,int * total)1336 int PTDoPrime(int* done, int* total)
1337 {
1338 	PTHash * pth;
1339 
1340 	if (primepos >= PTHASHHEADSIZ) {
1341 		// done
1342 		return 0;
1343 	}
1344 
1345 	if (primepos == 0) {
1346 		int i;
1347 
1348 		// first, unload all the textures that are not marked
1349 		for (i=PTHASHHEADSIZ-1; i>=0; i--) {
1350 			pth = pthashhead[i];
1351 			while (pth) {
1352 				if (pth->primecnt == 0) {
1353 					pt_unload(pth);
1354 				}
1355 				pth = pth->next;
1356 			}
1357 		}
1358 	}
1359 
1360 	pth = pthashhead[primepos];
1361 	while (pth) {
1362 		if (pth->primecnt > 0) {
1363 			primedone++;
1364 			pt_load(pth);
1365 		}
1366 		pth = pth->next;
1367 	}
1368 
1369 	*done = primedone;
1370 	*total = primecnt;
1371 	primepos++;
1372 
1373 	return (primepos < PTHASHHEADSIZ);
1374 }
1375 
1376 /**
1377  * Resets the texture hash but leaves the headers in memory
1378  */
PTReset()1379 void PTReset()
1380 {
1381 	PTHash * pth;
1382 	int i;
1383 
1384 	for (i=PTHASHHEADSIZ-1; i>=0; i--) {
1385 		pth = pthashhead[i];
1386 		while (pth) {
1387 			pt_unload(pth);
1388 			pth = pth->next;
1389 		}
1390 	}
1391 }
1392 
1393 /**
1394  * Clears the texture hash of all content
1395  */
PTClear()1396 void PTClear()
1397 {
1398 	PTHash * pth, * next;
1399 	PTMHash * ptmh, * mnext;
1400 	int i;
1401 
1402 	for (i=PTHASHHEADSIZ-1; i>=0; i--) {
1403 		pth = pthashhead[i];
1404 		while (pth) {
1405 			next = pth->next;
1406 			pt_unload(pth);
1407 			free(pth);
1408 			pth = next;
1409 		}
1410 		pthashhead[i] = 0;
1411 	}
1412 
1413 	for (i=PTMHASHHEADSIZ-1; i>=0; i--) {
1414 		ptmh = ptmhashhead[i];
1415 		while (ptmh) {
1416 			mnext = ptmh->next;
1417 			free(ptmh);
1418 			ptmh = mnext;
1419 		}
1420 		ptmhashhead[i] = 0;
1421 	}
1422 }
1423 
1424 
1425 
1426 /**
1427  * Fetches a texture header ready for rendering
1428  * @param picnum
1429  * @param palnum
1430  * @param flags
1431  * @param peek if !0, does not try and create a header if none exists
1432  * @return pointer to the header, or null if peek!=0 and none exists
1433  */
PT_GetHead(int picnum,int palnum,unsigned short flags,int peek)1434 PTHead * PT_GetHead(int picnum, int palnum, unsigned short flags, int peek)
1435 {
1436 	PTHash * pth;
1437 
1438 	pth = pt_findhash(picnum, palnum, flags, peek == 0);
1439 	if (pth == 0) {
1440 		return 0;
1441 	}
1442 
1443 	if (!pt_load(pth)) {
1444 		return 0;
1445 	}
1446 
1447 	while (pth->deferto) {
1448 		// this might happen if pt_load needs to defer to ART
1449 		pth = pth->deferto;
1450 	}
1451 
1452 	return &pth->head;
1453 }
1454 
1455 
1456 
1457 
1458 
ptiter_matches(PTIter iter)1459 static inline int ptiter_matches(PTIter iter)
1460 {
1461 	if (iter->match == 0) {
1462 		return 1;	// matching every item
1463 	}
1464 	if ((iter->match & PTITER_PICNUM) && iter->pth->head.picnum != iter->picnum) {
1465 		return 0;
1466 	}
1467 	if ((iter->match & PTITER_PALNUM) && iter->pth->head.palnum != iter->palnum) {
1468 		return 0;
1469 	}
1470 	if ((iter->match & PTITER_FLAGS) && (iter->pth->head.flags & iter->flagsmask) != iter->flags) {
1471 		return 0;
1472 	}
1473 	return 1;
1474 }
1475 
ptiter_seekforward(PTIter iter)1476 static void ptiter_seekforward(PTIter iter)
1477 {
1478 	while (1) {
1479 		if (iter->pth && ptiter_matches(iter)) {
1480 			break;
1481 		}
1482 		if (iter->pth == 0) {
1483 			if ((iter->match & PTITER_PICNUM)) {
1484 				// because the hash key is based on picture number,
1485 				// reaching the end of the hash chain means we need
1486 				// not look further
1487 				break;
1488 			}
1489 			iter->i++;
1490 			if (iter->i >= PTHASHHEADSIZ) {
1491 				// end of hash
1492 				iter->pth = 0;
1493 				iter->i = PTHASHHEADSIZ;
1494 				break;
1495 			}
1496 			iter->pth = pthashhead[iter->i];
1497 		} else {
1498 			iter->pth = iter->pth->next;
1499 		}
1500 	}
1501 }
1502 
1503 /**
1504  * Creates a new iterator for walking the header hash looking for particular
1505  * parameters that match.
1506  * @param match PTITER_* flags indicating which parameters to test
1507  * @param picnum when (match&PTITER_PICNUM), specifies the picnum
1508  * @param palnum when (match&PTITER_PALNUM), specifies the palnum
1509  * @param flagsmask when (match&PTITER_FLAGS), specifies the mask to apply to flags
1510  * @param flags when (match&PTITER_FLAGS), specifies the flags to test
1511  * @return an iterator
1512  */
PTIterNewMatch(int match,int picnum,int palnum,unsigned short flagsmask,unsigned short flags)1513 PTIter PTIterNewMatch(int match, int picnum, int palnum, unsigned short flagsmask, unsigned short flags)
1514 {
1515 	PTIter iter;
1516 
1517 	iter = (PTIter) malloc(sizeof(struct PTIter_typ));
1518 	if (!iter) {
1519 		return 0;
1520 	}
1521 
1522 	iter->i = 0;
1523 	iter->pth = pthashhead[0];
1524 	iter->match = match;
1525 	iter->picnum = picnum;
1526 	iter->palnum = palnum;
1527 	iter->flagsmask = flagsmask;
1528 	iter->flags = flags;
1529 
1530 	if ((match & PTITER_PICNUM)) {
1531 		iter->i = pt_gethashhead(picnum);
1532 		iter->pth = pthashhead[iter->i];
1533 		if (iter->pth == 0) {
1534 			iter->pth = 0;
1535 			return iter;
1536 		}
1537 	}
1538 
1539 	ptiter_seekforward(iter);
1540 
1541 	return iter;
1542 }
1543 
1544 /**
1545  * Creates a new iterator for walking the header hash
1546  * @return an iterator
1547  */
PTIterNew(void)1548 PTIter PTIterNew(void)
1549 {
1550 	return PTIterNewMatch(0, 0, 0, 0, 0);
1551 }
1552 
1553 /**
1554  * Gets the next header from an iterator
1555  * @param iter the iterator
1556  * @return the next header, or null if at the end
1557  */
PTIterNext(PTIter iter)1558 PTHead * PTIterNext(PTIter iter)
1559 {
1560 	PTHead * found = 0;
1561 
1562 	if (!iter) return 0;
1563 
1564 	if (iter->pth == 0) {
1565 		return 0;
1566 	}
1567 
1568 	found = &iter->pth->head;
1569 	iter->pth = iter->pth->next;
1570 
1571 	ptiter_seekforward(iter);
1572 
1573 	return found;
1574 }
1575 
1576 /**
1577  * Frees an iterator
1578  * @param iter the iterator
1579  */
PTIterFree(PTIter iter)1580 void PTIterFree(PTIter iter)
1581 {
1582 	if (!iter) return;
1583 	free(iter);
1584 }
1585 
1586 #endif //USE_OPENGL
1587