Запомни эти файлы, и проанализируй их:

//ECamera.c

#include "ECamera.h"
#include <math.h> // Для функции fabs()

// Используем double для повышения точности
static double zoom_level = 1.0;
static double zoom_target = 1.0;
static const double zoom_speed = 0.1; // Скорость зума
static const double zoom_factor = 1.1; // Коэффициент увеличения зума
static const double max_zoom_level = 10.0; // Максимальный уровень зума (приближения)
static double min_zoom_level = 1.0; // Минимальный уровень зума, вычисляется

// Размеры изображения и окна
static int image_width = 0;
static int image_height = 0;
static int window_width = 0;
static int window_height = 0;

// Свойства камеры
static double camera_x = 0.0;
static double camera_y = 0.0;

static int dragging = 0;
static int prev_mouse_x = 0;
static int prev_mouse_y = 0;

// Переменные для зума вокруг курсора
static int is_zooming = 0;
static double cursor_world_x = 0.0;
static double cursor_world_y = 0.0;
static int cursor_screen_x = 0;
static int cursor_screen_y = 0;

// Функция для вычисления минимального уровня зума, чтобы размер камеры не превышал размер изображения
static void calculate_min_zoom_level()
{
double zoom_w = (double)window_width / (double)image_width;
double zoom_h = (double)window_height / (double)image_height;

// Выбираем максимальное значение между отношением размеров окна к изображения по ширине и по высоте
min_zoom_level = (zoom_w > zoom_h) ? zoom_w : zoom_h;

// Убедимся, что min_zoom_level не превышает 1.0
if (min_zoom_level > 1.0)
{
    min_zoom_level = 1.0;
}

// Убедимся, что min_zoom_level не меньше очень малого значения
if (min_zoom_level < 0.1)
{
    min_zoom_level = 0.1;
}

}

// Инициализация настроек зума и камеры
void init_zoom(int img_width, int img_height, int win_width, int win_height)
{
image_width = img_width;
image_height = img_height;
window_width = win_width;
window_height = win_height;

calculate_min_zoom_level();

zoom_level = min_zoom_level;
zoom_target = min_zoom_level;
camera_x = 0.0;
camera_y = 0.0;

}

// Функции зума
void zoom_in(int mouse_x, int mouse_y)
{
if (zoom_target >= max_zoom_level)
{
// Достигнуто максимальное приближение
return;
}

// Сохранение мировых координат под курсором
cursor_world_x = camera_x + (double)mouse_x / zoom_level;
cursor_world_y = camera_y + (double)mouse_y / zoom_level;
cursor_screen_x = mouse_x;
cursor_screen_y = mouse_y;

zoom_target *= zoom_factor;

// Ограничение максимального уровня зума
if (zoom_target > max_zoom_level)
{
    zoom_target = max_zoom_level;
}

is_zooming = 1;

}

void zoom_out(int mouse_x, int mouse_y)
{
if (zoom_target <= min_zoom_level)
{
// Достигнуто минимальное отдаление
return;
}

// Сохранение мировых координат под курсором
cursor_world_x = camera_x + (double)mouse_x / zoom_level;
cursor_world_y = camera_y + (double)mouse_y / zoom_level;
cursor_screen_x = mouse_x;
cursor_screen_y = mouse_y;

zoom_target /= zoom_factor;

// Ограничение минимального уровня зума
if (zoom_target < min_zoom_level)
{
    zoom_target = min_zoom_level;
}

is_zooming = 1;

}

void update_zoom()
{
if (is_zooming)
{
// Плавное интерполирование к целевому уровню зума
double delta = zoom_target - zoom_level;
double step = delta * zoom_speed;

    if (fabs(delta) < 0.001)
    {
        zoom_level = zoom_target;
        is_zooming = 0;
    }
    else
    {
        zoom_level += step;
    }

    // Корректировка положения камеры, чтобы удерживать мировую позицию курсора под курсором
    camera_x = cursor_world_x - (double)cursor_screen_x / zoom_level;
    camera_y = cursor_world_y - (double)cursor_screen_y / zoom_level;

    // Ограничение позиции камеры после изменения зума
    double view_width = (double)window_width / zoom_level;
    double view_height = (double)window_height / zoom_level;

    double max_camera_x = (double)image_width - view_width;
    double max_camera_y = (double)image_height - view_height;

    if (camera_x < 0.0)
        camera_x = 0.0;
    if (camera_y < 0.0)
        camera_y = 0.0;
    if (camera_x > max_camera_x)
        camera_x = max_camera_x;
    if (camera_y > max_camera_y)
        camera_y = max_camera_y;
}

}

