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
+164
-93
No files matched your search
+2
-2
@@ -10,9 +10,9 @@ extern "C" {
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#endif
|
#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);
|
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);
|
uint32_t* image_load_resized(const char* filename, int desired_width, int desired_height);
|
||||||
// Frees image data.
|
// Frees image data.
|
||||||
void image_free(uint32_t* data);
|
void image_free(uint32_t* data);
|
||||||
|
|||||||
+14
-18
@@ -9,9 +9,6 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <image.h>
|
#include <image.h>
|
||||||
|
|
||||||
#define STR2(x) #x
|
|
||||||
#define STR(x) STR2(x)
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
uint32_t* image_load(const char* filename, int* width, int* height) {
|
uint32_t* image_load(const char* filename, int* width, int* height) {
|
||||||
int n;
|
int n;
|
||||||
@@ -41,7 +38,7 @@ uint32_t* image_load_resized(const char* filename, int desired_width, int desire
|
|||||||
if (!data) return nullptr;
|
if (!data) return nullptr;
|
||||||
|
|
||||||
// Allocate buffer for resized image
|
// 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) {
|
if (!resized) {
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -49,26 +46,25 @@ uint32_t* image_load_resized(const char* filename, int desired_width, int desire
|
|||||||
|
|
||||||
// Resize the image
|
// Resize the image
|
||||||
stbir_resize_uint8_linear(data, original_width, original_height, 0,
|
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);
|
STBIR_RGBA);
|
||||||
|
|
||||||
stbi_image_free(data);
|
stbi_image_free(data);
|
||||||
|
|
||||||
// Convert RGBA to ARGB
|
// In-place conversion from RGBA to ARGB
|
||||||
uint32_t* out = (uint32_t*)malloc(desired_width * desired_height * sizeof(uint32_t));
|
// We iterate backwards to ensure we don't overwrite bytes before reading them,
|
||||||
if (!out) {
|
// though since we are working within the same 4-byte boundaries, forward works too!
|
||||||
free(resized);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < desired_width * desired_height; ++i) {
|
for (int i = 0; i < desired_width * desired_height; ++i) {
|
||||||
uint8_t r = resized[4*i + 0];
|
uint8_t* pixel = (uint8_t*)&resized[i];
|
||||||
uint8_t g = resized[4*i + 1];
|
uint8_t r = pixel[0];
|
||||||
uint8_t b = resized[4*i + 2];
|
uint8_t g = pixel[1];
|
||||||
uint8_t a = resized[4*i + 3];
|
uint8_t b = pixel[2];
|
||||||
out[i] = (a << 24) | (r << 16) | (g << 8) | b;
|
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) {
|
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
|
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 {
|
EngineHandle& operator=(EngineHandle&& other) noexcept {
|
||||||
if (this != &other) {
|
T* temp = other.ptr;
|
||||||
if (ptr) DestroyFn(ptr); // Clean up our current resource first
|
other.ptr = ptr;
|
||||||
ptr = other.ptr; // Steal the resource from the other handle
|
ptr = temp;
|
||||||
other.ptr = nullptr; // Nullify the old one
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +82,8 @@ enum class WaitKind {
|
|||||||
END, // script finished, keep rendering until told to quit
|
END, // script finished, keep rendering until told to quit
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Interpreter {
|
class Interpreter {
|
||||||
|
public:
|
||||||
std::vector<Command> script;
|
std::vector<Command> script;
|
||||||
size_t pc = 0;
|
size_t pc = 0;
|
||||||
|
|
||||||
|
|||||||
+142
-66
@@ -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 <window.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <atomic>
|
||||||
|
#endif
|
||||||
|
|
||||||
struct window_t {
|
struct window_t {
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
SDL_Renderer* renderer;
|
SDL_Renderer* renderer;
|
||||||
@@ -15,9 +26,21 @@ struct window_t {
|
|||||||
int mouse_x;
|
int mouse_x;
|
||||||
int mouse_y;
|
int mouse_y;
|
||||||
uint32_t mouse_buttons;
|
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) {
|
static void window_cleanup(window_t* s) {
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
if (s->texture) SDL_DestroyTexture(s->texture);
|
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);
|
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" {
|
extern "C" {
|
||||||
|
|
||||||
window_t* window_create(int width, int height, const char* title) {
|
window_t* window_create(int width, int height, const char* title) {
|
||||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) {
|
#ifdef _WIN32
|
||||||
std::fprintf(stderr, "SDL_InitSubSystem failed: %s\n", SDL_GetError());
|
window_t* s = new window_t{};
|
||||||
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{};
|
window_t* s = new window_t{};
|
||||||
if (!s) return nullptr;
|
|
||||||
|
|
||||||
s->width = width;
|
s->width = width;
|
||||||
s->height = height;
|
s->height = height;
|
||||||
|
|
||||||
s->window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0);
|
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);
|
s->renderer = SDL_CreateRenderer(s->window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
if (!s->renderer) {
|
s->texture = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||||
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->keyboard_state = SDL_GetKeyboardState(nullptr);
|
s->keyboard_state = SDL_GetKeyboardState(nullptr);
|
||||||
return s;
|
return s;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void window_destroy(window_t* s) {
|
void window_destroy(window_t* s) {
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
|
#ifdef _WIN32
|
||||||
|
s->running = false;
|
||||||
|
if (s->worker.joinable()) s->worker.join();
|
||||||
|
delete[] s->shared_fb;
|
||||||
|
#else
|
||||||
window_cleanup(s);
|
window_cleanup(s);
|
||||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||||
|
#endif
|
||||||
delete s;
|
delete s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool window_pollevent(window_t* 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;
|
SDL_Event ev;
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
|
|
||||||
while (SDL_PollEvent(&ev)) {
|
while (SDL_PollEvent(&ev)) {
|
||||||
if (ev.type == SDL_QUIT)
|
if (ev.type == SDL_QUIT) quit = true;
|
||||||
quit = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s->mouse_buttons = SDL_GetMouseState(&s->mouse_x, &s->mouse_y);
|
s->mouse_buttons = SDL_GetMouseState(&s->mouse_x, &s->mouse_y);
|
||||||
return quit;
|
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) {
|
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;
|
if (sc < 0 || sc >= SDL_NUM_SCANCODES) return false;
|
||||||
return s->keyboard_state[sc] != 0;
|
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 window_get_mouse_position(window_t* s) {
|
||||||
mouse_pos_t pos = { s->mouse_x, s->mouse_y };
|
return { 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user