Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libmupen64plus/mupen64plus-video-glide64mk2/src/GlideHQ/TxHiResCache.cpp
2 views
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
62
TxHiResCache::~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
81
TxHiResCache::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
122
boolean
123
TxHiResCache::empty()
124
{
125
return _cache.empty();
126
}
127
128
boolean
129
TxHiResCache::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
166
boolean
167
TxHiResCache::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
}
1089
1090