Glide Plugin GLES2 port from mupen64plus-ae, but with special FrameSkip code
[mupen64plus-pandora.git] / source / gles2glide64 / src / GlideHQ / TxReSample.cpp
CommitLineData
98e75f2d 1/*
2 * Texture Filtering
3 * Version: 1.0
4 *
5 * Copyright (C) 2007 Hiroshi Morii All Rights Reserved.
6 * Email koolsmoky(at)users.sourceforge.net
7 * Web http://www.3dfxzone.it/koolsmoky
8 *
9 * this is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * this is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with GNU Make; see the file COPYING. If not, write to
21 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24#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
36int
37TxReSample::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
51boolean
52TxReSample::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 */
149double
150TxReSample::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
157double
158TxReSample::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
165double
166TxReSample::sinc(double x)
167{
168 if (x == 0) return 1.0;
169 x *= M_PI;
170 return (sin(x) / x);
171}
172
173double
174TxReSample::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 */
187double
188TxReSample::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 */
213double
214TxReSample::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
233double
234TxReSample::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
242boolean
243TxReSample::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}