diff --git a/stepmania/src/RageBitmapTexture.cpp b/stepmania/src/RageBitmapTexture.cpp index 39815de2c5..a5f329d1d2 100644 --- a/stepmania/src/RageBitmapTexture.cpp +++ b/stepmania/src/RageBitmapTexture.cpp @@ -157,8 +157,6 @@ void RageBitmapTexture::Create() if( img->w != m_iImageWidth || img->h != m_iImageHeight ) { - /* resize currently only does RGBA8888 */ - RageSurfaceUtils::ConvertSurface(img, img->w, img->h, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); zoomSurface(img, m_iImageWidth, m_iImageHeight ); } diff --git a/stepmania/src/SDL_rotozoom.cpp b/stepmania/src/SDL_rotozoom.cpp index 2514967cd3..15bc773fc1 100644 --- a/stepmania/src/SDL_rotozoom.cpp +++ b/stepmania/src/SDL_rotozoom.cpp @@ -1,6 +1,7 @@ #include "global.h" #include "SDL_rotozoom.h" #include "RageSurface.h" +#include "RageSurfaceUtils.h" #include "RageUtil.h" #include @@ -14,92 +15,105 @@ using namespace std; * lines.) */ -static void ZoomSurface( RageSurface * src, RageSurface * dst ) +static void InitVectors( vector &s0, vector &s1, vector &percent, int src, int dst ) { - /* Ratio from source to dest. */ - const float sx = float(src->w) / dst->w; - const float sy = float(src->h) / dst->h; - - /* For each destination coordinate, two source rows, two source columns - * and the percentage of the first row and first column: */ - vector esx0, esx1, esy0, esy1; - vector ex0, ey0; - - 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++ ) + if( src >= dst ) { - float sax = sx*x + sx/2; - - /* 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. */ - const float xstep = sx/4; - - /* source x coordinates of left and right pixels to sample */ - esx0.push_back(int(sax-xstep)); - esx1.push_back(int(sax+xstep)); - - if( esx1[x] == esx0[x] ) + float sx = float(src) / dst; + printf("sx %f\n", sx); + for( int x = 0; x < dst; x++ ) { - /* If the sampled pixels happen to be the same, the distance - * will be 0. Avoid division by zero. */ - ex0.push_back( 1.f ); - } else { - const int xdist = esx1[x] - esx0[x]; + /* sax 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. */ + const float sax = sx*x + sx/2; - /* fleft is the left pixel sampled; +.5 is the center: */ - const float fleft = esx0[x] + .5f; - - /* sax is somewhere between the centers of both sampled - * pixels; find the percentage: */ - const float p = (sax - fleft) / xdist; - ex0.push_back(1-p); + /* 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. */ + const float xstep = sx/4; + + /* source x coordinates of left and right pixels to sample */ + s0.push_back(int(sax-xstep)); + s1.push_back(int(sax+xstep)); + + if( s0[x] == s1[x] ) + { + /* If the sampled pixels happen to be the same, the distance + * will be 0. Avoid division by zero. */ + percent.push_back( 1.f ); + } else { + const int xdist = s1[x] - s0[x]; + + /* fleft is the left pixel sampled; +.5 is the center: */ + const float fleft = s0[x] + .5f; + + /* sax is somewhere between the centers of both sampled + * pixels; find the percentage: */ + const float p = (sax - fleft) / xdist; + percent.push_back(1-p); + printf("p %.3f\n", p); + } } - } - - for( y = 0; y < dst->h; y++ ) + } + else { - float say = sy*y + sy/2; - - float ystep = sy/4; - - esy0.push_back( int(say-ystep) ); - esy1.push_back( int(say+ystep) ); - - if( esy0[y] == esy1[y] ) + /* + * Fencepost: If we have source: + * abcd + * and dest: + * xyz + * then we want x to be sampled entirely from a, and z entirely from d; + * the inner pixels are interpolated. (This behavior mimics Photoshop's + * resize.) + */ + float sx = float(src-1) / (dst-1); + printf("sx %f\n", sx); + for( int x = 0; x < dst; x++ ) { - ey0.push_back( 1.f ); - } else { - const int ydist = esy1[y] - esy0[y]; - const float ftop = esy0[y] + .5f; - const float p = (say - ftop) / ydist; - ey0.push_back( 1-p ); + const float sax = sx*x; + + /* source x coordinates of left and right pixels to sample */ + s0.push_back( clamp(int(sax), 0, src-1)); + s1.push_back( clamp(int(sax+1), 0, src-1) ); + printf("sax: %.3f, esx0 %i, esx1 %i\n", sax, s0.back(), s1.back()); + + const float p = 1 - (sax - floorf(sax)); + percent.push_back( p ); + printf("p %.3f\n", p); } - } + } +} - const uint8_t *sp = (uint8_t *) src->pixels; +static void ZoomSurface( const RageSurface * src, RageSurface * dst ) +{ + /* For each destination coordinate, two source rows, two source columns + * and the percentage of the first row and first column: */ + vector esx0, esx1, esy0, esy1; + vector ex0, ey0; - for( y = 0; y < dst->h; y++ ) + InitVectors( esx0, esx1, ex0, src->w, dst->w ); + InitVectors( esy0, esy1, ey0, src->h, dst->h ); + + /* This is where all of the real work is done. */ + const uint8_t *sp = (uint8_t *) src->pixels; + for( int y = 0; y < dst->h; y++ ) { - uint8_t *dp = ((uint8_t *) dst->pixels + dst->pitch*y); + uint32_t *dp = (uint32_t *) (dst->pixels + dst->pitch*y); /* current source pointer and next source pointer (first and second * rows sampled for this row): */ - const uint8_t *csp = (uint8_t *) (sp + esy0[y] * src->pitch); - const uint8_t *ncsp = (uint8_t *) (sp + esy1[y] * src->pitch); + const uint8_t *csp = sp + esy0[y] * src->pitch; + const uint8_t *ncsp = sp + esy1[y] * src->pitch; - for( x = 0; x < dst->w; x++ ) + for( int x = 0; x < dst->w; x++ ) { /* Grab pointers to the sampled pixels: */ - const uint8_t *c00 = (uint8_t *) (csp + esx0[x]*4); - const uint8_t *c01 = (uint8_t *) (csp + esx1[x]*4); - const uint8_t *c10 = (uint8_t *) (ncsp + esx0[x]*4); - const uint8_t *c11 = (uint8_t *) (ncsp + esx1[x]*4); + const uint8_t *c00 = csp + esx0[x]*4; + const uint8_t *c01 = csp + esx1[x]*4; + const uint8_t *c10 = ncsp + esx0[x]*4; + const uint8_t *c11 = ncsp + esx1[x]*4; uint8_t color[4]; for( int c = 0; c < 4; ++c ) @@ -113,10 +127,10 @@ static void ZoomSurface( RageSurface * src, RageSurface * dst ) const float res = (x0 * ey0[y]) + (x1 * (1-ey0[y])); color[c] = uint8_t(res); } - *(uint32_t *) dp = *(uint32_t *) color; + *dp = *(uint32_t *) color; /* Advance destination pointer. */ - dp += 4; + ++dp; } } } @@ -127,19 +141,26 @@ void zoomSurface( RageSurface *&src, int dstwidth, int dstheight ) if( src == NULL ) return; + /* resize currently only does RGBA8888 */ + if( src->fmt.BytesPerPixel != 4 ) + { + RageSurfaceUtils::ConvertSurface( src, src->w, src->h, 32, + 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); + } + while( src->w != dstwidth || src->h != dstheight ) { float xscale = float(dstwidth)/src->w; float yscale = float(dstheight)/src->h; - /* Our 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 ); + /* Our filter is a simple linear filter, so it can't scale to less than + * 1:2 or more than 2:1 very well. If we need to go beyond that, do it + * iteratively. */ + xscale = clamp( xscale, .5f, 2.0f ); + yscale = clamp( yscale, .5f, 2.0f ); - int target_width = int(src->w*xscale + .5); - int target_height = int(src->h*yscale + .5); + int target_width = lrintf( src->w*xscale ); + int target_height = lrintf( src->h*yscale ); RageSurface *dst = CreateSurface(target_width, target_height, 32,