#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdbool.h>
typedef unsigned char u8;
#define TITLE "Image Viewer"
#define WIDTH 1500
#define HEIGHT 900
#define ZOOM_COEFF 0.1
int dpi_w, dpi_h;
SDL_Window *window;
SDL_Renderer *renderer;
bool init(const char *title, int width, int height) {
int res = SDL_Init(SDL_INIT_VIDEO);
if (res != 0) {
SDL_Log("SDL_Init() failed (%d)", res);
return false;
}
window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_RESIZABLE);
if (!window) {
SDL_Log("SDL_CreateWindow() failed");
return false;
}
float hdpi, vdpi;
SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(window), NULL, &hdpi, &vdpi);
dpi_w = (int)hdpi;
dpi_h = (int)vdpi;
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) {
SDL_Log("SDL_CreateRenderer() failed");
return false;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
return true;
}
int clamp(int n, int min, int max) {
if (n < min)
return min;
if (n > max && max >= min)
return max;
return n;
}
int main(int argc, char **argv) {
if (!init(TITLE, WIDTH, HEIGHT))
return 1;
SDL_Surface *map_sf = IMG_Load(argv[1]);
if (!map_sf)
return 2;
int map_w = map_sf->w;
int map_h = map_sf->h;
SDL_Texture *map = SDL_CreateTextureFromSurface(renderer, map_sf);
SDL_FreeSurface(map_sf);
float x = 0, y = 0;
float scale = 1.0;
float cursor_x = 0, cursor_y = 0;
int click_x = -1, click_y = -1;
int prev_mouse_x = -1, prev_mouse_y = -1;
bool lmouse = false;
bool ctrl = false;
while (true) {
bool quit = false;
bool lclick = false;
bool lrelease = false;
int scroll_y = 0;
int mouse_x = -1, mouse_y = -1;
SDL_GetMouseState(&mouse_x, &mouse_y);
SDL_Event event;
while (!quit && SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
quit = true;
break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT) {
lclick = true;
lmouse = true;
click_x = mouse_x;
click_y = mouse_y;
}
SDL_CaptureMouse((SDL_bool)true);
break;
case SDL_MOUSEBUTTONUP:
lmouse = false;
lrelease = false;
SDL_CaptureMouse((SDL_bool)false);
break;
case SDL_MOUSEWHEEL:
scroll_y = event.wheel.y;
break;
case SDL_KEYDOWN:
{
SDL_Keycode key = event.key.keysym.sym;
if (key == SDLK_LCTRL) ctrl = true;
break;
}
case SDL_KEYUP:
{
SDL_Keycode key = event.key.keysym.sym;
if (key == SDLK_LCTRL) ctrl = false;
break;
}
}
}
if (quit)
break;
int sc_w, sc_h;
SDL_GetRendererOutputSize(renderer, &sc_w, &sc_h);
int half_sc_w = sc_w / 2;
int half_sc_h = sc_h / 2;
if (ctrl) {
if (scroll_y != 0) {
float coeff = 1.0 + ZOOM_COEFF;
if (scroll_y < 0)
coeff = 1.0 / coeff;
int cur_x = (mouse_x - half_sc_w) / scale;
int cur_y = (mouse_y - half_sc_h) / scale;
scale *= coeff;
x += cur_x - (mouse_x - half_sc_w) / scale;
y += cur_y - (mouse_y - half_sc_h) / scale;
}
if (lmouse && prev_mouse_x >= 0 && prev_mouse_y >= 0) {
x += (prev_mouse_x - mouse_x) / scale;
y += (prev_mouse_y - mouse_y) / scale;
}
}
x = clamp(x, 0, map_w);
y = clamp(y, 0, map_h);
int scaled_half_w = half_sc_w / scale;
int scaled_half_h = half_sc_h / scale;
int scaled_w = sc_w / scale;
int scaled_h = sc_h / scale;
int end_x = map_w - scaled_w;
int end_y = map_h - scaled_h;
SDL_Rect src = {
x - scaled_half_w,
y - scaled_half_h,
sc_w / scale,
sc_h / scale
};
SDL_Rect dst = {
0,
0,
sc_w,
sc_h
};
if (src.x < 0) {
dst.x = -src.x * scale;
src.x = 0;
}
if (src.x > end_x) {
dst.w = sc_w - (src.x - end_x) * scale;
src.w = dst.w / scale;
}
if (src.y < 0) {
dst.y = -src.y * scale;
src.y = 0;
}
if (src.y > end_y) {
dst.h = sc_h - (src.y - end_y) * scale;
src.h = dst.h / scale;
}
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, map, &src, &dst);
SDL_RenderPresent(renderer);
prev_mouse_x = mouse_x;
prev_mouse_y = mouse_y;
}
SDL_DestroyTexture(map);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}