292f9317 |
1 | /* |
2 | Copyright (C) 2003 Rice1964 |
3 | |
4 | This program is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU General Public License |
6 | as published by the Free Software Foundation; either version 2 |
7 | of the License, or (at your option) any later version. |
8 | |
9 | This program is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License |
15 | along with this program; if not, write to the Free Software |
16 | Foundation, 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 | |
45 | void 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 |
154 | void 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 | /************************************************************************/ |
263 | void 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 | |
326 | void 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 | /************************************************************************/ |
396 | void 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 | |
501 | void 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 | |
610 | void 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 | /************************************************************************/ |
796 | void 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 | |
814 | void 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 | |
836 | void 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 | |
903 | enum 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 | }; |
912 | typedef 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 | |
927 | CSortedList<uint64,ExtTxtrInfo> gTxtrDumpInfos; |
928 | CSortedList<uint64,ExtTxtrInfo> gHiresTxtrInfos; |
929 | |
930 | extern char * right(const char * src, int nchars); |
931 | |
932 | #define SURFFMT_P8 41 |
933 | |
934 | int 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 | |
1003 | BOOL 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 | ********************************************************************************************************************/ |
1032 | void 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 | |
1315 | bool 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 | |
1343 | const 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 | |
1351 | void 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 | ********************************************************************************************************************/ |
1402 | void 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 | |
1434 | void 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 | |
1449 | void 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 | |
1464 | void 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 | ********************************************************************************************************************/ |
1481 | void 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 | |
1490 | void 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 | ********************************************************************************************************************/ |
1507 | void 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 | ********************************************************************************************************************/ |
1533 | int 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 | ********************************************************************************************************************/ |
1566 | int 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 | |
1639 | bool SaveCITextureToFile(TxtrCacheEntry &entry, char *filename, bool bShow, bool bWhole); |
1640 | |
1641 | void 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 | |
1740 | bool 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 | |
1837 | bool 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 | |
1967 | bool 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 | *******************************************************/ |
2023 | void 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 | |