Cropping and Positioning Images

Fitting an Image to Window in SDL2

How do I ensure an image always fits within the window, regardless of its size?

Abstract art representing computer programming

Ensuring that an image always fits within the window, regardless of its size, is a common requirement in many applications. This involves scaling the image while maintaining its aspect ratio. Here's how you can implement this in SDL2:

#include <SDL.h>

#include <algorithm>
#include <iostream>

class ScalableImage {
public:
  ScalableImage(const char* file) : surface{
    SDL_LoadBMP(file)} {
    if (!surface) {
      std::cerr << "Failed to load image: " <<
        SDL_GetError() << '\n';
    }
  }

  void RenderFit(SDL_Surface* destSurface) {
    if (!surface || !destSurface) return;

    // Calculate scaling factors
    float scaleX = static_cast<float>(
      destSurface->w) / surface->w;
    float scaleY = static_cast<float>(
      destSurface->h) / surface->h;
    float scale = std::min(scaleX, scaleY);

    // Calculate new dimensions
    int newWidth = static_cast<int>(surface->w *
      scale);
    int newHeight = static_cast<int>(surface->h
      * scale);

    // Calculate position to center the image
    int x = (destSurface->w - newWidth) / 2;
    int y = (destSurface->h - newHeight) / 2;

    SDL_Rect srcRect{
      0, 0, surface->w, surface->h};
    SDL_Rect destRect{
      x, y, newWidth, newHeight};

    SDL_BlitScaled(surface, &srcRect,
                   destSurface, &destRect);

    std::cout << "Rendered at: x=" << destRect.x
      << ", y=" << destRect.y
      << ", w=" << destRect.w << ", h=" <<
      destRect.h << '\n';
  }

  ~ScalableImage() {
    if (surface) SDL_FreeSurface(surface);
  }

private:
  SDL_Surface* surface;
};

int main() {
  SDL_Init(SDL_INIT_VIDEO);

  SDL_Window* window =
    SDL_CreateWindow("Fit Image to Window",
                     SDL_WINDOWPOS_UNDEFINED,
                     SDL_WINDOWPOS_UNDEFINED,
                     640, 480,
                     SDL_WINDOW_RESIZABLE);
  SDL_Surface* screenSurface =
    SDL_GetWindowSurface(window);

  ScalableImage img{"example.bmp"};

  bool quit = false;
  SDL_Event e;

  while (!quit) {
    while (SDL_PollEvent(&e) != 0) {
      if (e.type == SDL_QUIT) {
        quit = true;
      } else if (e.type == SDL_WINDOWEVENT &&
        e.window.event ==
        SDL_WINDOWEVENT_SIZE_CHANGED) {
        screenSurface = SDL_GetWindowSurface(
          window);
      }
    }

    SDL_FillRect(screenSurface, nullptr,
      SDL_MapRGB(screenSurface->format,
        255, 255, 255));
    img.RenderFit(screenSurface);
    SDL_UpdateWindowSurface(window);
  }

  SDL_DestroyWindow(window);
  SDL_Quit();

  return 0;
}

This example introduces a ScalableImage class with a RenderFit() method that scales the image to fit within the window. Here's how it works:

  1. We calculate scaling factors for both width and height.
  2. We use the smaller of these factors to maintain the aspect ratio.
  3. We calculate the new dimensions based on this scale factor.
  4. We center the scaled image within the window.
  5. We use SDL_BlitScaled() instead of SDL_BlitSurface() to perform the scaling.

The key part is in the RenderFit() method:

float scaleX = static_cast<float>(destSurface->
  w) / surface->w;
float scaleY = static_cast<float>(destSurface->
  h) / surface->h;
float scale = std::min(scaleX, scaleY);

int newWidth = static_cast<int>(surface->w *
  scale);
int newHeight = static_cast<int>(surface->h *
  scale);

This ensures that the image is scaled to fit either the width or height of the window, whichever is smaller, thus guaranteeing it always fits.

In the main() function, we've made the window resizable and added an event loop to handle window size changes. This allows us to demonstrate that the image continues to fit even as the window is resized.

Remember that SDL_BlitScaled() might not provide the highest quality scaling for all types of images. For more advanced scaling techniques, you might want to consider using SDL2's rendering API with SDL_RenderCopy(), or a dedicated image processing library.

This technique is useful for:

  • Creating responsive UIs that adapt to different screen sizes
  • Implementing "fit to screen" functionality in image viewers
  • Displaying background images that always cover the entire window
  • Creating scalable game UIs that work across different resolutions
This Question is from the Lesson:

Cropping and Positioning Images

Learn to precisely control image display using source and destination rectangles.

Answers to questions are automatically generated and may not have been reviewed.

This Question is from the Lesson:

Cropping and Positioning Images

Learn to precisely control image display using source and destination rectangles.

sdl2-promo.jpg
Part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 51 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved