Glide Plugin GLES2 port from mupen64plus-ae, but with special FrameSkip code
[mupen64plus-pandora.git] / source / gles2glide64 / src / GlideHQ / TxReSample.cpp
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 }