Path: blob/master/libmupen64plus/mupen64plus-video-glide64mk2/src/GlideHQ/TxHiResCache.cpp
2 views
/*1* Texture Filtering2* Version: 1.03*4* Copyright (C) 2007 Hiroshi Morii All Rights Reserved.5* Email koolsmoky(at)users.sourceforge.net6* Web http://www.3dfxzone.it/koolsmoky7*8* this is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2, or (at your option)11* any later version.12*13* this is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with GNU Make; see the file COPYING. If not, write to20* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.21*/2223/* 2007 Gonetz <gonetz(at)ngs.ru>24* Added callback to display hires texture info. */2526#ifdef __MSC__27#pragma warning(disable: 4786)28#endif2930/* handle oversized textures by31* 0: minification32* 1: Glide64 style tiling33*/34#define TEXTURE_TILING 13536/* use power of 2 texture size37* (0:disable, 1:enable, 2:3dfx) */38#define POW2_TEXTURES 23940#if TEXTURE_TILING41#undef POW2_TEXTURES42#define POW2_TEXTURES 243#endif4445/* hack to reduce texture footprint to achieve46* better performace on midrange gfx cards.47* (0:disable, 1:enable) */48#define REDUCE_TEXTURE_FOOTPRINT 04950/* use aggressive format assumption for quantization51* (0:disable, 1:enable, 2:extreme) */52#define AGGRESSIVE_QUANTIZATION 15354#include <zlib.h>55#include <string>56#include <SDL.h>57#include "TxHiResCache.h"58#include "TxDbg.h"59#include "../Glide64/Gfx_1.3.h"6061TxHiResCache::~TxHiResCache()62{63#ifdef DUMP_CACHE64if ((_options & DUMP_HIRESTEXCACHE) && !_haveCache && !_abortLoad) {65/* dump cache to disk */66std::wstring filename = _ident + L"_HIRESTEXTURES.dat";67boost::filesystem::wpath cachepath(_cachepath);68cachepath /= boost::filesystem::wpath(L"glidehq");69int config = _options & (HIRESTEXTURES_MASK|COMPRESS_HIRESTEX|COMPRESSION_MASK|TILE_HIRESTEX|FORCE16BPP_HIRESTEX|GZ_HIRESTEXCACHE|LET_TEXARTISTS_FLY);7071TxCache::save(cachepath.wstring().c_str(), filename.c_str(), config);72}73#endif7475delete _txImage;76delete _txQuantize;77delete _txReSample;78}7980TxHiResCache::TxHiResCache(int maxwidth, int maxheight, int maxbpp, int options,81const wchar_t *datapath, const wchar_t *cachepath,82const wchar_t *ident, dispInfoFuncExt callback83) : TxCache((options & ~GZ_TEXCACHE), 0, datapath, cachepath, ident, callback)84{85_txImage = new TxImage();86_txQuantize = new TxQuantize();87_txReSample = new TxReSample();8889_maxwidth = maxwidth;90_maxheight = maxheight;91_maxbpp = maxbpp;92_abortLoad = 0;93_haveCache = 0;9495/* assert local options */96if (!(_options & COMPRESS_HIRESTEX))97_options &= ~COMPRESSION_MASK;9899if (_cachepath.empty() || _ident.empty()) {100_options &= ~DUMP_HIRESTEXCACHE;101return;102}103104#ifdef DUMP_CACHE105/* read in hires texture cache */106if (_options & DUMP_HIRESTEXCACHE) {107/* find it on disk */108std::wstring filename = _ident + L"_HIRESTEXTURES.dat";109boost::filesystem::wpath cachepath(_cachepath);110cachepath /= boost::filesystem::wpath(L"glidehq");111int config = _options & (HIRESTEXTURES_MASK|COMPRESS_HIRESTEX|COMPRESSION_MASK|TILE_HIRESTEX|FORCE16BPP_HIRESTEX|GZ_HIRESTEXCACHE|LET_TEXARTISTS_FLY);112113_haveCache = TxCache::load(cachepath.wstring().c_str(), filename.c_str(), config);114}115#endif116117/* read in hires textures */118if (!_haveCache) TxHiResCache::load(0);119}120121boolean122TxHiResCache::empty()123{124return _cache.empty();125}126127boolean128TxHiResCache::load(boolean replace) /* 0 : reload, 1 : replace partial */129{130if (!_datapath.empty() && !_ident.empty()) {131132if (!replace) TxCache::clear();133134boost::filesystem::wpath dir_path(_datapath);135136switch (_options & HIRESTEXTURES_MASK) {137case GHQ_HIRESTEXTURES:138break;139case RICE_HIRESTEXTURES:140INFO(80, L"-----\n");141INFO(80, L"using Rice hires texture format...\n");142INFO(80, L" must be one of the following;\n");143INFO(80, L" 1) *_rgb.png + *_a.png\n");144INFO(80, L" 2) *_all.png\n");145INFO(80, L" 3) *_ciByRGBA.png\n");146INFO(80, L" 4) *_allciByRGBA.png\n");147INFO(80, L" 5) *_ci.bmp\n");148INFO(80, L" usage of only 2) and 3) highly recommended!\n");149INFO(80, L" folder names must be in US-ASCII characters!\n");150151dir_path /= boost::filesystem::wpath(L"hires_texture");152dir_path /= boost::filesystem::wpath(_ident);153loadHiResTextures(dir_path, replace);154break;155case JABO_HIRESTEXTURES:156;157}158159return 1;160}161162return 0;163}164165boolean166TxHiResCache::loadHiResTextures(boost::filesystem::wpath dir_path, boolean replace)167{168uint32_t last, now, diff;169DBG_INFO(80, L"-----\n");170DBG_INFO(80, L"path: %ls\n", dir_path.string().c_str());171last = SDL_GetTicks();172173/* find it on disk */174if (!boost::filesystem::exists(dir_path)) {175INFO(80, L"Error: path not found!\n");176return 0;177}178179/* XXX: deal with UNICODE fiasco!180* stupidity flows forth beneath this...181*182* I opted to use chdir in order to use fopen() for windows 9x.183*/184#ifdef WIN32185wchar_t curpath[MAX_PATH];186GETCWD(MAX_PATH, curpath);187CHDIR(dir_path.wstring().c_str());188#else189char curpath[MAX_PATH];190char cbuf[MAX_PATH];191wcstombs(cbuf, dir_path.wstring().c_str(), MAX_PATH);192if (GETCWD(MAX_PATH, curpath) == NULL)193ERRLOG("Error while retrieving working directory!");194if (CHDIR(cbuf) != 0)195ERRLOG("Error while changing current directory to '%s'!", cbuf);196#endif197198/* NOTE: I could use the boost::wdirectory_iterator and boost::wpath199* to resolve UNICODE file names and paths. But then, _wfopen() is200* required to get the file descriptor for MS Windows to pass into201* libpng, which is incompatible with Win9x. Win9x's fopen() cannot202* handle UNICODE names. UNICODE capable boost::filesystem is available203* with Boost1.34.1 built with VC8.0 (bjam --toolset=msvc-8.0 stage).204*205* RULE OF THUMB: NEVER save texture packs in NON-ASCII names!!206*/207boost::filesystem::directory_iterator it(dir_path);208boost::filesystem::directory_iterator end_it; /* default construction yields past-the-end */209210for (; it != end_it; ++it) {211212if (KBHIT(0x1B)) {213_abortLoad = 1;214if (_callback) (*_callback)(L"Aborted loading hiresolution texture!\n");215INFO(80, L"Error: aborted loading hiresolution texture!\n");216}217if (_abortLoad) break;218219/* recursive read into sub-directory */220if (boost::filesystem::is_directory(it->status())) {221loadHiResTextures(it->path(), replace);222continue;223}224225DBG_INFO(80, L"-----\n");226DBG_INFO(80, L"file: %ls\n", it->path().leaf().c_str());227228int width = 0, height = 0;229uint16 format = 0;230uint8 *tex = NULL;231int tmpwidth = 0, tmpheight = 0;232uint16 tmpformat = 0;233uint8 *tmptex= NULL;234int untiled_width = 0, untiled_height = 0;235uint16 destformat = 0;236237/* Rice hi-res textures: begin238*/239uint32 chksum = 0, fmt = 0, siz = 0, palchksum = 0;240char *pfname = NULL, fname[MAX_PATH];241std::string ident;242FILE *fp = NULL;243244wcstombs(fname, _ident.c_str(), MAX_PATH);245/* XXX case sensitivity fiasco!246* files must use _a, _rgb, _all, _allciByRGBA, _ciByRGBA, _ci247* and file extensions must be in lower case letters! */248#ifdef WIN32249{250unsigned int i;251for (i = 0; i < strlen(fname); i++) fname[i] = tolower(fname[i]);252}253#endif254ident.assign(fname);255256/* read in Rice's file naming convention */257#define CRCFMTSIZ_LEN 13258#define PALCRC_LEN 9259//wcstombs(fname, it->path().leaf().c_str(), MAX_PATH);260strncpy(fname, it->path().leaf().string().c_str(), sizeof(fname));261fname[sizeof(fname) - 1] = '\0';262/* XXX case sensitivity fiasco!263* files must use _a, _rgb, _all, _allciByRGBA, _ciByRGBA, _ci264* and file extensions must be in lower case letters! */265#ifdef WIN32266{267unsigned int i;268for (i = 0; i < strlen(fname); i++) fname[i] = tolower(fname[i]);269}270#endif271pfname = fname + strlen(fname) - 4;272if (!(pfname == strstr(fname, ".png") ||273pfname == strstr(fname, ".bmp") ||274pfname == strstr(fname, ".dds"))) {275#if !DEBUG276INFO(80, L"-----\n");277INFO(80, L"path: %ls\n", dir_path.string().c_str());278INFO(80, L"file: %ls\n", it->path().leaf().c_str());279#endif280INFO(80, L"Error: not png or bmp or dds!\n");281continue;282}283pfname = strstr(fname, ident.c_str());284if (pfname != fname) pfname = 0;285if (pfname) {286if (sscanf(pfname + ident.size(), "#%08X#%01X#%01X#%08X", &chksum, &fmt, &siz, &palchksum) == 4)287pfname += (ident.size() + CRCFMTSIZ_LEN + PALCRC_LEN);288else if (sscanf(pfname + ident.size(), "#%08X#%01X#%01X", &chksum, &fmt, &siz) == 3)289pfname += (ident.size() + CRCFMTSIZ_LEN);290else291pfname = 0;292}293if (!pfname) {294#if !DEBUG295INFO(80, L"-----\n");296INFO(80, L"path: %ls\n", dir_path.string().c_str());297INFO(80, L"file: %ls\n", it->path().leaf().c_str());298#endif299INFO(80, L"Error: not Rice texture naming convention!\n");300continue;301}302if (!chksum) {303#if !DEBUG304INFO(80, L"-----\n");305INFO(80, L"path: %ls\n", dir_path.string().c_str());306INFO(80, L"file: %ls\n", it->path().leaf().c_str());307#endif308INFO(80, L"Error: crc32 = 0!\n");309continue;310}311312/* check if we already have it in hires texture cache */313if (!replace) {314uint64 chksum64 = (uint64)palchksum;315chksum64 <<= 32;316chksum64 |= (uint64)chksum;317if (TxCache::is_cached(chksum64)) {318#if !DEBUG319INFO(80, L"-----\n");320INFO(80, L"path: %ls\n", dir_path.string().c_str());321INFO(80, L"file: %ls\n", it->path().leaf().c_str());322#endif323INFO(80, L"Error: already cached! duplicate texture!\n");324continue;325}326}327328DBG_INFO(80, L"rom: %ls chksum:%08X %08X fmt:%x size:%x\n", _ident.c_str(), chksum, palchksum, fmt, siz);329330/* Deal with the wackiness some texture packs utilize Rice format.331* Read in the following order: _a.* + _rgb.*, _all.png _ciByRGBA.png,332* _allciByRGBA.png, and _ci.bmp. PNG are prefered over BMP.333*334* For some reason there are texture packs that include them all. Some335* even have RGB textures named as _all.* and ARGB textures named as336* _rgb.*... Someone pleeeez write a GOOD guideline for the texture337* designers!!!338*339* We allow hires textures to have higher bpp than the N64 originals.340*/341/* N64 formats342* Format: 0 - RGBA, 1 - YUV, 2 - CI, 3 - IA, 4 - I343* Size: 0 - 4bit, 1 - 8bit, 2 - 16bit, 3 - 32 bit344*/345346/*347* read in _rgb.* and _a.*348*/349if (pfname == strstr(fname, "_rgb.") || pfname == strstr(fname, "_a.")) {350strcpy(pfname, "_rgb.png");351if (!boost::filesystem::exists(fname)) {352strcpy(pfname, "_rgb.bmp");353if (!boost::filesystem::exists(fname)) {354#if !DEBUG355INFO(80, L"-----\n");356INFO(80, L"path: %ls\n", dir_path.string().c_str());357INFO(80, L"file: %ls\n", it->path().leaf().c_str());358#endif359INFO(80, L"Error: missing _rgb.*! _a.* must be paired with _rgb.*!\n");360continue;361}362}363/* _a.png */364strcpy(pfname, "_a.png");365if ((fp = fopen(fname, "rb")) != NULL) {366tmptex = _txImage->readPNG(fp, &tmpwidth, &tmpheight, &tmpformat);367fclose(fp);368}369if (!tmptex) {370/* _a.bmp */371strcpy(pfname, "_a.bmp");372if ((fp = fopen(fname, "rb")) != NULL) {373tmptex = _txImage->readBMP(fp, &tmpwidth, &tmpheight, &tmpformat);374fclose(fp);375}376}377/* _rgb.png */378strcpy(pfname, "_rgb.png");379if ((fp = fopen(fname, "rb")) != NULL) {380tex = _txImage->readPNG(fp, &width, &height, &format);381fclose(fp);382}383if (!tex) {384/* _rgb.bmp */385strcpy(pfname, "_rgb.bmp");386if ((fp = fopen(fname, "rb")) != NULL) {387tex = _txImage->readBMP(fp, &width, &height, &format);388fclose(fp);389}390}391if (tmptex) {392/* check if _rgb.* and _a.* have matching size and format. */393if (!tex || width != tmpwidth || height != tmpheight ||394format != GR_TEXFMT_ARGB_8888 || tmpformat != GR_TEXFMT_ARGB_8888) {395#if !DEBUG396INFO(80, L"-----\n");397INFO(80, L"path: %ls\n", dir_path.string().c_str());398INFO(80, L"file: %ls\n", it->path().leaf().c_str());399#endif400if (!tex) {401INFO(80, L"Error: missing _rgb.*!\n");402} else if (width != tmpwidth || height != tmpheight) {403INFO(80, L"Error: _rgb.* and _a.* have mismatched width or height!\n");404} else if (format != GR_TEXFMT_ARGB_8888 || tmpformat != GR_TEXFMT_ARGB_8888) {405INFO(80, L"Error: _rgb.* or _a.* not in 32bit color!\n");406}407if (tex) free(tex);408if (tmptex) free(tmptex);409tex = NULL;410tmptex = NULL;411continue;412}413}414/* make adjustments */415if (tex) {416if (tmptex) {417/* merge (A)RGB and A comp */418DBG_INFO(80, L"merge (A)RGB and A comp\n");419int i;420for (i = 0; i < height * width; i++) {421#if 1422/* use R comp for alpha. this is what Rice uses. sigh... */423((uint32*)tex)[i] &= 0x00ffffff;424((uint32*)tex)[i] |= ((((uint32*)tmptex)[i] & 0x00ff0000) << 8);425#endif426#if 0427/* use libpng style grayscale conversion */428uint32 texel = ((uint32*)tmptex)[i];429uint32 acomp = (((texel >> 16) & 0xff) * 6969 +430((texel >> 8) & 0xff) * 23434 +431((texel ) & 0xff) * 2365) / 32768;432((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff);433#endif434#if 0435/* use the standard NTSC gray scale conversion */436uint32 texel = ((uint32*)tmptex)[i];437uint32 acomp = (((texel >> 16) & 0xff) * 299 +438((texel >> 8) & 0xff) * 587 +439((texel ) & 0xff) * 114) / 1000;440((uint32*)tex)[i] = (acomp << 24) | (((uint32*)tex)[i] & 0x00ffffff);441#endif442}443free(tmptex);444tmptex = NULL;445} else {446/* clobber A comp. never a question of alpha. only RGB used. */447#if !DEBUG448INFO(80, L"-----\n");449INFO(80, L"path: %ls\n", dir_path.string().c_str());450INFO(80, L"file: %ls\n", it->path().leaf().c_str());451#endif452INFO(80, L"Warning: missing _a.*! only using _rgb.*. treat as opaque texture.\n");453int i;454for (i = 0; i < height * width; i++) {455((uint32*)tex)[i] |= 0xff000000;456}457}458}459} else460461/*462* read in _all.png, _all.dds, _allciByRGBA.png, _allciByRGBA.dds463* _ciByRGBA.png, _ciByRGBA.dds, _ci.bmp464*/465if (pfname == strstr(fname, "_all.png") ||466pfname == strstr(fname, "_all.dds") ||467#ifdef WIN32468pfname == strstr(fname, "_allcibyrgba.png") ||469pfname == strstr(fname, "_allcibyrgba.dds") ||470pfname == strstr(fname, "_cibyrgba.png") ||471pfname == strstr(fname, "_cibyrgba.dds") ||472#else473pfname == strstr(fname, "_allciByRGBA.png") ||474pfname == strstr(fname, "_allciByRGBA.dds") ||475pfname == strstr(fname, "_ciByRGBA.png") ||476pfname == strstr(fname, "_ciByRGBA.dds") ||477#endif478pfname == strstr(fname, "_ci.bmp")) {479if ((fp = fopen(fname, "rb")) != NULL) {480if (strstr(fname, ".png")) tex = _txImage->readPNG(fp, &width, &height, &format);481else if (strstr(fname, ".dds")) tex = _txImage->readDDS(fp, &width, &height, &format);482else tex = _txImage->readBMP(fp, &width, &height, &format);483fclose(fp);484}485/* XXX: auto-adjustment of dxt dds textures unsupported for now */486if (tex && strstr(fname, ".dds")) {487const float aspectratio = (width > height) ? (float)width/(float)height : (float)height/(float)width;488if (!(aspectratio == 1.0 ||489aspectratio == 2.0 ||490aspectratio == 4.0 ||491aspectratio == 8.0)) {492free(tex);493tex = NULL;494#if !DEBUG495INFO(80, L"-----\n");496INFO(80, L"path: %ls\n", dir_path.string().c_str());497INFO(80, L"file: %ls\n", it->path().leaf().c_str());498#endif499INFO(80, L"Error: W:H aspect ratio range not 8:1 - 1:8!\n");500continue;501}502if (width != _txReSample->nextPow2(width) ||503height != _txReSample->nextPow2(height)) {504free(tex);505tex = NULL;506#if !DEBUG507INFO(80, L"-----\n");508INFO(80, L"path: %ls\n", dir_path.string().c_str());509INFO(80, L"file: %ls\n", it->path().leaf().c_str());510#endif511INFO(80, L"Error: not power of 2 size!\n");512continue;513}514}515}516517/* if we do not have a texture at this point we are screwed */518if (!tex) {519#if !DEBUG520INFO(80, L"-----\n");521INFO(80, L"path: %ls\n", dir_path.string().c_str());522INFO(80, L"file: %ls\n", it->path().leaf().c_str());523#endif524INFO(80, L"Error: load failed!\n");525continue;526}527DBG_INFO(80, L"read in as %d x %d gfmt:%x\n", tmpwidth, tmpheight, tmpformat);528529/* check if size and format are OK */530if (!(format == GR_TEXFMT_ARGB_8888 ||531format == GR_TEXFMT_P_8 ||532format == GR_TEXFMT_ARGB_CMP_DXT1 ||533format == GR_TEXFMT_ARGB_CMP_DXT3 ||534format == GR_TEXFMT_ARGB_CMP_DXT5) ||535(width * height) < 4) { /* TxQuantize requirement: width * height must be 4 or larger. */536free(tex);537tex = NULL;538#if !DEBUG539INFO(80, L"-----\n");540INFO(80, L"path: %ls\n", dir_path.string().c_str());541INFO(80, L"file: %ls\n", it->path().leaf().c_str());542#endif543INFO(80, L"Error: not width * height > 4 or 8bit palette color or 32bpp or dxt1 or dxt3 or dxt5!\n");544continue;545}546547/* analyze and determine best format to quantize */548if (format == GR_TEXFMT_ARGB_8888) {549int i;550int alphabits = 0;551int fullalpha = 0;552boolean intensity = 1;553554if (!(_options & LET_TEXARTISTS_FLY)) {555/* HACK ALERT! */556/* Account for Rice's weirdness with fmt:0 siz:2 textures.557* Although the conditions are relaxed with other formats,558* the D3D RGBA5551 surface is used for this format in certain559* cases. See Nintemod's SuperMario64 life gauge and power560* meter. The same goes for fmt:2 textures. See Mollymutt's561* PaperMario text. */562if ((fmt == 0 && siz == 2) || fmt == 2) {563DBG_INFO(80, L"Remove black, white, etc borders along the alpha edges.\n");564/* round A comp */565for (i = 0; i < height * width; i++) {566uint32 texel = ((uint32*)tex)[i];567((uint32*)tex)[i] = ((texel & 0xff000000) == 0xff000000 ? 0xff000000 : 0) |568(texel & 0x00ffffff);569}570/* Substitute texel color with the average of the surrounding571* opaque texels. This removes borders regardless of hardware572* texture filtering (bilinear, etc). */573int j;574for (i = 0; i < height; i++) {575for (j = 0; j < width; j++) {576uint32 texel = ((uint32*)tex)[i * width + j];577if ((texel & 0xff000000) != 0xff000000) {578uint32 tmptexel[8];579uint32 k, numtexel, r, g, b;580numtexel = r = g = b = 0;581memset(&tmptexel, 0, sizeof(tmptexel));582if (i > 0) {583tmptexel[0] = ((uint32*)tex)[(i - 1) * width + j]; /* north */584if (j > 0) tmptexel[1] = ((uint32*)tex)[(i - 1) * width + j - 1]; /* north-west */585if (j < width - 1) tmptexel[2] = ((uint32*)tex)[(i - 1) * width + j + 1]; /* north-east */586}587if (i < height - 1) {588tmptexel[3] = ((uint32*)tex)[(i + 1) * width + j]; /* south */589if (j > 0) tmptexel[4] = ((uint32*)tex)[(i + 1) * width + j - 1]; /* south-west */590if (j < width - 1) tmptexel[5] = ((uint32*)tex)[(i + 1) * width + j + 1]; /* south-east */591}592if (j > 0) tmptexel[6] = ((uint32*)tex)[i * width + j - 1]; /* west */593if (j < width - 1) tmptexel[7] = ((uint32*)tex)[i * width + j + 1]; /* east */594for (k = 0; k < 8; k++) {595if ((tmptexel[k] & 0xff000000) == 0xff000000) {596r += ((tmptexel[k] & 0x00ff0000) >> 16);597g += ((tmptexel[k] & 0x0000ff00) >> 8);598b += ((tmptexel[k] & 0x000000ff) );599numtexel++;600}601}602if (numtexel) {603((uint32*)tex)[i * width + j] = ((r / numtexel) << 16) |604((g / numtexel) << 8) |605((b / numtexel) );606} else {607((uint32*)tex)[i * width + j] = texel & 0x00ffffff;608}609}610}611}612}613}614615/* simple analysis of texture */616for (i = 0; i < height * width; i++) {617uint32 texel = ((uint32*)tex)[i];618if (alphabits != 8) {619#if AGGRESSIVE_QUANTIZATION620if ((texel & 0xff000000) < 0x00000003) {621alphabits = 1;622fullalpha++;623} else if ((texel & 0xff000000) < 0xfe000000) {624alphabits = 8;625}626#else627if ((texel & 0xff000000) == 0x00000000) {628alphabits = 1;629fullalpha++;630} else if ((texel & 0xff000000) != 0xff000000) {631alphabits = 8;632}633#endif634}635if (intensity) {636int rcomp = (texel >> 16) & 0xff;637int gcomp = (texel >> 8) & 0xff;638int bcomp = (texel ) & 0xff;639#if AGGRESSIVE_QUANTIZATION640if (abs(rcomp - gcomp) > 8 || abs(rcomp - bcomp) > 8 || abs(gcomp - bcomp) > 8) intensity = 0;641#else642if (rcomp != gcomp || rcomp != bcomp || gcomp != bcomp) intensity = 0;643#endif644}645if (!intensity && alphabits == 8) break;646}647DBG_INFO(80, L"required alpha bits:%d zero acomp texels:%d rgb as intensity:%d\n", alphabits, fullalpha, intensity);648649/* preparations based on above analysis */650#if !REDUCE_TEXTURE_FOOTPRINT651if (_maxbpp < 32 || _options & (FORCE16BPP_HIRESTEX|COMPRESSION_MASK)) {652#endif653if (alphabits == 0) destformat = GR_TEXFMT_RGB_565;654else if (alphabits == 1) destformat = GR_TEXFMT_ARGB_1555;655else destformat = GR_TEXFMT_ARGB_8888;656#if !REDUCE_TEXTURE_FOOTPRINT657} else {658destformat = GR_TEXFMT_ARGB_8888;659}660#endif661if (fmt == 4 && alphabits == 0) {662destformat = GR_TEXFMT_ARGB_8888;663/* Rice I format; I = (R + G + B) / 3 */664for (i = 0; i < height * width; i++) {665uint32 texel = ((uint32*)tex)[i];666uint32 icomp = (((texel >> 16) & 0xff) +667((texel >> 8) & 0xff) +668((texel ) & 0xff)) / 3;669((uint32*)tex)[i] = (icomp << 24) | (texel & 0x00ffffff);670}671}672if (intensity) {673if (alphabits == 0) {674if (fmt == 4) destformat = GR_TEXFMT_ALPHA_8;675else destformat = GR_TEXFMT_INTENSITY_8;676} else {677destformat = GR_TEXFMT_ALPHA_INTENSITY_88;678}679}680681DBG_INFO(80, L"best gfmt:%x\n", destformat);682}683/*684* Rice hi-res textures: end */685686687/* XXX: only ARGB8888 for now. comeback to this later... */688if (format == GR_TEXFMT_ARGB_8888) {689690#if TEXTURE_TILING691692/* Glide64 style texture tiling */693/* NOTE: narrow wide textures can be tiled into 256x256 size textures */694695/* adjust texture size to allow tiling for V1, Rush, V2, Banshee, V3 */696/* NOTE: we skip this for palette textures that need minification697* becasue it will look ugly. */698699/* minification */700{701int ratio = 1;702703/* minification to enable glide64 style texture tiling */704/* determine the minification ratio to tile the texture into 256x256 size */705if ((_options & TILE_HIRESTEX) && _maxwidth >= 256 && _maxheight >= 256) {706DBG_INFO(80, L"determine minification ratio to tile\n");707tmpwidth = width;708tmpheight = height;709if (height > 256) {710ratio = ((height - 1) >> 8) + 1;711tmpwidth = width / ratio;712tmpheight = height / ratio;713DBG_INFO(80, L"height > 256, minification ratio:%d %d x %d -> %d x %d\n",714ratio, width, height, tmpwidth, tmpheight);715}716if (tmpwidth > 256 && (((tmpwidth - 1) >> 8) + 1) * tmpheight > 256) {717ratio *= ((((((tmpwidth - 1) >> 8) + 1) * tmpheight) - 1) >> 8) + 1;718DBG_INFO(80, L"width > 256, minification ratio:%d %d x %d -> %d x %d\n",719ratio, width, height, width / ratio, height / ratio);720}721} else {722/* normal minification to fit max texture size */723if (width > _maxwidth || height > _maxheight) {724DBG_INFO(80, L"determine minification ratio to fit max texture size\n");725tmpwidth = width;726tmpheight = height;727while (tmpwidth > _maxwidth) {728tmpheight >>= 1;729tmpwidth >>= 1;730ratio <<= 1;731}732while (tmpheight > _maxheight) {733tmpheight >>= 1;734tmpwidth >>= 1;735ratio <<= 1;736}737DBG_INFO(80, L"minification ratio:%d %d x %d -> %d x %d\n",738ratio, width, height, tmpwidth, tmpheight);739}740}741742if (ratio > 1) {743if (!_txReSample->minify(&tex, &width, &height, ratio)) {744free(tex);745tex = NULL;746DBG_INFO(80, L"Error: minification failed!\n");747continue;748}749}750}751752/* tiling */753if ((_options & TILE_HIRESTEX) && _maxwidth >= 256 && _maxheight >= 256) {754boolean usetile = 0;755756/* to tile or not to tile, that is the question */757if (width > 256 && height <= 128 && (((width - 1) >> 8) + 1) * height <= 256) {758759if (width > _maxwidth) usetile = 1;760else {761/* tile if the tiled texture memory footprint is smaller */762int tilewidth = 256;763int tileheight = _txReSample->nextPow2((((width - 1) >> 8) + 1) * height);764tmpwidth = width;765tmpheight = height;766767/* 3dfx Glide3 tmpheight, W:H aspect ratio range (8:1 - 1:8) */768if (tilewidth > (tileheight << 3)) tileheight = tilewidth >> 3;769770/* HACKALERT: see TxReSample::pow2(); */771if (tmpwidth > 64) tmpwidth -= 4;772else if (tmpwidth > 16) tmpwidth -= 2;773else if (tmpwidth > 4) tmpwidth -= 1;774775if (tmpheight > 64) tmpheight -= 4;776else if (tmpheight > 16) tmpheight -= 2;777else if (tmpheight > 4) tmpheight -= 1;778779tmpwidth = _txReSample->nextPow2(tmpwidth);780tmpheight = _txReSample->nextPow2(tmpheight);781782/* 3dfx Glide3 tmpheight, W:H aspect ratio range (8:1 - 1:8) */783if (tmpwidth > tmpheight) {784if (tmpwidth > (tmpheight << 3)) tmpheight = tmpwidth >> 3;785} else {786if (tmpheight > (tmpwidth << 3)) tmpwidth = tmpheight >> 3;787}788789usetile = (tilewidth * tileheight < tmpwidth * tmpheight);790}791792}793794/* tile it! do the actual tiling into 256x256 size */795if (usetile) {796DBG_INFO(80, L"Glide64 style texture tiling\n");797798int x, y, z, ratio, offset;799offset = 0;800ratio = ((width - 1) >> 8) + 1;801tmptex = (uint8 *)malloc(_txUtil->sizeofTx(256, height * ratio, format));802if (tmptex) {803for (x = 0; x < ratio; x++) {804for (y = 0; y < height; y++) {805if (x < ratio - 1) {806memcpy(&tmptex[offset << 2], &tex[(x * 256 + y * width) << 2], 256 << 2);807} else {808for (z = 0; z < width - 256 * (ratio - 1); z++) {809((uint32*)tmptex)[offset + z] = ((uint32*)tex)[x * 256 + y * width + z];810}811for (; z < 256; z++) {812((uint32*)tmptex)[offset + z] = ((uint32*)tmptex)[offset + z - 1];813}814}815offset += 256;816}817}818free(tex);819tex = tmptex;820untiled_width = width;821untiled_height = height;822width = 256;823height *= ratio;824DBG_INFO(80, L"Tiled: %d x %d -> %d x %d\n", untiled_width, untiled_height, width, height);825}826}827}828829#else /* TEXTURE_TILING */830831/* minification */832if (width > _maxwidth || height > _maxheight) {833int ratio = 1;834if (width / _maxwidth > height / _maxheight) {835ratio = (int)ceil((double)width / _maxwidth);836} else {837ratio = (int)ceil((double)height / _maxheight);838}839if (!_txReSample->minify(&tex, &width, &height, ratio)) {840free(tex);841tex = NULL;842DBG_INFO(80, L"Error: minification failed!\n");843continue;844}845}846847#endif /* TEXTURE_TILING */848849/* texture compression */850if ((_options & COMPRESSION_MASK) &&851(width >= 64 && height >= 64) /* Texture compression is not suitable for low pixel coarse detail852* textures. The assumption here is that textures larger than 64x64853* have enough detail to produce decent quality when compressed. The854* down side is that narrow stripped textures that the N64 often use855* for large background textures are also ignored. It would be more856* reasonable if decisions are made based on fourier-transform857* spectrum or RMS error.858*859* NOTE: texture size must be checked before expanding to pow2 size.860*/861) {862int dataSize = 0;863int compressionType = _options & COMPRESSION_MASK;864865#if POW2_TEXTURES866#if (POW2_TEXTURES == 2)867/* 3dfx Glide3x aspect ratio (8:1 - 1:8) */868if (!_txReSample->nextPow2(&tex, &width , &height, 32, 1)) {869#else870/* normal pow2 expansion */871if (!_txReSample->nextPow2(&tex, &width , &height, 32, 0)) {872#endif873free(tex);874tex = NULL;875DBG_INFO(80, L"Error: aspect ratio adjustment failed!\n");876continue;877}878#endif879880switch (_options & COMPRESSION_MASK) {881case S3TC_COMPRESSION:882switch (destformat) {883case GR_TEXFMT_ARGB_8888:884#if GLIDE64_DXTN885case GR_TEXFMT_ARGB_1555: /* for ARGB1555 use DXT5 instead of DXT1 */886#endif887case GR_TEXFMT_ALPHA_INTENSITY_88:888dataSize = width * height;889break;890#if !GLIDE64_DXTN891case GR_TEXFMT_ARGB_1555:892#endif893case GR_TEXFMT_RGB_565:894case GR_TEXFMT_INTENSITY_8:895dataSize = (width * height) >> 1;896break;897case GR_TEXFMT_ALPHA_8: /* no size benefit with dxtn */898;899}900break;901case FXT1_COMPRESSION:902switch (destformat) {903case GR_TEXFMT_ARGB_1555:904case GR_TEXFMT_RGB_565:905case GR_TEXFMT_INTENSITY_8:906dataSize = (width * height) >> 1;907break;908/* XXX: textures that use 8bit alpha channel look bad with the current909* fxt1 library, so we substitute it with dxtn for now. afaik all gfx910* cards that support fxt1 also support dxtn. (3dfx and Intel) */911case GR_TEXFMT_ALPHA_INTENSITY_88:912case GR_TEXFMT_ARGB_8888:913compressionType = S3TC_COMPRESSION;914dataSize = width * height;915break;916case GR_TEXFMT_ALPHA_8: /* no size benefit with dxtn */917;918}919}920/* compress it! */921if (dataSize) {922#if 0 /* TEST: dither before compression for better results with gradients */923tmptex = (uint8 *)malloc(_txUtil->sizeofTx(width, height, destformat));924if (tmptex) {925if (_txQuantize->quantize(tex, tmptex, width, height, GR_TEXFMT_ARGB_8888, destformat, 0))926_txQuantize->quantize(tmptex, tex, width, height, destformat, GR_TEXFMT_ARGB_8888, 0);927free(tmptex);928}929#endif930tmptex = (uint8 *)malloc(dataSize);931if (tmptex) {932if (_txQuantize->compress(tex, tmptex,933width, height, destformat,934&tmpwidth, &tmpheight, &tmpformat,935compressionType)) {936free(tex);937tex = tmptex;938width = tmpwidth;939height = tmpheight;940format = destformat = tmpformat;941} else {942free(tmptex);943}944}945}946947} else {948949#if POW2_TEXTURES950#if (POW2_TEXTURES == 2)951/* 3dfx Glide3x aspect ratio (8:1 - 1:8) */952if (!_txReSample->nextPow2(&tex, &width , &height, 32, 1)) {953#else954/* normal pow2 expansion */955if (!_txReSample->nextPow2(&tex, &width , &height, 32, 0)) {956#endif957free(tex);958tex = NULL;959DBG_INFO(80, L"Error: aspect ratio adjustment failed!\n");960continue;961}962#endif963}964965/* quantize */966{967tmptex = (uint8 *)malloc(_txUtil->sizeofTx(width, height, destformat));968if (tmptex) {969switch (destformat) {970case GR_TEXFMT_ARGB_8888:971case GR_TEXFMT_ARGB_4444:972#if !REDUCE_TEXTURE_FOOTPRINT973if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX)974#endif975destformat = GR_TEXFMT_ARGB_4444;976break;977case GR_TEXFMT_ARGB_1555:978#if !REDUCE_TEXTURE_FOOTPRINT979if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX)980#endif981destformat = GR_TEXFMT_ARGB_1555;982break;983case GR_TEXFMT_RGB_565:984#if !REDUCE_TEXTURE_FOOTPRINT985if (_maxbpp < 32 || _options & FORCE16BPP_HIRESTEX)986#endif987destformat = GR_TEXFMT_RGB_565;988break;989case GR_TEXFMT_ALPHA_INTENSITY_88:990case GR_TEXFMT_ALPHA_INTENSITY_44:991#if !REDUCE_TEXTURE_FOOTPRINT992destformat = GR_TEXFMT_ALPHA_INTENSITY_88;993#else994destformat = GR_TEXFMT_ALPHA_INTENSITY_44;995#endif996break;997case GR_TEXFMT_ALPHA_8:998destformat = GR_TEXFMT_ALPHA_8; /* yes, this is correct. ALPHA_8 instead of INTENSITY_8 */999break;1000case GR_TEXFMT_INTENSITY_8:1001destformat = GR_TEXFMT_INTENSITY_8;1002}1003if (_txQuantize->quantize(tex, tmptex, width, height, GR_TEXFMT_ARGB_8888, destformat, 0)) {1004format = destformat;1005free(tex);1006tex = tmptex;1007}1008}1009}10101011}101210131014/* last minute validations */1015if (!tex || !chksum || !width || !height || !format || width > _maxwidth || height > _maxheight) {1016#if !DEBUG1017INFO(80, L"-----\n");1018INFO(80, L"path: %ls\n", dir_path.string().c_str());1019INFO(80, L"file: %ls\n", it->path().leaf().c_str());1020#endif1021if (tex) {1022free(tex);1023tex = NULL;1024INFO(80, L"Error: bad format or size! %d x %d gfmt:%x\n", width, height, format);1025} else {1026INFO(80, L"Error: load failed!!\n");1027}1028continue;1029}10301031/* load it into hires texture cache. */1032{1033uint64 chksum64 = (uint64)palchksum;1034chksum64 <<= 32;1035chksum64 |= (uint64)chksum;10361037GHQTexInfo tmpInfo;1038memset(&tmpInfo, 0, sizeof(GHQTexInfo));10391040tmpInfo.data = tex;1041tmpInfo.width = width;1042tmpInfo.height = height;1043tmpInfo.format = format;1044tmpInfo.largeLodLog2 = _txUtil->grLodLog2(width, height);1045tmpInfo.smallLodLog2 = tmpInfo.largeLodLog2;1046tmpInfo.aspectRatioLog2 = _txUtil->grAspectRatioLog2(width, height);1047tmpInfo.is_hires_tex = 1;10481049#if TEXTURE_TILING1050/* Glide64 style texture tiling. */1051if (untiled_width && untiled_height) {1052tmpInfo.tiles = ((untiled_width - 1) >> 8) + 1;1053tmpInfo.untiled_width = untiled_width;1054tmpInfo.untiled_height = untiled_height;1055}1056#endif10571058/* remove redundant in cache */1059if (replace && TxCache::del(chksum64)) {1060DBG_INFO(80, L"removed duplicate old cache.\n");1061}10621063/* add to cache */1064if (TxCache::add(chksum64, &tmpInfo)) {1065now = SDL_GetTicks();1066diff = now - last;10671068/* Callback to display hires texture info.1069* Gonetz <gonetz(at)ngs.ru> */1070if (_callback && diff > 250) {1071wchar_t tmpbuf[MAX_PATH];1072mbstowcs(tmpbuf, fname, MAX_PATH);1073(*_callback)(L"[%d] total mem:%.2fmb - %ls\n", _cache.size(), (float)_totalSize/1000000, tmpbuf);1074last = now;1075}1076DBG_INFO(80, L"texture loaded!\n");1077}1078free(tex);1079}10801081}10821083if (CHDIR(curpath) != 0)1084ERRLOG("Error while changing current directory back to original path of '%s'!", curpath);10851086return 1;1087}108810891090