image.cpp: remove unused macros, in-place conversion from rgba to argb.

main.cpp: change move assignment in generic raii wrapper, change the Interpreter from struct to class to better reflect what it actually is.
window.cpp: implement a workaround for the window to not freeze during dragging. fuck you ms.
This commit is contained in:
maksw committed 2026-04-15 18:23:28 +02:00
1 parent 469f0db39c
commit a2671bed5d
4 files changed
+163 -92

No files matched your search

+2 -2
View File
@@ -10,9 +10,9 @@ extern "C" {
#include <stdint.h>
#endif
// Loads an image file as ARGB8888. Returns pointer to pixel data, sets width/height.
// Loads an image file as ARGB8888. Returns pointer to pixel data, sets width/height. NULL on failure.
uint32_t* image_load(const char* filename, int* width, int* height);
// Loads and resizes an image file to desired dimensions as ARGB8888. Returns pointer to pixel data.
// Loads and resizes an image file to desired dimensions as ARGB8888. Returns pointer to pixel data. NULL on failure.
uint32_t* image_load_resized(const char* filename, int desired_width, int desired_height);
// Frees image data.
void image_free(uint32_t* data);
+14 -18
View File
@@ -9,9 +9,6 @@
#include <cstdint>
#include <image.h>
#define STR2(x) #x
#define STR(x) STR2(x)
extern "C" {
uint32_t* image_load(const char* filename, int* width, int* height) {
int n;
@@ -41,7 +38,7 @@ uint32_t* image_load_resized(const char* filename, int desired_width, int desire
if (!data) return nullptr;
// Allocate buffer for resized image
unsigned char* resized = (unsigned char*)malloc(desired_width * desired_height * 4);
uint32_t* resized = (uint32_t*)malloc(desired_width * desired_height * 4);
if (!resized) {
stbi_image_free(data);
return nullptr;
@@ -49,26 +46,25 @@ uint32_t* image_load_resized(const char* filename, int desired_width, int desire
// Resize the image
stbir_resize_uint8_linear(data, original_width, original_height, 0,
resized, desired_width, desired_height, 0,
(unsigned char* )resized, desired_width, desired_height, 0,
STBIR_RGBA);
stbi_image_free(data);
// Convert RGBA to ARGB
uint32_t* out = (uint32_t*)malloc(desired_width * desired_height * sizeof(uint32_t));
if (!out) {
free(resized);
return nullptr;
}
// In-place conversion from RGBA to ARGB
// We iterate backwards to ensure we don't overwrite bytes before reading them,
// though since we are working within the same 4-byte boundaries, forward works too!
for (int i = 0; i < desired_width * desired_height; ++i) {
uint8_t r = resized[4*i + 0];
uint8_t g = resized[4*i + 1];
uint8_t b = resized[4*i + 2];
uint8_t a = resized[4*i + 3];
out[i] = (a << 24) | (r << 16) | (g << 8) | b;
uint8_t* pixel = (uint8_t*)&resized[i];
uint8_t r = pixel[0];
uint8_t g = pixel[1];
uint8_t b = pixel[2];
uint8_t a = pixel[3];
resized[i] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
}
free(resized);
return out;
return resized;
}
void image_free(uint32_t* data) {
+6 -7
View File
@@ -38,13 +38,11 @@ struct EngineHandle {
other.ptr = nullptr; // Nullify the old pointer so it doesn't get destroyed
}
// ALLOW Move Assignment
// Move Assignment using Swap
EngineHandle& operator=(EngineHandle&& other) noexcept {
if (this != &other) {
if (ptr) DestroyFn(ptr); // Clean up our current resource first
ptr = other.ptr; // Steal the resource from the other handle
other.ptr = nullptr; // Nullify the old one
}
T* temp = other.ptr;
other.ptr = ptr;
ptr = temp;
return *this;
}
@@ -84,7 +82,8 @@ enum class WaitKind {
END, // script finished, keep rendering until told to quit
};
struct Interpreter {
class Interpreter {
public:
std::vector<Command> script;
size_t pc = 0;
+141 -65
View File
@@ -1,9 +1,20 @@
// window.cpp
// This file implements the windowing and input system using SDL2.
// It provides a simple API for creating a window, blitting pixel data, and handling input events.
// Since Microsoft is retarded and forces us to live in 1985, we use a thread to manage the window
// to avoid freezing the app during window move or resize operations. Of course, normal OSes don't need that.
#include <window.h>
#include <SDL2/SDL.h>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#ifdef _WIN32
#include <thread>
#include <mutex>
#include <atomic>
#endif
struct window_t {
SDL_Window* window;
SDL_Renderer* renderer;
@@ -15,9 +26,21 @@ struct window_t {
int mouse_x;
int mouse_y;
uint32_t mouse_buttons;
#ifdef _WIN32
std::thread worker;
std::atomic<bool> running{true};
std::atomic<bool> quit_requested{false};
std::mutex buffer_mutex;
uint32_t* shared_fb;
std::mutex input_mutex;
uint8_t kb_copy[SDL_NUM_SCANCODES];
#endif
};
// Internal helper
// Internal helper for cleanup
static void window_cleanup(window_t* s) {
if (!s) return;
if (s->texture) SDL_DestroyTexture(s->texture);
@@ -25,71 +48,152 @@ static void window_cleanup(window_t* s) {
if (s->window) SDL_DestroyWindow(s->window);
}
#ifdef _WIN32
// Internal function to perform the actual blit and present on the worker thread
static void win32_internal_render(window_t* s) {
if (!s->renderer || !s->texture) return;
void* pixels;
int pitch;
if (SDL_LockTexture(s->texture, nullptr, &pixels, &pitch) == 0) {
{
std::lock_guard<std::mutex> lock(s->buffer_mutex);
std::memcpy(pixels, s->shared_fb, s->width * s->height * 4);
}
SDL_UnlockTexture(s->texture);
}
SDL_RenderClear(s->renderer);
SDL_RenderCopy(s->renderer, s->texture, nullptr, nullptr);
SDL_RenderPresent(s->renderer);
}
// THE FIX: This callback is triggered by SDL even when Windows is in a modal drag loop.
static int SDLCALL win32_event_watch(void* userdata, SDL_Event* event) {
window_t* s = (window_t*)userdata;
if (event->type == SDL_WINDOWEVENT) {
if (event->window.event == SDL_WINDOWEVENT_MOVED ||
event->window.event == SDL_WINDOWEVENT_RESIZED ||
event->window.event == SDL_WINDOWEVENT_EXPOSED) {
win32_internal_render(s);
}
}
return 0;
}
static void win32_window_worker(window_t* s, const char* title) {
SDL_InitSubSystem(SDL_INIT_VIDEO);
s->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, s->width, s->height, 0);
s->renderer = SDL_CreateRenderer(s->window, -1, SDL_RENDERER_ACCELERATED);
s->texture = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, s->width, s->height);
// Register the watch to handle rendering during the drag-loop
SDL_AddEventWatch(win32_event_watch, s);
while (s->running) {
SDL_Event ev;
// Use a timeout so we don't hog CPU but stay responsive
if (SDL_WaitEventTimeout(&ev, 8)) {
if (ev.type == SDL_QUIT) s->quit_requested = true;
}
// Sync input state for the main thread
{
std::lock_guard<std::mutex> lock(s->input_mutex);
const uint8_t* state = SDL_GetKeyboardState(nullptr);
std::memcpy(s->kb_copy, state, SDL_NUM_SCANCODES);
s->mouse_buttons = SDL_GetMouseState(&s->mouse_x, &s->mouse_y);
}
// Normal frame render
win32_internal_render(s);
}
SDL_DelEventWatch(win32_event_watch, s);
window_cleanup(s);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
#endif
extern "C" {
window_t* window_create(int width, int height, const char* title) {
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
std::fprintf(stderr, "SDL_InitSubSystem failed: %s\n", SDL_GetError());
return nullptr;
}
#ifdef _WIN32
window_t* s = new window_t{};
if (!s) return nullptr;
s->width = width;
s->height = height;
s->shared_fb = new uint32_t[width * height];
s->keyboard_state = s->kb_copy;
// Spawn the worker thread which owns the SDL context
s->worker = std::thread(win32_window_worker, s, title);
// Wait briefly for the worker to initialize window/renderer pointers
while (s->running && !s->renderer) { std::this_thread::yield(); }
return s;
#else
// Original non-threaded implementation for Linux/others
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) return nullptr;
window_t* s = new window_t{};
s->width = width;
s->height = height;
s->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0);
if (!s->window) {
std::fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
delete s;
return nullptr;
}
s->renderer = SDL_CreateRenderer(s->window, -1, SDL_RENDERER_ACCELERATED);
if (!s->renderer) {
std::fprintf(stderr, "SDL_CreateRenderer failed: %s\n", SDL_GetError());
window_cleanup(s);
delete s;
return nullptr;
}
s->texture = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING, width, height);
if (!s->texture) {
std::fprintf(stderr, "SDL_CreateTexture failed: %s\n", SDL_GetError());
window_cleanup(s);
delete s;
return nullptr;
}
s->texture = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
s->keyboard_state = SDL_GetKeyboardState(nullptr);
return s;
#endif
}
void window_destroy(window_t* s) {
if (!s) return;
#ifdef _WIN32
s->running = false;
if (s->worker.joinable()) s->worker.join();
delete[] s->shared_fb;
#else
window_cleanup(s);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
#endif
delete s;
}
bool window_pollevent(window_t* s) {
#ifdef _WIN32
// Main thread just checks the status reported by the worker
std::lock_guard<std::mutex> lock(s->input_mutex);
return s->quit_requested;
#else
SDL_Event ev;
bool quit = false;
while (SDL_PollEvent(&ev)) {
if (ev.type == SDL_QUIT)
quit = true;
if (ev.type == SDL_QUIT) quit = true;
}
s->mouse_buttons = SDL_GetMouseState(&s->mouse_x, &s->mouse_y);
return quit;
#endif
}
void window_blit(window_t* s, const uint32_t* fb) {
#ifdef _WIN32
// Just update the shared buffer; the worker thread will pick it up and present it
std::lock_guard<std::mutex> lock(s->buffer_mutex);
std::memcpy(s->shared_fb, fb, s->width * s->height * 4);
#else
// Original immediate blit logic
void* pixels; int pitch;
SDL_LockTexture(s->texture, nullptr, &pixels, &pitch);
std::memcpy(pixels, fb, s->width * s->height * 4);
SDL_UnlockTexture(s->texture);
SDL_RenderClear(s->renderer);
SDL_RenderCopy(s->renderer, s->texture, nullptr, nullptr);
SDL_RenderPresent(s->renderer);
#endif
}
// Input getters remain unchanged as they read from the struct's state fields
bool window_is_key_down(window_t* s, int sc) {
if (!s->keyboard_state) return false;
if (sc < 0 || sc >= SDL_NUM_SCANCODES) return false;
return s->keyboard_state[sc] != 0;
}
@@ -104,35 +208,7 @@ bool window_is_mouse_button_down(window_t* s, int button) {
}
mouse_pos_t window_get_mouse_position(window_t* s) {
mouse_pos_t pos = { s->mouse_x, s->mouse_y };
return pos;
}
void window_blit(window_t* s, const uint32_t* fb) {
if (!s->texture) return;
void* pixels;
int pitch;
SDL_LockTexture(s->texture, nullptr, &pixels, &pitch);
const int expected_pitch = s->width * 4;
const size_t total = (size_t)s->width * (size_t)s->height * 4;
if (pitch == expected_pitch) {
std::memcpy(pixels, fb, total);
} else {
for (int y = 0; y < s->height; ++y) {
uint8_t* row = (uint8_t*)pixels + y * pitch;
std::memcpy(row, fb + y * s->width, expected_pitch);
}
}
SDL_UnlockTexture(s->texture);
SDL_RenderClear(s->renderer);
SDL_RenderCopy(s->renderer, s->texture, nullptr, nullptr);
SDL_RenderPresent(s->renderer);
return { s->mouse_x, s->mouse_y };
}
}