Path: blob/master/libmupen64plus/mupen64plus-video-glide64mk2/src/GlideHQ/TxReSample.cpp
2 views
/*1* Texture Filtering2* Version: 1.03*4* Copyright (C) 2007 Hiroshi Morii All Rights Reserved.5* Email koolsmoky(at)users.sourceforge.net6* Web http://www.3dfxzone.it/koolsmoky7*8* this is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2, or (at your option)11* any later version.12*13* this is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with GNU Make; see the file COPYING. If not, write to20* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.21*/2223#include "TxReSample.h"24#include "TxDbg.h"25#include <stdlib.h>26#include <memory.h>2728#define _USE_MATH_DEFINES29#include <math.h>3031#ifndef M_PI32#define M_PI 3.1415926535897932384633#endif3435int36TxReSample::nextPow2(int num)37{38num = num - 1;39num = num | (num >> 1);40num = num | (num >> 2);41num = num | (num >> 4);42num = num | (num >> 8);43num = num | (num >> 16);44/*num = num | (num >> 32);*//* for 64bit architecture */45num = num + 1;4647return num;48}4950boolean51TxReSample::nextPow2(uint8** image, int* width, int* height, int bpp, boolean use_3dfx = 0)52{53/* NOTE: bpp must be one of the follwing: 8, 16, 24, 32 bits per pixel */5455if (!*image || !*width || !*height || !bpp)56return 0;5758int row_bytes = ((*width * bpp) >> 3);59int o_row_bytes = row_bytes;60int o_width = *width;61int n_width = *width;62int o_height = *height;63int n_height = *height;6465/* HACKALERT: I have explicitly subtracted (n) from width/height to66* adjust textures that have (n) pixel larger width/height than67* power of 2 size. This is a dirty hack for textures that have68* munged aspect ratio by (n) pixel to the original.69*/70if (n_width > 64) n_width -= 4;71else if (n_width > 16) n_width -= 2;72else if (n_width > 4) n_width -= 1;7374if (n_height > 64) n_height -= 4;75else if (n_height > 16) n_height -= 2;76else if (n_height > 4) n_height -= 1;7778n_width = nextPow2(n_width);79n_height = nextPow2(n_height);80row_bytes = (n_width * bpp) >> 3;8182/* 3dfx Glide3 format, W:H aspect ratio range (8:1 - 1:8) */83if (use_3dfx) {84if (n_width > n_height) {85if (n_width > (n_height << 3))86n_height = n_width >> 3;87} else {88if (n_height > (n_width << 3)) {89n_width = n_height >> 3;90row_bytes = (n_width * bpp) >> 3;91}92}93DBG_INFO(80, L"using 3dfx W:H aspect ratio range (8:1 - 1:8).\n");94}9596/* do we really need to do this ? */97if (o_width == n_width && o_height == n_height)98return 1; /* nope */99100DBG_INFO(80, L"expand image to next power of 2 dimensions. %d x %d -> %d x %d\n",101o_width, o_height, n_width, n_height);102103if (o_width > n_width)104o_width = n_width;105106if (o_height > n_height)107o_height = n_height;108109/* allocate memory to read in image */110uint8 *pow2image = (uint8*)malloc(row_bytes * n_height);111112/* read in image */113if (pow2image) {114int i, j;115uint8 *tmpimage = *image, *tmppow2image = pow2image;116117for (i = 0; i < o_height; i++) {118/* copy row */119memcpy(tmppow2image, tmpimage, ((o_width * bpp) >> 3));120121/* expand to pow2 size by replication */122for(j = ((o_width * bpp) >> 3); j < row_bytes; j++)123tmppow2image[j] = tmppow2image[j - (bpp >> 3)];124125tmppow2image += row_bytes;126tmpimage += o_row_bytes;127}128/* expand to pow2 size by replication */129for (i = o_height; i < n_height; i++)130memcpy(&pow2image[row_bytes * i], &pow2image[row_bytes * (i - 1)], row_bytes);131132free(*image);133134*image = pow2image;135*height = n_height;136*width = n_width;137138return 1;139}140141return 0;142}143144/* Ken Turkowski145* Filters for Common Resampling Tasks146* Apple Computer 1990147*/148double149TxReSample::tent(double x)150{151if (x < 0.0) x = -x;152if (x < 1.0) return (1.0 - x);153return 0.0;154}155156double157TxReSample::gaussian(double x)158{159if (x < 0) x = -x;160if (x < 2.0) return pow(2.0, -2.0 * x * x);161return 0.0;162}163164double165TxReSample::sinc(double x)166{167if (x == 0) return 1.0;168x *= M_PI;169return (sin(x) / x);170}171172double173TxReSample::lanczos3(double x)174{175if (x < 0) x = -x;176if (x < 3.0) return (sinc(x) * sinc(x/3.0));177return 0.0;178}179180/* Don P. Mitchell and Arun N. Netravali181* Reconstruction Filters in Computer Graphics182* SIGGRAPH '88183* Proceedings of the 15th annual conference on Computer184* graphics and interactive techniques, pp221-228, 1988185*/186double187TxReSample::mitchell(double x)188{189if (x < 0) x = -x;190if (x < 2.0) {191const double B = 1.0 / 3.0;192const double C = 1.0 / 3.0;193if (x < 1.0) {194x = (((12.0 - 9.0 * B - 6.0 * C) * (x * x * x))195+ ((-18.0 + 12.0 * B + 6.0 * C) * (x * x))196+ (6.0 - 2.0 * B));197} else {198x = (((-1.0 * B - 6.0 * C) * (x * x * x))199+ ((6.0 * B + 30.0 * C) * (x * x))200+ ((-12.0 * B - 48.0 * C) * x)201+ (8.0 * B + 24.0 * C));202}203return (x / 6.0);204}205return 0.0;206}207208/* J. F. Kaiser and W. A. Reed209* Data smoothing using low-pass digital filters210* Rev. Sci. instrum. 48 (11), pp1447-1457, 1977211*/212double213TxReSample::besselI0(double x)214{215/* zero-order modified bessel function of the first kind */216const double eps_coeff = 1E-16; /* small enough */217double xh, sum, pow, ds;218xh = 0.5 * x;219sum = 1.0;220pow = 1.0;221ds = 1.0;222int k = 0;223while (ds > sum * eps_coeff) {224k++;225pow *= (xh / k);226ds = pow * pow;227sum = sum + ds;228}229return sum;230}231232double233TxReSample::kaiser(double x)234{235const double alpha = 4.0;236const double half_window = 5.0;237const double ratio = x / half_window;238return sinc(x) * besselI0(alpha * sqrt(1 - ratio * ratio)) / besselI0(alpha);239}240241boolean242TxReSample::minify(uint8 **src, int *width, int *height, int ratio)243{244/* NOTE: src must be ARGB8888, ratio is the inverse representation */245246#if 0247if (!*src || ratio < 2) return 0;248249/* Box filtering.250* It would be nice to do Kaiser filtering.251* N64 uses narrow strip textures which makes it hard to filter effectively.252*/253254int x, y, x2, y2, offset, numtexel;255uint32 A, R, G, B, texel;256257int tmpwidth = *width / ratio;258int tmpheight = *height / ratio;259260uint8 *tmptex = (uint8*)malloc((tmpwidth * tmpheight) << 2);261262if (tmptex) {263numtexel = ratio * ratio;264for (y = 0; y < tmpheight; y++) {265offset = ratio * y * *width;266for (x = 0; x < tmpwidth; x++) {267A = R = G = B = 0;268for (y2 = 0; y2 < ratio; y2++) {269for (x2 = 0; x2 < ratio; x2++) {270texel = ((uint32*)*src)[offset + *width * y2 + x2];271A += (texel >> 24);272R += ((texel >> 16) & 0x000000ff);273G += ((texel >> 8) & 0x000000ff);274B += (texel & 0x000000ff);275}276}277A = (A + ratio) / numtexel;278R = (R + ratio) / numtexel;279G = (G + ratio) / numtexel;280B = (B + ratio) / numtexel;281((uint32*)tmptex)[y * tmpwidth + x] = ((A << 24) | (R << 16) | (G << 8) | B);282offset += ratio;283}284}285free(*src);286*src = tmptex;287*width = tmpwidth;288*height = tmpheight;289290DBG_INFO(80, L"minification ratio:%d -> %d x %d\n", ratio, *width, *height);291292return 1;293}294295DBG_INFO(80, L"Error: failed minification!\n");296297return 0;298299#else300301if (!*src || ratio < 2) return 0;302303/* Image Resampling */304305/* half width of filter window.306* NOTE: must be 1.0 or larger.307*308* kaiser-bessel 5, lanczos3 3, mitchell 2, gaussian 1.5, tent 1309*/310double half_window = 5.0;311312int x, y, x2, y2, z;313double A, R, G, B;314uint32 texel;315316int tmpwidth = *width / ratio;317int tmpheight = *height / ratio;318319/* resampled destination */320uint8 *tmptex = (uint8*)malloc((tmpwidth * tmpheight) << 2);321if (!tmptex) return 0;322323/* work buffer. single row */324uint8 *workbuf = (uint8*)malloc(*width << 2);325if (!workbuf) {326free(tmptex);327return 0;328}329330/* prepare filter lookup table. only half width required for symetric filters. */331double *weight = (double*)malloc((int)((half_window * ratio) * sizeof(double)));332if (!weight) {333free(tmptex);334free(workbuf);335return 0;336}337for (x = 0; x < half_window * ratio; x++) {338//weight[x] = tent((double)x / ratio) / ratio;339//weight[x] = gaussian((double)x / ratio) / ratio;340//weight[x] = lanczos3((double)x / ratio) / ratio;341//weight[x] = mitchell((double)x / ratio) / ratio;342weight[x] = kaiser((double)x / ratio) / ratio;343}344345/* linear convolution */346for (y = 0; y < tmpheight; y++) {347for (x = 0; x < *width; x++) {348texel = ((uint32*)*src)[y * ratio * *width + x];349A = (double)(texel >> 24) * weight[0];350R = (double)((texel >> 16) & 0xff) * weight[0];351G = (double)((texel >> 8) & 0xff) * weight[0];352B = (double)((texel ) & 0xff) * weight[0];353for (y2 = 1; y2 < half_window * ratio; y2++) {354z = y * ratio + y2;355if (z >= *height) z = *height - 1;356texel = ((uint32*)*src)[z * *width + x];357A += (double)(texel >> 24) * weight[y2];358R += (double)((texel >> 16) & 0xff) * weight[y2];359G += (double)((texel >> 8) & 0xff) * weight[y2];360B += (double)((texel ) & 0xff) * weight[y2];361z = y * ratio - y2;362if (z < 0) z = 0;363texel = ((uint32*)*src)[z * *width + x];364A += (double)(texel >> 24) * weight[y2];365R += (double)((texel >> 16) & 0xff) * weight[y2];366G += (double)((texel >> 8) & 0xff) * weight[y2];367B += (double)((texel ) & 0xff) * weight[y2];368}369if (A < 0) A = 0; else if (A > 255) A = 255;370if (R < 0) R = 0; else if (R > 255) R = 255;371if (G < 0) G = 0; else if (G > 255) G = 255;372if (B < 0) B = 0; else if (B > 255) B = 255;373((uint32*)workbuf)[x] = (((uint32)A << 24) | ((uint32)R << 16) | ((uint32)G << 8) | (uint32)B);374}375for (x = 0; x < tmpwidth; x++) {376texel = ((uint32*)workbuf)[x * ratio];377A = (double)(texel >> 24) * weight[0];378R = (double)((texel >> 16) & 0xff) * weight[0];379G = (double)((texel >> 8) & 0xff) * weight[0];380B = (double)((texel ) & 0xff) * weight[0];381for (x2 = 1; x2 < half_window * ratio; x2++) {382z = x * ratio + x2;383if (z >= *width) z = *width - 1;384texel = ((uint32*)workbuf)[z];385A += (double)(texel >> 24) * weight[x2];386R += (double)((texel >> 16) & 0xff) * weight[x2];387G += (double)((texel >> 8) & 0xff) * weight[x2];388B += (double)((texel ) & 0xff) * weight[x2];389z = x * ratio - x2;390if (z < 0) z = 0;391texel = ((uint32*)workbuf)[z];392A += (double)(texel >> 24) * weight[x2];393R += (double)((texel >> 16) & 0xff) * weight[x2];394G += (double)((texel >> 8) & 0xff) * weight[x2];395B += (double)((texel ) & 0xff) * weight[x2];396}397if (A < 0) A = 0; else if (A > 255) A = 255;398if (R < 0) R = 0; else if (R > 255) R = 255;399if (G < 0) G = 0; else if (G > 255) G = 255;400if (B < 0) B = 0; else if (B > 255) B = 255;401((uint32*)tmptex)[y * tmpwidth + x] = (((uint32)A << 24) | ((uint32)R << 16) | ((uint32)G << 8) | (uint32)B);402}403}404405free(*src);406*src = tmptex;407free(weight);408free(workbuf);409*width = tmpwidth;410*height = tmpheight;411412DBG_INFO(80, L"minification ratio:%d -> %d x %d\n", ratio, *width, *height);413414return 1;415#endif416}417418419