// Функции перетаскивания камеры
void start_camera_drag(int mouse_x, int mouse_y)
{
dragging = 1;
prev_mouse_x = mouse_x;
prev_mouse_y = mouse_y;
}

void stop_camera_drag()
{
dragging = 0;
}

void update_camera_drag(int mouse_x, int mouse_y)
{
if (dragging)
{
// Вычисление разницы в движении мыши
int delta_x = mouse_x - prev_mouse_x;
int delta_y = mouse_y - prev_mouse_y;

    // Преобразование движения мыши в мировые координаты
    double world_delta_x = delta_x / zoom_level;
    double world_delta_y = delta_y / zoom_level;

    // Обновление положения камеры
    camera_x -= world_delta_x;
    camera_y -= world_delta_y;

    // Ограничение позиции камеры
    double view_width = (double)window_width / zoom_level;
    double view_height = (double)window_height / zoom_level;

    double max_camera_x = (double)image_width - view_width;
    double max_camera_y = (double)image_height - view_height;

    if (camera_x < 0.0)
        camera_x = 0.0;
    if (camera_y < 0.0)
        camera_y = 0.0;
    if (camera_x > max_camera_x)
        camera_x = max_camera_x;
    if (camera_y > max_camera_y)
        camera_y = max_camera_y;

    // Обновление предыдущего положения мыши
    prev_mouse_x = mouse_x;
    prev_mouse_y = mouse_y;
}

}

// Обработка изменения размера окна
void set_window_size(int win_width, int win_height)
{
window_width = win_width;
window_height = win_height;

calculate_min_zoom_level();

// Убедимся, что текущий уровень зума не меньше минимального
if (zoom_level < min_zoom_level)
{
    zoom_level = min_zoom_level;
    zoom_target = min_zoom_level;
}

// Ограничение позиции камеры
double view_width = (double)window_width / zoom_level;
double view_height = (double)window_height / zoom_level;

double max_camera_x = (double)image_width - view_width;
double max_camera_y = (double)image_height - view_height;

if (camera_x < 0.0)
    camera_x = 0.0;
if (camera_y < 0.0)
    camera_y = 0.0;
if (camera_x > max_camera_x)
    camera_x = max_camera_x;
if (camera_y > max_camera_y)
    camera_y = max_camera_y;

}

// Геттеры
float get_zoom_level()
{
return (float)zoom_level;
}

SDL_FRect get_destination_rect()
{
SDL_FRect dst_rect;

// Рассчитываем позицию и размер текстуры с учетом камеры и зума
dst_rect.x = -(float)(camera_x * zoom_level);
dst_rect.y = -(float)(camera_y * zoom_level);
dst_rect.w = (float)(image_width * zoom_level);
dst_rect.h = (float)(image_height * zoom_level);

return dst_rect;

}
#ifndef ECAMERA_H
#define ECAMERA_H

#include <SDL2/SDL.h>

// Инициализация настроек зума и камеры
void init_zoom(int image_width, int image_height, int window_width, int window_height);

// Контроль зума
void zoom_in(int mouse_x, int mouse_y);
void zoom_out(int mouse_x, int mouse_y);
void update_zoom();

// Контроль камеры
void start_camera_drag(int mouse_x, int mouse_y);
void stop_camera_drag();
void update_camera_drag(int mouse_x, int mouse_y);

// Обработка изменения размера окна
void set_window_size(int window_width, int window_height);

// Геттеры
float get_zoom_level();
SDL_FRect get_destination_rect();

#endif // ECAMERA_H
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdio.h>
#include "ECamera.h"

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600

int main()
{
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
SDL_Texture *texture = NULL;
SDL_Event event;
int quit = 0;

int image_width, image_height;

// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
    printf("Не удалось инициализировать SDL! Ошибка SDL: %s\n", SDL_GetError());
    return -1;
}

