0

I am currently developing a tile-based game in C and I'm trying to implement zooming using the nearest neighbor algorithm.

This is how the algorithm it looks right now:

u32 *DestPixel = (u32 *)DestBitmap.Pixels;
u32 *SourcePixel = (u32 *)SourceBitmap->Pixels;

f64 RatioX = (f64)SourceBitmap->Width / (f64)DestBitmap.Width;
f64 RatioY = (f64)SourceBitmap->Height / (f64)DestBitmap.Height;

for(s32 Y = 0; 
    Y < DestBitmap.Height;
    ++Y)
{
    for(s32 X = 0;
        X < DestBitmap.Width;
        ++X)
    {
        s32 ScaledX = (s32)(X * RatioX);
        s32 ScaledY = (s32)(Y * RatioY);
        
        s32 DestOffset = Y*DestBitmap.Width + X;
        s32 SourceOffset = ScaledY*SourceBitmap->Width + ScaledX;
        *(DestPixel + DestOffset) = *(SourcePixel + SourceOffset); 
    }
}

However, it is not producing the results I want. When trying to convert the source bitmap (64x64) to a bitmap whose size is not a power of 2 (in this case 30x30), the scaled bitmap looks weird on the right side. Left side is the original bitmap while the right side is the scaled bitmap:

enter image description here

What I've tried:

  • Rounding ScaledX and ScaledY (instead of truncating)
  • Flooring ScaledX and ScaledY (instead of truncating)
  • Just a guess, but I'd try rounding commercially (note that casting only always rounds towards zero; note that + .5 is not correct for negative values, but won't occur here anyway): `x * ratioX + .5` or using [`round`](https://en.cppreference.com/w/c/numeric/math/round) function. – Aconcagua Oct 04 '22 at 09:47
  • @Aconcagua This produces the same results: `s32 ScaledX = (s32)round(X * RatioX); s32 ScaledY = (s32)round(Y * RatioY);` –  Oct 04 '22 at 09:51
  • Just a side note: You can profit from syntactic sugar to read and write a bit more conveniently: `destPixel[destOffset] = sourcePixel[sourceOffset];` – Aconcagua Oct 04 '22 at 10:17
  • Any particular reason why you are using non-standard types and exotic for loop formatting? – Lundin Oct 04 '22 at 10:36
  • @Lundin u32 are typedef for uint32_t and so on to ensure that the sizes are the same for every platform. My for loop formatting is because i code with a splitted window and I dont want my for loop to expand outside my window. I also think the for loop looks clearer this way –  Oct 05 '22 at 11:15
  • Use uint32_t, don't invent a non-standard. Don't let your poor tool chain setup dictate how you write source code. Or do you intend to rewrite all your source code next time you change monitor? "I also think the for loop looks clearer this way" Clear `for` loops look like how 99% of all C programmers write them and expect them to look. Millions of people have programmed in C before you, so the chance of you coming up with some revolutionary coding style nobody has considered before is close to non-existent. – Lundin Oct 05 '22 at 12:04
  • @Lundin To your first point: while i agree that you shouldn't use non-standard types, i don't agree that s32 or u32 doesnt conform to the standard. I have read plenty of professional ode that typdef int32_t to either int32, s32 or i32. And its not like its not portable. To your second point: i won't change the syntax if I change my monitor, the cpu doesnt really car how the for loop looks and it is not like it is unreadable. I write my code depending on mu current setp. To your third point: i never claimed it is the best style for everyone, i personally just like it and therefore stick with it –  Oct 05 '22 at 13:08
  • Standard = everyone doing the same thing. Not everyone doing things after personal preferences. Also there is plenty of other "code smell" in this little snippet alone that I didn't even comment on, like those very fishy pointer casts. Consider posting the complete working code at https://codereview.stackexchange.com/ – Lundin Oct 05 '22 at 13:58
  • @Lundin Where did you read that int32_t is the standard? As i said, I have read lots of professional code that typedefs int32_t. I think you are making stuff up. You dont have to follow the standard either, nobody follows the standard and therefore i dont think there is any standard. The casting is a very simple one which is very popular in game development to cast a 8 bit pointer which points to the bitmap memory, to a 32 bit pointer which can then be interpreted as pixels. I assume you haven't done much game programming if you think that smells. –  Oct 05 '22 at 14:09
  • @HampusJohansson I read it in _the_ standard ISO/IEC 9899:2018 Programming Languages - C. Chapter 7.20. Everyone follows this standard. – Lundin Oct 05 '22 at 14:11
  • "The casting is a very simple one which is very popular in game development to cast a 8 bit pointer which points to the bitmap memory, to a 32 bit pointer which can then be interpreted as pixels. I assume you haven't done much game programming if you think that smells" I guess you have never heard of alignment and the strict aliasing rule. I guess you haven't done much computer programming if you don't know about such basic things as alignment. – Lundin Oct 05 '22 at 14:12
  • @Lundin The pointer pointing to the memory of the bitmap (DestBitmap.Pixels) will never be written to directly and can therefore be of any type, such as uint32 *. It doesnt matter and therefore does the strict aliasing rule not apply here. Doing casting like this will never lead to UB and is standard in gameprogramming. There are also serveras cases where the rule could be broken to improve performance and other –  Oct 05 '22 at 14:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/248567/discussion-between-hampus-johansson-and-lundin). –  Oct 05 '22 at 14:33

1 Answers1

2

I actually solved it. It was the rendering of the bitmaps that the problem was, not the alogrithm. I was drawing 4 pixels at a time in my rendering code which doesn't work with 30x30, but only with power of 2s.

  • Great you found it (algorithm seemed correct to me as well) – still would apply the rounding, though, should find the nearest neighbour more precisely... – Aconcagua Oct 04 '22 at 10:16
  • @Aconcagua Yup. I applied the rundning. Thank you! –  Oct 05 '22 at 11:16