Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libmupen64plus/mupen64plus-video-glide64mk2/src/GlideHQ/TxReSample.cpp
2 views
1
/*
2
* Texture Filtering
3
* Version: 1.0
4
*
5
* Copyright (C) 2007 Hiroshi Morii All Rights Reserved.
6
* Email koolsmoky(at)users.sourceforge.net
7
* Web http://www.3dfxzone.it/koolsmoky
8
*
9
* this is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License as published by
11
* the Free Software Foundation; either version 2, or (at your option)
12
* any later version.
13
*
14
* this is distributed in the hope that it will be useful,
15
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
* GNU General Public License for more details.
18
*
19
* You should have received a copy of the GNU General Public License
20
* along with GNU Make; see the file COPYING. If not, write to
21
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22
*/
23
24
#include "TxReSample.h"
25
#include "TxDbg.h"
26
#include <stdlib.h>
27
#include <memory.h>
28
29
#define _USE_MATH_DEFINES
30
#include <math.h>
31
32
#ifndef M_PI
33
#define M_PI 3.14159265358979323846
34
#endif
35
36
int
37
TxReSample::nextPow2(int num)
38
{
39
num = num - 1;
40
num = num | (num >> 1);
41
num = num | (num >> 2);
42
num = num | (num >> 4);
43
num = num | (num >> 8);
44
num = num | (num >> 16);
45
/*num = num | (num >> 32);*//* for 64bit architecture */
46
num = num + 1;
47
48
return num;
49
}
50
51
boolean
52
TxReSample::nextPow2(uint8** image, int* width, int* height, int bpp, boolean use_3dfx = 0)
53
{
54
/* NOTE: bpp must be one of the follwing: 8, 16, 24, 32 bits per pixel */
55
56
if (!*image || !*width || !*height || !bpp)
57
return 0;
58
59
int row_bytes = ((*width * bpp) >> 3);
60
int o_row_bytes = row_bytes;
61
int o_width = *width;
62
int n_width = *width;
63
int o_height = *height;
64
int n_height = *height;
65
66
/* HACKALERT: I have explicitly subtracted (n) from width/height to
67
* adjust textures that have (n) pixel larger width/height than
68
* power of 2 size. This is a dirty hack for textures that have
69
* munged aspect ratio by (n) pixel to the original.
70
*/
71
if (n_width > 64) n_width -= 4;
72
else if (n_width > 16) n_width -= 2;
73
else if (n_width > 4) n_width -= 1;
74
75
if (n_height > 64) n_height -= 4;
76
else if (n_height > 16) n_height -= 2;
77
else if (n_height > 4) n_height -= 1;
78
79
n_width = nextPow2(n_width);
80
n_height = nextPow2(n_height);
81
row_bytes = (n_width * bpp) >> 3;
82
83
/* 3dfx Glide3 format, W:H aspect ratio range (8:1 - 1:8) */
84
if (use_3dfx) {
85
if (n_width > n_height) {
86
if (n_width > (n_height << 3))
87
n_height = n_width >> 3;
88
} else {
89
if (n_height > (n_width << 3)) {
90
n_width = n_height >> 3;
91
row_bytes = (n_width * bpp) >> 3;
92
}
93
}
94
DBG_INFO(80, L"using 3dfx W:H aspect ratio range (8:1 - 1:8).\n");
95
}
96
97
/* do we really need to do this ? */
98
if (o_width == n_width && o_height == n_height)
99
return 1; /* nope */
100
101
DBG_INFO(80, L"expand image to next power of 2 dimensions. %d x %d -> %d x %d\n",
102
o_width, o_height, n_width, n_height);
103
104
if (o_width > n_width)
105
o_width = n_width;
106
107
if (o_height > n_height)
108
o_height = n_height;
109
110
/* allocate memory to read in image */
111
uint8 *pow2image = (uint8*)malloc(row_bytes * n_height);
112
113
/* read in image */
114
if (pow2image) {
115
int i, j;
116
uint8 *tmpimage = *image, *tmppow2image = pow2image;
117
118
for (i = 0; i < o_height; i++) {
119
/* copy row */
120
memcpy(tmppow2image, tmpimage, ((o_width * bpp) >> 3));
121
122
/* expand to pow2 size by replication */
123
for(j = ((o_width * bpp) >> 3); j < row_bytes; j++)
124
tmppow2image[j] = tmppow2image[j - (bpp >> 3)];
125
126
tmppow2image += row_bytes;
127
tmpimage += o_row_bytes;
128
}
129
/* expand to pow2 size by replication */
130
for (i = o_height; i < n_height; i++)
131
memcpy(&pow2image[row_bytes * i], &pow2image[row_bytes * (i - 1)], row_bytes);
132
133
free(*image);
134
135
*image = pow2image;
136
*height = n_height;
137
*width = n_width;
138
139
return 1;
140
}
141
142
return 0;
143
}
144
145
/* Ken Turkowski
146
* Filters for Common Resampling Tasks
147
* Apple Computer 1990
148
*/
149
double
150
TxReSample::tent(double x)
151
{
152
if (x < 0.0) x = -x;
153
if (x < 1.0) return (1.0 - x);
154
return 0.0;
155
}
156
157
double
158
TxReSample::gaussian(double x)
159
{
160
if (x < 0) x = -x;
161
if (x < 2.0) return pow(2.0, -2.0 * x * x);
162
return 0.0;
163
}
164
165
double
166
TxReSample::sinc(double x)
167
{
168
if (x == 0) return 1.0;
169
x *= M_PI;
170
return (sin(x) / x);
171
}
172
173
double
174
TxReSample::lanczos3(double x)
175
{
176
if (x < 0) x = -x;
177
if (x < 3.0) return (sinc(x) * sinc(x/3.0));
178
return 0.0;
179
}
180
181
/* Don P. Mitchell and Arun N. Netravali
182
* Reconstruction Filters in Computer Graphics
183
* SIGGRAPH '88
184
* Proceedings of the 15th annual conference on Computer
185
* graphics and interactive techniques, pp221-228, 1988
186
*/
187
double
188
TxReSample::mitchell(double x)
189
{
190
if (x < 0) x = -x;
191
if (x < 2.0) {
192
const double B = 1.0 / 3.0;
193
const double C = 1.0 / 3.0;
194
if (x < 1.0) {
195
x = (((12.0 - 9.0 * B - 6.0 * C) * (x * x * x))
196
+ ((-18.0 + 12.0 * B + 6.0 * C) * (x * x))
197
+ (6.0 - 2.0 * B));
198
} else {
199
x = (((-1.0 * B - 6.0 * C) * (x * x * x))
200
+ ((6.0 * B + 30.0 * C) * (x * x))
201
+ ((-12.0 * B - 48.0 * C) * x)
202
+ (8.0 * B + 24.0 * C));
203
}
204
return (x / 6.0);
205
}
206
return 0.0;
207
}
208
209
/* J. F. Kaiser and W. A. Reed
210
* Data smoothing using low-pass digital filters
211
* Rev. Sci. instrum. 48 (11), pp1447-1457, 1977
212
*/
213
double
214
TxReSample::besselI0(double x)
215
{
216
/* zero-order modified bessel function of the first kind */
217
const double eps_coeff = 1E-16; /* small enough */
218
double xh, sum, pow, ds;
219
xh = 0.5 * x;
220
sum = 1.0;
221
pow = 1.0;
222
ds = 1.0;
223
int k = 0;
224
while (ds > sum * eps_coeff) {
225
k++;
226
pow *= (xh / k);
227
ds = pow * pow;
228
sum = sum + ds;
229
}
230
return sum;
231
}
232
233
double
234
TxReSample::kaiser(double x)
235
{
236
const double alpha = 4.0;
237
const double half_window = 5.0;
238
const double ratio = x / half_window;
239
return sinc(x) * besselI0(alpha * sqrt(1 - ratio * ratio)) / besselI0(alpha);
240
}
241
242
boolean
243
TxReSample::minify(uint8 **src, int *width, int *height, int ratio)
244
{
245
/* NOTE: src must be ARGB8888, ratio is the inverse representation */
246
247
#if 0
248
if (!*src || ratio < 2) return 0;
249
250
/* Box filtering.
251
* It would be nice to do Kaiser filtering.
252
* N64 uses narrow strip textures which makes it hard to filter effectively.
253
*/
254
255
int x, y, x2, y2, offset, numtexel;
256
uint32 A, R, G, B, texel;
257
258
int tmpwidth = *width / ratio;
259
int tmpheight = *height / ratio;
260
261
uint8 *tmptex = (uint8*)malloc((tmpwidth * tmpheight) << 2);
262
263
if (tmptex) {
264
numtexel = ratio * ratio;
265
for (y = 0; y < tmpheight; y++) {
266
offset = ratio * y * *width;
267
for (x = 0; x < tmpwidth; x++) {
268
A = R = G = B = 0;
269
for (y2 = 0; y2 < ratio; y2++) {
270
for (x2 = 0; x2 < ratio; x2++) {
271
texel = ((uint32*)*src)[offset + *width * y2 + x2];
272
A += (texel >> 24);
273
R += ((texel >> 16) & 0x000000ff);
274
G += ((texel >> 8) & 0x000000ff);
275
B += (texel & 0x000000ff);
276
}
277
}
278
A = (A + ratio) / numtexel;
279
R = (R + ratio) / numtexel;
280
G = (G + ratio) / numtexel;
281
B = (B + ratio) / numtexel;
282
((uint32*)tmptex)[y * tmpwidth + x] = ((A << 24) | (R << 16) | (G << 8) | B);
283
offset += ratio;
284
}
285
}
286
free(*src);
287
*src = tmptex;
288
*width = tmpwidth;
289
*height = tmpheight;
290
291
DBG_INFO(80, L"minification ratio:%d -> %d x %d\n", ratio, *width, *height);
292
293
return 1;
294
}
295
296
DBG_INFO(80, L"Error: failed minification!\n");
297
298
return 0;
299
300
#else
301
302
if (!*src || ratio < 2) return 0;
303
304
/* Image Resampling */
305
306
/* half width of filter window.
307
* NOTE: must be 1.0 or larger.
308
*
309
* kaiser-bessel 5, lanczos3 3, mitchell 2, gaussian 1.5, tent 1
310
*/
311
double half_window = 5.0;
312
313
int x, y, x2, y2, z;
314
double A, R, G, B;
315
uint32 texel;
316
317
int tmpwidth = *width / ratio;
318
int tmpheight = *height / ratio;
319
320
/* resampled destination */
321
uint8 *tmptex = (uint8*)malloc((tmpwidth * tmpheight) << 2);
322
if (!tmptex) return 0;
323
324
/* work buffer. single row */
325
uint8 *workbuf = (uint8*)malloc(*width << 2);
326
if (!workbuf) {
327
free(tmptex);
328
return 0;
329
}
330
331
/* prepare filter lookup table. only half width required for symetric filters. */
332
double *weight = (double*)malloc((int)((half_window * ratio) * sizeof(double)));
333
if (!weight) {
334
free(tmptex);
335
free(workbuf);
336
return 0;
337
}
338
for (x = 0; x < half_window * ratio; x++) {
339
//weight[x] = tent((double)x / ratio) / ratio;
340
//weight[x] = gaussian((double)x / ratio) / ratio;
341
//weight[x] = lanczos3((double)x / ratio) / ratio;
342
//weight[x] = mitchell((double)x / ratio) / ratio;
343
weight[x] = kaiser((double)x / ratio) / ratio;
344
}
345
346
/* linear convolution */
347
for (y = 0; y < tmpheight; y++) {
348
for (x = 0; x < *width; x++) {
349
texel = ((uint32*)*src)[y * ratio * *width + x];
350
A = (double)(texel >> 24) * weight[0];
351
R = (double)((texel >> 16) & 0xff) * weight[0];
352
G = (double)((texel >> 8) & 0xff) * weight[0];
353
B = (double)((texel ) & 0xff) * weight[0];
354
for (y2 = 1; y2 < half_window * ratio; y2++) {
355
z = y * ratio + y2;
356
if (z >= *height) z = *height - 1;
357
texel = ((uint32*)*src)[z * *width + x];
358
A += (double)(texel >> 24) * weight[y2];
359
R += (double)((texel >> 16) & 0xff) * weight[y2];
360
G += (double)((texel >> 8) & 0xff) * weight[y2];
361
B += (double)((texel ) & 0xff) * weight[y2];
362
z = y * ratio - y2;
363
if (z < 0) z = 0;
364
texel = ((uint32*)*src)[z * *width + x];
365
A += (double)(texel >> 24) * weight[y2];
366
R += (double)((texel >> 16) & 0xff) * weight[y2];
367
G += (double)((texel >> 8) & 0xff) * weight[y2];
368
B += (double)((texel ) & 0xff) * weight[y2];
369
}
370
if (A < 0) A = 0; else if (A > 255) A = 255;
371
if (R < 0) R = 0; else if (R > 255) R = 255;
372
if (G < 0) G = 0; else if (G > 255) G = 255;
373
if (B < 0) B = 0; else if (B > 255) B = 255;
374
((uint32*)workbuf)[x] = (((uint32)A << 24) | ((uint32)R << 16) | ((uint32)G << 8) | (uint32)B);
375
}
376
for (x = 0; x < tmpwidth; x++) {
377
texel = ((uint32*)workbuf)[x * ratio];
378
A = (double)(texel >> 24) * weight[0];
379
R = (double)((texel >> 16) & 0xff) * weight[0];
380
G = (double)((texel >> 8) & 0xff) * weight[0];
381
B = (double)((texel ) & 0xff) * weight[0];
382
for (x2 = 1; x2 < half_window * ratio; x2++) {
383
z = x * ratio + x2;
384
if (z >= *width) z = *width - 1;
385
texel = ((uint32*)workbuf)[z];
386
A += (double)(texel >> 24) * weight[x2];
387
R += (double)((texel >> 16) & 0xff) * weight[x2];
388
G += (double)((texel >> 8) & 0xff) * weight[x2];
389
B += (double)((texel ) & 0xff) * weight[x2];
390
z = x * ratio - x2;
391
if (z < 0) z = 0;
392
texel = ((uint32*)workbuf)[z];
393
A += (double)(texel >> 24) * weight[x2];
394
R += (double)((texel >> 16) & 0xff) * weight[x2];
395
G += (double)((texel >> 8) & 0xff) * weight[x2];
396
B += (double)((texel ) & 0xff) * weight[x2];
397
}
398
if (A < 0) A = 0; else if (A > 255) A = 255;
399
if (R < 0) R = 0; else if (R > 255) R = 255;
400
if (G < 0) G = 0; else if (G > 255) G = 255;
401
if (B < 0) B = 0; else if (B > 255) B = 255;
402
((uint32*)tmptex)[y * tmpwidth + x] = (((uint32)A << 24) | ((uint32)R << 16) | ((uint32)G << 8) | (uint32)B);
403
}
404
}
405
406
free(*src);
407
*src = tmptex;
408
free(weight);
409
free(workbuf);
410
*width = tmpwidth;
411
*height = tmpheight;
412
413
DBG_INFO(80, L"minification ratio:%d -> %d x %d\n", ratio, *width, *height);
414
415
return 1;
416
#endif
417
}
418
419