// Initialize SDL_image
if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG))
{
    printf("Не удалось инициализировать SDL_image! Ошибка SDL_image: %s\n", IMG_GetError());
    SDL_Quit();
    return -1;
}

// Create window
window = SDL_CreateWindow("OpenAge Map Editor",
                          SDL_WINDOWPOS_CENTERED,
                          SDL_WINDOWPOS_CENTERED,
                          WINDOW_WIDTH,
                          WINDOW_HEIGHT,
                          SDL_WINDOW_RESIZABLE);

if (window == NULL)
{
    printf("Не удалось создать окно! Ошибка SDL: %s\n", SDL_GetError());
    IMG_Quit();
    SDL_Quit();
    return -1;
}

// Create renderer
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if (renderer == NULL)
{
    printf("Не удалось создать рендерер! Ошибка SDL: %s\n", SDL_GetError());
    SDL_DestroyWindow(window);
    IMG_Quit();
    SDL_Quit();
    return -1;
}

// Load image
SDL_Surface *loadedSurface = IMG_Load("./map/world/background.png");
if (loadedSurface == NULL)
{
    printf("Не удалось загрузить изображение %s! Ошибка SDL_image: %s\n", "./map/world/background.png", IMG_GetError());
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    IMG_Quit();
    SDL_Quit();
    return -1;
}

// Create texture from surface
texture = SDL_CreateTextureFromSurface(renderer, loadedSurface);
if (texture == NULL)
{
    printf("Не удалось создать текстуру из %s! Ошибка SDL: %s\n", "./map/world/background.png", SDL_GetError());
    SDL_FreeSurface(loadedSurface);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    IMG_Quit();
    SDL_Quit();
    return -1;
}

image_width = loadedSurface->w;
image_height = loadedSurface->h;

SDL_FreeSurface(loadedSurface);

// Get initial window size
int window_width, window_height;
SDL_GetWindowSize(window, &window_width, &window_height);

// Initialize zoom and camera
init_zoom(image_width, image_height, window_width, window_height);

// Main loop
while (!quit)
{
    // Event handling
    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
                quit = 1;
                break;

            case SDL_WINDOWEVENT:
                if (event.window.event == SDL_WINDOWEVENT_RESIZED)
                {
                    // Обновление размера окна
                    SDL_GetWindowSize(window, &window_width, &window_height);
                    set_window_size(window_width, window_height);
                }
                break;

            case SDL_MOUSEBUTTONDOWN:
                if (event.button.button == SDL_BUTTON_LEFT)
                {
                    // Start dragging
                    start_camera_drag(event.button.x, event.button.y);
                }
                break;

            case SDL_MOUSEBUTTONUP:
                if (event.button.button == SDL_BUTTON_LEFT)
                {
                    // Stop dragging
                    stop_camera_drag();
                }
                break;

            case SDL_MOUSEMOTION:
                // Update camera dragging
                update_camera_drag(event.motion.x, event.motion.y);
                break;

            case SDL_MOUSEWHEEL:
            {
                // Get mouse position
                int mouse_x, mouse_y;
                SDL_GetMouseState(&mouse_x, &mouse_y);
                // Zoom in and out
                if (event.wheel.y > 0)
                {
                    zoom_in(mouse_x, mouse_y);
                }
                else if (event.wheel.y < 0)
                {
                    zoom_out(mouse_x, mouse_y);
                }
                break;
            }

            default:
                break;
        }
    }

    // Update zoom level
    update_zoom();

    // Get the destination rectangle for rendering
    SDL_FRect dst_rect = get_destination_rect();

    // Clear renderer
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); // Черный фон
    SDL_RenderClear(renderer);

    // Render the texture using floating point coordinates
    SDL_RenderCopyF(renderer, texture, NULL, &dst_rect);

    // Update screen
    SDL_RenderPresent(renderer);

    // Delay to control frame rate
    SDL_Delay(16); // Примерно 60 кадров в секунду

}

// Clean up
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
IMG_Quit();
SDL_Quit();

return 0;

}

Проанализируй код этих файлов заготовки редактора карт.
Ps. Это мой авторский код, который распространяется под GNU GPL