Last active
February 14, 2025 22:44
-
-
Save nandakoryaaa/ae872e2ccbfe10c93c7f3a8f207481cc to your computer and use it in GitHub Desktop.
Bitmap rotation in C + SDL2 using Bresenham lines
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define SDL_MAIN_HANDLED | |
#include <SDL2/SDL.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <math.h> | |
#define SURF_W 640 | |
#define SURF_H 640 | |
void draw_img_line( | |
SDL_Surface* surf, uint32_t* img_data, uint16_t img_w, | |
uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1 | |
) { | |
int16_t dx = x1 - x0; | |
int16_t dy = y1 - y0; | |
uint16_t dist_x = abs(dx); | |
uint16_t dist_y = abs(dy); | |
int16_t step_x = dx < 0 ? -1 : 1; | |
int16_t step_y = dy < 0 ? -surf->pitch / 4 : surf->pitch / 4; | |
uint16_t dist = dist_y > dist_x ? dist_y : dist_x; | |
uint32_t* addr = (uint32_t*)(surf->pixels + y0 * surf->pitch + x0 * 4); | |
uint16_t err_x = 0; | |
uint16_t err_y = 0; | |
uint16_t err_img = 0; | |
for (uint16_t i = 0; i < dist; i++) { | |
*addr = *img_data; | |
err_x += dist_x; | |
err_y += dist_y; | |
if (err_x >= dist) { | |
err_x -= dist; | |
addr += step_x; | |
*addr = *img_data; | |
} | |
if (err_y >= dist) { | |
err_y -= dist; | |
addr += step_y; | |
} | |
err_img += img_w; | |
while (err_img >= dist) { | |
err_img -= dist; | |
img_data++; | |
} | |
} | |
} | |
void draw_rotated_img( | |
SDL_Surface* surf, SDL_Surface* img_surf, uint16_t x, uint16_t y, | |
uint16_t w, uint16_t h, float rotation | |
) { | |
int16_t cx = x + (w >> 1); | |
int16_t cy = y + (h >> 1); | |
int16_t x0 = x - cx; | |
int16_t y0 = y - cy; | |
int16_t x1 = x0 + w - 1; | |
int16_t y1 = y0; | |
int16_t x2 = x0; | |
int16_t y2 = y0 + h - 1; | |
float cos_val = cos(rotation); | |
float sin_val = sin(rotation); | |
int16_t rx0 = (int16_t) (cos_val * x0 + sin_val * y0); | |
int16_t ry0 = (int16_t) (cos_val * y0 - sin_val * x0); | |
int16_t rx1 = (int16_t) (cos_val * x1 + sin_val * y1); | |
int16_t ry1 = (int16_t) (cos_val * y1 - sin_val * x1); | |
int16_t rx2 = (int16_t) (cos_val * x2 + sin_val * y2); | |
int16_t ry2 = (int16_t) (cos_val * y2 - sin_val * x2); | |
int16_t dx = rx2 - rx0; | |
int16_t dy = ry2 - ry0; | |
int16_t step_x = dx < 0 ? -1 : 1; | |
int16_t step_y = dy < 0 ? -1 : 1; | |
uint16_t dist_x = abs(dx); | |
uint16_t dist_y = abs(dy); | |
uint16_t dist = dist_x > dist_y ? dist_x : dist_y; | |
uint16_t err_x = 0; | |
uint16_t err_y = 0; | |
uint16_t err_img = 0; | |
rx0 += cx; | |
ry0 += cy; | |
rx1 += cx; | |
ry1 += cy; | |
uint8_t* img_data = (uint8_t*) img_surf->pixels; | |
for (uint16_t i = 0; i < dist; i++) { | |
draw_img_line(surf, (uint32_t*)img_data, img_surf->w, rx0, ry0, rx1, ry1); | |
err_img += h; | |
while (err_img >= dist) { | |
img_data += img_surf->pitch; | |
err_img -= dist; | |
} | |
err_x += dist_x; | |
err_y += dist_y; | |
if (err_x >= dist) { | |
err_x -= dist; | |
rx0 += step_x; | |
rx1 += step_x; | |
} | |
if (err_y >= dist) { | |
err_y -= dist; | |
ry0 += step_y; | |
ry1 += step_y; | |
} | |
} | |
} | |
int main(int argc, char* argv[]) | |
{ | |
if (argc < 2) { | |
printf("Usage: rotate image.bmp\n"); | |
return 1; | |
} | |
SDL_Init(SDL_INIT_VIDEO); | |
SDL_Window *window = SDL_CreateWindow( | |
"Rotation", | |
SDL_WINDOWPOS_CENTERED, | |
SDL_WINDOWPOS_CENTERED, | |
SURF_W, | |
SURF_H, | |
SDL_WINDOW_ALLOW_HIGHDPI | |
); | |
if (window == NULL) { | |
printf("Could not create window: %s\n", SDL_GetError()); | |
return 1; | |
} | |
SDL_Surface *bmpSurface = SDL_LoadBMP(argv[1]); | |
if (!bmpSurface) { | |
SDL_Quit(); | |
printf("Error: %s\n", SDL_GetError()); | |
return 1; | |
} | |
if (bmpSurface->w > 512 || bmpSurface->h > 512 || bmpSurface->w < 32 || bmpSurface->h < 32) { | |
printf("Image dimensions must be 32..512 pixels\n"); | |
SDL_FreeSurface(bmpSurface); | |
SDL_Quit(); | |
return 1; | |
} | |
SDL_Surface *screenSurface = SDL_GetWindowSurface(window); | |
SDL_Surface *imgSurface = SDL_ConvertSurface(bmpSurface, screenSurface->format, 0); | |
SDL_FreeSurface(bmpSurface); | |
float rot = 0.0; | |
SDL_Event event; | |
uint16_t w = imgSurface->w; | |
uint16_t h = imgSurface->h; | |
int in_game = 1; | |
while (in_game) { | |
SDL_FillRect(screenSurface, NULL, 0); | |
draw_rotated_img(screenSurface, imgSurface, (SURF_W - w) / 2, (SURF_H - h) / 2, w, h, rot); | |
rot += M_PI / 180; | |
if (rot > M_PI * 2) { | |
rot -= M_PI * 2; | |
} | |
SDL_UpdateWindowSurface(window); | |
while (SDL_PollEvent(&event)) { | |
if (event.type == SDL_QUIT) { | |
in_game = 0; | |
break; | |
} | |
} | |
SDL_Delay(10); | |
} | |
SDL_FreeSurface(imgSurface); | |
SDL_FreeSurface(screenSurface); | |
SDL_DestroyWindow(window); | |
SDL_Quit(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment