cg-from-scratch/src/window/window.c

192 lines
4.6 KiB
C

#include "window/window.h"
#include "aliases.h"
#include "math/math_utils.h"
#include "vector/vec.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_error.h>
#include <SDL2/SDL_pixels.h>
#include <SDL2/SDL_surface.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
u32 index_from_coordinates(window_t *wnd, u32 x, u32 y);
i32 denormalise(i32 value, u32 max, u32 abs_half);
vec2i_t denormalised_coords(const window_t *wnd, i32 x, i32 y);
void set_screen_pixel(window_t *wnd, u32 x, u32 y, colour_t colour);
bool init_window(window_t *wnd, u32 width, u32 height, const char *title) {
if (SDL_Init(SDL_INIT_EVERYTHING) != 0) {
printf("Failed to initialise SDL: %s\n", SDL_GetError());
return false;
}
if (width % 2 != 0) {
++width;
}
if (height % 2 != 0) {
++height;
}
wnd->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, width, height, 0);
if (!(wnd->window)) {
printf("Failed to create window: %s\n", SDL_GetError());
close_window(wnd);
return false;
}
wnd->front_buffer = SDL_GetWindowSurface(wnd->window);
if (!(wnd->front_buffer)) {
printf("Failed to get front buffer: %s\n", SDL_GetError());
close_window(wnd);
return false;
}
wnd->back_buffer = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32,
SDL_PIXELFORMAT_ABGR32);
if (!(wnd->back_buffer)) {
printf("Failed to create back buffer: %s\n", SDL_GetError());
close_window(wnd);
return false;
}
wnd->width = width;
wnd->height = height;
wnd->half_width = width / 2;
wnd->half_height = height / 2;
wnd->title = title;
return true;
}
void close_window(window_t *wnd) {
if (wnd) {
wnd->width = 0;
wnd->height = 0;
wnd->title = "";
if (wnd->window) {
SDL_DestroyWindow(wnd->window);
wnd->window = NULL;
}
if (wnd->back_buffer) {
SDL_FreeSurface(wnd->back_buffer);
wnd->back_buffer = NULL;
}
}
SDL_Quit();
}
void clear_window(window_t *wnd, colour_t colour) {
SDL_LockSurface(wnd->back_buffer);
u32 *pixels = (u32 *)(wnd->back_buffer->pixels);
u32 count = wnd->back_buffer->w * wnd->back_buffer->h;
for (u32 i = 0; i < count; ++i) {
pixels[i] = colour.colour;
}
SDL_UnlockSurface(wnd->back_buffer);
}
void set_pixel(window_t *wnd, i32 x, i32 y, colour_t colour) {
vec2i_t coords = denormalised_coords(wnd, x, y);
if (coords.x < 0 || coords.y < 0) {
return;
}
SDL_LockSurface(wnd->back_buffer);
set_screen_pixel(wnd, (u32)(coords.x), (u32)(coords.y), colour);
SDL_UnlockSurface(wnd->back_buffer);
}
void swap_buffers(window_t *wnd) {
SDL_BlitSurface(wnd->back_buffer, NULL, wnd->front_buffer, NULL);
SDL_UpdateWindowSurface(wnd->window);
}
u32 index_from_coordinates(window_t *wnd, u32 x, u32 y) {
return y * wnd->width + x;
}
i32 denormalise(i32 value, u32 max, u32 abs_half) {
if (max == 0) {
return -1;
}
i32 normalised_min = -abs_half;
i32 normalised_max = abs_half;
i32 denormalised = (i32)(((value - normalised_min) * max) /
(normalised_max - normalised_min));
if (denormalised < 0 || denormalised >= max) {
return -1;
}
return denormalised;
}
vec2i_t denormalised_coords(const window_t *wnd, i32 x, i32 y) {
i32 screen_x = denormalise(x, wnd->width, wnd->half_width);
i32 screen_y = denormalise(-y, wnd->height, wnd->half_height);
return (vec2i_t){.x = screen_x, .y = screen_y};
}
void set_screen_pixel(window_t *wnd, u32 x, u32 y, colour_t colour) {
u32 index = index_from_coordinates(wnd, x, y);
u32 *pixels = (u32 *)(wnd->back_buffer->pixels);
pixels[index] = colour.colour;
}
vec3f_t window_to_viewport(window_t *wnd, i32 x, i32 y, vec3f_t viewport) {
return (vec3f_t){
.x = x * viewport.x / wnd->width,
.y = y * viewport.y / wnd->height,
.z = viewport.z,
};
}
colour_t colour_add_colour(colour_t a, colour_t b) {
f32 alpha = clamp((f32)(a.rgba.a + b.rgba.a), 0.0f, (f32)UINT8_MAX);
return (colour_t){.rgba.r = a.rgba.r + b.rgba.r,
.rgba.g = a.rgba.g + b.rgba.g,
.rgba.b = a.rgba.b + b.rgba.b,
.rgba.a = (u8)alpha};
}
colour_t colour_mul(colour_t colour, f32 scalar) {
f32 r = (f32)colour.rgba.r * scalar;
r = clamp(r, 0.0f, (f32)UINT8_MAX);
f32 g = (f32)colour.rgba.g * scalar;
g = clamp(g, 0.0f, (f32)UINT8_MAX);
f32 b = (f32)colour.rgba.b * scalar;
b = clamp(b, 0.0f, (f32)UINT8_MAX);
return (colour_t){.rgba.r = (u8)r,
.rgba.g = (u8)g,
.rgba.b = (u8)b,
.rgba.a = colour.rgba.a};
}