Rice Video Plugin for GLES1.1
[mupen64plus-pandora.git] / source / rice_gles / src / TextureFilters.cpp
CommitLineData
d07c171f 1/*
2Copyright (C) 2003 Rice1964
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18*/
19
20#include "osal_files.h"
21
22#define M64P_PLUGIN_PROTOTYPES 1
23#include "m64p_plugin.h"
24#include "typedefs.h"
25#include "ConvertImage.h"
26#include "DeviceBuilder.h"
27#include "TextureFilters.h"
28#include "Render.h"
29#include "Video.h"
30
31#include "liblinux/BMGLibPNG.h"
32#include "liblinux/BMGDLL.h"
33#include <sys/types.h>
34#include <algorithm>
35
36#ifdef min
37#undef min
38#endif
39
40/************************************************************************/
41/* 2X filters */
42/************************************************************************/
43// Basic 2x R8G8B8A8 filter with interpolation
44
45void Texture2x_32( DrawInfo &srcInfo, DrawInfo &destInfo)
46{
47 uint32 *pDst1, *pDst2;
48 uint32 *pSrc, *pSrc2;
49 uint32 nWidth = srcInfo.dwWidth;
50 uint32 nHeight = srcInfo.dwHeight;
51
52 uint32 b1;
53 uint32 g1;
54 uint32 r1;
55 uint32 a1;
56 uint32 b2 = 0;
57 uint32 g2 = 0;
58 uint32 r2 = 0;
59 uint32 a2 = 0;
60 uint32 b3 = 0;
61 uint32 g3 = 0;
62 uint32 r3 = 0;
63 uint32 a3 = 0;
64 uint32 b4 = 0;
65 uint32 g4 = 0;
66 uint32 r4 = 0;
67 uint32 a4 = 0;
68
69
70 for (uint32 ySrc = 0; ySrc < nHeight; ySrc++)
71 {
72 pSrc = (uint32*)(((uint8*)srcInfo.lpSurface)+ySrc*srcInfo.lPitch);
73 pSrc2 = (uint32*)(((uint8*)srcInfo.lpSurface)+(ySrc+1)*srcInfo.lPitch);
74 pDst1 = (uint32*)(((uint8*)destInfo.lpSurface)+(ySrc*2)*destInfo.lPitch);
75 pDst2 = (uint32*)(((uint8*)destInfo.lpSurface)+(ySrc*2+1)*destInfo.lPitch);
76
77 for (uint32 xSrc = 0; xSrc < nWidth; xSrc++)
78 {
79 b1 = (pSrc[xSrc]>>0)&0xFF;
80 g1 = (pSrc[xSrc]>>8)&0xFF;
81 r1 = (pSrc[xSrc]>>16)&0xFF;
82 a1 = (pSrc[xSrc]>>24)&0xFF;
83
84 if( xSrc<nWidth-1 )
85 {
86 b2 = (pSrc[xSrc+1]>>0)&0xFF;
87 g2 = (pSrc[xSrc+1]>>8)&0xFF;
88 r2 = (pSrc[xSrc+1]>>16)&0xFF;
89 a2 = (pSrc[xSrc+1]>>24)&0xFF;
90 }
91
92 if( ySrc<nHeight-1 )
93 {
94 b3 = (pSrc2[xSrc]>>0)&0xFF;
95 g3 = (pSrc2[xSrc]>>8)&0xFF;
96 r3 = (pSrc2[xSrc]>>16)&0xFF;
97 a3 = (pSrc2[xSrc]>>24)&0xFF;
98 if( xSrc<nWidth-1 )
99 {
100 b4 = (pSrc2[xSrc+1]>>0)&0xFF;
101 g4 = (pSrc2[xSrc+1]>>8)&0xFF;
102 r4 = (pSrc2[xSrc+1]>>16)&0xFF;
103 a4 = (pSrc2[xSrc+1]>>24)&0xFF;
104 }
105 }
106
107
108 // Pixel 1
109 pDst1[xSrc*2] = pSrc[xSrc];
110
111 // Pixel 2
112 if( xSrc<nWidth-1 )
113 {
114 pDst1[xSrc*2+1] = DWORD_MAKE((r1+r2)/2, (g1+g2)/2, (b1+b2)/2, (a1+a2)/2);
115 }
116 else
117 pDst1[xSrc*2+1] = pSrc[xSrc];
118
119
120 // Pixel 3
121 if( ySrc<nHeight-1 )
122 {
123 pDst2[xSrc*2] = DWORD_MAKE((r1+r3)/2, (g1+g3)/2, (b1+b3)/2, (a1+a3)/2);
124 }
125 else
126 pDst2[xSrc*2] = pSrc[xSrc];
127
128 // Pixel 4
129 if( xSrc<nWidth-1 )
130 {
131 if( ySrc<nHeight-1 )
132 {
133 pDst2[xSrc*2+1] = DWORD_MAKE((r1+r2+r3+r4)/4, (g1+g2+g3+g4)/4, (b1+b2+b3+b4)/4, (a1+a2+a3+a4)/4);
134 }
135 else
136 {
137 pDst2[xSrc*2+1] = DWORD_MAKE((r1+r2)/2, (g1+g2)/2, (b1+b2)/2, (a1+a2)/2);
138 }
139 }
140 else
141 {
142 if( ySrc<nHeight-1 )
143 {
144 pDst2[xSrc*2+1] = DWORD_MAKE((r1+r3)/2, (g1+g3)/2, (b1+b3)/2, (a1+a3)/2);
145 }
146 else
147 pDst2[xSrc*2+1] = pSrc[xSrc];
148 }
149 }
150 }
151}
152
153// Basic 2x R4G4B4A4 filter with interpolation
154void Texture2x_16( DrawInfo &srcInfo, DrawInfo &destInfo )
155{
156 uint16 *pDst1, *pDst2;
157 uint16 *pSrc, *pSrc2;
158 uint32 nWidth = srcInfo.dwWidth;
159 uint32 nHeight = srcInfo.dwHeight;
160
161 uint16 b1;
162 uint16 g1;
163 uint16 r1;
164 uint16 a1;
165 uint16 b2 = 0;
166 uint16 g2 = 0;
167 uint16 r2 = 0;
168 uint16 a2 = 0;
169 uint16 b3 = 0;
170 uint16 g3 = 0;
171 uint16 r3 = 0;
172 uint16 a3 = 0;
173 uint16 b4 = 0;
174 uint16 g4 = 0;
175 uint16 r4 = 0;
176 uint16 a4 = 0;
177
178 for (uint16 ySrc = 0; ySrc < nHeight; ySrc++)
179 {
180 pSrc = (uint16*)(((uint8*)srcInfo.lpSurface)+ySrc*srcInfo.lPitch);
181 pSrc2 = (uint16*)(((uint8*)srcInfo.lpSurface)+(ySrc+1)*srcInfo.lPitch);
182 pDst1 = (uint16*)(((uint8*)destInfo.lpSurface)+(ySrc*2)*destInfo.lPitch);
183 pDst2 = (uint16*)(((uint8*)destInfo.lpSurface)+(ySrc*2+1)*destInfo.lPitch);
184
185 for (uint16 xSrc = 0; xSrc < nWidth; xSrc++)
186 {
187 b1 = (pSrc[xSrc]>> 0)&0xF;
188 g1 = (pSrc[xSrc]>> 4)&0xF;
189 r1 = (pSrc[xSrc]>> 8)&0xF;
190 a1 = (pSrc[xSrc]>>12)&0xF;
191
192 if( xSrc<nWidth-1 )
193 {
194 b2 = (pSrc[xSrc+1]>> 0)&0xF;
195 g2 = (pSrc[xSrc+1]>> 4)&0xF;
196 r2 = (pSrc[xSrc+1]>> 8)&0xF;
197 a2 = (pSrc[xSrc+1]>>12)&0xF;
198 }
199
200 if( ySrc<nHeight-1 )
201 {
202 b3 = (pSrc2[xSrc]>> 0)&0xF;
203 g3 = (pSrc2[xSrc]>> 4)&0xF;
204 r3 = (pSrc2[xSrc]>> 8)&0xF;
205 a3 = (pSrc2[xSrc]>>12)&0xF;
206 if( xSrc<nWidth-1 )
207 {
208 b4 = (pSrc2[xSrc+1]>> 0)&0xF;
209 g4 = (pSrc2[xSrc+1]>> 4)&0xF;
210 r4 = (pSrc2[xSrc+1]>> 8)&0xF;
211 a4 = (pSrc2[xSrc+1]>>12)&0xF;
212 }
213 }
214
215 // Pixel 1
216 pDst1[xSrc*2] = pSrc[xSrc];
217
218 // Pixel 2
219 if( xSrc<nWidth-1 )
220 {
221 pDst1[xSrc*2+1] = WORD_MAKE((r1+r2)/2, (g1+g2)/2, (b1+b2)/2, (a1+a2)/2);
222 }
223 else
224 pDst1[xSrc*2+1] = pSrc[xSrc];
225
226
227 // Pixel 3
228 if( ySrc<nHeight-1 )
229 {
230 pDst2[xSrc*2] = WORD_MAKE((r1+r3)/2, (g1+g3)/2, (b1+b3)/2, (a1+a3)/2);
231 }
232 else
233 pDst2[xSrc*2] = pSrc[xSrc];
234
235 // Pixel 4
236 if( xSrc<nWidth-1 )
237 {
238 if( ySrc<nHeight-1 )
239 {
240 pDst2[xSrc*2+1] = WORD_MAKE((r1+r2+r3+r4)/4, (g1+g2+g3+g4)/4, (b1+b2+b3+b4)/4, (a1+a2+a3+a4)/4);
241 }
242 else
243 {
244 pDst2[xSrc*2+1] = WORD_MAKE((r1+r2)/2, (g1+g2)/2, (b1+b2)/2, (a1+a2)/2);
245 }
246 }
247 else
248 {
249 if( ySrc<nHeight-1 )
250 {
251 pDst2[xSrc*2+1] = WORD_MAKE((r1+r3)/2, (g1+g3)/2, (b1+b3)/2, (a1+a3)/2);
252 }
253 else
254 pDst2[xSrc*2+1] = pSrc[xSrc];
255 }
256 }
257 }
258}
259
260/************************************************************************/
261/* Sharpen filters */
262/************************************************************************/
263void SharpenFilter_32(uint32 *pdata, uint32 width, uint32 height, uint32 pitch, uint32 filter)
264{
265 uint32 len=height*pitch;
266 uint32 *pcopy = new uint32[len];
267
268 if( !pcopy ) return;
269
270 memcpy(pcopy, pdata, len<<2);
271
272 uint32 mul1, mul2, mul3, shift4;
273 switch( filter )
274 {
275 case TEXTURE_SHARPEN_MORE_ENHANCEMENT:
276 mul1=1;
277 mul2=8;
278 mul3=12;
279 shift4=2;
280 break;
281 case TEXTURE_SHARPEN_ENHANCEMENT:
282 default:
283 mul1=1;
284 mul2=8;
285 mul3=16;
286 shift4=3;
287 break;
288 }
289
290 uint32 x,y,z;
291 uint32 *src1, *src2, *src3, *dest;
292 uint32 val[4];
293 uint32 t1,t2,t3,t4,t5,t6,t7,t8,t9;
294
295 for( y=1; y<height-1; y++)
296 {
297 dest = pdata+y*pitch;
298 src1 = pcopy+(y-1)*pitch;
299 src2 = src1 + pitch;
300 src3 = src2 + pitch;
301 for( x=1; x<width-1; x++)
302 {
303 for( z=0; z<4; z++ )
304 {
305 t1 = *((uint8*)(src1+x-1)+z);
306 t2 = *((uint8*)(src1+x )+z);
307 t3 = *((uint8*)(src1+x+1)+z);
308 t4 = *((uint8*)(src2+x-1)+z);
309 t5 = *((uint8*)(src2+x )+z);
310 t6 = *((uint8*)(src2+x+1)+z);
311 t7 = *((uint8*)(src3+x-1)+z);
312 t8 = *((uint8*)(src3+x )+z);
313 t9 = *((uint8*)(src3+x+1)+z);
314 val[z]=t5;
315 if( (t5*mul2) > (t1+t3+t7+t9+t2+t4+t6+t8)*mul1 )
316 {
317 val[z]= std::min((((t5*mul3) - (t1+t3+t7+t9+t2+t4+t6+t8)*mul1)>>shift4),0xFFU);
318 }
319 }
320 dest[x] = val[0]|(val[1]<<8)|(val[2]<<16)|(val[3]<<24);
321 }
322 }
323 delete [] pcopy;
324}
325
326void SharpenFilter_16(uint16 *pdata, uint32 width, uint32 height, uint32 pitch, uint32 filter)
327{
328 //return; // Sharpen does not make sense for 16 bits
329
330 uint32 len=height*pitch;
331 uint16 *pcopy = new uint16[len];
332
333 if( !pcopy ) return;
334
335 memcpy(pcopy, pdata, len<<1);
336
337 uint16 mul1, mul2, mul3, shift4;
338 switch( filter )
339 {
340 case TEXTURE_SHARPEN_MORE_ENHANCEMENT:
341 mul1=1;
342 mul2=8;
343 mul3=12;
344 shift4=2;
345 break;
346 case TEXTURE_SHARPEN_ENHANCEMENT:
347 default:
348 mul1=1;
349 mul2=8;
350 mul3=16;
351 shift4=3;
352 break;
353 }
354
355 uint32 x,y,z;
356 uint16 *src1, *src2, *src3, *dest;
357 uint16 val[4];
358 uint16 t1,t2,t3,t4,t5,t6,t7,t8,t9;
359
360 for( y=1; y<height-1; y++)
361 {
362 dest = pdata+y*pitch;
363 src1 = pcopy+(y-1)*pitch;
364 src2 = src1 + pitch;
365 src3 = src2 + pitch;
366 for( x=1; x<width-1; x++)
367 {
368 for( z=0; z<4; z++ )
369 {
370 uint32 shift = (z%1)?4:0;
371 t1 = (*((uint8*)(src1+x-1)+(z>>1)))>>shift;
372 t2 = (*((uint8*)(src1+x )+(z>>1)))>>shift;
373 t3 = (*((uint8*)(src1+x+1)+(z>>1)))>>shift;
374 t4 = (*((uint8*)(src2+x-1)+(z>>1)))>>shift;
375 t5 = (*((uint8*)(src2+x )+(z>>1)))>>shift;
376 t6 = (*((uint8*)(src2+x+1)+(z>>1)))>>shift;
377 t7 = (*((uint8*)(src3+x-1)+(z>>1)))>>shift;
378 t8 = (*((uint8*)(src3+x )+(z>>1)))>>shift;
379 t9 = (*((uint8*)(src3+x+1)+(z>>1)))>>shift;
380 val[z]=t5;
381 if( (t5*mul2) > (t1+t3+t7+t9+t2+t4+t6+t8)*mul1 )
382 {
383 val[z] = (((t5*mul3) - (t1+t3+t7+t9+t2+t4+t6+t8)*mul1)>>shift4);
384 val[z]= std::min(val[z],(unsigned short)0xFU);
385 }
386 }
387 dest[x] = val[0]|(val[1]<<4)|(val[2]<<8)|(val[3]<<12);
388 }
389 }
390 delete [] pcopy;
391}
392
393/************************************************************************/
394/* Smooth filters */
395/************************************************************************/
396void SmoothFilter_32(uint32 *pdata, uint32 width, uint32 height, uint32 pitch, uint32 filter)
397{
398 uint32 len=height*pitch;
399 uint32 *pcopy = new uint32[len];
400
401 if( !pcopy ) return;
402
403 memcpy(pcopy, pdata, len<<2);
404
405 uint32 mul1, mul2, mul3, shift4;
406 switch( filter )
407 {
408 case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_1:
409 mul1=1;
410 mul2=2;
411 mul3=4;
412 shift4=4;
413 break;
414 case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_2:
415 mul1=1;
416 mul2=1;
417 mul3=8;
418 shift4=4;
419 break;
420 case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_3:
421 mul1=1;
422 mul2=1;
423 mul3=2;
424 shift4=2;
425 break;
426 case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_4:
427 default:
428 mul1=1;
429 mul2=1;
430 mul3=6;
431 shift4=3;
432 break;
433 }
434
435 uint32 x,y,z;
436 uint32 *src1, *src2, *src3, *dest;
437 uint32 val[4];
438 uint32 t1,t2,t3,t4,t5,t6,t7,t8,t9;
439
440 if( filter == TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_3 || filter == TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_4 )
441 {
442 for( y=1; y<height-1; y+=2)
443 {
444 dest = pdata+y*pitch;
445 src1 = pcopy+(y-1)*pitch;
446 src2 = src1 + pitch;
447 src3 = src2 + pitch;
448 for( x=0; x<width; x++)
449 {
450 for( z=0; z<4; z++ )
451 {
452 t2 = *((uint8*)(src1+x )+z);
453 t5 = *((uint8*)(src2+x )+z);
454 t8 = *((uint8*)(src3+x )+z);
455 val[z] = ((t2+t8)*mul2+(t5*mul3))>>shift4;
456 }
457 dest[x] = val[0]|(val[1]<<8)|(val[2]<<16)|(val[3]<<24);
458 }
459 }
460 }
461 else
462 {
463 for( y=0; y<height; y++)
464 {
465 dest = pdata+y*pitch;
466 if( y>0 )
467 {
468 src1 = pcopy+(y-1)*pitch;
469 src2 = src1 + pitch;
470 }
471 else
472 {
473 src1 = src2 = pcopy;
474 }
475
476 src3 = src2;
477 if( y<height-1) src3 += pitch;
478
479 for( x=1; x<width-1; x++)
480 {
481 for( z=0; z<4; z++ )
482 {
483 t1 = *((uint8*)(src1+x-1)+z);
484 t2 = *((uint8*)(src1+x )+z);
485 t3 = *((uint8*)(src1+x+1)+z);
486 t4 = *((uint8*)(src2+x-1)+z);
487 t5 = *((uint8*)(src2+x )+z);
488 t6 = *((uint8*)(src2+x+1)+z);
489 t7 = *((uint8*)(src3+x-1)+z);
490 t8 = *((uint8*)(src3+x )+z);
491 t9 = *((uint8*)(src3+x+1)+z);
492 val[z] = ((t1+t3+t7+t9)*mul1+((t2+t4+t6+t8)*mul2)+(t5*mul3))>>shift4;
493 }
494 dest[x] = val[0]|(val[1]<<8)|(val[2]<<16)|(val[3]<<24);
495 }
496 }
497 }
498 delete [] pcopy;
499}
500
501void SmoothFilter_16(uint16 *pdata, uint32 width, uint32 height, uint32 pitch, uint32 filter)
502{
503 uint32 len=height*pitch;
504 uint16 *pcopy = new uint16[len];
505
506 if( !pcopy )
507 return;
508
509 memcpy(pcopy, pdata, len<<1);
510
511 uint16 mul1, mul2, mul3, shift4;
512 switch( filter )
513 {
514 case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_1:
515 mul1=1;
516 mul2=2;
517 mul3=4;
518 shift4=4;
519 break;
520 case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_2:
521 mul1=1;
522 mul2=1;
523 mul3=8;
524 shift4=4;
525 break;
526 case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_3:
527 mul1=1;
528 mul2=1;
529 mul3=2;
530 shift4=2;
531 break;
532 case TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_4:
533 default:
534 mul1=1;
535 mul2=1;
536 mul3=6;
537 shift4=3;
538 break;
539 }
540
541 uint32 x,y,z;
542 uint16 *src1, *src2, *src3, *dest;
543 uint16 val[4];
544 uint16 t1,t2,t3,t4,t5,t6,t7,t8,t9;
545
546 if( filter == TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_3 || filter == TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_4 )
547 {
548 for( y=1; y<height-1; y+=2)
549 {
550 dest = pdata+y*pitch;
551 src1 = pcopy+(y-1)*pitch;
552 src2 = src1 + pitch;
553 src3 = src2 + pitch;
554 for( x=0; x<width; x++)
555 {
556 for( z=0; z<4; z++ )
557 {
558 uint32 shift = (z&1)?4:0;
559 t2 = (*((uint8*)(src1+x )+(z>>1)))>>shift;
560 t5 = (*((uint8*)(src2+x )+(z>>1)))>>shift;
561 t8 = (*((uint8*)(src3+x )+(z>>1)))>>shift;
562 val[z] = ((t2+t8)*mul2+(t5*mul3))>>shift4;
563 }
564 dest[x] = val[0]|(val[1]<<4)|(val[2]<<8)|(val[3]<<12);
565 }
566 }
567 }
568 else
569 {
570 for( y=0; y<height; y++)
571 {
572 dest = pdata+y*pitch;
573 if( y>0 )
574 {
575 src1 = pcopy+(y-1)*pitch;
576 src2 = src1 + pitch;
577 }
578 else
579 {
580 src1 = src2 = pcopy;
581 }
582
583 src3 = src2;
584 if( y<height-1) src3 += pitch;
585
586 for( x=1; x<width-1; x++)
587 {
588 for( z=0; z<4; z++ )
589 {
590 uint32 shift = (z&1)?4:0;
591 t1 = (*((uint8*)(src1+x-1)+(z>>1)))>>shift;
592 t2 = (*((uint8*)(src1+x )+(z>>1)))>>shift;
593 t3 = (*((uint8*)(src1+x+1)+(z>>1)))>>shift;
594 t4 = (*((uint8*)(src2+x-1)+(z>>1)))>>shift;
595 t5 = (*((uint8*)(src2+x )+(z>>1)))>>shift;
596 t6 = (*((uint8*)(src2+x+1)+(z>>1)))>>shift;
597 t7 = (*((uint8*)(src3+x-1)+(z>>1)))>>shift;
598 t8 = (*((uint8*)(src3+x )+(z>>1)))>>shift;
599 t9 = (*((uint8*)(src3+x+1)+(z>>1)))>>shift;
600 val[z] = ((t1+t3+t7+t9)*mul1+((t2+t4+t6+t8)*mul2)+(t5*mul3))>>shift4;
601 }
602 dest[x] = val[0]|(val[1]<<4)|(val[2]<<8)|(val[3]<<12);
603 }
604 }
605 }
606 delete [] pcopy;
607}
608
609
610void EnhanceTexture(TxtrCacheEntry *pEntry)
611{
612 if( pEntry->dwEnhancementFlag == options.textureEnhancement )
613 {
614 // The texture has already been enhanced
615 return;
616 }
617 else if( options.textureEnhancement == TEXTURE_NO_ENHANCEMENT )
618 {
619 //Texture enhancement has being turned off
620 //Delete any allocated memory for the enhanced texture
621 SAFE_DELETE(pEntry->pEnhancedTexture);
622 //Set the enhancement flag so the texture wont be processed again
623 pEntry->dwEnhancementFlag = TEXTURE_NO_ENHANCEMENT;
624 return;
625 }
626
627 if( status.primitiveType != PRIM_TEXTRECT && options.bTexRectOnly )
628 {
629 return;
630 }
631
632 DrawInfo srcInfo;
633 //Start the draw update
634 if( pEntry->pTexture->StartUpdate(&srcInfo) == false )
635 {
636 //If we get here we were unable to start the draw update
637 //Delete any allocated memory for the enhanced texture
638 SAFE_DELETE(pEntry->pEnhancedTexture);
639 return;
640 }
641
642 uint32 realwidth = srcInfo.dwWidth;
643 uint32 realheight = srcInfo.dwHeight;
644 uint32 nWidth = srcInfo.dwCreatedWidth;
645 uint32 nHeight = srcInfo.dwCreatedHeight;
646
647 //Sharpen option is enabled, sharpen the texture
648 if( options.textureEnhancement == TEXTURE_SHARPEN_ENHANCEMENT || options.textureEnhancement == TEXTURE_SHARPEN_MORE_ENHANCEMENT )
649 {
650 if( pEntry->pTexture->GetPixelSize() == 4 )
651 SharpenFilter_32((uint32*)srcInfo.lpSurface, nWidth, nHeight, nWidth, options.textureEnhancement);
652 else
653 SharpenFilter_16((uint16*)srcInfo.lpSurface, nWidth, nHeight, nWidth, options.textureEnhancement);
654 pEntry->dwEnhancementFlag = options.textureEnhancement;
655 //End the draw update
656 pEntry->pTexture->EndUpdate(&srcInfo);
657 //Delete any allocated memory for the enhanced texture
658 SAFE_DELETE(pEntry->pEnhancedTexture);
659 return;
660 }
661
662 pEntry->dwEnhancementFlag = options.textureEnhancement;
663 if( options.bSmallTextureOnly )
664 {
665 if( nWidth + nHeight > 256 )
666 {
667 //End the draw update
668 pEntry->pTexture->EndUpdate(&srcInfo);
669 //Delete any data allocated for the enhanced texture
670 SAFE_DELETE(pEntry->pEnhancedTexture);
671 //Set the enhancement flag so the texture wont be processed again
672 pEntry->dwEnhancementFlag = TEXTURE_NO_ENHANCEMENT;
673 return;
674 }
675 }
676
677
678 CTexture* pSurfaceHandler = NULL;
679 if( options.textureEnhancement == TEXTURE_HQ4X_ENHANCEMENT )
680 {
681 if( nWidth + nHeight > 1024/4 )
682 {
683 // Don't enhance for large textures
684 pEntry->pTexture->EndUpdate(&srcInfo);
685 SAFE_DELETE(pEntry->pEnhancedTexture);
686 pEntry->dwEnhancementFlag = TEXTURE_NO_ENHANCEMENT;
687 return;
688 }
689 pSurfaceHandler = CDeviceBuilder::GetBuilder()->CreateTexture(nWidth*4, nHeight*4);
690 }
691 else
692 {
693 if( nWidth + nHeight > 1024/2 )
694 {
695 // Don't enhance for large textures
696 pEntry->pTexture->EndUpdate(&srcInfo);
697 SAFE_DELETE(pEntry->pEnhancedTexture);
698 pEntry->dwEnhancementFlag = TEXTURE_NO_ENHANCEMENT;
699 return;
700 }
701 pSurfaceHandler = CDeviceBuilder::GetBuilder()->CreateTexture(nWidth*2, nHeight*2);
702 }
703 DrawInfo destInfo;
704 if(pSurfaceHandler)
705 {
706 if(pSurfaceHandler->StartUpdate(&destInfo))
707 {
708 if( options.textureEnhancement == TEXTURE_2XSAI_ENHANCEMENT )
709 {
710 if( pEntry->pTexture->GetPixelSize() == 4 )
711 Super2xSaI_32((uint32*)(srcInfo.lpSurface),(uint32*)(destInfo.lpSurface), nWidth, realheight, nWidth);
712 else
713 Super2xSaI_16((uint16*)(srcInfo.lpSurface),(uint16*)(destInfo.lpSurface), nWidth, realheight, nWidth);
714 }
715 else if( options.textureEnhancement == TEXTURE_HQ2X_ENHANCEMENT )
716 {
717 if( pEntry->pTexture->GetPixelSize() == 4 )
718 {
719 hq2x_init(32);
720 hq2x_32((uint8*)(srcInfo.lpSurface), srcInfo.lPitch, (uint8*)(destInfo.lpSurface), destInfo.lPitch, nWidth, realheight);
721 }
722 else
723 {
724 hq2x_init(16);
725 hq2x_16((uint8*)(srcInfo.lpSurface), srcInfo.lPitch, (uint8*)(destInfo.lpSurface), destInfo.lPitch, nWidth, realheight);
726 }
727 }
728 else if( options.textureEnhancement == TEXTURE_LQ2X_ENHANCEMENT )
729 {
730 if( pEntry->pTexture->GetPixelSize() == 4 )
731 {
732 hq2x_init(32);
733 lq2x_32((uint8*)(srcInfo.lpSurface), srcInfo.lPitch, (uint8*)(destInfo.lpSurface), destInfo.lPitch, nWidth, realheight);
734 }
735 else
736 {
737 hq2x_init(16);
738 lq2x_16((uint8*)(srcInfo.lpSurface), srcInfo.lPitch, (uint8*)(destInfo.lpSurface), destInfo.lPitch, nWidth, realheight);
739 }
740 }
741 else if( options.textureEnhancement == TEXTURE_HQ4X_ENHANCEMENT )
742 {
743 if( pEntry->pTexture->GetPixelSize() == 4 )
744 {
745 hq4x_InitLUTs();
746 hq4x_32((uint8*)(srcInfo.lpSurface), (uint8*)(destInfo.lpSurface), realwidth, realheight, nWidth, destInfo.lPitch);
747 }
748 else
749 {
750 hq4x_InitLUTs();
751 hq4x_16((uint8*)(srcInfo.lpSurface), (uint8*)(destInfo.lpSurface), realwidth, realheight, nWidth, destInfo.lPitch);
752 }
753 }
754 else
755 {
756 if( pEntry->pTexture->GetPixelSize() == 4 )
757 Texture2x_32( srcInfo, destInfo);
758 else
759 Texture2x_16( srcInfo, destInfo);
760 }
761
762 if( options.textureEnhancementControl >= TEXTURE_ENHANCEMENT_WITH_SMOOTH_FILTER_1 )
763 {
764 if( options.textureEnhancement != TEXTURE_HQ4X_ENHANCEMENT )
765 {
766 if( pEntry->pTexture->GetPixelSize() == 4 )
767 SmoothFilter_32((uint32*)destInfo.lpSurface, realwidth<<1, realheight<<1, nWidth<<1, options.textureEnhancementControl);
768 else
769 SmoothFilter_16((uint16*)destInfo.lpSurface, realwidth<<1, realheight<<1, nWidth<<1, options.textureEnhancementControl);
770 }
771 else
772 {
773 if( pEntry->pTexture->GetPixelSize() == 4 )
774 SmoothFilter_32((uint32*)destInfo.lpSurface, realwidth<<2, realheight<<2, nWidth<<2, options.textureEnhancementControl);
775 else
776 SmoothFilter_16((uint16*)destInfo.lpSurface, realwidth<<2, realheight<<2, nWidth<<2, options.textureEnhancementControl);
777 }
778 }
779
780 pSurfaceHandler->EndUpdate(&destInfo);
781 }
782
783 pSurfaceHandler->SetOthersVariables();
784 pSurfaceHandler->m_bIsEnhancedTexture = true;
785 }
786
787 pEntry->pTexture->EndUpdate(&srcInfo);
788
789 pEntry->pEnhancedTexture = pSurfaceHandler;
790}
791
792
793/************************************************************************/
794/* */
795/************************************************************************/
796void MirrorEmulator_DrawLine(DrawInfo& destInfo, DrawInfo& srcInfo, uint32 *pSource, uint32 *pDest, uint32 nWidth, BOOL bFlipLeftRight)
797{
798 if(!bFlipLeftRight)
799 {
800 memcpy(pDest, pSource, nWidth * 4);
801 }
802 else
803 {
804 uint32 *pMaxDest = pDest + nWidth;
805 pSource += nWidth - 1;
806 for(; pDest < pMaxDest; pDest++, pSource--)
807 {
808 *pDest = *pSource;
809 }
810 }
811}
812
813
814void MirrorEmulator_Draw(DrawInfo& destInfo, DrawInfo& srcInfo, uint32 nDestX, uint32 nDestY, BOOL bFlipLeftRight, BOOL bFlipUpDown)
815{
816 uint8 *pDest = (uint8 *) destInfo.lpSurface + (destInfo.lPitch * nDestY) + (4 * nDestX);
817 uint8 *pMaxDest = pDest + (destInfo.lPitch * srcInfo.dwHeight);
818 uint8 *pSource = (uint8 *)(srcInfo.lpSurface);
819 if(!bFlipUpDown)
820 {
821 for(; pDest < pMaxDest; pDest += destInfo.lPitch, pSource += srcInfo.lPitch)
822 {
823 MirrorEmulator_DrawLine(destInfo, srcInfo, (uint32*)pSource, (uint32*)pDest, srcInfo.dwWidth, bFlipLeftRight);
824 }
825 }
826 else
827 {
828 pSource += (srcInfo.lPitch * (srcInfo.dwHeight - 1));
829 for(; pDest < pMaxDest; pDest += destInfo.lPitch, pSource -= srcInfo.lPitch)
830 {
831 MirrorEmulator_DrawLine(destInfo, srcInfo, (uint32*)pSource, (uint32*)pDest, srcInfo.dwWidth, bFlipLeftRight);
832 }
833 }
834}
835
836void MirrorTexture(uint32 dwTile, TxtrCacheEntry *pEntry)
837{
838 if( ((gRDP.tiles[dwTile].bMirrorS) || (gRDP.tiles[dwTile].bMirrorT)) && CGraphicsContext::Get()->m_supportTextureMirror == false )
839 {
840 if(pEntry->pEnhancedTexture)
841 {
842 return;
843 }
844 else
845 {
846 CTexture* pSurfaceHandler = NULL;
847
848 // FIXME: Compute the correct values. 2/2 seems to always work correctly in Mario64
849 uint32 nXTimes = gRDP.tiles[dwTile].bMirrorS ? 2 : 1;
850 uint32 nYTimes = gRDP.tiles[dwTile].bMirrorT ? 2 : 1;
851
852 // For any texture need to use mirror, we should not need to rescale it
853 // because texture need to be mirrored must with MaskS and MaskT
854
855 // But again, check me
856
857 //if( pEntry->pTexture->m_bScaledS == false || pEntry->pTexture->m_bScaledT == false)
858 //{
859 // pEntry->pTexture->ScaleImageToSurface();
860 //}
861
862 DrawInfo srcInfo;
863 if( pEntry->pTexture->StartUpdate(&srcInfo) )
864 {
865 uint32 nWidth = srcInfo.dwWidth;
866 uint32 nHeight = srcInfo.dwHeight;
867
868 pSurfaceHandler = CDeviceBuilder::GetBuilder()->CreateTexture(nWidth * nXTimes, nHeight * nYTimes);
869 if( pSurfaceHandler )
870 {
871 DrawInfo destInfo;
872 if( pSurfaceHandler->StartUpdate(&destInfo) )
873 {
874 for(uint32 nY = 0; nY < nYTimes; nY++)
875 {
876 for(uint32 nX = 0; nX < nXTimes; nX++)
877 {
878 MirrorEmulator_Draw(destInfo, srcInfo, nWidth * nX, nHeight * nY, nX & 0x1, nY & 0x1);
879 }
880 }
881
882 pSurfaceHandler->EndUpdate(&destInfo);
883 }
884
885 // FIXME: There should be a flag to tell that it is a mirrored texture handler
886 // not the original texture handlers, so all texture coordinate should be divided by 2
887 pSurfaceHandler->SetOthersVariables();
888 }
889
890 pEntry->pTexture->EndUpdate(&srcInfo);
891 pEntry->dwEnhancementFlag = TEXTURE_MIRRORED;
892 }
893
894
895 pEntry->pEnhancedTexture = pSurfaceHandler;
896 }
897 }
898}
899/****
900 All code bellow, CLEAN ME
901****/
902
903enum TextureType
904{
905 NO_TEXTURE,
906 RGB_PNG,
907 COLOR_INDEXED_BMP,
908 RGB_WITH_ALPHA_TOGETHER_PNG,
909 RGBA_PNG_FOR_CI,
910 RGBA_PNG_FOR_ALL_CI,
911};
912typedef struct {
913 unsigned int width;
914 unsigned int height;
915 int fmt;
916 int siz;
917 int crc32;
918 int pal_crc32;
919 char *foldername;
920 char *filename;
921 char *filename_a;
922 //char name[40];
923 TextureType type;
924 bool bSeparatedAlpha;
925} ExtTxtrInfo;
926
927CSortedList<uint64,ExtTxtrInfo> gTxtrDumpInfos;
928CSortedList<uint64,ExtTxtrInfo> gHiresTxtrInfos;
929
930extern char * right(const char * src, int nchars);
931
932#define SURFFMT_P8 41
933
934int GetImageInfoFromFile(char* pSrcFile, IMAGE_INFO *pSrcInfo)
935{
936 unsigned char sig[8];
937 FILE *f;
938
939 f = fopen(pSrcFile, "rb");
940 if (f == NULL)
941 {
942 DebugMessage(M64MSG_ERROR, "GetImageInfoFromFile() error: couldn't open file '%s'", pSrcFile);
943 return 1;
944 }
945 if (fread(sig, 1, 8, f) != 8)
946 {
947 DebugMessage(M64MSG_ERROR, "GetImageInfoFromFile() error: couldn't read first 8 bytes of file '%s'", pSrcFile);
948 fclose(f);
949 return 1;
950 }
951 fclose(f);
952
953 if(sig[0] == 'B' && sig[1] == 'M') // BMP
954 {
955 struct BMGImageStruct img;
956 memset(&img, 0, sizeof(BMGImageStruct));
957 BMG_Error code = ReadBMP(pSrcFile, &img);
958 if( code == BMG_OK )
959 {
960 pSrcInfo->Width = img.width;
961 pSrcInfo->Height = img.height;
962 pSrcInfo->Depth = img.bits_per_pixel;
963 pSrcInfo->MipLevels = 1;
964 if(img.bits_per_pixel == 32)
965 pSrcInfo->Format = SURFFMT_A8R8G8B8;
966 else if(img.bits_per_pixel == 8)
967 pSrcInfo->Format = SURFFMT_P8;
968 // Resource and File Format ignored
969 FreeBMGImage(&img);
970 return 0;
971 }
972 DebugMessage(M64MSG_ERROR, "Couldn't read BMP file '%s'; error = %i", pSrcFile, code);
973 return 1;
974 }
975 else if(sig[0] == 137 && sig[1] == 'P' && sig[2] == 'N' && sig[3] == 'G' && sig[4] == '\r' && sig[5] == '\n' &&
976 sig[6] == 26 && sig[7] == '\n') // PNG
977 {
978 struct BMGImageStruct img;
979 memset(&img, 0, sizeof(BMGImageStruct));
980 BMG_Error code = ReadPNGInfo(pSrcFile, &img);
981 if( code == BMG_OK )
982 {
983 pSrcInfo->Width = img.width;
984 pSrcInfo->Height = img.height;
985 pSrcInfo->Depth = img.bits_per_pixel;
986 pSrcInfo->MipLevels = 1;
987 if(img.bits_per_pixel == 32)
988 pSrcInfo->Format = SURFFMT_A8R8G8B8;
989 else if(img.bits_per_pixel == 8)
990 pSrcInfo->Format = SURFFMT_P8;
991 // Resource and File Format ignored
992 FreeBMGImage(&img);
993 return 0;
994 }
995 DebugMessage(M64MSG_ERROR, "Couldn't read PNG file '%s'; error = %i", pSrcFile, code);
996 return 1;
997 }
998
999 DebugMessage(M64MSG_ERROR, "GetImageInfoFromFile : unknown file format (%s)", pSrcFile);
1000 return 1;
1001}
1002
1003BOOL PathFileExists(char* pszPath)
1004{
1005 FILE *f;
1006 f = fopen(pszPath, "rb");
1007 if(f != NULL)
1008 {
1009 fclose(f);
1010 return TRUE;
1011 }
1012 return FALSE;
1013}
1014
1015/********************************************************************************************************************
1016 * Truncates the current list with information about hires textures and scans the hires folder for hires textures and
1017 * creates a list with records of properties of the hires textures.
1018 * parameter:
1019 * foldername: the folder that should be scaned for valid hires textures.
1020 * infos: a pointer that will point to the list containing the records with the infos about the found hires textures.
1021 * In case of enabled caching, these records will also contain the actual textures.
1022 * extraCheck: ?
1023 * bRecursive: flag that indicates if also subfolders should be scanned for hires textures
1024 * bCacheTextures: flag that indicates if the identified hires textures should also be cached
1025 * bMainFolder: indicates if the folder is the main folder that will be scanned. That way, texture counting does not
1026 * start at 1 each time a subfolder is accessed. (microdev: I know that is not important but it really
1027 * bugged me ;-))
1028 * return:
1029 * infos: the list with the records of the identified hires textures. Be aware that these records also contains the
1030 * actual textures if caching is enabled.
1031 ********************************************************************************************************************/
1032void FindAllTexturesFromFolder(char *foldername, CSortedList<uint64,ExtTxtrInfo> &infos, bool extraCheck, bool bRecursive)
1033{
1034 // check if folder actually exists
1035 if (!osal_is_directory(foldername))
1036 return;
1037
1038 // the path of the texture
1039 char texturefilename[PATH_MAX];
1040 //
1041 IMAGE_INFO imgInfo;
1042 //
1043 IMAGE_INFO imgInfo2;
1044
1045 void *dir;
1046 dir = osal_search_dir_open(foldername);
1047 const char *foundfilename;
1048
1049 int crc, palcrc32;
1050 unsigned int fmt, siz;
1051 char crcstr[16], crcstr2[16];
1052
1053 do
1054 {
1055 foundfilename = osal_search_dir_read_next(dir);
1056
1057 // The array is empty, break the current operation
1058 if (foundfilename == NULL)
1059 break;
1060 // The current file is a hidden one
1061 if (foundfilename[0] == '.' )
1062 // These files we don't need
1063 continue;
1064
1065 // Get the folder name
1066 strcpy(texturefilename, foldername);
1067 // And append the file name
1068 strcat(texturefilename, foundfilename);
1069
1070 // Check if the current file is a directory and if recursive scanning is enabled
1071 if (osal_is_directory(texturefilename) && bRecursive )
1072 {
1073 // Add file-separator
1074 strcat(texturefilename, OSAL_DIR_SEPARATOR_STR);
1075 // Scan detected folder for hires textures (recursive call)
1076 FindAllTexturesFromFolder(texturefilename, infos, extraCheck, bRecursive);
1077 continue;
1078 }
1079 // well, the current file is actually no file (probably a directory & recursive scanning is not enabled)
1080 if( strstr(foundfilename,(const char*)g_curRomInfo.szGameName) == 0 )
1081 // go on with the next one
1082 continue;
1083
1084 TextureType type = NO_TEXTURE;
1085 bool bSeparatedAlpha = false;
1086
1087 // Detect the texture type by it's extention
1088 // microdev: this is not the smartest way. Should be done by header analysis if possible
1089 if( strcasecmp(right(foundfilename,7), "_ci.bmp") == 0 )
1090 {
1091 // Identify type
1092 if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0)
1093 {
1094 DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename);
1095 continue;
1096 }
1097
1098 if( imgInfo.Format == SURFFMT_P8 )
1099 // and store it to the record
1100 type = COLOR_INDEXED_BMP;
1101 else
1102 // Type is not supported, go on with the next one
1103 continue;
1104 }
1105 // Detect the texture type by its extention
1106 else if( strcasecmp(right(foundfilename,13), "_ciByRGBA.png") == 0 )
1107 {
1108 // Identify type
1109 if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0 )
1110 {
1111 DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename);
1112 continue;
1113 }
1114
1115 if( imgInfo.Format == SURFFMT_A8R8G8B8 )
1116 // and store it to the record
1117 type = RGBA_PNG_FOR_CI;
1118 else
1119 // Type is not supported, go on with the next one
1120 continue;
1121 }
1122 // Detect the texture type by its extention
1123 else if( strcasecmp(right(foundfilename,16), "_allciByRGBA.png") == 0 )
1124 {
1125 // Identify type
1126 if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0 )
1127 {
1128 DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename);
1129 continue;
1130 }
1131 if( imgInfo.Format == SURFFMT_A8R8G8B8 )
1132 // and store it to the record
1133 type = RGBA_PNG_FOR_ALL_CI;
1134 else
1135 // Type not supported, go on with next one
1136 continue;
1137 }
1138 // Detect the texture type by its extention
1139 else if( strcasecmp(right(foundfilename,8), "_rgb.png") == 0 )
1140 {
1141 // Identify type
1142 if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0 )
1143 {
1144 DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename);
1145 continue;
1146 }
1147
1148 // Store type to the record
1149 type = RGB_PNG;
1150
1151 char filename2[PATH_MAX];
1152 // Assemble the file name for the separate alpha channel file
1153 strcpy(filename2,texturefilename);
1154 strcpy(filename2+strlen(filename2)-8,"_a.png");
1155 // Check if the file actually exists
1156 if( PathFileExists(filename2) )
1157 {
1158 // Check if the file with this name is actually a texture (well an alpha channel one)
1159 if( GetImageInfoFromFile(filename2, &imgInfo2) != 0 )
1160 {
1161 // Nope, it isn't. => Go on with the next file
1162 DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", filename2);
1163 continue;
1164 }
1165
1166 // Yes it is a texture file. Check if the size of the alpha channel is the same as the one of the texture
1167 if( extraCheck && (imgInfo2.Width != imgInfo.Width || imgInfo2.Height != imgInfo.Height) )
1168 {
1169 // Nope, it isn't => go on with next file
1170 DebugMessage(M64MSG_WARNING, "RGB and alpha texture size mismatch: %s", filename2);
1171 continue;
1172 }
1173
1174 bSeparatedAlpha = true;
1175 }
1176 }
1177 // Detect the texture type by its extention
1178 else if( strcasecmp(right(foundfilename,8), "_all.png") == 0 )
1179 {
1180 // Check if texture is of expected type
1181 if( GetImageInfoFromFile(texturefilename, &imgInfo) != 0 )
1182 {
1183 DebugMessage(M64MSG_WARNING, "Cannot get image info for file: %s", foundfilename);
1184 // Nope, continue with next file
1185 continue;
1186 }
1187
1188 // Indicate file type
1189 type = RGB_WITH_ALPHA_TOGETHER_PNG;
1190 }
1191
1192 // If a known texture format has been detected...
1193 if( type != NO_TEXTURE )
1194 {
1195 /*
1196 Try to read image information here.
1197
1198 (CASTLEVANIA2)#(58E2333F)#(2)#(0)#(D7A5C6D9)_ciByRGBA.png
1199 (------1-----)#(---2----)#(3)#(4)#(---5----)_ciByRGBA.png
1200
1201 1. Internal ROM name
1202 2. The DRAM CRC
1203 3. The image pixel size (8b=0, 16b=1, 24b=2, 32b=3)
1204 4. The texture format (RGBA=0, YUV=1, CI=2, IA=3, I=4)
1205 5. The palette CRC
1206
1207 <internal Rom name>#<DRAM CRC>#<24bit>#<RGBA>#<PAL CRC>_ciByRGBA.png
1208 */
1209
1210 // Get the actual file name
1211 strcpy(texturefilename, foundfilename);
1212 // Place the pointer before the DRAM-CRC (first occurrence of '#')
1213 char *ptr = strchr(texturefilename,'#');
1214 // Terminate the string ('0' means end of string - or in this case begin of string)
1215 *ptr++ = 0;
1216 if( type == RGBA_PNG_FOR_CI )
1217 {
1218 // Extract the information from the file name; information is:
1219 // <DRAM(or texture)-CRC><texture type><texture format><PAL(or palette)-CRC>
1220 sscanf(ptr,"%8c#%d#%d#%8c", crcstr, &fmt, &siz,crcstr2);
1221 // Terminate the ascii represntation of the palette crc
1222 crcstr2[8] = 0;
1223 // Transform the ascii presentation of the hex value to an unsigned integer
1224 palcrc32 = strtoul(crcstr2,NULL,16);
1225 }
1226 else
1227 {
1228 // Extract the information from the file name - this file does not have a palette crc; information is:
1229 // <DRAM(or texture)-CRC><texture type><texture format>
1230 // o gosh, commenting source code is really boring - but necessary!! Thus do it! (and don't use drugs ;-))
1231 sscanf(ptr,"%8c#%d#%d", crcstr, &fmt, &siz);
1232 // Use dummy for palette crc - that way each texture can be handled in a heterogeneous way
1233 palcrc32 = 0xFFFFFFFF;
1234 }
1235 // Terminate the ascii represntation of the texture crc
1236 crcstr[8]=0;
1237 // Transform the ascii presentation of the hex value to an unsigned integer
1238 crc = strtoul(crcstr,NULL,16);
1239 // For the detection of an existing item
1240
1241 int foundIdx = -1;
1242 for( int k=0; k<infos.size(); k++)
1243 {
1244 // Check if texture already exists in the list
1245 // microdev: that's why I somehow love documenting code: that makes the implementation of a WIP folder check
1246 // fucking easy :-)
1247 if( infos[k].crc32 == crc && infos[k].pal_crc32 == palcrc32 )
1248 {
1249 // Indeeed, the texture already exists
1250 // microdev: MAYBE ADD CODE TO MOVE IT TO A 'DUBLICATE' FOLDER TO EASE WORK OF RETEXTURERS
1251 foundIdx = k;
1252 break;
1253 }
1254 }
1255
1256 if( foundIdx < 0 || type != infos[foundIdx].type)
1257 {
1258 // Create a new entry
1259 ExtTxtrInfo newinfo;
1260 // Store the width
1261 newinfo.width = imgInfo.Width;
1262 // Store the height
1263 newinfo.height = imgInfo.Height;
1264 // Store the name of the folder it has been found in
1265 //strcpy(newinfo.name,g_curRomInfo.szGameName);
1266 newinfo.foldername = new char[strlen(foldername)+1];
1267 strcpy(newinfo.foldername,foldername);
1268 // store the filename
1269 newinfo.filename = strdup(foundfilename);
1270 newinfo.filename_a = NULL;
1271 // Store the format
1272 newinfo.fmt = fmt;
1273 // Store the size (bit-size, not texture size)
1274 newinfo.siz = siz;
1275 // Store DRAM (texture) CRC
1276 newinfo.crc32 = crc;
1277 // Store PAL (palette) CRC (the actual one, or the dummy value ('FFFFFFFF'))
1278 newinfo.pal_crc32 = palcrc32;
1279 // Store the texture type
1280 newinfo.type = type;
1281 //Indicate if there is a separate alpha file that has to be loaded
1282 newinfo.bSeparatedAlpha = bSeparatedAlpha;
1283 if (bSeparatedAlpha) {
1284 char filename2[PATH_MAX];
1285 strcpy(filename2, foundfilename);
1286 strcpy(filename2+strlen(filename2)-8,"_a.png");
1287 newinfo.filename_a = strdup(filename2);
1288 }
1289 // Generate the key for the record describing the hires texture.
1290 // This key is used to find it back in the list
1291 // The key format is: <DRAM(texture)-CRC-8byte><PAL(palette)-CRC-6byte(2bytes have been truncated to have space for format and size)><format-1byte><size-1byte>
1292 uint64 crc64 = newinfo.crc32;
1293 crc64 <<= 32;
1294 if (options.bLoadHiResCRCOnly)
1295 crc64 |= newinfo.pal_crc32&0xFFFFFFFF;
1296 else
1297 crc64 |= (newinfo.pal_crc32&0xFFFFFF00)|(newinfo.fmt<<4)|newinfo.siz;
1298 // Add the new record to the list
1299 infos.add(crc64,newinfo);
1300 }
1301 }
1302 } while(foundfilename != NULL);
1303
1304 osal_search_dir_close(dir);
1305}
1306/********************************************************************************************************************
1307 * Checks if a folder is actually existant. If not, it tries to create this folder
1308 * parameter:
1309 * pathname: the name of the folder that should be checked or created if not existant
1310 * return:
1311 * return value: flag that indicates true if the folder is existant or could be created. If none was the case,
1312 * false will be returned
1313 ********************************************************************************************************************/
1314
1315bool CheckAndCreateFolder(const char* pathname)
1316{
1317 // Check if provided folder already exists
1318 if( !PathFileExists((char*)pathname) )
1319 {
1320 // It didn't. Try creating it.
1321 if (osal_mkdirp(pathname, 0700) != 0)
1322 {
1323 // It didn't work (probably insufficient permissions or read-only media) ==> return false
1324 DebugMessage(M64MSG_WARNING, "Can not create new folder: %s", pathname);
1325 return false;
1326 }
1327 }
1328 // success
1329
1330 return true;
1331}
1332// microdev: THIS HAS TO BE CLEANED UP...
1333
1334
1335// Texture dumping filenaming
1336// GameName_FrameCount_CRC_Fmt_Siz.bmp
1337// File format: BMP
1338// GameName: N64 game internal name
1339// CRC: 32 bit, 8 hex digits
1340// Fmt: 0 - 4
1341// Siz: 0 - 3
1342
1343const char *subfolders[] = {
1344 "png_all",
1345 "png_by_rgb_a",
1346 "ci_bmp",
1347 "ci_bmp_with_pal_crc",
1348 "ci_by_png",
1349};
1350
1351void FindAllDumpedTextures(void)
1352{
1353 char foldername[PATH_MAX + 64];
1354 strncpy(foldername, ConfigGetUserDataPath(), PATH_MAX);
1355 foldername[PATH_MAX] = 0;
1356
1357 if (foldername[strlen(foldername) - 1] != OSAL_DIR_SEPARATOR_CHAR)
1358 strcat(foldername, OSAL_DIR_SEPARATOR_STR);
1359 strcat(foldername,"texture_dump" OSAL_DIR_SEPARATOR_STR);
1360
1361 CheckAndCreateFolder(foldername);
1362
1363 strcat(foldername,(const char*)g_curRomInfo.szGameName);
1364 strcat(foldername, OSAL_DIR_SEPARATOR_STR);
1365
1366 gTxtrDumpInfos.clear();
1367 if( !PathFileExists(foldername) )
1368 {
1369 CheckAndCreateFolder(foldername);
1370 char foldername2[PATH_MAX];
1371 for( int i=0; i<5; i++)
1372 {
1373 strcpy(foldername2,foldername);
1374 strcat(foldername2,subfolders[i]);
1375 CheckAndCreateFolder(foldername2);
1376 }
1377 return;
1378 }
1379 else
1380 {
1381 gTxtrDumpInfos.clear();
1382 FindAllTexturesFromFolder(foldername,gTxtrDumpInfos, false, true);
1383
1384 char foldername2[PATH_MAX];
1385 for( int i=0; i<5; i++)
1386 {
1387 strcpy(foldername2,foldername);
1388 strcat(foldername2,subfolders[i]);
1389 CheckAndCreateFolder(foldername2);
1390 }
1391 }
1392}
1393
1394/********************************************************************************************************************
1395 * Truncates the current list with information about hires textures and scans the hires folder for hires textures and
1396 * creates a list with records of properties of the hires textures.
1397 * parameter:
1398 * none
1399 * return:
1400 * none
1401 ********************************************************************************************************************/
1402void FindAllHiResTextures(void)
1403{
1404 char foldername[PATH_MAX + 64];
1405 strncpy(foldername, ConfigGetUserDataPath(), PATH_MAX);
1406 foldername[PATH_MAX] = 0;
1407
1408 // Assure that a backslash exists at the end (should be handled by GetPluginDir())
1409 if(foldername[strlen(foldername) - 1] != OSAL_DIR_SEPARATOR_CHAR)
1410 strcat(foldername, OSAL_DIR_SEPARATOR_STR);
1411 // Add the relative path to the hires folder
1412 strcat(foldername,"hires_texture" OSAL_DIR_SEPARATOR_STR);
1413 // It does not exist? => Create it
1414 CheckAndCreateFolder(foldername);
1415
1416 // Add the path to a sub-folder corresponding to the rom name
1417 // HOOK IN: PACK SELECT
1418 strcat(foldername,(const char*)g_curRomInfo.szGameName);
1419 strcat(foldername, OSAL_DIR_SEPARATOR_STR);
1420 // Truncate the current list with the hires texture info
1421 gHiresTxtrInfos.clear();
1422 if (!osal_is_directory(foldername))
1423 {
1424 DebugMessage(M64MSG_WARNING, "Couldn't open hi-res texture directory: %s", foldername);
1425 return;
1426 }
1427 else
1428 {
1429 // Find all hires textures and also cache them if configured to do so
1430 FindAllTexturesFromFolder(foldername,gHiresTxtrInfos, true, true);
1431 }
1432}
1433
1434void CloseHiresTextures(void)
1435{
1436 for( int i=0; i<gHiresTxtrInfos.size(); i++)
1437 {
1438 if( gHiresTxtrInfos[i].foldername )
1439 delete [] gHiresTxtrInfos[i].foldername;
1440 if( gHiresTxtrInfos[i].filename )
1441 delete [] gHiresTxtrInfos[i].filename;
1442 if( gHiresTxtrInfos[i].filename_a )
1443 delete [] gHiresTxtrInfos[i].filename_a;
1444 }
1445
1446 gHiresTxtrInfos.clear();
1447}
1448
1449void CloseTextureDump(void)
1450{
1451 for( int i=0; i<gTxtrDumpInfos.size(); i++)
1452 {
1453 if( gTxtrDumpInfos[i].foldername )
1454 delete [] gTxtrDumpInfos[i].foldername;
1455 if( gTxtrDumpInfos[i].filename )
1456 delete [] gTxtrDumpInfos[i].filename;
1457 if( gTxtrDumpInfos[i].filename_a )
1458 delete [] gTxtrDumpInfos[i].filename_a;
1459 }
1460
1461 gTxtrDumpInfos.clear();
1462}
1463
1464void CloseExternalTextures(void)
1465{
1466 CloseHiresTextures();
1467 CloseTextureDump();
1468}
1469
1470/********************************************************************************************************************
1471 * Scans the hires folder for hires textures and creates a list with records of properties of the hires textures.
1472 * in case of enabled hires caching also the actual hires textures will be added to the record. Before textures will
1473 * be loaded, existing list of texture information will be truncated.
1474 * parameter:
1475 * bWIPFolder: Indicates if all textures should be inited or just the WIP folder. Just the content of the WIP folder
1476 * will be reloaded if a savestate has been loaded or if there has been a switch between window and full-
1477 * screen mode. (Not implemented yet)
1478 * return:
1479 * none
1480 ********************************************************************************************************************/
1481void InitHiresTextures(void)
1482{
1483 if( options.bLoadHiResTextures )
1484 {
1485 DebugMessage(M64MSG_INFO, "Texture loading option is enabled. Finding all hires textures");
1486 FindAllHiResTextures();
1487 }
1488}
1489
1490void InitTextureDump(void)
1491{
1492 if( options.bDumpTexturesToFiles )
1493 {
1494 DebugMessage(M64MSG_INFO, "Texture dump option is enabled. Finding all dumpped textures");
1495 FindAllDumpedTextures();
1496 }
1497}
1498
1499/********************************************************************************************************************
1500 * Inits the hires textures. For doing so, all hires textures info & the cached textures (for dumping and the hires ones)
1501 * are deleted. Afterwards they are reloaded from file system. This only takes place if a new rom has been loaded.
1502 * parameter:
1503 * none
1504 * return:
1505 * none
1506 ********************************************************************************************************************/
1507void InitExternalTextures(void)
1508{
1509 DebugMessage(M64MSG_VERBOSE, "InitExternalTextures");
1510 // remove all hires & dump textures from cache
1511 CloseExternalTextures();
1512 // reload and recache hires textures
1513 InitHiresTextures();
1514 // prepare list of already dumped textures (for avoiding to redump them). Available hires textures will
1515 // also be excluded from dumping
1516 InitTextureDump();
1517}
1518
1519/********************************************************************************************************************
1520 * Determines the scale factor for resizing the original texture to the hires replacement. The scale factor is a left
1521 * shift. That means scale factor 1 = size(original texture)*2= size(hires texture),
1522 * factor 2 = size(original texture)*4= size(hires texture), etc. (I'm not yet sure why it has to be 2^x. Most probably
1523 * because of block size. Has to be further determined.
1524 * parameter:
1525 * info: the record describing the external texture
1526 * entry: the original texture in the texture cache
1527 * return:
1528 * info.scaleShift: the value for left shift the original texture size to the corresponding hires texture size
1529 * return value: the value for left shift the original texture size to the corresponding hires texture size.
1530 * The function returns -1 if the dimensions of the hires texture are not a power of two of the
1531 * original texture.
1532 ********************************************************************************************************************/
1533int FindScaleFactor(const ExtTxtrInfo &info, TxtrCacheEntry &entry)
1534{
1535 // init scale shift
1536 int scaleShift = 0;
1537 // check if the original texture dimensions (x and y) scaled with the current shift is still smaller or of the same size as the hires one
1538 while(info.height >= entry.ti.HeightToLoad*(1<<scaleShift) && info.width >= entry.ti.WidthToLoad*(1<<scaleShift))
1539 {
1540 // check if the original texture dimensions (x and y)scaled with the current shift have the same size as the hires one
1541 if(info.height == entry.ti.HeightToLoad*(1<<scaleShift) && info.width == entry.ti.WidthToLoad*(1<<scaleShift))
1542 // found appropriate scale shift, return it
1543 return scaleShift;
1544
1545 scaleShift++;
1546 }
1547
1548 // original texture dimensions (x or y or both) scaled with the last scale shift have become larger than the dimensions
1549 // of the hires texture. That means the dimensions of the hires replacement are not power of 2 of the original texture.
1550 // Therefore indicate a crop shift (or -1 when the hires_texture was smaller from the beginning)
1551 scaleShift -= 1;
1552 return scaleShift;
1553}
1554
1555
1556/********************************************************************************************************************
1557 * Checks if a hires replacement for a texture is available.
1558 * parameter:
1559 * infos: The list of external textures
1560 * entry: the original texture in the texture cache
1561 * return:
1562 * indexa: returns the index in "infos" where a hires replacement for a texture without
1563 * palette crc or a RGBA_PNG_FOR_ALL_CI texture has been found
1564 * return value: the index in "infos" where the corresponding hires texture has been found
1565 ********************************************************************************************************************/
1566int CheckTextureInfos( CSortedList<uint64,ExtTxtrInfo> &infos, TxtrCacheEntry &entry, int &indexa, int &scaleShift, bool bForDump = false)
1567{
1568 if ((entry.ti.WidthToLoad != 0 && entry.ti.WidthToCreate / entry.ti.WidthToLoad > 2) ||
1569 (entry.ti.HeightToLoad != 0 && entry.ti.HeightToCreate / entry.ti.HeightToLoad > 2 ))
1570 {
1571 //DebugMessage(M64MSG_WARNING, "Hires texture does not support extreme texture replication");
1572 return -1;
1573 }
1574 // determine if texture is a color-indexed (CI) texture
1575
1576 bool bCI = (gRDP.otherMode.text_tlut>=2 || entry.ti.Format == TXT_FMT_CI || entry.ti.Format == TXT_FMT_RGBA) && entry.ti.Size <= TXT_SIZE_8b;
1577 // generate two alternative ids
1578
1579 uint64 crc64a = entry.dwCRC;
1580 crc64a <<= 32;
1581 uint64 crc64b = crc64a;
1582 if (options.bLoadHiResCRCOnly) {
1583 crc64a |= (0xFFFFFFFF);
1584 crc64b |= (entry.dwPalCRC&0xFFFFFFFF);
1585 } else {
1586 crc64a |= (0xFFFFFF00|(entry.ti.Format<<4)|entry.ti.Size);
1587 crc64b |= ((entry.dwPalCRC&0xFFFFFF00)|(entry.ti.Format<<4)|entry.ti.Size);
1588 }
1589
1590 // infos is the list containing the references to the detected external textures
1591 // get the number of items contained in this list
1592 int infosize = infos.size();
1593 int indexb=-1;
1594 // try to identify the external texture that
1595 // corresponds to the original texture
1596 indexa = infos.find(crc64a); // For CI without pal CRC, and for RGBA_PNG_FOR_ALL_CI
1597 if( bCI )
1598 // and also for textures with separate alpha channel
1599 indexb = infos.find(crc64b); // For CI or PNG with pal CRC
1600
1601 // did not found the ext. text.
1602 if( indexa >= infosize )
1603 indexa = -1;
1604 // did not found the ext. text. w/ sep. alpha channel
1605 if( indexb >= infosize )
1606 indexb = -1;
1607
1608 scaleShift = -1;
1609
1610 // found texture with sep. alpha channel
1611
1612 if( indexb >= 0 )
1613 {
1614 // determine the factor for scaling
1615 scaleShift = FindScaleFactor(infos[indexb], entry);
1616 // ok. the scale factor is supported. A valid replacement has been found
1617 if( scaleShift >= 0 )
1618 return indexb;
1619 }
1620 // if texture is 4bit, should be dumped and there is no match in the list of external textures
1621
1622 if( bForDump && bCI && indexb < 0)
1623 // than return that there is no replacement & therefore texture can be dumped (microdev: not sure about that...)
1624 return -1;
1625
1626 // texture has no separate alpha channel, try to find it in the ext. text. list
1627 if( indexa >= 0 )
1628 scaleShift = FindScaleFactor(infos[indexa], entry);
1629 // ok. the scale factor is supported. A valid replacement has been found
1630 // this is a texture without ext. alpha channel
1631
1632 if( scaleShift >= 0 )
1633 return indexa;
1634 // no luck at all. there is no valid replacement
1635 else
1636 return -1;
1637}
1638
1639bool SaveCITextureToFile(TxtrCacheEntry &entry, char *filename, bool bShow, bool bWhole);
1640
1641void DumpCachedTexture( TxtrCacheEntry &entry )
1642{
1643 char cSep = '/';
1644
1645 CTexture *pSrcTexture = entry.pTexture;
1646 if( pSrcTexture )
1647 {
1648 // Check the vector table
1649 int ciidx, scaleShift;
1650 if( CheckTextureInfos(gTxtrDumpInfos,entry,ciidx,scaleShift,true) >= 0 )
1651 return; // This texture has been dumpped
1652
1653 char filename1[PATH_MAX + 64];
1654 char filename2[PATH_MAX + 64];
1655 char filename3[PATH_MAX + 64];
1656 char gamefolder[PATH_MAX + 64];
1657 strncpy(gamefolder, ConfigGetUserDataPath(), PATH_MAX);
1658 gamefolder[PATH_MAX] = 0;
1659
1660 strcat(gamefolder,"texture_dump" OSAL_DIR_SEPARATOR_STR);
1661 strcat(gamefolder,(const char*)g_curRomInfo.szGameName);
1662 strcat(gamefolder, OSAL_DIR_SEPARATOR_STR);
1663
1664 //sprintf(filename1+strlen(filename1), "%08X#%d#%d", entry.dwCRC, entry.ti.Format, entry.ti.Size);
1665 sprintf(filename1, "%s%s#%08X#%d#%d", gamefolder, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size);
1666
1667 if( (gRDP.otherMode.text_tlut>=2 || entry.ti.Format == TXT_FMT_CI || entry.ti.Format == TXT_FMT_RGBA) && entry.ti.Size <= TXT_SIZE_8b )
1668 {
1669 if( ciidx < 0 )
1670 {
1671 sprintf(filename1, "%sci_bmp%c%s#%08X#%d#%d_ci", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size);
1672 SaveCITextureToFile(entry, filename1, false, false);
1673 }
1674
1675 sprintf(filename1, "%sci_bmp_with_pal_crc%c%s#%08X#%d#%d#%08X_ci", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size,entry.dwPalCRC);
1676 SaveCITextureToFile(entry, filename1, false, false);
1677
1678 sprintf(filename1, "%sci_by_png%c%s#%08X#%d#%d#%08X_ciByRGBA", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size,entry.dwPalCRC);
1679 CRender::g_pRender->SaveTextureToFile(*pSrcTexture, filename1, TXT_RGBA, false, false, entry.ti.WidthToLoad, entry.ti.HeightToLoad);
1680 }
1681 else
1682 {
1683 sprintf(filename1, "%spng_by_rgb_a%c%s#%08X#%d#%d_rgb", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size);
1684 sprintf(filename2, "%spng_by_rgb_a%c%s#%08X#%d#%d_a", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size);
1685 sprintf(filename3, "%spng_all%c%s#%08X#%d#%d_all", gamefolder, cSep, g_curRomInfo.szGameName, entry.dwCRC, entry.ti.Format, entry.ti.Size);
1686
1687
1688 CRender::g_pRender->SaveTextureToFile(*pSrcTexture, filename1, TXT_RGB, false, false, entry.ti.WidthToLoad, entry.ti.HeightToLoad);
1689 CRender::g_pRender->SaveTextureToFile(*pSrcTexture, filename3, TXT_RGBA, false, false, entry.ti.WidthToLoad, entry.ti.HeightToLoad);
1690 if( entry.ti.Format != TXT_FMT_I )
1691 {
1692 DrawInfo srcInfo;
1693 uint32 aFF = 0xFF;
1694 if( pSrcTexture->StartUpdate(&srcInfo) )
1695 {
1696 // Copy RGB to buffer
1697 for( int i=entry.ti.HeightToLoad-1; i>=0; i--)
1698 {
1699 unsigned char *pSrc = (unsigned char*)srcInfo.lpSurface+srcInfo.lPitch * i;
1700 for( uint32 j=0; j<entry.ti.WidthToLoad; j++)
1701 {
1702 aFF &= pSrc[3];
1703 pSrc += 4;
1704 }
1705 }
1706 pSrcTexture->EndUpdate(&srcInfo);
1707 }
1708
1709 if( aFF != 0xFF)
1710 CRender::g_pRender->SaveTextureToFile(*pSrcTexture, filename2, TXT_ALPHA, false, false);
1711 }
1712 }
1713
1714 ExtTxtrInfo newinfo;
1715 newinfo.width = entry.ti.WidthToLoad;
1716 newinfo.height = entry.ti.HeightToLoad;
1717 //strcpy(newinfo.name,g_curRomInfo.szGameName);
1718 newinfo.fmt = entry.ti.Format;
1719 newinfo.siz = entry.ti.Size;
1720 newinfo.crc32 = entry.dwCRC;
1721
1722 newinfo.pal_crc32 = entry.dwPalCRC;
1723 newinfo.foldername = NULL;
1724 newinfo.filename = NULL;
1725 newinfo.filename_a = NULL;
1726 newinfo.type = NO_TEXTURE;
1727 newinfo.bSeparatedAlpha = false;
1728
1729 uint64 crc64 = newinfo.crc32;
1730 crc64 <<= 32;
1731 if (options.bLoadHiResCRCOnly)
1732 crc64 |= newinfo.pal_crc32&0xFFFFFFFF;
1733 else
1734 crc64 |= (newinfo.pal_crc32&0xFFFFFF00)|(newinfo.fmt<<4)|newinfo.siz;
1735 gTxtrDumpInfos.add(crc64,newinfo);
1736
1737 }
1738}
1739
1740bool LoadRGBBufferFromPNGFile(char *filename, unsigned char **pbuf, int &width, int &height, int bits_per_pixel = 24 )
1741{
1742 struct BMGImageStruct img;
1743 memset(&img, 0, sizeof(BMGImageStruct));
1744 if (!PathFileExists(filename))
1745 {
1746 DebugMessage(M64MSG_ERROR, "File at '%s' doesn't exist in LoadRGBBufferFromPNGFile!", filename);
1747 return false;
1748 }
1749
1750 BMG_Error code = ReadPNG( filename, &img );
1751 if( code == BMG_OK )
1752 {
1753 *pbuf = NULL;
1754
1755 *pbuf = new unsigned char[img.width*img.height*bits_per_pixel/8];
1756 if (*pbuf == NULL)
1757 {
1758 DebugMessage(M64MSG_ERROR, "new[] returned NULL for image width=%i height=%i bpp=%i", img.width, img.height, bits_per_pixel);
1759 return false;
1760 }
1761 if (img.bits_per_pixel == bits_per_pixel)
1762 {
1763 memcpy(*pbuf, img.bits, img.width*img.height*bits_per_pixel/8);
1764 }
1765 else if (img.bits_per_pixel == 24 && bits_per_pixel == 32)
1766 {
1767 unsigned char *pSrc = img.bits;
1768 unsigned char *pDst = *pbuf;
1769 for (int i = 0; i < (int)(img.width*img.height); i++)
1770 {
1771 *pDst++ = *pSrc++;
1772 *pDst++ = *pSrc++;
1773 *pDst++ = *pSrc++;
1774 *pDst++ = 0;
1775 }
1776 }
1777 // loaded image has alpha, needed image has to be without alpha channel
1778 else if (img.bits_per_pixel == 32 && bits_per_pixel == 24)
1779 {
1780 // pointer to source image data
1781 unsigned char *pSrc = img.bits;
1782 // buffer for destination image
1783 unsigned char *pDst = *pbuf;
1784 // copy data of the loaded image to the buffer by skipping the alpha byte
1785 for (int i = 0; i < (int)(img.width*img.height); i++)
1786 {
1787 // copy R
1788 *pDst++ = *pSrc++;
1789 // copy G
1790 *pDst++ = *pSrc++;
1791 // copy B
1792 *pDst++ = *pSrc++;
1793 // skip the alpha byte of the loaded image
1794 pSrc++;
1795 }
1796 }
1797 else if (img.bits_per_pixel == 8 && (bits_per_pixel == 24 || bits_per_pixel == 32))
1798 {
1799 // do palette lookup and convert 8bpp to 24/32bpp
1800 int destBytePP = bits_per_pixel / 8;
1801 int paletteBytePP = img.bytes_per_palette_entry;
1802 unsigned char *pSrc = img.bits;
1803 unsigned char *pDst = *pbuf;
1804 // clear the destination image data (just to clear alpha if bpp=32)
1805 memset(*pbuf, 0, img.width*img.height*destBytePP);
1806 for (int i = 0; i < (int)(img.width*img.height); i++)
1807 {
1808 unsigned char clridx = *pSrc++;
1809 unsigned char *palcolor = img.palette + clridx * paletteBytePP;
1810 pDst[0] = palcolor[2]; // red
1811 pDst[1] = palcolor[1]; // green
1812 pDst[2] = palcolor[0]; // blue
1813 pDst += destBytePP;
1814 }
1815 }
1816 else
1817 {
1818 DebugMessage(M64MSG_ERROR, "PNG file '%s' is %i bpp but texture is %i bpp.", filename, img.bits_per_pixel, bits_per_pixel);
1819 delete [] *pbuf;
1820 *pbuf = NULL;
1821 }
1822
1823 width = img.width;
1824 height = img.height;
1825 FreeBMGImage(&img);
1826
1827 return true;
1828 }
1829 else
1830 {
1831 DebugMessage(M64MSG_ERROR, "ReadPNG() returned error for '%s' in LoadRGBBufferFromPNGFile!", filename);
1832 *pbuf = NULL;
1833 return false;
1834 }
1835}
1836
1837bool LoadRGBABufferFromColorIndexedFile(char *filename, TxtrCacheEntry &entry, unsigned char **pbuf, int &width, int &height)
1838{
1839 BITMAPFILEHEADER fileHeader;
1840 BITMAPINFOHEADER infoHeader;
1841
1842 FILE *f;
1843 f = fopen(filename, "rb");
1844 if(f != NULL)
1845 {
1846 if (fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f) != 1 ||
1847 fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f) != 1)
1848 {
1849 DebugMessage(M64MSG_ERROR, "Couldn't read BMP headers in file '%s'", filename);
1850 return false;
1851 }
1852
1853 if( infoHeader.biBitCount != 4 && infoHeader.biBitCount != 8 )
1854 {
1855 fclose(f);
1856 DebugMessage(M64MSG_ERROR, "Unsupported BMP file format: %s", filename);
1857 *pbuf = NULL;
1858 return false;
1859 }
1860
1861 int tablesize = infoHeader.biBitCount == 4 ? 16 : 256;
1862 uint32 *pTable = new uint32[tablesize];
1863 if (fread(pTable, tablesize*4, 1, f) != 1)
1864 {
1865 DebugMessage(M64MSG_ERROR, "Couldn't read BMP palette in file '%s'", filename);
1866 delete [] pTable;
1867 return false;
1868 }
1869
1870 // Create the pallette table
1871 uint16 * pPal = (uint16 *)entry.ti.PalAddress;
1872 if( entry.ti.Size == TXT_SIZE_4b )
1873 {
1874 // 4-bit table
1875 for( int i=0; i<16; i++ )
1876 {
1877 pTable[i] = entry.ti.TLutFmt == TLUT_FMT_RGBA16 ? Convert555ToRGBA(pPal[i^1]) : ConvertIA16ToRGBA(pPal[i^1]);
1878 }
1879 }
1880 else
1881 {
1882 // 8-bit table
1883 for( int i=0; i<256; i++ )
1884 {
1885 pTable[i] = entry.ti.TLutFmt == TLUT_FMT_RGBA16 ? Convert555ToRGBA(pPal[i^1]) : ConvertIA16ToRGBA(pPal[i^1]);
1886 }
1887 }
1888
1889 *pbuf = new unsigned char[infoHeader.biWidth*infoHeader.biHeight*4];
1890 if( *pbuf )
1891 {
1892 unsigned char *colorIdxBuf = new unsigned char[infoHeader.biSizeImage];
1893 if( colorIdxBuf )
1894 {
1895 if (fread(colorIdxBuf, infoHeader.biSizeImage, 1, f) != 1)
1896 {
1897 DebugMessage(M64MSG_ERROR, "Couldn't read BMP image data in file '%s'", filename);
1898 }
1899
1900 width = infoHeader.biWidth;
1901 height = infoHeader.biHeight;
1902
1903 // Converting pallette texture to RGBA texture
1904 int idx = 0;
1905 uint32 *pbuf2 = (uint32*) *pbuf;
1906
1907 for( int i=height-1; i>=0; i--)
1908 {
1909 for( int j=0; j<width; j++)
1910 {
1911 if( entry.ti.Size == TXT_SIZE_4b )
1912 {
1913 // 4 bits
1914 if( idx%2 )
1915 {
1916 // 1
1917 *pbuf2++ = pTable[colorIdxBuf[(idx++)>>1]&0xF];
1918 }
1919 else
1920 {
1921 // 0
1922 *pbuf2++ = pTable[(colorIdxBuf[(idx++)>>1]>>4)&0xF];
1923 }
1924 }
1925 else
1926 {
1927 // 8 bits
1928 *pbuf2++ = pTable[colorIdxBuf[idx++]];
1929 }
1930 }
1931 if( entry.ti.Size == TXT_SIZE_4b )
1932 {
1933 if( idx%8 ) idx = (idx/8+1)*8;
1934 }
1935 else
1936 {
1937 if( idx%4 ) idx = (idx/4+1)*4;
1938 }
1939 }
1940
1941 delete [] colorIdxBuf;
1942 }
1943 else
1944 {
1945 TRACE0("Out of memory");
1946 }
1947
1948 delete [] pTable;
1949 return true;
1950 }
1951 else
1952 {
1953 fclose(f);
1954 delete [] pTable;
1955 return false;
1956 }
1957 }
1958 else
1959 {
1960 // Do something
1961 TRACE1("Fail to open file %s", filename);
1962 *pbuf = NULL;
1963 return false;
1964 }
1965}
1966
1967bool LoadRGBBufferFromBMPFile(char *filename, unsigned char **pbuf, int &width, int &height)
1968{
1969 BITMAPFILEHEADER fileHeader;
1970 BITMAPINFOHEADER infoHeader;
1971
1972 FILE *f;
1973 f = fopen(filename, "rb");
1974 if(f != NULL)
1975 {
1976 if (fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, f) != 1 ||
1977 fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, f) != 1)
1978 {
1979 DebugMessage(M64MSG_ERROR, "Couldn't read BMP headers in file '%s'", filename);
1980 return false;
1981 }
1982
1983 if( infoHeader.biBitCount != 24 )
1984 {
1985 fclose(f);
1986 DebugMessage(M64MSG_ERROR, "Unsupported BMP file 16 bits format: %s", filename);
1987 *pbuf = NULL;
1988 return false;
1989 }
1990
1991 *pbuf = new unsigned char[infoHeader.biWidth*infoHeader.biHeight*3];
1992 if( *pbuf )
1993 {
1994 if (fread(*pbuf, infoHeader.biWidth*infoHeader.biHeight*3, 1, f) != 1)
1995 DebugMessage(M64MSG_ERROR, "Couldn't read RGB BMP image data in file '%s'", filename);
1996 fclose(f);
1997 width = infoHeader.biWidth;
1998 height = infoHeader.biHeight;
1999 return true;
2000 }
2001 else
2002 {
2003 fclose(f);
2004 return false;
2005 }
2006 }
2007 else
2008 {
2009 // Do something
2010 DebugMessage(M64MSG_WARNING, "Fail to open file %s", filename);
2011 *pbuf = NULL;
2012 return false;
2013 }
2014}
2015
2016/*******************************************************
2017 * Loads the hires equivaltent of a texture
2018 * parameter:
2019 * TxtrCacheEntry: The original texture in the texture cache
2020 * return:
2021 * none
2022 *******************************************************/
2023void LoadHiresTexture( TxtrCacheEntry &entry )
2024{
2025 // check if the external texture has already been loaded
2026 if( entry.bExternalTxtrChecked )
2027 return;
2028 // there is already an enhanced texture (e.g. a filtered one)
2029
2030 if( entry.pEnhancedTexture )
2031 {
2032 // delete it from memory before loading the external one
2033 SAFE_DELETE(entry.pEnhancedTexture);
2034 }
2035
2036 int ciidx, scaleShift;
2037 // search the index of the appropriate hires replacement texture
2038 // in the list containing the infos of the external textures
2039 // ciidx is not needed here (just needed for dumping)
2040 int idx = CheckTextureInfos(gHiresTxtrInfos,entry,ciidx,scaleShift,false);
2041 if( idx < 0 )
2042 {
2043 // there is no hires replacement => indicate that
2044 entry.bExternalTxtrChecked = true;
2045 return;
2046 }
2047
2048 // Load the bitmap file
2049 char filename_rgb[PATH_MAX];
2050 char filename_a[PATH_MAX];
2051
2052 strcpy(filename_rgb, gHiresTxtrInfos[idx].foldername);
2053 strcat(filename_rgb, gHiresTxtrInfos[idx].filename);
2054
2055 if (gHiresTxtrInfos[idx].filename_a) {
2056 strcpy(filename_a, gHiresTxtrInfos[idx].foldername);
2057 strcat(filename_a, gHiresTxtrInfos[idx].filename_a);
2058 } else {
2059 strcpy(filename_a, "");
2060 }
2061
2062 // Load BMP image to buffer_rbg
2063 unsigned char *buf_rgba = NULL;
2064 unsigned char *buf_a = NULL;
2065 int width, height;
2066
2067 bool bResRGBA=false, bResA=false;
2068 bool bCI = ((gRDP.otherMode.text_tlut>=2 || entry.ti.Format == TXT_FMT_CI || entry.ti.Format == TXT_FMT_RGBA) && entry.ti.Size <= TXT_SIZE_8b );
2069
2070 switch( gHiresTxtrInfos[idx].type )
2071 {
2072 case RGB_PNG:
2073 if( bCI )
2074 return;
2075 else
2076 {
2077 bResRGBA = LoadRGBBufferFromPNGFile(filename_rgb, &buf_rgba, width, height);
2078 if( bResRGBA && gHiresTxtrInfos[idx].bSeparatedAlpha )
2079 bResA = LoadRGBBufferFromPNGFile(filename_a, &buf_a, width, height);
2080 }
2081 break;
2082 case COLOR_INDEXED_BMP:
2083 if( bCI )
2084 bResRGBA = LoadRGBABufferFromColorIndexedFile(filename_rgb, entry, &buf_rgba, width, height);
2085 else
2086 return;
2087 break;
2088 case RGBA_PNG_FOR_CI:
2089 case RGBA_PNG_FOR_ALL_CI:
2090 if( bCI )
2091 bResRGBA = LoadRGBBufferFromPNGFile(filename_rgb, &buf_rgba, width, height, 32);
2092 else
2093 return;
2094 break;
2095 case RGB_WITH_ALPHA_TOGETHER_PNG:
2096 if( bCI )
2097 return;
2098 else
2099 bResRGBA = LoadRGBBufferFromPNGFile(filename_rgb, &buf_rgba, width, height, 32);
2100 break;
2101 default:
2102 return;
2103 }
2104
2105 if( !bResRGBA || !buf_rgba )
2106 {
2107 DebugMessage(M64MSG_ERROR, "RGBBuffer creation failed for file '%s'.", filename_rgb);
2108 return;
2109 }
2110 // check if the alpha channel has been loaded if the texture has a separate alpha channel
2111 else if( gHiresTxtrInfos[idx].bSeparatedAlpha && !bResA )
2112 {
2113 DebugMessage(M64MSG_ERROR, "Alpha buffer creation failed for file '%s'.", filename_a);
2114 delete [] buf_rgba;
2115 return;
2116 }
2117
2118 // calculate the texture size magnification by comparing the N64 texture size and the hi-res texture size
2119 int scale = 1 << scaleShift;
2120 int mirrorx = 1;
2121 int mirrory = 1;
2122 int input_height_shift = height - entry.ti.HeightToLoad * scale;
2123 int input_pitch_a = width;
2124 int input_pitch_rgb = width;
2125 width = entry.ti.WidthToLoad * scale;
2126 height = entry.ti.HeightToLoad * scale;
2127 if (entry.ti.WidthToCreate/entry.ti.WidthToLoad == 2) mirrorx = 2;
2128 if (entry.ti.HeightToCreate/entry.ti.HeightToLoad == 2) mirrory = 2;
2129 entry.pEnhancedTexture = CDeviceBuilder::GetBuilder()->CreateTexture(entry.ti.WidthToCreate*scale, entry.ti.HeightToCreate*scale);
2130 DrawInfo info;
2131
2132 if( entry.pEnhancedTexture && entry.pEnhancedTexture->StartUpdate(&info) )
2133 {
2134
2135 if( gHiresTxtrInfos[idx].type == RGB_PNG )
2136 {
2137 input_pitch_rgb *= 3;
2138 input_pitch_a *= 3;
2139
2140 if (info.lPitch < width * 4)
2141 DebugMessage(M64MSG_ERROR, "Texture pitch %i less than width %i times 4", info.lPitch, width);
2142 if (height > info.dwHeight)
2143 DebugMessage(M64MSG_ERROR, "Texture source height %i greater than destination height %i", height, info.dwHeight);
2144
2145 // Update the texture by using the buffer
2146 for( int i=0; i<height; i++)
2147 {
2148 unsigned char *pRGB = buf_rgba + (input_height_shift + i) * input_pitch_rgb;
2149 unsigned char *pA = buf_a + (input_height_shift + i) * input_pitch_a;
2150 unsigned char* pdst = (unsigned char*)info.lpSurface + (height - i - 1)*info.lPitch;
2151 for( int j=0; j<width; j++)
2152 {
2153 *pdst++ = *pRGB++; // R
2154 *pdst++ = *pRGB++; // G
2155 *pdst++ = *pRGB++; // B
2156
2157 if( gHiresTxtrInfos[idx].bSeparatedAlpha )
2158 {
2159 *pdst++ = *pA;
2160 pA += 3;
2161 }
2162 else if( entry.ti.Format == TXT_FMT_I )
2163 {
2164 *pdst++ = pRGB[-1];
2165 }
2166 else
2167 {
2168 *pdst++ = 0xFF;
2169 }
2170 }
2171 }
2172 }
2173 else
2174 {
2175 input_pitch_rgb *= 4;
2176
2177 // Update the texture by using the buffer
2178 for( int i=height-1; i>=0; i--)
2179 {
2180 uint32 *pRGB = (uint32*)(buf_rgba + (input_height_shift + i) * input_pitch_rgb);
2181 uint32 *pdst = (uint32*)((unsigned char*)info.lpSurface + (height - i - 1)*info.lPitch);
2182 for( int j=0; j<width; j++)
2183 {
2184 *pdst++ = *pRGB++; // RGBA
2185 }
2186 }
2187 }
2188
2189 if (mirrorx == 2)
2190 {
2191 //printf("Mirror: ToCreate: (%d,%d) ToLoad: (%d,%d) Scale: (%i,%i) Mirror: (%i,%i) Size: (%i,%i) Mask: %i\n", entry.ti.WidthToCreate, entry.ti.HeightToCreate, entry.ti.WidthToLoad, entry.ti.HeightToLoad, scale, scale, mirrorx, mirrory, width, height, entry.ti.maskS+scaleShift);
2192 gTextureManager.Mirror(info.lpSurface, width, entry.ti.maskS+scaleShift, width*2, width*2, height, S_FLAG, 4 );
2193 }
2194
2195 if (mirrory == 2)
2196 {
2197 //printf("Mirror: ToCreate: (%d,%d) ToLoad: (%d,%d) Scale: (%i,%i) Mirror: (%i,%i) Size: (%i,%i) Mask: %i\n", entry.ti.WidthToCreate, entry.ti.HeightToCreate, entry.ti.WidthToLoad, entry.ti.HeightToLoad, scale, scale, mirrorx, mirrory, width, height, entry.ti.maskT+scaleShift);
2198 gTextureManager.Mirror(info.lpSurface, height, entry.ti.maskT+scaleShift, height*2, entry.pEnhancedTexture->m_dwCreatedTextureWidth, height, T_FLAG, 4 );
2199 }
2200
2201 if( entry.ti.WidthToCreate*scale < entry.pEnhancedTexture->m_dwCreatedTextureWidth )
2202 {
2203 // Clamp
2204 gTextureManager.Clamp(info.lpSurface, width, entry.pEnhancedTexture->m_dwCreatedTextureWidth, entry.pEnhancedTexture->m_dwCreatedTextureWidth, height, S_FLAG, 4 );
2205 }
2206 if( entry.ti.HeightToCreate*scale < entry.pEnhancedTexture->m_dwCreatedTextureHeight )
2207 {
2208 // Clamp
2209 gTextureManager.Clamp(info.lpSurface, height, entry.pEnhancedTexture->m_dwCreatedTextureHeight, entry.pEnhancedTexture->m_dwCreatedTextureWidth, height, T_FLAG, 4 );
2210 }
2211
2212 entry.pEnhancedTexture->EndUpdate(&info);
2213 entry.pEnhancedTexture->SetOthersVariables();
2214 entry.pEnhancedTexture->m_bIsEnhancedTexture = true;
2215 entry.dwEnhancementFlag = TEXTURE_EXTERNAL;
2216
2217 DebugMessage(M64MSG_VERBOSE, "Loaded hi-res texture: %s", filename_rgb);
2218 }
2219 else
2220 {
2221 DebugMessage(M64MSG_ERROR, "New texture creation failed.");
2222 TRACE0("Cannot create a new texture");
2223 }
2224
2225 if( buf_rgba )
2226 {
2227 delete [] buf_rgba;
2228 }
2229
2230 if( buf_a )
2231 {
2232 delete [] buf_a;
2233 }
2234}
2235