SDL Image rendering
So wanted to make a live wallpaper manager that doesn't make my GPU go brrrr..but this ain't so easy at least for where I am today.
My idea was to use C++ and keep it insanely scoped ! But before that I need to start with basics:
- C++
- SDL3
I decided to make a simple image viewer which I achieved and here is rant and basically note on what I learned and why I did what I did because I know I will forget this stuff so better document it for future me !
Basic C++ game loop logic using SDL3 and SDL3_image to render the image.
Quick recap on what I learned on this one because the image scaling is what will take the rest of the page:
Base SDL3 only supports loading Bitmap images. It does so by having a struct called SDL_Surface this struct holds pixel info and resides on RAM (obv) !, I used the SDL_LoadBMP() to load a Bitmap image into my SDL_Surface struct !
Then calling SDL_CreateTextureFromSurface function would load it into my GPU/iGPU which after that I use SDL_RenderTexture() to write it to the buffer and finally calling SDL_RenderPresent() renders the actual image !
After this I downloaded SDL3_image which would allow me directly load the image in GPU memory and skipping this SDL_Surface step totally or at least it abstracts it, I don't know. Moreover SDL3_image supports wide range of image extensions like JPG, PNG, WEBP, etc. It even worked with JFIF !
Now for the spicy part the image scaling and moving it to the middle of the screen !
The issue:
Images are not of the same size some scale really well if the aspect ratio of the image and my main window matches. I had my window at 1280x720 so images with resolution like 1920x1080 fit perfectly but images like 736x1584 would just stretch fully to fill the window.
Cause ? This -> bool success = SDL_RenderTexture(renderer, texture, nullptr, nullptr);
I passed both the source and destination as nullptr ! This was intentional at first cuz I was trying to just render the image ! Now what is this src and dest ?
So basically SDL Texture rendering needs to know the bounding box for the image (src) and the bounding box for the holder inside the main window where the image will sit (dest).
These bounding boxes can be defined using SDL_FRect structs which have x, y, w, h just these params. (X-axis, Y-axis, width, height) !
What is this bounding box ?
Bounding box is just like a frame. We got 2 frames one placed to measure the image on how much the image will be cropped. For example if a image is 300x300, and If I put a 200x200 frame then the image will get cropped off, that is exactly what the src bounding box decides how much the image will be cropped from source.
Now for the dest bounding box, its also a frame but place on the window...The image after getting cropped from the src frame will sit inside this box and it will fill this box no matter what ! If this box is bigger than than the main window it will just cut off the main window.
And for that we need to scale the image in such a way that it fits perfectly without stretching or cutting off.
Scaling the Image
This one was a challenge because it involves math ! And I hate maths !
First and foremost -> If I resize a image from either the width side or height side only I either get a fat image or a skinny image ! So to actually get it look properly and scaled it down I must reduce both the width and the height simultaneously !
With that said lets take example
I have an window of 1280x720 and an image of 600x800 !
I need to fit that image inside my main window, the width fits no issues but the height ? Its bigger than my main window height so I need to shrink it and as discussed just shrinking the height would make it come fat ! FAT CAT lol 😆 Jokes aside,
Lets see this intuitively, If I have 100 and only want to keep 50 I can do 100/2 = 50, here 2 is the factor I needed to reduce 100 to 50 !
I other words,
I have: 100
I need: 50
100 * x = 50
or, x = 50/100 = 0.5
So its saying I have 100 and what else do I need to multiply to get 50 and its obvious that the value must be something between 0 and 1 !
Similarly, in this case I am trying to fit a 800px high image into a 720px high window so we scale down,
I have: 800
I need: 720
So,
800 * x = 720
or, x = 720/800 = 0.9
That 0.9 is the factor, the how much times I need to shrink both my height and width of the image to actually fit it inside the window !
Calculating...
Scaled width = 600 * 0.9 = 540
Scaled height = 800 * 0.9 = 720 //Now this fits !
With this not it fits !
Lets look at another example
Same window 1280x720 but image is 1600x1200
Both width and height needs to scaled down so how do we calculate the factor here ? Lets do width first
I have: 1600
I need: 1280
So,
1600 * x = 1280
or, x = 1280/1600 = 0.8
Let's scale down !
Scaled width = 1600 * 0.8 = 1280 //fits
Scaled height = 1200 * 0.8 = 960 //doesn't fit !
So scaling based on width failed because the width scaled down fits but the height still is more, now lets do the same height wise
I have: 1200
I need: 720
So,
1200 * x = 720
or, x = 720/1200 = 0.6
Let's scale down !
Scaled width = 1600 * 0.6 = 960 //fits
Scaled height = 1200 * 0.6 = 720 //Now this fits too !
With height scaling both fits ! Long story short I calculated the factor for both and took the smaller factor !
Took quite a while to understand !
Centering the Image
Another math based thing...I am tired seriously...
Now it was time to center it !
First I found the center of the window which was easy
(Window_width / 2, Window_height / 2) is the center of the window ! Seriously, the half of width and height is the center of the screen ! Yk what lets calculate !
Window width = 1280; 1280/2 = 640, Window height = 720; 720/2 = 360, So, Middle of the window is (640, 360) !
I want the image to be in the middle too ! The key insight for this that if the image is also centered,
The center of the image and the center of the window will intersect !
This was the key insight !
Now lets see what we have:
- Center of window = (640, 360);
- Width of scaled image = 960px;
- Height of scaled image = 720px;
With this info we got things we can work out !
Since I want my image's and window's center to intersect that means both share the same center and from the center if I subtract half of image's width I will land on the edge of the image on the X axis (horizontally) and if I do the same vertically I will land on the edge of the image on the Y-axis (vertically).
So taking a point in the center I first move horizontally to the left (X-axis) I land on edge and now I move vertically towards the top (Y-axis) and land on the starting point of the image ! Therefore, Now I got where the image should start from if the center is at (640, 360) in this case.
imageStartWidth = windowWidthCenter - (scaledImageWidth / 2);
imageStartHeight = windowHeightCenter - (scaledImageHeight / 2);
Note: Can I be wrong in some explanations and understandings ? Definitely I am no god or intelligent guy. What I wrote is the best to my knowledge and only intended for me ! And this note is for someone who just might by accident happen to read this rant of mine...
Cheers 🩵
References:
Thanks to these I was able to code and understand what I did today:
- https://freepascal-meets-sdl.net/rendering-a-bitmap-file-with-sdl3/
- https://freepascal-meets-sdl.net/rendering-image-files-with-any-format-sdl3_image/
- https://wiki.libsdl.org/SDL3/FrontPage
- https://wiki.libsdl.org/SDL3_image/FrontPage
- https://chatgpt.com (Obviously nobody would sit through me to teach me those maths !)
- My repo: https://github.com/NexusWasLost/SDL_Playground/tree/got_distracted