Arg. This is ugly and slow, but at least it's a real linear filter.

Handle extreme resizing iteratively, which gives us something like
a triangle filter--slower and probably not quite as good, but much
better than it was.
This commit is contained in:
Glenn Maynard
2002-10-30 23:36:16 +00:00
parent d754ddc550
commit f2856485aa
3 changed files with 119 additions and 135 deletions
+24 -13
View File
@@ -183,8 +183,8 @@ void RageBitmapTexture::Create(
/* Don't use more than we were hinted to. */
src_alpha_bits = min( iAlphaBits, src_alpha_bits );
/* XXX Scan the image, and see if it actually uses its alpha channel/color key
* (if any). Reduce to 1 or 0 bits of alpha if possible. */
/* XXX Scan the image, and see if it actually uses its alpha channel/color key
* (if any). Reduce to 1 or 0 bits of alpha if possible. */
switch( src_alpha_bits ) {
case 0: fmtTexture = D3DFMT_R5G6B5; break;
@@ -250,25 +250,36 @@ void RageBitmapTexture::Create(
if( bStretch ) {
/* zoomSurface takes a ratio. I'm not sure if it's always exact; we don't
* want to accidentally create a 513x513 texture, for example (it won't just
* cause lines; it'll be copied to the texture completely wrong). */
* want to accidentally create a 513x513 texture, for example (it won't just
* cause lines; it'll be copied to the texture completely wrong). */
/* resize currently only does RGBA8888 */
if(img->format->BitsPerPixel != 32 || img->format->colorkey) {
{
int mask = 0;
ConvertSDLSurface(img, img->w, img->h, PixFmtMasks[mask][4],
PixFmtMasks[mask][0], PixFmtMasks[mask][1], PixFmtMasks[mask][2], PixFmtMasks[mask][3]);
}
SDL_Surface *dst;
dst = zoomSurface(img, float(m_iTextureWidth)/m_iImageWidth,
float(m_iTextureHeight) / m_iImageHeight, SMOOTHING_ON);
SDL_FreeSurface(img);
img = dst;
while (m_iImageWidth != m_iTextureWidth || m_iImageHeight != m_iTextureHeight) {
SDL_Surface *dst;
/* The new image size is the full texture size. */
m_iImageWidth = m_iTextureWidth;
m_iImageHeight = m_iTextureHeight;
float xscale = float(m_iTextureWidth)/m_iImageWidth;
float yscale = float(m_iTextureHeight)/m_iImageHeight;
/* Our simple filter is a simple linear filter, so it can't
* scale to less than .5 very well. If we need to go lower
* than .5, do it iteratively. */
xscale = max(xscale, .5f);
yscale = max(yscale, .5f);
dst = zoomSurface(img, xscale, yscale);
SDL_FreeSurface(img);
img = dst;
/* The new image size is the full texture size. */
m_iImageWidth = int(m_iImageWidth * xscale + .0001);
m_iImageHeight = int(m_iImageHeight * yscale + .0001);
}
}
if( bDither )
+94 -116
View File
@@ -13,15 +13,12 @@
#include "SDL_rotozoom.h"
#include <stdlib.h>
#include <math.h>
struct tColorRGBA {
Uint8 r,g,b,a;
Uint8 c[4];
};
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
/*
32bit Zoomer with optional anti-aliasing by bilinear interpolation.
@@ -29,133 +26,114 @@ struct tColorRGBA {
Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
*/
/* Coordinate 0x0 represents the exact top-left corner of a bitmap. .5x.5
* represents the center of the top-left pixel; 1x1 is the center of the top
* square of pixels.
*
* (Look at a grid: map coordinates to the lines, not the squares between the
* lines.)
*/
int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int smooth)
float frac(float x) { return x-int(x); }
int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst)
{
int x, y, sx, sy, *sax, *say;
/* Ratio from source to dest. */
float sx = float(src->w) / dst->w;
float sy = float(src->h) / dst->h;
/* Variable setup */
// if (smooth) {
/* For interpolation: assume source dimension is one pixel */
/* smaller to avoid overflow on right and bottom edge. */
/* XXX: no, this misaligns stuff; we need to clamp properly -glenn */
// sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w);
// sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h);
// } else {
sx = 65536 * src->w / dst->w;
sy = 65536 * src->h / dst->h;
//}
/* Allocate memory for row increments */
if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL)
return (-1);
if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
free(sax);
return (-1);
}
float *sax = new float[dst->w];
float *say = new float[dst->h];
/* Precalculate row increments */
int csx = 0;
int *csax = sax;
for (x = 0; x <= dst->w; x++) {
*csax = csx;
csx &= 0xffff;
csax++;
csx += sx;
}
int csy = 0;
int *csay = say;
for (y = 0; y <= dst->h; y++) {
*csay = csy;
csy &= 0xffff;
csay++;
csy += sy;
}
int x, y;
/* sax[x] is the exact (floating-point) x coordinate in the source
* that the destination pixel at x should from. For example, if we're
* going 512->256, then dst[0] should come from the pixels from 0..1 and
* 1..2, so sax[0] is 1. sx is the total number of pixels, so sx/2 is the
* distance from the start of the sample to its center. */
for (x = 0; x < dst->w; x++)
sax[x] = sx*x + sx/2;
/* Pointer setup */
tColorRGBA *sp, *csp;
sp = csp = (tColorRGBA *) src->pixels;
for (y = 0; y < dst->h; y++)
say[y] = sy*y + sy/2;
/* Switch between interpolating and non-interpolating code */
csay = say;
if (smooth) {
/* Interpolating Zoom */
tColorRGBA *sp = (tColorRGBA *) src->pixels;
/* Scan destination */
for (y = 0; y < dst->h; y++) {
/* Set up color source pointers */
tColorRGBA *c00 = csp;
tColorRGBA *c01 = csp+1;
tColorRGBA *c10 = (tColorRGBA *) ((Uint8 *) csp + src->pitch);
tColorRGBA *c11 = c10+1;
csax = sax;
/* Set destination pointer. */
tColorRGBA *dp = (tColorRGBA *) ((Uint8 *) dst->pixels + dst->pitch*y);
/* Scan destination */
for (y = 0; y < dst->h; y++) {
/* Set destination pointer. */
for (x = 0; x < dst->w; x++) {
/* sx/2 is the distance from the start of the sample to the center;
* sx/4 is the distance from the center of the sample to the center of
* either pixel. */
float xstep = sx/4;
float ystep = sy/4;
/*
* Interpolate colors
*/
int ex = *csax & 0xffff;
int ey = *csay & 0xffff;
int t1, t2;
t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
dp->r = (Uint8)((((t2 - t1) * ey) >> 16) + t1);
t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
dp->g = (Uint8)((((t2 - t1) * ey) >> 16) + t1);
t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
dp->b = (Uint8)((((t2 - t1) * ey) >> 16) + t1);
t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
dp->a = (Uint8)((((t2 - t1) * ey) >> 16) + t1);
int ytop = int(say[y]-ystep);
int ybottom = int(say[y]+ystep);
/* Advance source pointers. */
csax++;
int sstep = (*csax >> 16);
c00 += sstep;
c01 += sstep;
c10 += sstep;
c11 += sstep;
/* Advance destination pointer. */
dp++;
tColorRGBA *dp = (tColorRGBA *) ((Uint8 *) dst->pixels + dst->pitch*y);
tColorRGBA *csp = (tColorRGBA *) ((Uint8 *) sp + ytop * src->pitch);
tColorRGBA *ncsp = (tColorRGBA *) ((Uint8 *) sp + ybottom * src->pitch);
for (x = 0; x < dst->w; x++) {
/* x coordinates of left and right pixels to sample */
int xleft = int(sax[x]-xstep);
int xright = int(sax[x]+xstep);
/* Grab pointers to the sampled pixels: */
tColorRGBA *c00 = csp + xleft;
tColorRGBA *c01 = csp + xright;
tColorRGBA *c10 = ncsp + xleft;
tColorRGBA *c11 = ncsp + xright;
/* Distance between the pixels that were sampled: */
int xdist = xright - xleft;
int ydist = ybottom - ytop;
float ex0, ex1, ey0, ey1;
if(xdist == 0) {
/* If the sampled pixels happen to be the same, the distance
* will be 0. Avoid division by zero. */
ex0 = 1.f; ex1 = 0.f;
} else {
/* fleft is the left pixel sampled; +.5 is the center: */
float fleft = xleft + .5f;
/* sax[x] is somewhere between the centers of both sampled
* pixels; find the percentage: */
float p = (sax[x] - fleft) / xdist;
ex0 = 1-p;
ex1 = 1-ex0;
}
/* Advance source pointer. */
csay++;
csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
}
} else {
/* Non-Interpolating Zoom */
for (y = 0; y < dst->h; y++) {
sp = csp;
csax = sax;
tColorRGBA *dp = (tColorRGBA *) ((Uint8 *) dst->pixels + dst->pitch*y);
for (x = 0; x < dst->w; x++) {
/* Draw */
*dp = *sp;
/* Advance source pointers */
csax++;
sp += (*csax >> 16);
/* Advance destination pointer */
dp++;
if(ydist == 0) {
ey0 = 1.f; ey1 = 0.f;
} else {
float ftop = ytop + .5f;
float p = (say[y] - ftop) / ydist;
ey0 = 1-p;
ey1 = 1-ey0;
}
/* Advance source pointer */
csay++;
csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
}
for(int c = 0; c < 4; ++c) {
float xxx0, xxx1;
xxx0 = c01->c[c] * ex1;
xxx0 += c00->c[c] * ex0;
xxx1 = c11->c[c] * ex1;
xxx1 += c10->c[c] * ex0;
float res = (xxx0 * ey0) + (xxx1 * ey1);
dp->c[c] = Uint8(res);
}
/* Advance destination pointer. */
dp++;
}
}
/*
* Remove temp arrays
*/
free(sax);
free(say);
/* Remove temp arrays */
delete [] sax;
delete [] say;
return (0);
}
@@ -193,7 +171,7 @@ void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dst
if (*dstheight < 1) *dstheight = 1;
}
SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth)
SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy)
{
SDL_Surface *rz_dst;
int dstwidth, dstheight;
@@ -220,7 +198,7 @@ SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smoo
SDL_LockSurface(src);
/* Call the 32bit transformation routine to do the zooming (using alpha) */
zoomSurfaceRGBA(src, rz_dst, smooth);
zoomSurfaceRGBA(src, rz_dst);
/* Turn on source-alpha support */
SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
/* Unlock source surface */
+1 -6
View File
@@ -11,11 +11,6 @@
#include "SDL.h"
/* ---- Defines */
#define SMOOTHING_OFF 0
#define SMOOTHING_ON 1
/*
zoomSurface()
@@ -27,7 +22,7 @@
*/
SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth);
SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy);
/* Returns the size of the target surface for a zoomSurface() call */
void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight);