X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=source%2Fgles2glide64%2Fsrc%2FGlideHQ%2FTxReSample.cpp;fp=source%2Fgles2glide64%2Fsrc%2FGlideHQ%2FTxReSample.cpp;h=138428b77f37ae173ace845bed6b699467fecc88;hb=98e75f2d18c02c233da543560f76282f04fc796c;hp=0000000000000000000000000000000000000000;hpb=0ced54f867d36e8b324155bef49e8abfebfc3237;p=mupen64plus-pandora.git diff --git a/source/gles2glide64/src/GlideHQ/TxReSample.cpp b/source/gles2glide64/src/GlideHQ/TxReSample.cpp new file mode 100644 index 0000000..138428b --- /dev/null +++ b/source/gles2glide64/src/GlideHQ/TxReSample.cpp @@ -0,0 +1,417 @@ +/* + * Texture Filtering + * Version: 1.0 + * + * Copyright (C) 2007 Hiroshi Morii All Rights Reserved. + * Email koolsmoky(at)users.sourceforge.net + * Web http://www.3dfxzone.it/koolsmoky + * + * this is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * this is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Make; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "TxReSample.h" +#include "TxDbg.h" +#include +#include + +#define _USE_MATH_DEFINES +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +int +TxReSample::nextPow2(int num) +{ + num = num - 1; + num = num | (num >> 1); + num = num | (num >> 2); + num = num | (num >> 4); + num = num | (num >> 8); + num = num | (num >> 16); + /*num = num | (num >> 32);*//* for 64bit architecture */ + num = num + 1; + + return num; +} + +boolean +TxReSample::nextPow2(uint8** image, int* width, int* height, int bpp, boolean use_3dfx = 0) +{ + /* NOTE: bpp must be one of the follwing: 8, 16, 24, 32 bits per pixel */ + + if (!*image || !*width || !*height || !bpp) + return 0; + + int row_bytes = ((*width * bpp) >> 3); + int o_row_bytes = row_bytes; + int o_width = *width; + int n_width = *width; + int o_height = *height; + int n_height = *height; + + /* HACKALERT: I have explicitly subtracted (n) from width/height to + * adjust textures that have (n) pixel larger width/height than + * power of 2 size. This is a dirty hack for textures that have + * munged aspect ratio by (n) pixel to the original. + */ + if (n_width > 64) n_width -= 4; + else if (n_width > 16) n_width -= 2; + else if (n_width > 4) n_width -= 1; + + if (n_height > 64) n_height -= 4; + else if (n_height > 16) n_height -= 2; + else if (n_height > 4) n_height -= 1; + + n_width = nextPow2(n_width); + n_height = nextPow2(n_height); + row_bytes = (n_width * bpp) >> 3; + + /* 3dfx Glide3 format, W:H aspect ratio range (8:1 - 1:8) */ + if (use_3dfx) { + if (n_width > n_height) { + if (n_width > (n_height << 3)) + n_height = n_width >> 3; + } else { + if (n_height > (n_width << 3)) { + n_width = n_height >> 3; + row_bytes = (n_width * bpp) >> 3; + } + } + DBG_INFO(80, L"using 3dfx W:H aspect ratio range (8:1 - 1:8).\n"); + } + + /* do we really need to do this ? */ + if (o_width == n_width && o_height == n_height) + return 1; /* nope */ + + DBG_INFO(80, L"expand image to next power of 2 dimensions. %d x %d -> %d x %d\n", + o_width, o_height, n_width, n_height); + + if (o_width > n_width) + o_width = n_width; + + if (o_height > n_height) + o_height = n_height; + + /* allocate memory to read in image */ + uint8 *pow2image = (uint8*)malloc(row_bytes * n_height); + + /* read in image */ + if (pow2image) { + int i, j; + uint8 *tmpimage = *image, *tmppow2image = pow2image; + + for (i = 0; i < o_height; i++) { + /* copy row */ + memcpy(tmppow2image, tmpimage, ((o_width * bpp) >> 3)); + + /* expand to pow2 size by replication */ + for(j = ((o_width * bpp) >> 3); j < row_bytes; j++) + tmppow2image[j] = tmppow2image[j - (bpp >> 3)]; + + tmppow2image += row_bytes; + tmpimage += o_row_bytes; + } + /* expand to pow2 size by replication */ + for (i = o_height; i < n_height; i++) + memcpy(&pow2image[row_bytes * i], &pow2image[row_bytes * (i - 1)], row_bytes); + + free(*image); + + *image = pow2image; + *height = n_height; + *width = n_width; + + return 1; + } + + return 0; +} + +/* Ken Turkowski + * Filters for Common Resampling Tasks + * Apple Computer 1990 + */ +double +TxReSample::tent(double x) +{ + if (x < 0.0) x = -x; + if (x < 1.0) return (1.0 - x); + return 0.0; +} + +double +TxReSample::gaussian(double x) +{ + if (x < 0) x = -x; + if (x < 2.0) return pow(2.0, -2.0 * x * x); + return 0.0; +} + +double +TxReSample::sinc(double x) +{ + if (x == 0) return 1.0; + x *= M_PI; + return (sin(x) / x); +} + +double +TxReSample::lanczos3(double x) +{ + if (x < 0) x = -x; + if (x < 3.0) return (sinc(x) * sinc(x/3.0)); + return 0.0; +} + +/* Don P. Mitchell and Arun N. Netravali + * Reconstruction Filters in Computer Graphics + * SIGGRAPH '88 + * Proceedings of the 15th annual conference on Computer + * graphics and interactive techniques, pp221-228, 1988 + */ +double +TxReSample::mitchell(double x) +{ + if (x < 0) x = -x; + if (x < 2.0) { + const double B = 1.0 / 3.0; + const double C = 1.0 / 3.0; + if (x < 1.0) { + x = (((12.0 - 9.0 * B - 6.0 * C) * (x * x * x)) + + ((-18.0 + 12.0 * B + 6.0 * C) * (x * x)) + + (6.0 - 2.0 * B)); + } else { + x = (((-1.0 * B - 6.0 * C) * (x * x * x)) + + ((6.0 * B + 30.0 * C) * (x * x)) + + ((-12.0 * B - 48.0 * C) * x) + + (8.0 * B + 24.0 * C)); + } + return (x / 6.0); + } + return 0.0; +} + +/* J. F. Kaiser and W. A. Reed + * Data smoothing using low-pass digital filters + * Rev. Sci. instrum. 48 (11), pp1447-1457, 1977 + */ +double +TxReSample::besselI0(double x) +{ + /* zero-order modified bessel function of the first kind */ + const double eps_coeff = 1E-16; /* small enough */ + double xh, sum, pow, ds; + xh = 0.5 * x; + sum = 1.0; + pow = 1.0; + ds = 1.0; + int k = 0; + while (ds > sum * eps_coeff) { + k++; + pow *= (xh / k); + ds = pow * pow; + sum = sum + ds; + } + return sum; +} + +double +TxReSample::kaiser(double x) +{ + const double alpha = 4.0; + const double half_window = 5.0; + const double ratio = x / half_window; + return sinc(x) * besselI0(alpha * sqrt(1 - ratio * ratio)) / besselI0(alpha); +} + +boolean +TxReSample::minify(uint8 **src, int *width, int *height, int ratio) +{ + /* NOTE: src must be ARGB8888, ratio is the inverse representation */ + +#if 0 + if (!*src || ratio < 2) return 0; + + /* Box filtering. + * It would be nice to do Kaiser filtering. + * N64 uses narrow strip textures which makes it hard to filter effectively. + */ + + int x, y, x2, y2, offset, numtexel; + uint32 A, R, G, B, texel; + + int tmpwidth = *width / ratio; + int tmpheight = *height / ratio; + + uint8 *tmptex = (uint8*)malloc((tmpwidth * tmpheight) << 2); + + if (tmptex) { + numtexel = ratio * ratio; + for (y = 0; y < tmpheight; y++) { + offset = ratio * y * *width; + for (x = 0; x < tmpwidth; x++) { + A = R = G = B = 0; + for (y2 = 0; y2 < ratio; y2++) { + for (x2 = 0; x2 < ratio; x2++) { + texel = ((uint32*)*src)[offset + *width * y2 + x2]; + A += (texel >> 24); + R += ((texel >> 16) & 0x000000ff); + G += ((texel >> 8) & 0x000000ff); + B += (texel & 0x000000ff); + } + } + A = (A + ratio) / numtexel; + R = (R + ratio) / numtexel; + G = (G + ratio) / numtexel; + B = (B + ratio) / numtexel; + ((uint32*)tmptex)[y * tmpwidth + x] = ((A << 24) | (R << 16) | (G << 8) | B); + offset += ratio; + } + } + free(*src); + *src = tmptex; + *width = tmpwidth; + *height = tmpheight; + + DBG_INFO(80, L"minification ratio:%d -> %d x %d\n", ratio, *width, *height); + + return 1; + } + + DBG_INFO(80, L"Error: failed minification!\n"); + + return 0; + +#else + + if (!*src || ratio < 2) return 0; + + /* Image Resampling */ + + /* half width of filter window. + * NOTE: must be 1.0 or larger. + * + * kaiser-bessel 5, lanczos3 3, mitchell 2, gaussian 1.5, tent 1 + */ + double half_window = 5.0; + + int x, y, x2, y2, z; + double A, R, G, B; + uint32 texel; + + int tmpwidth = *width / ratio; + int tmpheight = *height / ratio; + + /* resampled destination */ + uint8 *tmptex = (uint8*)malloc((tmpwidth * tmpheight) << 2); + if (!tmptex) return 0; + + /* work buffer. single row */ + uint8 *workbuf = (uint8*)malloc(*width << 2); + if (!workbuf) { + free(tmptex); + return 0; + } + + /* prepare filter lookup table. only half width required for symetric filters. */ + double *weight = (double*)malloc((int)((half_window * ratio) * sizeof(double))); + if (!weight) { + free(tmptex); + free(workbuf); + return 0; + } + for (x = 0; x < half_window * ratio; x++) { + //weight[x] = tent((double)x / ratio) / ratio; + //weight[x] = gaussian((double)x / ratio) / ratio; + //weight[x] = lanczos3((double)x / ratio) / ratio; + //weight[x] = mitchell((double)x / ratio) / ratio; + weight[x] = kaiser((double)x / ratio) / ratio; + } + + /* linear convolution */ + for (y = 0; y < tmpheight; y++) { + for (x = 0; x < *width; x++) { + texel = ((uint32*)*src)[y * ratio * *width + x]; + A = (double)(texel >> 24) * weight[0]; + R = (double)((texel >> 16) & 0xff) * weight[0]; + G = (double)((texel >> 8) & 0xff) * weight[0]; + B = (double)((texel ) & 0xff) * weight[0]; + for (y2 = 1; y2 < half_window * ratio; y2++) { + z = y * ratio + y2; + if (z >= *height) z = *height - 1; + texel = ((uint32*)*src)[z * *width + x]; + A += (double)(texel >> 24) * weight[y2]; + R += (double)((texel >> 16) & 0xff) * weight[y2]; + G += (double)((texel >> 8) & 0xff) * weight[y2]; + B += (double)((texel ) & 0xff) * weight[y2]; + z = y * ratio - y2; + if (z < 0) z = 0; + texel = ((uint32*)*src)[z * *width + x]; + A += (double)(texel >> 24) * weight[y2]; + R += (double)((texel >> 16) & 0xff) * weight[y2]; + G += (double)((texel >> 8) & 0xff) * weight[y2]; + B += (double)((texel ) & 0xff) * weight[y2]; + } + if (A < 0) A = 0; else if (A > 255) A = 255; + if (R < 0) R = 0; else if (R > 255) R = 255; + if (G < 0) G = 0; else if (G > 255) G = 255; + if (B < 0) B = 0; else if (B > 255) B = 255; + ((uint32*)workbuf)[x] = (((uint32)A << 24) | ((uint32)R << 16) | ((uint32)G << 8) | (uint32)B); + } + for (x = 0; x < tmpwidth; x++) { + texel = ((uint32*)workbuf)[x * ratio]; + A = (double)(texel >> 24) * weight[0]; + R = (double)((texel >> 16) & 0xff) * weight[0]; + G = (double)((texel >> 8) & 0xff) * weight[0]; + B = (double)((texel ) & 0xff) * weight[0]; + for (x2 = 1; x2 < half_window * ratio; x2++) { + z = x * ratio + x2; + if (z >= *width) z = *width - 1; + texel = ((uint32*)workbuf)[z]; + A += (double)(texel >> 24) * weight[x2]; + R += (double)((texel >> 16) & 0xff) * weight[x2]; + G += (double)((texel >> 8) & 0xff) * weight[x2]; + B += (double)((texel ) & 0xff) * weight[x2]; + z = x * ratio - x2; + if (z < 0) z = 0; + texel = ((uint32*)workbuf)[z]; + A += (double)(texel >> 24) * weight[x2]; + R += (double)((texel >> 16) & 0xff) * weight[x2]; + G += (double)((texel >> 8) & 0xff) * weight[x2]; + B += (double)((texel ) & 0xff) * weight[x2]; + } + if (A < 0) A = 0; else if (A > 255) A = 255; + if (R < 0) R = 0; else if (R > 255) R = 255; + if (G < 0) G = 0; else if (G > 255) G = 255; + if (B < 0) B = 0; else if (B > 255) B = 255; + ((uint32*)tmptex)[y * tmpwidth + x] = (((uint32)A << 24) | ((uint32)R << 16) | ((uint32)G << 8) | (uint32)B); + } + } + + free(*src); + *src = tmptex; + free(weight); + free(workbuf); + *width = tmpwidth; + *height = tmpheight; + + DBG_INFO(80, L"minification ratio:%d -> %d x %d\n", ratio, *width, *height); + + return 1; +#endif +}