ALL: Huge upstream synch + PerRom DelaySI & CountPerOp parameters
[mupen64plus-pandora.git] / source / gles2glide64 / src / GlideHQ / TxHiResCache.cpp
CommitLineData
98e75f2d 1/*
2 * Texture Filtering
3 * Version: 1.0
4 *
5 * Copyright (C) 2007 Hiroshi Morii All Rights Reserved.
6 * Email koolsmoky(at)users.sourceforge.net
7 * Web http://www.3dfxzone.it/koolsmoky
8 *
9 * this is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * this is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with GNU Make; see the file COPYING. If not, write to
21 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24/* 2007 Gonetz <gonetz(at)ngs.ru>
25 * Added callback to display hires texture info. */
26
27#ifdef __MSC__
28#pragma warning(disable: 4786)
29#endif
30
31/* handle oversized textures by
32 * 0: minification
33 * 1: Glide64 style tiling
34 */
35#define TEXTURE_TILING 1
36
37/* use power of 2 texture size
38 * (0:disable, 1:enable, 2:3dfx) */
39#define POW2_TEXTURES 2
40
41#if TEXTURE_TILING
42#undef POW2_TEXTURES
43#define POW2_TEXTURES 2
44#endif
45
46/* hack to reduce texture footprint to achieve
47 * better performace on midrange gfx cards.
48 * (0:disable, 1:enable) */
49#define REDUCE_TEXTURE_FOOTPRINT 0
50
51/* use aggressive format assumption for quantization
52 * (0:disable, 1:enable, 2:extreme) */
53#define AGGRESSIVE_QUANTIZATION 1
54
55#include <zlib.h>
56#include <string>
57#include <SDL.h>
58#include "TxHiResCache.h"
59#include "TxDbg.h"
60#include "../Glide64/Gfx_1.3.h"
61
62TxHiResCache::~TxHiResCache()
63{
64#ifdef DUMP_CACHE
65 if ((_options & DUMP_HIRESTEXCACHE) && !_haveCache && !_abortLoad) {
66 /* dump cache to disk */
67 std::wstring filename = _ident + L"_HIRESTEXTURES.dat";
68 boost::filesystem::wpath cachepath(_cachepath);
69 cachepath /= boost::filesystem::wpath(L"glidehq");
70 int config = _options & (HIRESTEXTURES_MASK|COMPRESS_HIRESTEX|COMPRESSION_MASK|TILE_HIRESTEX|FORCE16BPP_HIRESTEX|GZ_HIRESTEXCACHE|LET_TEXARTISTS_FLY);
71
72 TxCache::save(cachepath.wstring().c_str(), filename.c_str(), config);
73 }
74#endif
75
76 delete _txImage;
77 delete _txQuantize;
78 delete _txReSample;
79}
80
81TxHiResCache::TxHiResCache(int maxwidth, int maxheight, int maxbpp, int options,
82 const wchar_t *datapath, const wchar_t *cachepath,
83 const wchar_t *ident, dispInfoFuncExt callback
84 ) : TxCache((options & ~GZ_TEXCACHE), 0, datapath, cachepath, ident, callback)
85{
86 _txImage = new TxImage();
87 _txQuantize = new TxQuantize();
88 _txReSample = new TxReSample();
89
90 _maxwidth = maxwidth;
91 _maxheight = maxheight;
92 _maxbpp = maxbpp;
93 _abortLoad = 0;
94 _haveCache = 0;
95
96 /* assert local options */
97 if (!(_options & COMPRESS_HIRESTEX))
98 _options &= ~COMPRESSION_MASK;
99
100 if (_cachepath.empty() || _ident.empty()) {
101 _options &= ~DUMP_HIRESTEXCACHE;
102 return;
103 }
104
105#ifdef DUMP_CACHE
106 /* read in hires texture cache */
107 if (_options & DUMP_HIRESTEXCACHE) {
108 /* find it on disk */
109 std::wstring filename = _ident + L"_HIRESTEXTURES.dat";
110 boost::filesystem::wpath cachepath(_cachepath);
111 cachepath /= boost::filesystem::wpath(L"glidehq");
112 int config = _options & (HIRESTEXTURES_MASK|COMPRESS_HIRESTEX|COMPRESSION_MASK|TILE_HIRESTEX|FORCE16BPP_HIRESTEX|GZ_HIRESTEXCACHE|LET_TEXARTISTS_FLY);
113
114 _haveCache = TxCache::load(cachepath.wstring().c_str(), filename.c_str(), config);
115 }
116#endif
117
118 /* read in hires textures */
119 if (!_haveCache) TxHiResCache::load(0);
120}
121
122boolean
123TxHiResCache::empty()
124{
125 return _cache.empty();
126}
127
128boolean
129TxHiResCache::load(boolean replace) /* 0 : reload, 1 : replace partial */
130{
131 if (!_datapath.empty() && !_ident.empty()) {
132
133 if (!replace) TxCache::clear();
134
135 boost::filesystem::wpath dir_path(_datapath);
136
137 switch (_options & HIRESTEXTURES_MASK) {
138 case GHQ_HIRESTEXTURES:
139 break;
140 case RICE_HIRESTEXTURES:
141 INFO(80, L"-----\n");
142 INFO(80, L"using Rice hires texture format...\n");
143 INFO(80, L" must be one of the following;\n");
144 INFO(80, L" 1) *_rgb.png + *_a.png\n");
145 INFO(80, L" 2) *_all.png\n");
146 INFO(80, L" 3) *_ciByRGBA.png\n");
147 INFO(80, L" 4) *_allciByRGBA.png\n");
148 INFO(80, L" 5) *_ci.bmp\n");
149 INFO(80, L" usage of only 2) and 3) highly recommended!\n");
150 INFO(80, L" folder names must be in US-ASCII characters!\n");
151
152 dir_path /= boost::filesystem::wpath(L"hires_texture");
153 dir_path /= boost::filesystem::wpath(_ident);
154 loadHiResTextures(dir_path, replace);
155 break;
156 case JABO_HIRESTEXTURES:
157 ;
158 }
159
160 return 1;
161 }
162
163 return 0;
164}
165
166boolean
167TxHiResCache::loadHiResTextures(boost::filesystem::wpath dir_path, boolean replace)
168{
169 uint32_t last, now, diff;
170 DBG_INFO(80, L"-----\n");
171 DBG_INFO(80, L"path: %ls\n", dir_path.string().c_str());
172 last = SDL_GetTicks();
173
174 /* find it on disk */
175 if (!boost::filesystem::exists(dir_path)) {
176 INFO(80, L"Error: path not found!\n");
177 return 0;
178 }
179
180 /* XXX: deal with UNICODE fiasco!
181 * stupidity flows forth beneath this...
182 *
183 * I opted to use chdir in order to use fopen() for windows 9x.
184 */
185#ifdef WIN32
186 wchar_t curpath[MAX_PATH];
187 GETCWD(MAX_PATH, curpath);
188 CHDIR(dir_path.wstring().c_str());
189#else
190 char curpath[MAX_PATH];
191 char cbuf[MAX_PATH];
192 wcstombs(cbuf, dir_path.wstring().c_str(), MAX_PATH);
193 if (GETCWD(MAX_PATH, curpath) == NULL)
194 ERRLOG("Error while retrieving working directory!");
195 if (CHDIR(cbuf) != 0)
196 ERRLOG("Error while changing current directory to '%s'!", cbuf);
197#endif
198
199 /* NOTE: I could use the boost::wdirectory_iterator and boost::wpath
200 * to resolve UNICODE file names and paths. But then, _wfopen() is
201 * required to get the file descriptor for MS Windows to pass into
202 * libpng, which is incompatible with Win9x. Win9x's fopen() cannot
203 * handle UNICODE names. UNICODE capable boost::filesystem is available
204 * with Boost1.34.1 built with VC8.0 (bjam --toolset=msvc-8.0 stage).
205 *
206 * RULE OF THUMB: NEVER save texture packs in NON-ASCII names!!
207 */
208 boost::filesystem::directory_iterator it(dir_path);
209 boost::filesystem::directory_iterator end_it; /* default construction yields past-the-end */
210
211 for (; it != end_it; ++it) {
212
213 if (KBHIT(0x1B)) {
214 _abortLoad = 1;
215 if (_callback) (*_callback)(L"Aborted loading hiresolution texture!\n");
216 INFO(80, L"Error: aborted loading hiresolution texture!\n");
217 }
218 if (_abortLoad) break;
219
220 /* recursive read into sub-directory */
221 if (boost::filesystem::is_directory(it->status())) {
222 loadHiResTextures(it->path(), replace);
223 continue;
224 }
225
226 DBG_INFO(80, L"-----\n");
227 DBG_INFO(80, L"file: %ls\n", it->path().leaf().c_str());
228
229 int width = 0, height = 0;
230 uint16 format = 0;
231 uint8 *tex = NULL;
232 int tmpwidth = 0, tmpheight = 0;
233 uint16 tmpformat = 0;
234 uint8 *tmptex= NULL;
235 int untiled_width = 0, untiled_height = 0;
236 uint16 destformat = 0;
237
238 /* Rice hi-res textures: begin
239 */
240 uint32 chksum = 0, fmt = 0, siz = 0, palchksum = 0;
241 char *pfname = NULL, fname[MAX_PATH];
242 std::string ident;
243 FILE *fp = NULL;
244
245 wcstombs(fname, _ident.c_str(), MAX_PATH);
246 /* XXX case sensitivity fiasco!
247 * files must use _a, _rgb, _all, _allciByRGBA, _ciByRGBA, _ci
248 * and file extensions must be in lower case letters! */
249#ifdef WIN32
250 {
251 unsigned int i;
252 for (i = 0; i < strlen(fname); i++) fname[i] = tolower(fname[i]);
253 }
254#endif
255 ident.assign(fname);
256
257 /* read in Rice's file naming convention */
258#define CRCFMTSIZ_LEN 13
259#define PALCRC_LEN 9
260 //wcstombs(fname, it->path().leaf().c_str(), MAX_PATH);
261 strncpy(fname, it->path().leaf().string().c_str(), sizeof(fname));
262 fname[sizeof(fname) - 1] = '\0';
263 /* XXX case sensitivity fiasco!
264 * files must use _a, _rgb, _all, _allciByRGBA, _ciByRGBA, _ci
265 * and file extensions must be in lower case letters! */
266#ifdef WIN32
267 {
268 unsigned int i;
269 for (i = 0; i < strlen(fname); i++) fname[i] = tolower(fname[i]);
270 }
271#endif
272 pfname = fname + strlen(fname) - 4;
273 if (!(pfname == strstr(fname, ".png") ||
274 pfname == strstr(fname, ".bmp") ||
275 pfname == strstr(fname, ".dds"))) {
276#if !DEBUG
277 INFO(80, L"-----\n");
278 INFO(80, L"path: %ls\n", dir_path.string().c_str());
279 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
280#endif
281 INFO(80, L"Error: not png or bmp or dds!\n");
282 continue;
283 }
284 pfname = strstr(fname, ident.c_str());
285 if (pfname != fname) pfname = 0;
286 if (pfname) {
287 if (sscanf(pfname + ident.size(), "#%08X#%01X#%01X#%08X", &chksum, &fmt, &siz, &palchksum) == 4)
288 pfname += (ident.size() + CRCFMTSIZ_LEN + PALCRC_LEN);
289 else if (sscanf(pfname + ident.size(), "#%08X#%01X#%01X", &chksum, &fmt, &siz) == 3)
290 pfname += (ident.size() + CRCFMTSIZ_LEN);
291 else
292 pfname = 0;
293 }
294 if (!pfname) {
295#if !DEBUG
296 INFO(80, L"-----\n");
297 INFO(80, L"path: %ls\n", dir_path.string().c_str());
298 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
299#endif
300 INFO(80, L"Error: not Rice texture naming convention!\n");
301 continue;
302 }
303 if (!chksum) {
304#if !DEBUG
305 INFO(80, L"-----\n");
306 INFO(80, L"path: %ls\n", dir_path.string().c_str());
307 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
308#endif
309 INFO(80, L"Error: crc32 = 0!\n");
310 continue;
311 }
312
313 /* check if we already have it in hires texture cache */
314 if (!replace) {
315 uint64 chksum64 = (uint64)palchksum;
316 chksum64 <<= 32;
317 chksum64 |= (uint64)chksum;
318 if (TxCache::is_cached(chksum64)) {
319#if !DEBUG
320 INFO(80, L"-----\n");
321 INFO(80, L"path: %ls\n", dir_path.string().c_str());
322 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
323#endif
324 INFO(80, L"Error: already cached! duplicate texture!\n");
325 continue;
326 }
327 }
328
329 DBG_INFO(80, L"rom: %ls chksum:%08X %08X fmt:%x size:%x\n", _ident.c_str(), chksum, palchksum, fmt, siz);
330
331 /* Deal with the wackiness some texture packs utilize Rice format.
332 * Read in the following order: _a.* + _rgb.*, _all.png _ciByRGBA.png,
333 * _allciByRGBA.png, and _ci.bmp. PNG are prefered over BMP.
334 *
335 * For some reason there are texture packs that include them all. Some
336 * even have RGB textures named as _all.* and ARGB textures named as
337 * _rgb.*... Someone pleeeez write a GOOD guideline for the texture
338 * designers!!!
339 *
340 * We allow hires textures to have higher bpp than the N64 originals.
341 */
342 /* N64 formats
343 * Format: 0 - RGBA, 1 - YUV, 2 - CI, 3 - IA, 4 - I
344 * Size: 0 - 4bit, 1 - 8bit, 2 - 16bit, 3 - 32 bit
345 */
346
347 /*
348 * read in _rgb.* and _a.*
349 */
350 if (pfname == strstr(fname, "_rgb.") || pfname == strstr(fname, "_a.")) {
351 strcpy(pfname, "_rgb.png");
352 if (!boost::filesystem::exists(fname)) {
353 strcpy(pfname, "_rgb.bmp");
354 if (!boost::filesystem::exists(fname)) {
355#if !DEBUG
356 INFO(80, L"-----\n");
357 INFO(80, L"path: %ls\n", dir_path.string().c_str());
358 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
359#endif
360 INFO(80, L"Error: missing _rgb.*! _a.* must be paired with _rgb.*!\n");
361 continue;
362 }
363 }
364 /* _a.png */
365 strcpy(pfname, "_a.png");
366 if ((fp = fopen(fname, "rb")) != NULL) {
367 tmptex = _txImage->readPNG(fp, &tmpwidth, &tmpheight, &tmpformat);
368 fclose(fp);
369 }
370 if (!tmptex) {
371 /* _a.bmp */
372 strcpy(pfname, "_a.bmp");
373 if ((fp = fopen(fname, "rb")) != NULL) {
374 tmptex = _txImage->readBMP(fp, &tmpwidth, &tmpheight, &tmpformat);
375 fclose(fp);
376 }
377 }
378 /* _rgb.png */
379 strcpy(pfname, "_rgb.png");
380 if ((fp = fopen(fname, "rb")) != NULL) {
381 tex = _txImage->readPNG(fp, &width, &height, &format);
382 fclose(fp);
383 }
384 if (!tex) {
385 /* _rgb.bmp */
386 strcpy(pfname, "_rgb.bmp");
387 if ((fp = fopen(fname, "rb")) != NULL) {
388 tex = _txImage->readBMP(fp, &width, &height, &format);
389 fclose(fp);
390 }
391 }
392 if (tmptex) {
393 /* check if _rgb.* and _a.* have matching size and format. */
394 if (!tex || width != tmpwidth || height != tmpheight ||
395 format != GR_TEXFMT_ARGB_8888 || tmpformat != GR_TEXFMT_ARGB_8888) {
396#if !DEBUG
397 INFO(80, L"-----\n");
398 INFO(80, L"path: %ls\n", dir_path.string().c_str());
399 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
400#endif
401 if (!tex) {
402 INFO(80, L"Error: missing _rgb.*!\n");
403 } else if (width != tmpwidth || height != tmpheight) {
404 INFO(80, L"Error: _rgb.* and _a.* have mismatched width or height!\n");
405 } else if (format != GR_TEXFMT_ARGB_8888 || tmpformat != GR_TEXFMT_ARGB_8888) {
406 INFO(80, L"Error: _rgb.* or _a.* not in 32bit color!\n");
407 }
408 if (tex) free(tex);
409 if (tmptex) free(tmptex);
410 tex = NULL;
411 tmptex = NULL;
412 continue;
413 }
414 }
415 /* make adjustments */
416 if (tex) {
417 if (tmptex) {
418 /* merge (A)RGB and A comp */
419 DBG_INFO(80, L"merge (A)RGB and A comp\n");
420 int i;
421 for (i = 0; i < height * width; i++) {
422#if 1
423 /* use R comp for alpha. this is what Rice uses. sigh... */
424 ((uint32*)tex)[i] &= 0x00ffffff;
425 ((uint32*)tex)[i] |= ((((uint32*)tmptex)[i] & 0x00ff0000) << 8);
426#endif
427#if 0
428 /* use libpng style grayscale conversion */
429 uint32 texel = ((uint32*)tmptex)[i];
430 uint32 acomp = (((texel >> 16) & 0xff) * 6969 +
431 ((texel >> 8) & 0xff) * 23434 +
432 ((texel ) & 0xff) * 2365) / 32768;
433 ((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff);
434#endif
435#if 0
436 /* use the standard NTSC gray scale conversion */
437 uint32 texel = ((uint32*)tmptex)[i];
438 uint32 acomp = (((texel >> 16) & 0xff) * 299 +
439 ((texel >> 8) & 0xff) * 587 +
440 ((texel ) & 0xff) * 114) / 1000;
441 ((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff);
442#endif
443 }
444 free(tmptex);
445 tmptex = NULL;
446 } else {
447 /* clobber A comp. never a question of alpha. only RGB used. */
448#if !DEBUG
449 INFO(80, L"-----\n");
450 INFO(80, L"path: %ls\n", dir_path.string().c_str());
451 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
452#endif
453 INFO(80, L"Warning: missing _a.*! only using _rgb.*. treat as opaque texture.\n");
454 int i;
455 for (i = 0; i < height * width; i++) {
456 ((uint32*)tex)[i] |= 0xff000000;
457 }
458 }
459 }
460 } else
461
462 /*
463 * read in _all.png, _all.dds, _allciByRGBA.png, _allciByRGBA.dds
464 * _ciByRGBA.png, _ciByRGBA.dds, _ci.bmp
465 */
466 if (pfname == strstr(fname, "_all.png") ||
467 pfname == strstr(fname, "_all.dds") ||
468#ifdef WIN32
469 pfname == strstr(fname, "_allcibyrgba.png") ||
470 pfname == strstr(fname, "_allcibyrgba.dds") ||
471 pfname == strstr(fname, "_cibyrgba.png") ||
472 pfname == strstr(fname, "_cibyrgba.dds") ||
473#else
474 pfname == strstr(fname, "_allciByRGBA.png") ||
475 pfname == strstr(fname, "_allciByRGBA.dds") ||
476 pfname == strstr(fname, "_ciByRGBA.png") ||
477 pfname == strstr(fname, "_ciByRGBA.dds") ||
478#endif
479 pfname == strstr(fname, "_ci.bmp")) {
480 if ((fp = fopen(fname, "rb")) != NULL) {
481 if (strstr(fname, ".png")) tex = _txImage->readPNG(fp, &width, &height, &format);
482 else if (strstr(fname, ".dds")) tex = _txImage->readDDS(fp, &width, &height, &format);
483 else tex = _txImage->readBMP(fp, &width, &height, &format);
484 fclose(fp);
485 }
486 /* XXX: auto-adjustment of dxt dds textures unsupported for now */
487 if (tex && strstr(fname, ".dds")) {
488 const float aspectratio = (width > height) ? (float)width/(float)height : (float)height/(float)width;
489 if (!(aspectratio == 1.0 ||
490 aspectratio == 2.0 ||
491 aspectratio == 4.0 ||
492 aspectratio == 8.0)) {
493 free(tex);
494 tex = NULL;
495#if !DEBUG
496 INFO(80, L"-----\n");
497 INFO(80, L"path: %ls\n", dir_path.string().c_str());
498 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
499#endif
500 INFO(80, L"Error: W:H aspect ratio range not 8:1 - 1:8!\n");
501 continue;
502 }
503 if (width != _txReSample->nextPow2(width) ||
504 height != _txReSample->nextPow2(height)) {
505 free(tex);
506 tex = NULL;
507#if !DEBUG
508 INFO(80, L"-----\n");
509 INFO(80, L"path: %ls\n", dir_path.string().c_str());
510 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
511#endif
512 INFO(80, L"Error: not power of 2 size!\n");
513 continue;
514 }
515 }
516 }
517
518 /* if we do not have a texture at this point we are screwed */
519 if (!tex) {
520#if !DEBUG
521 INFO(80, L"-----\n");
522 INFO(80, L"path: %ls\n", dir_path.string().c_str());
523 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
524#endif
525 INFO(80, L"Error: load failed!\n");
526 continue;
527 }
528 DBG_INFO(80, L"read in as %d x %d gfmt:%x\n", tmpwidth, tmpheight, tmpformat);
529
530 /* check if size and format are OK */
531 if (!(format == GR_TEXFMT_ARGB_8888 ||
532 format == GR_TEXFMT_P_8 ||
533 format == GR_TEXFMT_ARGB_CMP_DXT1 ||
534 format == GR_TEXFMT_ARGB_CMP_DXT3 ||
535 format == GR_TEXFMT_ARGB_CMP_DXT5) ||
536 (width * height) < 4) { /* TxQuantize requirement: width * height must be 4 or larger. */
537 free(tex);
538 tex = NULL;
539#if !DEBUG
540 INFO(80, L"-----\n");
541 INFO(80, L"path: %ls\n", dir_path.string().c_str());
542 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
543#endif
544 INFO(80, L"Error: not width * height > 4 or 8bit palette color or 32bpp or dxt1 or dxt3 or dxt5!\n");
545 continue;
546 }
547
548 /* analyze and determine best format to quantize */
549 if (format == GR_TEXFMT_ARGB_8888) {
550 int i;
551 int alphabits = 0;
552 int fullalpha = 0;
553 boolean intensity = 1;
554
555 if (!(_options & LET_TEXARTISTS_FLY)) {
556 /* HACK ALERT! */
557 /* Account for Rice's weirdness with fmt:0 siz:2 textures.
558 * Although the conditions are relaxed with other formats,
559 * the D3D RGBA5551 surface is used for this format in certain
560 * cases. See Nintemod's SuperMario64 life gauge and power
561 * meter. The same goes for fmt:2 textures. See Mollymutt's
562 * PaperMario text. */
563 if ((fmt == 0 && siz == 2) || fmt == 2) {
564 DBG_INFO(80, L"Remove black, white, etc borders along the alpha edges.\n");
565 /* round A comp */
566 for (i = 0; i < height * width; i++) {
567 uint32 texel = ((uint32*)tex)[i];
568 ((uint32*)tex)[i] = ((texel & 0xff000000) == 0xff000000 ? 0xff000000 : 0) |
569 (texel & 0x00ffffff);
570 }
571 /* Substitute texel color with the average of the surrounding
572 * opaque texels. This removes borders regardless of hardware
573 * texture filtering (bilinear, etc). */
574 int j;
575 for (i = 0; i < height; i++) {
576 for (j = 0; j < width; j++) {
577 uint32 texel = ((uint32*)tex)[i * width + j];
578 if ((texel & 0xff000000) != 0xff000000) {
579 uint32 tmptexel[8];
580 uint32 k, numtexel, r, g, b;
581 numtexel = r = g = b = 0;
582 memset(&tmptexel, 0, sizeof(tmptexel));
583 if (i > 0) {
584 tmptexel[0] = ((uint32*)tex)[(i - 1) * width + j]; /* north */
585 if (j > 0) tmptexel[1] = ((uint32*)tex)[(i - 1) * width + j - 1]; /* north-west */
586 if (j < width - 1) tmptexel[2] = ((uint32*)tex)[(i - 1) * width + j + 1]; /* north-east */
587 }
588 if (i < height - 1) {
589 tmptexel[3] = ((uint32*)tex)[(i + 1) * width + j]; /* south */
590 if (j > 0) tmptexel[4] = ((uint32*)tex)[(i + 1) * width + j - 1]; /* south-west */
591 if (j < width - 1) tmptexel[5] = ((uint32*)tex)[(i + 1) * width + j + 1]; /* south-east */
592 }
593 if (j > 0) tmptexel[6] = ((uint32*)tex)[i * width + j - 1]; /* west */
594 if (j < width - 1) tmptexel[7] = ((uint32*)tex)[i * width + j + 1]; /* east */
595 for (k = 0; k < 8; k++) {
596 if ((tmptexel[k] & 0xff000000) == 0xff000000) {
597 r += ((tmptexel[k] & 0x00ff0000) >> 16);
598 g += ((tmptexel[k] & 0x0000ff00) >> 8);
599 b += ((tmptexel[k] & 0x000000ff) );
600 numtexel++;
601 }
602 }
603 if (numtexel) {
604 ((uint32*)tex)[i * width + j] = ((r / numtexel) << 16) |
605 ((g / numtexel) << 8) |
606 ((b / numtexel) );
607 } else {
608 ((uint32*)tex)[i * width + j] = texel & 0x00ffffff;
609 }
610 }
611 }
612 }
613 }
614 }
615
616 /* simple analysis of texture */
617 for (i = 0; i < height * width; i++) {
618 uint32 texel = ((uint32*)tex)[i];
619 if (alphabits != 8) {
620#if AGGRESSIVE_QUANTIZATION
621 if ((texel & 0xff000000) < 0x00000003) {
622 alphabits = 1;
623 fullalpha++;
624 } else if ((texel & 0xff000000) < 0xfe000000) {
625 alphabits = 8;
626 }
627#else
628 if ((texel & 0xff000000) == 0x00000000) {
629 alphabits = 1;
630 fullalpha++;
631 } else if ((texel & 0xff000000) != 0xff000000) {
632 alphabits = 8;
633 }
634#endif
635 }
636 if (intensity) {
637 int rcomp = (texel >> 16) & 0xff;
638 int gcomp = (texel >> 8) & 0xff;
639 int bcomp = (texel ) & 0xff;
640#if AGGRESSIVE_QUANTIZATION
641 if (abs(rcomp - gcomp) > 8 || abs(rcomp - bcomp) > 8 || abs(gcomp - bcomp) > 8) intensity = 0;
642#else
643 if (rcomp != gcomp || rcomp != bcomp || gcomp != bcomp) intensity = 0;
644#endif
645 }
646 if (!intensity && alphabits == 8) break;
647 }
648 DBG_INFO(80, L"required alpha bits:%d zero acomp texels:%d rgb as intensity:%d\n", alphabits, fullalpha, intensity);
649
650 /* preparations based on above analysis */
651#if !REDUCE_TEXTURE_FOOTPRINT
652 if (_maxbpp < 32 || _options & (FORCE16BPP_HIRESTEX|COMPRESSION_MASK)) {
653#endif
654 if (alphabits == 0) destformat = GR_TEXFMT_RGB_565;
655 else if (alphabits == 1) destformat = GR_TEXFMT_ARGB_1555;
656 else destformat = GR_TEXFMT_ARGB_8888;
657#if !REDUCE_TEXTURE_FOOTPRINT
658 } else {
659 destformat = GR_TEXFMT_ARGB_8888;
660 }
661#endif
662 if (fmt == 4 && alphabits == 0) {
663 destformat = GR_TEXFMT_ARGB_8888;
664 /* Rice I format; I = (R + G + B) / 3 */
665 for (i = 0; i < height * width; i++) {
666 uint32 texel = ((uint32*)tex)[i];
667 uint32 icomp = (((texel >> 16) & 0xff) +
668 ((texel >> 8) & 0xff) +
669 ((texel ) & 0xff)) / 3;
670 ((uint32*)tex)[i] = (icomp << 24) | (texel & 0x00ffffff);
671 }
672 }
673 if (intensity) {
674 if (alphabits == 0) {
675 if (fmt == 4) destformat = GR_TEXFMT_ALPHA_8;
676 else destformat = GR_TEXFMT_INTENSITY_8;
677 } else {
678 destformat = GR_TEXFMT_ALPHA_INTENSITY_88;
679 }
680 }
681
682 DBG_INFO(80, L"best gfmt:%x\n", destformat);
683 }
684 /*
685 * Rice hi-res textures: end */
686
687
688 /* XXX: only ARGB8888 for now. comeback to this later... */
689 if (format == GR_TEXFMT_ARGB_8888) {
690
691#if TEXTURE_TILING
692
693 /* Glide64 style texture tiling */
694 /* NOTE: narrow wide textures can be tiled into 256x256 size textures */
695
696 /* adjust texture size to allow tiling for V1, Rush, V2, Banshee, V3 */
697 /* NOTE: we skip this for palette textures that need minification
698 * becasue it will look ugly. */
699
700 /* minification */
701 {
702 int ratio = 1;
703
704 /* minification to enable glide64 style texture tiling */
705 /* determine the minification ratio to tile the texture into 256x256 size */
706 if ((_options & TILE_HIRESTEX) && _maxwidth >= 256 && _maxheight >= 256) {
707 DBG_INFO(80, L"determine minification ratio to tile\n");
708 tmpwidth = width;
709 tmpheight = height;
710 if (height > 256) {
711 ratio = ((height - 1) >> 8) + 1;
712 tmpwidth = width / ratio;
713 tmpheight = height / ratio;
714 DBG_INFO(80, L"height > 256, minification ratio:%d %d x %d -> %d x %d\n",
715 ratio, width, height, tmpwidth, tmpheight);
716 }
717 if (tmpwidth > 256 && (((tmpwidth - 1) >> 8) + 1) * tmpheight > 256) {
718 ratio *= ((((((tmpwidth - 1) >> 8) + 1) * tmpheight) - 1) >> 8) + 1;
719 DBG_INFO(80, L"width > 256, minification ratio:%d %d x %d -> %d x %d\n",
720 ratio, width, height, width / ratio, height / ratio);
721 }
722 } else {
723 /* normal minification to fit max texture size */
724 if (width > _maxwidth || height > _maxheight) {
725 DBG_INFO(80, L"determine minification ratio to fit max texture size\n");
726 tmpwidth = width;
727 tmpheight = height;
728 while (tmpwidth > _maxwidth) {
729 tmpheight >>= 1;
730 tmpwidth >>= 1;
731 ratio <<= 1;
732 }
733 while (tmpheight > _maxheight) {
734 tmpheight >>= 1;
735 tmpwidth >>= 1;
736 ratio <<= 1;
737 }
738 DBG_INFO(80, L"minification ratio:%d %d x %d -> %d x %d\n",
739 ratio, width, height, tmpwidth, tmpheight);
740 }
741 }
742
743 if (ratio > 1) {
744 if (!_txReSample->minify(&tex, &width, &height, ratio)) {
745 free(tex);
746 tex = NULL;
747 DBG_INFO(80, L"Error: minification failed!\n");
748 continue;
749 }
750 }
751 }
752
753 /* tiling */
754 if ((_options & TILE_HIRESTEX) && _maxwidth >= 256 && _maxheight >= 256) {
755 boolean usetile = 0;
756
757 /* to tile or not to tile, that is the question */
758 if (width > 256 && height <= 128 && (((width - 1) >> 8) + 1) * height <= 256) {
759
760 if (width > _maxwidth) usetile = 1;
761 else {
762 /* tile if the tiled texture memory footprint is smaller */
763 int tilewidth = 256;
764 int tileheight = _txReSample->nextPow2((((width - 1) >> 8) + 1) * height);
765 tmpwidth = width;
766 tmpheight = height;
767
768 /* 3dfx Glide3 tmpheight, W:H aspect ratio range (8:1 - 1:8) */
769 if (tilewidth > (tileheight << 3)) tileheight = tilewidth >> 3;
770
771 /* HACKALERT: see TxReSample::pow2(); */
772 if (tmpwidth > 64) tmpwidth -= 4;
773 else if (tmpwidth > 16) tmpwidth -= 2;
774 else if (tmpwidth > 4) tmpwidth -= 1;
775
776 if (tmpheight > 64) tmpheight -= 4;
777 else if (tmpheight > 16) tmpheight -= 2;
778 else if (tmpheight > 4) tmpheight -= 1;
779
780 tmpwidth = _txReSample->nextPow2(tmpwidth);
781 tmpheight = _txReSample->nextPow2(tmpheight);
782
783 /* 3dfx Glide3 tmpheight, W:H aspect ratio range (8:1 - 1:8) */
784 if (tmpwidth > tmpheight) {
785 if (tmpwidth > (tmpheight << 3)) tmpheight = tmpwidth >> 3;
786 } else {
787 if (tmpheight > (tmpwidth << 3)) tmpwidth = tmpheight >> 3;
788 }
789
790 usetile = (tilewidth * tileheight < tmpwidth * tmpheight);
791 }
792
793 }
794
795 /* tile it! do the actual tiling into 256x256 size */
796 if (usetile) {
797 DBG_INFO(80, L"Glide64 style texture tiling\n");
798
799 int x, y, z, ratio, offset;
800 offset = 0;
801 ratio = ((width - 1) >> 8) + 1;
802 tmptex = (uint8 *)malloc(_txUtil->sizeofTx(256, height * ratio, format));
803 if (tmptex) {
804 for (x = 0; x < ratio; x++) {
805 for (y = 0; y < height; y++) {
806 if (x < ratio - 1) {
807 memcpy(&tmptex[offset << 2], &tex[(x * 256 + y * width) << 2], 256 << 2);
808 } else {
809 for (z = 0; z < width - 256 * (ratio - 1); z++) {
810 ((uint32*)tmptex)[offset + z] = ((uint32*)tex)[x * 256 + y * width + z];
811 }
812 for (; z < 256; z++) {
813 ((uint32*)tmptex)[offset + z] = ((uint32*)tmptex)[offset + z - 1];
814 }
815 }
816 offset += 256;
817 }
818 }
819 free(tex);
820 tex = tmptex;
821 untiled_width = width;
822 untiled_height = height;
823 width = 256;
824 height *= ratio;
825 DBG_INFO(80, L"Tiled: %d x %d -> %d x %d\n", untiled_width, untiled_height, width, height);
826 }
827 }
828 }
829
830#else /* TEXTURE_TILING */
831
832 /* minification */
833 if (width > _maxwidth || height > _maxheight) {
834 int ratio = 1;
835 if (width / _maxwidth > height / _maxheight) {
836 ratio = (int)ceil((double)width / _maxwidth);
837 } else {
838 ratio = (int)ceil((double)height / _maxheight);
839 }
840 if (!_txReSample->minify(&tex, &width, &height, ratio)) {
841 free(tex);
842 tex = NULL;
843 DBG_INFO(80, L"Error: minification failed!\n");
844 continue;
845 }
846 }
847
848#endif /* TEXTURE_TILING */
849
850 /* texture compression */
851 if ((_options & COMPRESSION_MASK) &&
852 (width >= 64 && height >= 64) /* Texture compression is not suitable for low pixel coarse detail
853 * textures. The assumption here is that textures larger than 64x64
854 * have enough detail to produce decent quality when compressed. The
855 * down side is that narrow stripped textures that the N64 often use
856 * for large background textures are also ignored. It would be more
857 * reasonable if decisions are made based on fourier-transform
858 * spectrum or RMS error.
859 *
860 * NOTE: texture size must be checked before expanding to pow2 size.
861 */
862 ) {
863 int dataSize = 0;
864 int compressionType = _options & COMPRESSION_MASK;
865
866#if POW2_TEXTURES
867#if (POW2_TEXTURES == 2)
868 /* 3dfx Glide3x aspect ratio (8:1 - 1:8) */
869 if (!_txReSample->nextPow2(&tex, &width , &height, 32, 1)) {
870#else
871 /* normal pow2 expansion */
872 if (!_txReSample->nextPow2(&tex, &width , &height, 32, 0)) {
873#endif
874 free(tex);
875 tex = NULL;
876 DBG_INFO(80, L"Error: aspect ratio adjustment failed!\n");
877 continue;
878 }
879#endif
880
881 switch (_options & COMPRESSION_MASK) {
882 case S3TC_COMPRESSION:
883 switch (destformat) {
884 case GR_TEXFMT_ARGB_8888:
885#if GLIDE64_DXTN
886 case GR_TEXFMT_ARGB_1555: /* for ARGB1555 use DXT5 instead of DXT1 */
887#endif
888 case GR_TEXFMT_ALPHA_INTENSITY_88:
889 dataSize = width * height;
890 break;
891#if !GLIDE64_DXTN
892 case GR_TEXFMT_ARGB_1555:
893#endif
894 case GR_TEXFMT_RGB_565:
895 case GR_TEXFMT_INTENSITY_8:
896 dataSize = (width * height) >> 1;
897 break;
898 case GR_TEXFMT_ALPHA_8: /* no size benefit with dxtn */
899 ;
900 }
901 break;
902 case FXT1_COMPRESSION:
903 switch (destformat) {
904 case GR_TEXFMT_ARGB_1555:
905 case GR_TEXFMT_RGB_565:
906 case GR_TEXFMT_INTENSITY_8:
907 dataSize = (width * height) >> 1;
908 break;
909 /* XXX: textures that use 8bit alpha channel look bad with the current
910 * fxt1 library, so we substitute it with dxtn for now. afaik all gfx
911 * cards that support fxt1 also support dxtn. (3dfx and Intel) */
912 case GR_TEXFMT_ALPHA_INTENSITY_88:
913 case GR_TEXFMT_ARGB_8888:
914 compressionType = S3TC_COMPRESSION;
915 dataSize = width * height;
916 break;
917 case GR_TEXFMT_ALPHA_8: /* no size benefit with dxtn */
918 ;
919 }
920 }
921 /* compress it! */
922 if (dataSize) {
923#if 0 /* TEST: dither before compression for better results with gradients */
924 tmptex = (uint8 *)malloc(_txUtil->sizeofTx(width, height, destformat));
925 if (tmptex) {
926 if (_txQuantize->quantize(tex, tmptex, width, height, GR_TEXFMT_ARGB_8888, destformat, 0))
927 _txQuantize->quantize(tmptex, tex, width, height, destformat, GR_TEXFMT_ARGB_8888, 0);
928 free(tmptex);
929 }
930#endif
931 tmptex = (uint8 *)malloc(dataSize);
932 if (tmptex) {
933 if (_txQuantize->compress(tex, tmptex,
934 width, height, destformat,
935 &tmpwidth, &tmpheight, &tmpformat,
936 compressionType)) {
937 free(tex);
938 tex = tmptex;
939 width = tmpwidth;
940 height = tmpheight;
941 format = destformat = tmpformat;
942 } else {
943 free(tmptex);
944 }
945 }
946 }
947
948 } else {
949
950#if POW2_TEXTURES
951#if (POW2_TEXTURES == 2)
952 /* 3dfx Glide3x aspect ratio (8:1 - 1:8) */
953 if (!_txReSample->nextPow2(&tex, &width , &height, 32, 1)) {
954#else
955 /* normal pow2 expansion */
956 if (!_txReSample->nextPow2(&tex, &width , &height, 32, 0)) {
957#endif
958 free(tex);
959 tex = NULL;
960 DBG_INFO(80, L"Error: aspect ratio adjustment failed!\n");
961 continue;
962 }
963#endif
964 }
965
966 /* quantize */
967 {
968 tmptex = (uint8 *)malloc(_txUtil->sizeofTx(width, height, destformat));
969 if (tmptex) {
970 switch (destformat) {
971 case GR_TEXFMT_ARGB_8888:
972 case GR_TEXFMT_ARGB_4444:
973#if !REDUCE_TEXTURE_FOOTPRINT
974 if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX)
975#endif
976 destformat = GR_TEXFMT_ARGB_4444;
977 break;
978 case GR_TEXFMT_ARGB_1555:
979#if !REDUCE_TEXTURE_FOOTPRINT
980 if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX)
981#endif
982 destformat = GR_TEXFMT_ARGB_1555;
983 break;
984 case GR_TEXFMT_RGB_565:
985#if !REDUCE_TEXTURE_FOOTPRINT
986 if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX)
987#endif
988 destformat = GR_TEXFMT_RGB_565;
989 break;
990 case GR_TEXFMT_ALPHA_INTENSITY_88:
991 case GR_TEXFMT_ALPHA_INTENSITY_44:
992#if !REDUCE_TEXTURE_FOOTPRINT
993 destformat = GR_TEXFMT_ALPHA_INTENSITY_88;
994#else
995 destformat = GR_TEXFMT_ALPHA_INTENSITY_44;
996#endif
997 break;
998 case GR_TEXFMT_ALPHA_8:
999 destformat = GR_TEXFMT_ALPHA_8; /* yes, this is correct. ALPHA_8 instead of INTENSITY_8 */
1000 break;
1001 case GR_TEXFMT_INTENSITY_8:
1002 destformat = GR_TEXFMT_INTENSITY_8;
1003 }
1004 if (_txQuantize->quantize(tex, tmptex, width, height, GR_TEXFMT_ARGB_8888, destformat, 0)) {
1005 format = destformat;
1006 free(tex);
1007 tex = tmptex;
1008 }
1009 }
1010 }
1011
1012 }
1013
1014
1015 /* last minute validations */
1016 if (!tex || !chksum || !width || !height || !format || width > _maxwidth || height > _maxheight) {
1017#if !DEBUG
1018 INFO(80, L"-----\n");
1019 INFO(80, L"path: %ls\n", dir_path.string().c_str());
1020 INFO(80, L"file: %ls\n", it->path().leaf().c_str());
1021#endif
1022 if (tex) {
1023 free(tex);
1024 tex = NULL;
1025 INFO(80, L"Error: bad format or size! %d x %d gfmt:%x\n", width, height, format);
1026 } else {
1027 INFO(80, L"Error: load failed!!\n");
1028 }
1029 continue;
1030 }
1031
1032 /* load it into hires texture cache. */
1033 {
1034 uint64 chksum64 = (uint64)palchksum;
1035 chksum64 <<= 32;
1036 chksum64 |= (uint64)chksum;
1037
1038 GHQTexInfo tmpInfo;
1039 memset(&tmpInfo, 0, sizeof(GHQTexInfo));
1040
1041 tmpInfo.data = tex;
1042 tmpInfo.width = width;
1043 tmpInfo.height = height;
1044 tmpInfo.format = format;
1045 tmpInfo.largeLodLog2 = _txUtil->grLodLog2(width, height);
1046 tmpInfo.smallLodLog2 = tmpInfo.largeLodLog2;
1047 tmpInfo.aspectRatioLog2 = _txUtil->grAspectRatioLog2(width, height);
1048 tmpInfo.is_hires_tex = 1;
1049
1050#if TEXTURE_TILING
1051 /* Glide64 style texture tiling. */
1052 if (untiled_width && untiled_height) {
1053 tmpInfo.tiles = ((untiled_width - 1) >> 8) + 1;
1054 tmpInfo.untiled_width = untiled_width;
1055 tmpInfo.untiled_height = untiled_height;
1056 }
1057#endif
1058
1059 /* remove redundant in cache */
1060 if (replace && TxCache::del(chksum64)) {
1061 DBG_INFO(80, L"removed duplicate old cache.\n");
1062 }
1063
1064 /* add to cache */
1065 if (TxCache::add(chksum64, &tmpInfo)) {
1066 now = SDL_GetTicks();
1067 diff = now - last;
1068
1069 /* Callback to display hires texture info.
1070 * Gonetz <gonetz(at)ngs.ru> */
1071 if (_callback && diff > 250) {
1072 wchar_t tmpbuf[MAX_PATH];
1073 mbstowcs(tmpbuf, fname, MAX_PATH);
1074 (*_callback)(L"[%d] total mem:%.2fmb - %ls\n", _cache.size(), (float)_totalSize/1000000, tmpbuf);
1075 last = now;
1076 }
1077 DBG_INFO(80, L"texture loaded!\n");
1078 }
1079 free(tex);
1080 }
1081
1082 }
1083
1084 if (CHDIR(curpath) != 0)
1085 ERRLOG("Error while changing current directory back to original path of '%s'!", curpath);
1086
1087 return 1;
1088}