I’m not sure if this problem is due to misunderstanding of
the way SDL works or just a stupid error on my part. For
some reason, this vanilla “nearest neighbour” scaling function
I’ve written correctly scales in the X direction but not in
the Y direction (the image appears interlaced or totally
corrupted with fractional scaling values):

void
scale(SDL_Surface *dst, const SDL_Surface *src, double scale_x, double
scale_y)
{
unsigned int dst_w = dst->w;
unsigned int dst_h = dst->h;
unsigned int src_w = src->w;
unsigned int x;
unsigned int y;
unsigned int src_y;
unsigned int src_x;
unsigned int dst_y;
uint32 *dst_pix = dst->pixels;
uint32 *src_pix = src->pixels;

scale_x = 1 / scale_x;
scale_y = 1 / scale_y;

for (y = 0; y < dst_h; ++y) {
src_y = (y * src_w) * scale_y;
dst_y = y * dst_w;
for (x = 0; x < dst_w; ++x) {
src_x = x * scale_x;
dst_pix[dst_y + x] = src_pix[src_y + src_x];
}
}
}

I have made sure that dst is large enough to hold the scaled
pixels and that both surfaces are in 32 bit RGBA format.

I think something is wrong with this line:
src_y = (y * src_w) * scale_y;
Here, you’re scaling the source y-coord, but you should be more careful. The part in parentheses is giving you an integer that represents the index of the beginning of a line. Then you scale this index by a double. That will end up giving you fractions of a line as the apparent beginning of the line. You would probably be best off by scaling the y-coordinate, rounding or flooring it, then finding the start of the line.
Try replacing that line similar to this:
src_y = int(y * scale_y) * src_w;

To: sdl at lists.libsdl.org
Date: Fri, 14 Dec 2007 08:29:12 -0800
Subject: [SDL] Somethng wrong with my scaling function?

Hi.

I’m not sure if this problem is due to misunderstanding of
the way SDL works or just a stupid error on my part. For
some reason, this vanilla “nearest neighbour” scaling function
I’ve written correctly scales in the X direction but not in
the Y direction (the image appears interlaced or totally
corrupted with fractional scaling values):

void
scale(SDL_Surface *dst, const SDL_Surface *src, double scale_x, double
scale_y)
{
unsigned int dst_w = dst->w;
unsigned int dst_h = dst->h;
unsigned int src_w = src->w;
unsigned int x;
unsigned int y;
unsigned int src_y;
unsigned int src_x;
unsigned int dst_y;
uint32 *dst_pix = dst->pixels;
uint32 *src_pix = src->pixels;

scale_x = 1 / scale_x;
scale_y = 1 / scale_y;

for (y = 0; y < dst_h; ++y) {
src_y = (y * src_w) * scale_y;
dst_y = y * dst_w;
for (x = 0; x < dst_w; ++x) {
src_x = x * scale_x;
dst_pix[dst_y + x] = src_pix[src_y + src_x];
}
}
}

I have made sure that dst is large enough to hold the scaled
pixels and that both surfaces are in 32 bit RGBA format.

On Fri, 14 Dec 2007 21:22:27 -0500, “Jonathan Dearborn”
said:

Hey,

I think something is wrong with this line:
src_y = (y * src_w) * scale_y;
Here, you’re scaling the source y-coord, but you should be more careful.
The part in parentheses is giving you an integer that represents the
index of the beginning of a line. Then you scale this index by a double.
That will end up giving you fractions of a line as the apparent
beginning of the line. You would probably be best off by scaling the
y-coordinate, rounding or flooring it, then finding the start of the
line.
Try replacing that line similar to this:
src_y = int(y * scale_y) * src_w;

Hello.

Yes, you’re most likely right. I gave up trying it this way and
ended up with the following code instead (which works flawlessly):

static inline void
scale(SDL_Surface *dst, const SDL_Surface *src, double scale_x, double
scale_y)
{
unsigned int dst_w = dst->w;
unsigned int dst_h = dst->h;
unsigned int src_w = src->w;
unsigned int x;
unsigned int y;
unsigned int sx;
unsigned int sy;
unsigned int s_off;
unsigned int d_off;
unsigned int dy_off;
unsigned int sy_off;
uint32 *dst_pix = dst->pixels;
uint32 *src_pix = src->pixels;

scale_x = 1 / scale_x;
scale_y = 1 / scale_y;

for (y = 0; y < dst_h; ++y) {
sy = FLOATCAST_DTL(floor(y * scale_y));
sy_off = sy * src_w;
dy_off = y * dst_w;
for (x = 0; x < dst_w; ++x) {
sx = FLOATCAST_DTL(floor(x * scale_x));
d_off = dy_off + x;
s_off = sy_off + sx;
dst_pix[d_off] = src_pix[s_off];
}
}
}

FLOATCAST_DTL() is a macro which evaluates to ‘lrintf()’ if the
host system has it, or a plain cast from double to long otherwise.