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
+16 -5
View File
@@ -254,21 +254,32 @@ void RageBitmapTexture::Create(
* cause lines; it'll be copied to the texture completely wrong). */ * cause lines; it'll be copied to the texture completely wrong). */
/* resize currently only does RGBA8888 */ /* resize currently only does RGBA8888 */
if(img->format->BitsPerPixel != 32 || img->format->colorkey) { {
int mask = 0; int mask = 0;
ConvertSDLSurface(img, img->w, img->h, PixFmtMasks[mask][4], ConvertSDLSurface(img, img->w, img->h, PixFmtMasks[mask][4],
PixFmtMasks[mask][0], PixFmtMasks[mask][1], PixFmtMasks[mask][2], PixFmtMasks[mask][3]); PixFmtMasks[mask][0], PixFmtMasks[mask][1], PixFmtMasks[mask][2], PixFmtMasks[mask][3]);
} }
while (m_iImageWidth != m_iTextureWidth || m_iImageHeight != m_iTextureHeight) {
SDL_Surface *dst; SDL_Surface *dst;
dst = zoomSurface(img, float(m_iTextureWidth)/m_iImageWidth,
float(m_iTextureHeight) / m_iImageHeight, SMOOTHING_ON); 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); SDL_FreeSurface(img);
img = dst; img = dst;
/* The new image size is the full texture size. */ /* The new image size is the full texture size. */
m_iImageWidth = m_iTextureWidth; m_iImageWidth = int(m_iImageWidth * xscale + .0001);
m_iImageHeight = m_iTextureHeight; m_iImageHeight = int(m_iImageHeight * yscale + .0001);
}
} }
if( bDither ) if( bDither )
+90 -112
View File
@@ -13,15 +13,12 @@
#include "SDL_rotozoom.h" #include "SDL_rotozoom.h"
#include <stdlib.h>
#include <math.h> #include <math.h>
struct tColorRGBA { 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. 32bit Zoomer with optional anti-aliasing by bilinear interpolation.
@@ -29,133 +26,114 @@ struct tColorRGBA {
Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. 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 */ float *sax = new float[dst->w];
// if (smooth) { float *say = new float[dst->h];
/* 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);
}
/* Precalculate row increments */ /* Precalculate row increments */
int csx = 0; int x, y;
int *csax = sax; /* sax[x] is the exact (floating-point) x coordinate in the source
for (x = 0; x <= dst->w; x++) { * that the destination pixel at x should from. For example, if we're
*csax = csx; * going 512->256, then dst[0] should come from the pixels from 0..1 and
csx &= 0xffff; * 1..2, so sax[0] is 1. sx is the total number of pixels, so sx/2 is the
csax++; * distance from the start of the sample to its center. */
csx += sx; for (x = 0; x < dst->w; x++)
} sax[x] = sx*x + sx/2;
int csy = 0;
int *csay = say;
for (y = 0; y <= dst->h; y++) {
*csay = csy;
csy &= 0xffff;
csay++;
csy += sy;
}
/* Pointer setup */ for (y = 0; y < dst->h; y++)
tColorRGBA *sp, *csp; say[y] = sy*y + sy/2;
sp = csp = (tColorRGBA *) src->pixels;
/* Switch between interpolating and non-interpolating code */ tColorRGBA *sp = (tColorRGBA *) src->pixels;
csay = say;
if (smooth) {
/* Interpolating Zoom */
/* Scan destination */ /* Scan destination */
for (y = 0; y < dst->h; y++) { 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. */ /* Set destination pointer. */
/* 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;
int ytop = int(say[y]-ystep);
int ybottom = int(say[y]+ystep);
tColorRGBA *dp = (tColorRGBA *) ((Uint8 *) dst->pixels + dst->pitch*y); 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++) { 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: */
* Interpolate colors tColorRGBA *c00 = csp + xleft;
*/ tColorRGBA *c01 = csp + xright;
int ex = *csax & 0xffff; tColorRGBA *c10 = ncsp + xleft;
int ey = *csay & 0xffff; tColorRGBA *c11 = ncsp + xright;
int t1, t2;
t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; /* Distance between the pixels that were sampled: */
t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; int xdist = xright - xleft;
dp->r = (Uint8)((((t2 - t1) * ey) >> 16) + t1); int ydist = ybottom - ytop;
t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; float ex0, ex1, ey0, ey1;
dp->g = (Uint8)((((t2 - t1) * ey) >> 16) + t1); if(xdist == 0) {
t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; /* If the sampled pixels happen to be the same, the distance
t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; * will be 0. Avoid division by zero. */
dp->b = (Uint8)((((t2 - t1) * ey) >> 16) + t1); ex0 = 1.f; ex1 = 0.f;
t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; } else {
t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; /* fleft is the left pixel sampled; +.5 is the center: */
dp->a = (Uint8)((((t2 - t1) * ey) >> 16) + t1); 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;
}
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;
}
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 source pointers. */
csax++;
int sstep = (*csax >> 16);
c00 += sstep;
c01 += sstep;
c10 += sstep;
c11 += sstep;
/* Advance destination pointer. */ /* Advance destination pointer. */
dp++; dp++;
} }
/* Advance source pointer. */
csay++;
csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
} }
} else { /* Remove temp arrays */
/* Non-Interpolating Zoom */ delete [] sax;
for (y = 0; y < dst->h; y++) { delete [] say;
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++;
}
/* Advance source pointer */
csay++;
csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
}
}
/*
* Remove temp arrays
*/
free(sax);
free(say);
return (0); return (0);
} }
@@ -193,7 +171,7 @@ void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dst
if (*dstheight < 1) *dstheight = 1; 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; SDL_Surface *rz_dst;
int dstwidth, dstheight; int dstwidth, dstheight;
@@ -220,7 +198,7 @@ SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smoo
SDL_LockSurface(src); SDL_LockSurface(src);
/* Call the 32bit transformation routine to do the zooming (using alpha) */ /* 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 */ /* Turn on source-alpha support */
SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
/* Unlock source surface */ /* Unlock source surface */
+1 -6
View File
@@ -11,11 +11,6 @@
#include "SDL.h" #include "SDL.h"
/* ---- Defines */
#define SMOOTHING_OFF 0
#define SMOOTHING_ON 1
/* /*
zoomSurface() 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 */ /* 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); void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight);