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:
1 parent
469f0db39c
commit
a2671bed5d
4 files changed
+163
-92
No files matched your search
+2
-2
@@ -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
@@ -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
@@ -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
@@ -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 };
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user