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 | #ifdef __MSC__ |
25 | #pragma warning(disable: 4786) |
26 | #endif |
27 | |
28 | #include "TxFilter.h" |
29 | #include "TextureFilters.h" |
30 | #include "TxDbg.h" |
31 | #include <functional> |
32 | #include <thread> |
33 | |
34 | void TxFilter::clear() |
35 | { |
36 | /* clear hires texture cache */ |
37 | delete _txHiResCache; |
38 | _txHiResCache = NULL; |
39 | |
40 | /* clear texture cache */ |
41 | delete _txTexCache; |
42 | _txTexCache = NULL; |
43 | |
44 | /* free memory */ |
45 | TxMemBuf::getInstance()->shutdown(); |
46 | |
47 | /* clear other stuff */ |
48 | delete _txImage; |
49 | _txImage = NULL; |
50 | delete _txQuantize; |
51 | _txQuantize = NULL; |
52 | delete _txUtil; |
53 | _txUtil = NULL; |
54 | } |
55 | |
56 | TxFilter::~TxFilter() |
57 | { |
58 | clear(); |
59 | } |
60 | |
61 | TxFilter::TxFilter(int maxwidth, int maxheight, int maxbpp, int options, |
62 | int cachesize, wchar_t *datapath, wchar_t *cachepath, |
63 | wchar_t *ident, dispInfoFuncExt callback) : |
64 | _numcore(1), _tex1(NULL), _tex2(NULL), _maxwidth(0), _maxheight(0), |
65 | _maxbpp(0), _options(0), _cacheSize(0), _ident(), _datapath(), _cachepath(), |
66 | _txQuantize(NULL), _txTexCache(NULL), _txHiResCache(NULL), _txUtil(NULL), |
67 | _txImage(NULL), _initialized(false) |
68 | { |
69 | clear(); /* gcc does not allow the destructor to be called */ |
70 | |
71 | /* shamelessness :P this first call to the debug output message creates |
72 | * a file in the executable directory. */ |
73 | INFO(0, L"------------------------------------------------------------------\n"); |
74 | #ifdef GHQCHK |
75 | INFO(0, L" GlideHQ Hires Texture Checker 1.02.00.%d\n", 0); |
76 | #else |
77 | INFO(0, L" GlideHQ version 1.02.00.%d\n", 0); |
78 | #endif |
79 | INFO(0, L" Copyright (C) 2010 Hiroshi Morii All Rights Reserved\n"); |
80 | INFO(0, L" email : koolsmoky(at)users.sourceforge.net\n"); |
81 | INFO(0, L" website : http://www.3dfxzone.it/koolsmoky\n"); |
82 | INFO(0, L"\n"); |
83 | INFO(0, L" Glide64 official website : http://glide64.emuxhaven.net\n"); |
84 | INFO(0, L"------------------------------------------------------------------\n"); |
85 | |
86 | _options = options; |
87 | |
88 | _txImage = new TxImage(); |
89 | _txQuantize = new TxQuantize(); |
90 | _txUtil = new TxUtil(); |
91 | |
92 | /* get number of CPU cores. */ |
93 | _numcore = _txUtil->getNumberofProcessors(); |
94 | |
95 | _initialized = 0; |
96 | |
97 | _tex1 = NULL; |
98 | _tex2 = NULL; |
99 | |
100 | /* XXX: anything larger than 1024 * 1024 is overkill */ |
101 | _maxwidth = maxwidth > 1024 ? 1024 : maxwidth; |
102 | _maxheight = maxheight > 1024 ? 1024 : maxheight; |
103 | _maxbpp = maxbpp; |
104 | |
105 | _cacheSize = cachesize; |
106 | |
107 | /* TODO: validate options and do overrides here*/ |
108 | |
109 | /* save path name */ |
110 | if (datapath) |
111 | _datapath.assign(datapath); |
112 | if (cachepath) |
113 | _cachepath.assign(cachepath); |
114 | |
115 | /* save ROM name */ |
116 | if (ident && wcscmp(ident, L"DEFAULT") != 0) |
117 | _ident.assign(ident); |
118 | |
119 | /* check for dxtn extensions */ |
120 | if (!TxLoadLib::getInstance()->getdxtCompressTexFuncExt()) |
121 | _options &= ~S3TC_COMPRESSION; |
122 | |
123 | if (!TxLoadLib::getInstance()->getfxtCompressTexFuncExt()) |
124 | _options &= ~FXT1_COMPRESSION; |
125 | |
126 | switch (options & COMPRESSION_MASK) { |
127 | case FXT1_COMPRESSION: |
128 | case S3TC_COMPRESSION: |
129 | break; |
130 | case NCC_COMPRESSION: |
131 | default: |
132 | _options &= ~COMPRESSION_MASK; |
133 | } |
134 | |
135 | if (TxMemBuf::getInstance()->init(_maxwidth, _maxheight)) { |
136 | if (!_tex1) |
137 | _tex1 = TxMemBuf::getInstance()->get(0); |
138 | |
139 | if (!_tex2) |
140 | _tex2 = TxMemBuf::getInstance()->get(1); |
141 | } |
142 | |
143 | #if !_16BPP_HACK |
144 | /* initialize hq4x filter */ |
145 | hq4x_init(); |
146 | #endif |
147 | |
148 | /* initialize texture cache in bytes. 128Mb will do nicely in most cases */ |
149 | _txTexCache = new TxTexCache(_options, _cacheSize, _datapath.c_str(), _cachepath.c_str(), _ident.c_str(), callback); |
150 | |
151 | /* hires texture */ |
152 | #if HIRES_TEXTURE |
153 | _txHiResCache = new TxHiResCache(_maxwidth, _maxheight, _maxbpp, _options, _datapath.c_str(), _cachepath.c_str(), _ident.c_str(), callback); |
154 | |
155 | if (_txHiResCache->empty()) |
156 | _options &= ~HIRESTEXTURES_MASK; |
157 | #endif |
158 | |
159 | if (!(_options & COMPRESS_TEX)) |
160 | _options &= ~COMPRESSION_MASK; |
161 | |
162 | if (_tex1 && _tex2) |
163 | _initialized = 1; |
164 | } |
165 | |
166 | boolean |
167 | TxFilter::filter(uint8 *src, int srcwidth, int srcheight, uint16 srcformat, uint64 g64crc, GHQTexInfo *info) |
168 | { |
169 | uint8 *texture = src; |
170 | uint8 *tmptex = _tex1; |
171 | uint16 destformat = srcformat; |
172 | |
173 | /* We need to be initialized first! */ |
174 | if (!_initialized) return 0; |
175 | |
176 | /* find cached textures */ |
177 | if (_cacheSize) { |
178 | |
179 | /* calculate checksum of source texture */ |
180 | if (!g64crc) |
181 | g64crc = (uint64)(_txUtil->checksumTx(texture, srcwidth, srcheight, srcformat)); |
182 | |
183 | DBG_INFO(80, L"filter: crc:%08X %08X %d x %d gfmt:%x\n", |
184 | (uint32)(g64crc >> 32), (uint32)(g64crc & 0xffffffff), srcwidth, srcheight, srcformat); |
185 | |
186 | #if 0 /* use hirestex to retrieve cached textures. */ |
187 | /* check if we have it in cache */ |
188 | if (!(g64crc & 0xffffffff00000000) && /* we reach here only when there is no hires texture for this crc */ |
189 | _txTexCache->get(g64crc, info)) { |
190 | DBG_INFO(80, L"cache hit: %d x %d gfmt:%x\n", info->width, info->height, info->format); |
191 | return 1; /* yep, we've got it */ |
192 | } |
193 | #endif |
194 | } |
195 | |
196 | /* Leave small textures alone because filtering makes little difference. |
197 | * Moreover, some filters require at least 4 * 4 to work. |
198 | * Bypass _options to do ARGB8888->16bpp if _maxbpp=16 or forced color reduction. |
199 | */ |
200 | if ((srcwidth >= 4 && srcheight >= 4) && |
201 | ((_options & (FILTER_MASK|ENHANCEMENT_MASK|COMPRESSION_MASK)) || |
202 | (srcformat == GR_TEXFMT_ARGB_8888 && (_maxbpp < 32 || _options & FORCE16BPP_TEX)))) { |
203 | |
204 | #if !_16BPP_HACK |
205 | /* convert textures to a format that the compressor accepts (ARGB8888) */ |
206 | if (_options & COMPRESSION_MASK) { |
207 | #endif |
208 | if (srcformat != GR_TEXFMT_ARGB_8888) { |
209 | if (!_txQuantize->quantize(texture, tmptex, srcwidth, srcheight, srcformat, GR_TEXFMT_ARGB_8888)) { |
210 | DBG_INFO(80, L"Error: unsupported format! gfmt:%x\n", srcformat); |
211 | return 0; |
212 | } |
213 | texture = tmptex; |
214 | destformat = GR_TEXFMT_ARGB_8888; |
215 | } |
216 | #if !_16BPP_HACK |
217 | } |
218 | #endif |
219 | |
220 | switch (destformat) { |
221 | case GR_TEXFMT_ARGB_8888: |
222 | |
223 | /* |
224 | * prepare texture enhancements (x2, x4 scalers) |
225 | */ |
226 | int scale_shift = 0, num_filters = 0; |
227 | uint32 filter = 0; |
228 | |
229 | if ((_options & ENHANCEMENT_MASK) == HQ4X_ENHANCEMENT) { |
230 | if (srcwidth <= (_maxwidth >> 2) && srcheight <= (_maxheight >> 2)) { |
231 | filter |= HQ4X_ENHANCEMENT; |
232 | scale_shift = 2; |
233 | num_filters++; |
234 | } else if (srcwidth <= (_maxwidth >> 1) && srcheight <= (_maxheight >> 1)) { |
235 | filter |= HQ2X_ENHANCEMENT; |
236 | scale_shift = 1; |
237 | num_filters++; |
238 | } |
239 | } else if (_options & ENHANCEMENT_MASK) { |
240 | if (srcwidth <= (_maxwidth >> 1) && srcheight <= (_maxheight >> 1)) { |
241 | filter |= (_options & ENHANCEMENT_MASK); |
242 | scale_shift = 1; |
243 | num_filters++; |
244 | } |
245 | } |
246 | |
247 | /* |
248 | * prepare texture filters |
249 | */ |
250 | if (_options & (SMOOTH_FILTER_MASK|SHARP_FILTER_MASK)) { |
251 | filter |= (_options & (SMOOTH_FILTER_MASK|SHARP_FILTER_MASK)); |
252 | num_filters++; |
253 | } |
254 | |
255 | /* |
256 | * execute texture enhancements and filters |
257 | */ |
258 | while (num_filters > 0) { |
259 | |
260 | tmptex = (texture == _tex1) ? _tex2 : _tex1; |
261 | |
262 | uint8 *_texture = texture; |
263 | uint8 *_tmptex = tmptex; |
264 | |
265 | #if !defined(NO_FILTER_THREAD) |
266 | unsigned int numcore = _numcore; |
267 | unsigned int blkrow = 0; |
268 | while (numcore > 1 && blkrow == 0) { |
269 | blkrow = (srcheight >> 2) / numcore; |
270 | numcore--; |
271 | } |
272 | if (blkrow > 0 && numcore > 1) { |
273 | std::thread *thrd[MAX_NUMCORE]; |
274 | unsigned int i; |
275 | int blkheight = blkrow << 2; |
276 | unsigned int srcStride = (srcwidth * blkheight) << 2; |
277 | unsigned int destStride = srcStride << scale_shift << scale_shift; |
278 | for (i = 0; i < numcore - 1; i++) { |
279 | thrd[i] = new std::thread(std::bind(filter_8888, |
280 | (uint32*)_texture, |
281 | srcwidth, |
282 | blkheight, |
283 | (uint32*)_tmptex, |
284 | filter)); |
285 | _texture += srcStride; |
286 | _tmptex += destStride; |
287 | } |
288 | thrd[i] = new std::thread(std::bind(filter_8888, |
289 | (uint32*)_texture, |
290 | srcwidth, |
291 | srcheight - blkheight * i, |
292 | (uint32*)_tmptex, |
293 | filter)); |
294 | for (i = 0; i < numcore; i++) { |
295 | thrd[i]->join(); |
296 | delete thrd[i]; |
297 | } |
298 | } else { |
299 | filter_8888((uint32*)_texture, srcwidth, srcheight, (uint32*)_tmptex, filter); |
300 | } |
301 | #else |
302 | filter_8888((uint32*)_texture, srcwidth, srcheight, (uint32*)_tmptex, filter); |
303 | #endif |
304 | |
305 | if (filter & ENHANCEMENT_MASK) { |
306 | srcwidth <<= scale_shift; |
307 | srcheight <<= scale_shift; |
308 | filter &= ~ENHANCEMENT_MASK; |
309 | scale_shift = 0; |
310 | } |
311 | |
312 | texture = tmptex; |
313 | num_filters--; |
314 | } |
315 | |
316 | /* |
317 | * texture compression |
318 | */ |
319 | /* ignored if we only have texture compression option on. |
320 | * only done when texture enhancer is used. see constructor. */ |
321 | if ((_options & COMPRESSION_MASK) && |
322 | (srcwidth >= 64 && srcheight >= 64) /* Texture compression is not suitable for low pixel coarse detail |
323 | * textures. The assumption here is that textures larger than 64x64 |
324 | * have enough detail to produce decent quality when compressed. The |
325 | * down side is that narrow stripped textures that the N64 often use |
326 | * for large background textures are also ignored. It would be more |
327 | * reasonable if decisions are made based on fourier-transform |
328 | * spectrum or RMS error. |
329 | */ |
330 | ) { |
331 | int compressionType = _options & COMPRESSION_MASK; |
332 | int tmpwidth, tmpheight; |
333 | uint16 tmpformat; |
334 | /* XXX: textures that use 8bit alpha channel look bad with the current |
335 | * fxt1 library, so we substitute it with dxtn for now. afaik all gfx |
336 | * cards that support fxt1 also support dxtn. (3dfx and Intel) */ |
337 | if ((destformat == GR_TEXFMT_ALPHA_INTENSITY_88) || |
338 | (destformat == GR_TEXFMT_ARGB_8888) || |
339 | (destformat == GR_TEXFMT_ALPHA_8)) { |
340 | compressionType = S3TC_COMPRESSION; |
341 | } |
342 | tmptex = (texture == _tex1) ? _tex2 : _tex1; |
343 | if (_txQuantize->compress(texture, tmptex, |
344 | srcwidth, srcheight, srcformat, |
345 | &tmpwidth, &tmpheight, &tmpformat, |
346 | compressionType)) { |
347 | srcwidth = tmpwidth; |
348 | srcheight = tmpheight; |
349 | destformat = tmpformat; |
350 | texture = tmptex; |
351 | } |
352 | } |
353 | |
354 | |
355 | /* |
356 | * texture (re)conversions |
357 | */ |
358 | if (destformat == GR_TEXFMT_ARGB_8888) { |
359 | if (srcformat == GR_TEXFMT_ARGB_8888 && (_maxbpp < 32 || _options & FORCE16BPP_TEX)) srcformat = GR_TEXFMT_ARGB_4444; |
360 | if (srcformat != GR_TEXFMT_ARGB_8888) { |
361 | tmptex = (texture == _tex1) ? _tex2 : _tex1; |
362 | if (!_txQuantize->quantize(texture, tmptex, srcwidth, srcheight, GR_TEXFMT_ARGB_8888, srcformat)) { |
363 | DBG_INFO(80, L"Error: unsupported format! gfmt:%x\n", srcformat); |
364 | return 0; |
365 | } |
366 | texture = tmptex; |
367 | destformat = srcformat; |
368 | } |
369 | } |
370 | |
371 | break; |
372 | #if !_16BPP_HACK |
373 | case GR_TEXFMT_ARGB_4444: |
374 | |
375 | int scale_shift = 0; |
376 | tmptex = (texture == _tex1) ? _tex2 : _tex1; |
377 | |
378 | switch (_options & ENHANCEMENT_MASK) { |
379 | case HQ4X_ENHANCEMENT: |
380 | if (srcwidth <= (_maxwidth >> 2) && srcheight <= (_maxheight >> 2)) { |
381 | hq4x_4444((uint8*)texture, (uint8*)tmptex, srcwidth, srcheight, srcwidth, srcwidth * 4 * 2); |
382 | scale_shift = 2; |
383 | }/* else if (srcwidth <= (_maxwidth >> 1) && srcheight <= (_maxheight >> 1)) { |
384 | hq2x_16((uint8*)texture, srcwidth * 2, (uint8*)tmptex, srcwidth * 2 * 2, srcwidth, srcheight); |
385 | scale_shift = 1; |
386 | }*/ |
387 | break; |
388 | case HQ2X_ENHANCEMENT: |
389 | if (srcwidth <= (_maxwidth >> 1) && srcheight <= (_maxheight >> 1)) { |
390 | hq2x_16((uint8*)texture, srcwidth * 2, (uint8*)tmptex, srcwidth * 2 * 2, srcwidth, srcheight); |
391 | scale_shift = 1; |
392 | } |
393 | break; |
394 | case HQ2XS_ENHANCEMENT: |
395 | if (srcwidth <= (_maxwidth >> 1) && srcheight <= (_maxheight >> 1)) { |
396 | hq2xS_16((uint8*)texture, srcwidth * 2, (uint8*)tmptex, srcwidth * 2 * 2, srcwidth, srcheight); |
397 | scale_shift = 1; |
398 | } |
399 | break; |
400 | case LQ2X_ENHANCEMENT: |
401 | if (srcwidth <= (_maxwidth >> 1) && srcheight <= (_maxheight >> 1)) { |
402 | lq2x_16((uint8*)texture, srcwidth * 2, (uint8*)tmptex, srcwidth * 2 * 2, srcwidth, srcheight); |
403 | scale_shift = 1; |
404 | } |
405 | break; |
406 | case LQ2XS_ENHANCEMENT: |
407 | if (srcwidth <= (_maxwidth >> 1) && srcheight <= (_maxheight >> 1)) { |
408 | lq2xS_16((uint8*)texture, srcwidth * 2, (uint8*)tmptex, srcwidth * 2 * 2, srcwidth, srcheight); |
409 | scale_shift = 1; |
410 | } |
411 | break; |
412 | case X2SAI_ENHANCEMENT: |
413 | if (srcwidth <= (_maxwidth >> 1) && srcheight <= (_maxheight >> 1)) { |
414 | Super2xSaI_4444((uint16*)texture, (uint16*)tmptex, srcwidth, srcheight, srcwidth); |
415 | scale_shift = 1; |
416 | } |
417 | break; |
418 | case X2_ENHANCEMENT: |
419 | if (srcwidth <= (_maxwidth >> 1) && srcheight <= (_maxheight >> 1)) { |
420 | Texture2x_16((uint8*)texture, srcwidth * 2, (uint8*)tmptex, srcwidth * 2 * 2, srcwidth, srcheight); |
421 | scale_shift = 1; |
422 | } |
423 | } |
424 | if (scale_shift) { |
425 | srcwidth <<= scale_shift; |
426 | srcheight <<= scale_shift; |
427 | texture = tmptex; |
428 | } |
429 | |
430 | if (_options & SMOOTH_FILTER_MASK) { |
431 | tmptex = (texture == _tex1) ? _tex2 : _tex1; |
432 | SmoothFilter_4444((uint16*)texture, srcwidth, srcheight, (uint16*)tmptex, (_options & SMOOTH_FILTER_MASK)); |
433 | texture = tmptex; |
434 | } else if (_options & SHARP_FILTER_MASK) { |
435 | tmptex = (texture == _tex1) ? _tex2 : _tex1; |
436 | SharpFilter_4444((uint16*)texture, srcwidth, srcheight, (uint16*)tmptex, (_options & SHARP_FILTER_MASK)); |
437 | texture = tmptex; |
438 | } |
439 | |
440 | break; |
441 | case GR_TEXFMT_ARGB_1555: |
442 | break; |
443 | case GR_TEXFMT_RGB_565: |
444 | break; |
445 | case GR_TEXFMT_ALPHA_8: |
446 | break; |
447 | #endif /* _16BPP_HACK */ |
448 | } |
449 | } |
450 | |
451 | /* fill in the texture info. */ |
452 | info->data = texture; |
453 | info->width = srcwidth; |
454 | info->height = srcheight; |
455 | info->format = destformat; |
456 | info->smallLodLog2 = _txUtil->grLodLog2(srcwidth, srcheight); |
457 | info->largeLodLog2 = info->smallLodLog2; |
458 | info->aspectRatioLog2 = _txUtil->grAspectRatioLog2(srcwidth, srcheight); |
459 | info->is_hires_tex = 0; |
460 | |
461 | /* cache the texture. */ |
462 | if (_cacheSize) _txTexCache->add(g64crc, info); |
463 | |
464 | DBG_INFO(80, L"filtered texture: %d x %d gfmt:%x\n", info->width, info->height, info->format); |
465 | |
466 | return 1; |
467 | } |
468 | |
469 | boolean |
470 | TxFilter::hirestex(uint64 g64crc, uint64 r_crc64, uint16 *palette, GHQTexInfo *info) |
471 | { |
472 | /* NOTE: Rice CRC32 sometimes return the same value for different textures. |
473 | * As a workaround, Glide64 CRC32 is used for the key for NON-hires |
474 | * texture cache. |
475 | * |
476 | * r_crc64 = hi:palette low:texture |
477 | * (separate crc. doesn't necessary have to be rice crc) |
478 | * g64crc = texture + palette glide64 crc32 |
479 | * (can be any other crc if robust) |
480 | */ |
481 | |
482 | DBG_INFO(80, L"hirestex: r_crc64:%08X %08X, g64crc:%08X %08X\n", |
483 | (uint32)(r_crc64 >> 32), (uint32)(r_crc64 & 0xffffffff), |
484 | (uint32)(g64crc >> 32), (uint32)(g64crc & 0xffffffff)); |
485 | |
486 | #if HIRES_TEXTURE |
487 | /* check if we have it in hires memory cache. */ |
488 | if ((_options & HIRESTEXTURES_MASK) && r_crc64) { |
489 | if (_txHiResCache->get(r_crc64, info)) { |
490 | DBG_INFO(80, L"hires hit: %d x %d gfmt:%x\n", info->width, info->height, info->format); |
491 | |
492 | /* TODO: Enable emulation for special N64 combiner modes. There are few ways |
493 | * to get this done. Also applies for CI textures below. |
494 | * |
495 | * Solution 1. Load the hiresolution textures in ARGB8888 (or A8, IA88) format |
496 | * to cache. When a cache is hit, then we take the modes passed in from Glide64 |
497 | * (also TODO) and apply the modification. Then we do color reduction or format |
498 | * conversion or compression if desired and stuff it into the non-hires texture |
499 | * cache. |
500 | * |
501 | * Solution 2. When a cache is hit and if the combiner modes are present, |
502 | * convert the texture to ARGB4444 and pass it back to Glide64 to process. |
503 | * If a texture is compressed, it needs to be decompressed first. Then add |
504 | * the processed texture to the non-hires texture cache. |
505 | * |
506 | * Solution 3. Hybrid of the above 2. Load the textures in ARGB8888 (A8, IA88) |
507 | * format. Convert the texture to ARGB4444 and pass it back to Glide64 when |
508 | * the combiner modes are present. Get the processed texture back from Glide64 |
509 | * and compress if desired and add it to the non-hires texture cache. |
510 | * |
511 | * Solution 4. Take the easy way out and forget about this whole thing. |
512 | */ |
513 | |
514 | return 1; /* yep, got it */ |
515 | } |
516 | if (_txHiResCache->get((r_crc64 & 0xffffffff), info)) { |
517 | DBG_INFO(80, L"hires hit: %d x %d gfmt:%x\n", info->width, info->height, info->format); |
518 | |
519 | /* for true CI textures, we use the passed in palette to convert to |
520 | * ARGB1555 and add it to memory cache. |
521 | * |
522 | * NOTE: we do this AFTER all other texture cache searches because |
523 | * only a few texture packs actually use true CI textures. |
524 | * |
525 | * NOTE: the pre-converted palette from Glide64 is in RGBA5551 format. |
526 | * A comp comes before RGB comp. |
527 | */ |
528 | if (palette && info->format == GR_TEXFMT_P_8) { |
529 | DBG_INFO(80, L"found GR_TEXFMT_P_8 format. Need conversion!!\n"); |
530 | |
531 | int width = info->width; |
532 | int height = info->height; |
533 | uint16 format = info->format; |
534 | /* XXX: avoid collision with zlib compression buffer in TxHiResTexture::get */ |
535 | uint8 *texture = info->data; |
536 | uint8 *tmptex = (texture == _tex1) ? _tex2 : _tex1; |
537 | |
538 | /* use palette and convert to 16bit format */ |
539 | _txQuantize->P8_16BPP((uint32*)texture, (uint32*)tmptex, info->width, info->height, (uint32*)palette); |
540 | texture = tmptex; |
541 | format = GR_TEXFMT_ARGB_1555; |
542 | |
543 | #if 1 |
544 | /* XXX: compressed if memory cache compression is ON */ |
545 | if (_options & COMPRESSION_MASK) { |
546 | tmptex = (texture == _tex1) ? _tex2 : _tex1; |
547 | if (_txQuantize->quantize(texture, tmptex, info->width, info->height, format, GR_TEXFMT_ARGB_8888)) { |
548 | texture = tmptex; |
549 | format = GR_TEXFMT_ARGB_8888; |
550 | } |
551 | if (format == GR_TEXFMT_ARGB_8888) { |
552 | tmptex = (texture == _tex1) ? _tex2 : _tex1; |
553 | if (_txQuantize->compress(texture, tmptex, |
554 | info->width, info->height, GR_TEXFMT_ARGB_1555, |
555 | &width, &height, &format, |
556 | _options & COMPRESSION_MASK)) { |
557 | texture = tmptex; |
558 | } else { |
559 | /*if (!_txQuantize->quantize(texture, tmptex, info->width, info->height, GR_TEXFMT_ARGB_8888, GR_TEXFMT_ARGB_1555)) { |
560 | DBG_INFO(80, L"Error: unsupported format! gfmt:%x\n", format); |
561 | return 0; |
562 | }*/ |
563 | texture = tmptex; |
564 | format = GR_TEXFMT_ARGB_1555; |
565 | } |
566 | } |
567 | } |
568 | #endif |
569 | |
570 | /* fill in the required info to return */ |
571 | info->data = texture; |
572 | info->width = width; |
573 | info->height = height; |
574 | info->format = format; |
575 | info->smallLodLog2 = _txUtil->grLodLog2(width, height); |
576 | info->largeLodLog2 = info->smallLodLog2; |
577 | info->aspectRatioLog2 = _txUtil->grAspectRatioLog2(width, height); |
578 | info->is_hires_tex = 1; |
579 | |
580 | /* XXX: add to hires texture cache!!! */ |
581 | _txHiResCache->add(r_crc64, info); |
582 | |
583 | DBG_INFO(80, L"GR_TEXFMT_P_8 loaded as gfmt:%x!\n", format); |
584 | } |
585 | |
586 | return 1; |
587 | } |
588 | } |
589 | #endif |
590 | |
591 | /* check if we have it in memory cache */ |
592 | if (_cacheSize && g64crc) { |
593 | if (_txTexCache->get(g64crc, info)) { |
594 | DBG_INFO(80, L"cache hit: %d x %d gfmt:%x\n", info->width, info->height, info->format); |
595 | return 1; /* yep, we've got it */ |
596 | } |
597 | } |
598 | |
599 | DBG_INFO(80, L"no cache hits.\n"); |
600 | |
601 | return 0; |
602 | } |
603 | |
604 | uint64 |
605 | TxFilter::checksum64(uint8 *src, int width, int height, int size, int rowStride, uint8 *palette) |
606 | { |
607 | if (_options & (HIRESTEXTURES_MASK|DUMP_TEX)) |
608 | return _txUtil->checksum64(src, width, height, size, rowStride, palette); |
609 | |
610 | return 0; |
611 | } |
612 | |
613 | boolean |
614 | TxFilter::dmptx(uint8 *src, int width, int height, int rowStridePixel, uint16 gfmt, uint16 n64fmt, uint64 r_crc64) |
615 | { |
616 | if (!_initialized) |
617 | return 0; |
618 | |
619 | if (!(_options & DUMP_TEX)) |
620 | return 0; |
621 | |
622 | #ifdef DUMP_CACHE |
623 | DBG_INFO(80, L"gfmt = %02x n64fmt = %02x\n", gfmt, n64fmt); |
624 | DBG_INFO(80, L"hirestex: r_crc64:%08X %08X\n", |
625 | (uint32)(r_crc64 >> 32), (uint32)(r_crc64 & 0xffffffff)); |
626 | |
627 | if (!_txQuantize->quantize(src, _tex1, rowStridePixel, height, (gfmt & 0x00ff), GR_TEXFMT_ARGB_8888)) |
628 | return 0; |
629 | |
630 | src = _tex1; |
631 | |
632 | if (!_datapath.empty() && !_ident.empty()) { |
633 | /* dump it to disk */ |
634 | FILE *fp = NULL; |
635 | std::wstring tmpbuf; |
636 | wchar_t texid[36]; |
637 | |
638 | /* create directories */ |
639 | tmpbuf.assign(_datapath + L"/texture_dump"); |
640 | if (!boost::filesystem::exists(tmpbuf) && |
641 | !boost::filesystem::create_directory(tmpbuf)) |
642 | return 0; |
643 | |
644 | tmpbuf.append(L"/" + _ident); |
645 | if (!boost::filesystem::exists(tmpbuf) && |
646 | !boost::filesystem::create_directory(tmpbuf)) |
647 | return 0; |
648 | |
649 | tmpbuf.append(L"/GlideHQ"); |
650 | if (!boost::filesystem::exists(tmpbuf) && |
651 | !boost::filesystem::create_directory(tmpbuf)) |
652 | return 0; |
653 | |
654 | if ((n64fmt >> 8) == 0x2) { |
655 | swprintf(texid, 36, L"%08X#%01X#%01X#%08X", (uint32)(r_crc64 & 0xffffffff), (uint32)(n64fmt >> 8), (uint32)(n64fmt & 0xf), (uint32)(r_crc64 >> 32)); |
656 | tmpbuf.append(L"/" + _ident + L"#" + texid + L"_ciByRGBA.png"); |
657 | } else { |
658 | swprintf(texid, 36, L"%08X#%01X#%01X", (uint32)(r_crc64 & 0xffffffff), (uint32)(n64fmt >> 8), (uint32)(n64fmt & 0xf)); |
659 | tmpbuf.append(L"/" + _ident + L"#" + texid + L"_all.png"); |
660 | } |
661 | |
662 | #ifdef WIN32 |
663 | if ((fp = _wfopen(tmpbuf.c_str(), L"wb")) != NULL) { |
664 | #else |
665 | char cbuf[MAX_PATH]; |
666 | wcstombs(cbuf, tmpbuf.c_str(), MAX_PATH); |
667 | if ((fp = fopen(cbuf, "wb")) != NULL) { |
668 | #endif |
669 | _txImage->writePNG(src, fp, width, height, (rowStridePixel << 2), 0x0003, 0); |
670 | fclose(fp); |
671 | return 1; |
672 | } |
673 | } |
674 | #endif |
675 | |
676 | return 0; |
677 | } |
678 | |
679 | boolean |
680 | TxFilter::reloadhirestex() |
681 | { |
682 | DBG_INFO(80, L"Reload hires textures from texture pack.\n"); |
683 | |
684 | if (_txHiResCache->load(0)) { |
685 | if (_txHiResCache->empty()) _options &= ~HIRESTEXTURES_MASK; |
686 | else _options |= HIRESTEXTURES_MASK; |
687 | |
688 | return 1; |
689 | } |
690 | |
691 | return 0; |
692 | } |