From ed0aca8f481d6bf1cd7c64e7d5c92181f608fa0f Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sun, 17 May 2026 20:41:46 +0300 Subject: [PATCH 001/130] Add initial software renderer implementation. Taken from https://github.com/iProgramMC/Butterscotch/tree/sw-renderer-old. --- CMakeLists.txt | 27 +- src/sw/debug_utils.c | 44 ++ src/sw/defines.h | 8 + src/sw/fb_convert.c | 104 +++ src/sw/fb_convert.h | 13 + src/sw/pixel_convert.h | 90 +++ src/sw/sw_renderer.c | 1630 ++++++++++++++++++++++++++++++++++++++++ src/sw/sw_renderer.h | 7 + src/utils.h | 5 + 9 files changed, 1920 insertions(+), 8 deletions(-) create mode 100755 src/sw/debug_utils.c create mode 100755 src/sw/defines.h create mode 100755 src/sw/fb_convert.c create mode 100755 src/sw/fb_convert.h create mode 100755 src/sw/pixel_convert.h create mode 100755 src/sw/sw_renderer.c create mode 100755 src/sw/sw_renderer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f2c18a3a9..e3340df1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -228,15 +228,26 @@ if(PLATFORM STREQUAL "glfw") $/gamecontrollerdb.txt ) elseif(PLATFORM STREQUAL "sdl") - if (NOT ENABLE_LEGACY_GL) - message(FATAL_ERROR "SDL requires legacy GL!") + option(ENABLE_SW_RENDERER "Enable the software renderer (SDL 1.2 only)" ON) + + if (NOT ENABLE_LEGACY_GL AND NOT ENABLE_SW_RENDERER) + message(FATAL_ERROR "SDL requires legacy GL or the software renderer!") + endif() + + if(ENABLE_LEGACY_GL) + file(GLOB GL_SOURCES src/gl_legacy/*.c src/gl_common/*.c src/image/*.c) + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_legacy) + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common) + target_sources(butterscotch PRIVATE ${GL_SOURCES}) + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl) + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image) + elseif(ENABLE_SW_RENDERER) + file(GLOB SWR_SOURCES src/sw/*.c src/image/*.c) + add_compile_definitions(ENABLE_SW_RENDERER) + target_sources(butterscotch PRIVATE ${SWR_SOURCES}) + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/sw) + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image) endif() - file(GLOB GL_SOURCES src/gl_legacy/*.c src/gl_common/*.c src/image/*.c) - target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_legacy) - target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common) - target_sources(butterscotch PRIVATE ${GL_SOURCES}) - target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl) - target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image) # Butterscotch VM/interpreter profiler option(ENABLE_VM_GML_PROFILER "Enable Butterscotch VM/interpreter profiler" ON) diff --git a/src/sw/debug_utils.c b/src/sw/debug_utils.c new file mode 100755 index 000000000..604dbe1ca --- /dev/null +++ b/src/sw/debug_utils.c @@ -0,0 +1,44 @@ +#include +#include + +int debug_fprintf(FILE* stream, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + if (stream == stderr) + { + static char buffer[4096]; + int len = vsnprintf(buffer, sizeof(buffer), fmt, args); + + OutputDebugStringA(buffer); + + FILE* stderr2 = fopen("log.txt", "a+"); + fwrite(buffer, 1, strlen(buffer), stderr2); + fclose(stderr2); + + va_end(args); + return len; + } + + int len = vfprintf(stream, fmt, args); + va_end(args); + return len; +} + +int debug_printf(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + static char buffer[4096]; + int len = vsnprintf(buffer, sizeof(buffer), fmt, args); + OutputDebugStringA(buffer); + + FILE* stderr2 = fopen("log.txt", "a+"); + fwrite(buffer, 1, strlen(buffer), stderr2); + fclose(stderr2); + + va_end(args); + return len; +} diff --git a/src/sw/defines.h b/src/sw/defines.h new file mode 100755 index 000000000..9160d57e7 --- /dev/null +++ b/src/sw/defines.h @@ -0,0 +1,8 @@ +#pragma once + +#define LIKELY(cond) __builtin_expect(!!(cond), 1) +#define UNLIKELY(cond) __builtin_expect(!!(cond), 0) + +#define UNUSED __attribute__ ((unused)) +#define FORCE_INLINE static inline __attribute__((always_inline)) + diff --git a/src/sw/fb_convert.c b/src/sw/fb_convert.c new file mode 100755 index 000000000..9b0ba9f7c --- /dev/null +++ b/src/sw/fb_convert.c @@ -0,0 +1,104 @@ +#include +#include +#include "fb_convert.h" +#include "pixel_convert.h" +#include "utils.h" + +FORCE_INLINE UNUSED +uint16_t argb8888_to_rgb1555(uint32_t xl) +{ + Pixel32ARGB x; + x.l = xl; + return (x.p.b >> 3) | ((x.p.g >> 3) << 5) | ((x.p.r >> 3) << 10); +} + +FORCE_INLINE UNUSED +uint8_t argb8888_to_rgb332(uint32_t xl) +{ + Pixel32ARGB x; + x.l = xl; + return (x.p.r >> 5) | ((x.p.g >> 5) << 3) | ((x.p.b >> 6) << 6); +} + +uint8_t* swrConvert32to24(uint8_t* dest, uint32_t* src, int width, int height) +{ + int size = width * height; + if (!dest) { + dest = safeCalloc(3, size); + } + + for (int i = 0, j = 0; i < size; i++) + { + Pixel32ARGB x; + x.l = src[i]; + + dest[j++] = x.p.b; + dest[j++] = x.p.g; + dest[j++] = x.p.r; + } + + return dest; +} + +uint16_t* swrConvert32to16(uint16_t* dest, uint32_t* src, int width, int height) +{ + int size = width * height; + if (!dest) { + dest = safeCalloc(sizeof(uint16_t), size); + } + + for (int i = 0; i < size; i++) + dest[i] = argb8888_to_rgb1555(src[i]); + + return dest; +} + +uint8_t* swrConvert32to8(uint8_t* dest, uint32_t* src, int width, int height) +{ + int size = width * height; + if (!dest) { + dest = safeCalloc(sizeof(uint8_t), size); + } + + for (int i = 0; i < size; i++) + dest[i] = argb8888_to_rgb332(src[i]); + + return dest; +} + +typedef struct +{ + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[256]; +} +BITMAPINFO_8BIT; + +BITMAPINFO* swrSetup8BitBitmapInfo(int width, int height) +{ + static BITMAPINFO_8BIT* pBmi; + if (!pBmi) { + pBmi = safeCalloc(1, sizeof(BITMAPINFO_8BIT)); + + // set up palette + int i = 0; + for (int b = 0; b < 4; b++) + for (int g = 0; g < 8; g++) + for (int r = 0; r < 8; r++) + { + pBmi->bmiColors[i].rgbRed = (r << 5) + (r << 2) + (r >> 1); + pBmi->bmiColors[i].rgbGreen = (g << 5) + (g << 2) + (g >> 1); + pBmi->bmiColors[i].rgbBlue = (b << 6) + (b << 4) + (b << 2) + b; + pBmi->bmiColors[i].rgbReserved = 255; + i++; + } + } + + pBmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pBmi->bmiHeader.biWidth = width; + pBmi->bmiHeader.biHeight = -height; + pBmi->bmiHeader.biPlanes = 1; + pBmi->bmiHeader.biCompression = BI_RGB; + pBmi->bmiHeader.biBitCount = 8; + + return (BITMAPINFO*) pBmi; +} diff --git a/src/sw/fb_convert.h b/src/sw/fb_convert.h new file mode 100755 index 000000000..dad12c663 --- /dev/null +++ b/src/sw/fb_convert.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +typedef struct tagBITMAPINFO BITMAPINFO; + +uint8_t* swrConvert32to24(uint8_t* dest, uint32_t* src, int width, int height); + +uint16_t* swrConvert32to16(uint16_t* dest, uint32_t* src, int width, int height); + +uint8_t* swrConvert32to8(uint8_t* dest, uint32_t* src, int width, int height); + +BITMAPINFO* swrSetup8BitBitmapInfo(int width, int height); diff --git a/src/sw/pixel_convert.h b/src/sw/pixel_convert.h new file mode 100755 index 000000000..08d592011 --- /dev/null +++ b/src/sw/pixel_convert.h @@ -0,0 +1,90 @@ +#pragma once + +#include "defines.h" + +// CONFIG: Change the size of a pixel. +// +// 32-bit: 0xAARRGGBB +// 16-bit: 0b0RRRRRGGGGGBBBBB +// 8-bit: 0bBBGGGRRR +#define PIXEL_SIZE 32 +//#define PIXEL_SIZE 16 +//#define PIXEL_SIZE 8 + +#if PIXEL_SIZE == 32 +typedef uint32_t uintpixel_t; +#elif PIXEL_SIZE == 16 +typedef uint16_t uintpixel_t; +#elif PIXEL_SIZE == 8 +typedef uint8_t uintpixel_t; +#define PXL_TRANSPARENT (0b10101010) +#else +#error "Unknown pixel size!" +#endif + +typedef union +{ + struct { + uint8_t r, g, b, a; + } p; + uint32_t l; +} +Pixel32ABGR; + +typedef union +{ + struct { + uint8_t b, g, r, a; + } p; + uint32_t l; +} +Pixel32ARGB; + +FORCE_INLINE UNUSED +uint16_t abgr8888_to_rgb1555(uint32_t xl) +{ + Pixel32ABGR x; + x.l = xl; + return (x.p.b >> 3) | ((x.p.g >> 3) << 5) | ((x.p.r >> 3) << 10) | ((x.p.a >> 7) << 15); +} + +FORCE_INLINE UNUSED +uint8_t abgr8888_to_rgb332(uint32_t xl) +{ + Pixel32ABGR x; + x.l = xl; + +#if PIXEL_SIZE == 8 + //check if transparent + if (x.p.a < 128) + return PXL_TRANSPARENT; +#endif + + uint8_t pxl = (x.p.r >> 5) | ((x.p.g >> 5) << 3) | ((x.p.b >> 6) << 6); + +#if PIXEL_SIZE == 8 + //hacky fixup + if (pxl == PXL_TRANSPARENT) + pxl++; +#endif + + return pxl; +} + +FORCE_INLINE UNUSED +uintpixel_t swrConvertPixelTexture(uint32_t gmPixel) +{ +#if PIXEL_SIZE == 32 + return (gmPixel & 0xFF00FF00) | ((gmPixel & 0xFF) << 16) | ((gmPixel >> 16) & 0xFF); +#elif PIXEL_SIZE == 16 + return abgr8888_to_rgb1555(gmPixel); +#elif PIXEL_SIZE == 8 + return abgr8888_to_rgb332(gmPixel); +#endif +} + +#if PIXEL_SIZE == 8 +#define swrConvertPixel(x) swrConvertPixelTexture((x) | 0xFF000000) +#else +#define swrConvertPixel swrConvertPixelTexture +#endif diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c new file mode 100755 index 000000000..ee69ef4da --- /dev/null +++ b/src/sw/sw_renderer.c @@ -0,0 +1,1630 @@ +#include +#include +#include +#include +#include "defines.h" +#include "sw_renderer.h" +#include "text_utils.h" +#include "pixel_convert.h" +#include "image/image_decoder.h" + +#define UNIMP() do { fprintf(stderr, "NYI %s\n", __func__); } while (0) +//#define UNIMP() do { } while (0) +#define UNIMP2() do { } while (0) + +#ifndef M_PI +#define M_PI 3.1415926535897932384626 +#endif + +#define TEXTURE_LRU_LENGTH 16 + +typedef struct +{ + uintpixel_t* buffer; + uint16_t width, height; +} +SWTexture; + +typedef struct +{ + Renderer base; + + // Window Properties + uint16_t width; + uint16_t height; + // Framebuffer + uintpixel_t* fb; + uint16_t fbPitch; // in sizeof(uintpixel_t) units, NOT in bytes! + + SWTexture** textures; + uint32_t* textureIndexLRU; + uint32_t textureIndexLRUHead; + uint32_t textureIndexLRUTail; + size_t textureCount; + + bool viewActive; + int viewX, viewY, viewW, viewH; + int portX, portY, portW, portH; + int gameW, gameH, windowW, windowH; +} +SWRenderer; + +void Runner_setNextFrame(uintpixel_t* framebuffer, int width, int height); + +FORCE_INLINE int swrMin(int a, int b) { return a < b ? a : b; } +FORCE_INLINE int swrMax(int a, int b) { return a > b ? a : b; } +FORCE_INLINE int swrAbs(int x) { return x < 0 ? -x : x; } + +FORCE_INLINE bool opaque(uintpixel_t color) +{ +#if PIXEL_SIZE == 32 + return (color & 0xFF000000) != 0; +#elif PIXEL_SIZE == 16 + return (color & 0x8000) != 0; +#else + return (color != PXL_TRANSPARENT); +#endif +} + +typedef union +{ + struct { uint8_t b, g, r, a; } p; + uint32_t l; +} +Color32; + +FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) +{ +#if PIXEL_SIZE == 32 + Color32 x, y; + + if ((tintColor & 0xFFFFFF) == 0xFFFFFF) + return color; + + x.l = color; + y.l = tintColor; + + x.p.b = (int)x.p.b * y.p.b / 255; + x.p.g = (int)x.p.g * y.p.g / 255; + x.p.r = (int)x.p.r * y.p.r / 255; + return x.l; +#elif PIXEL_SIZE == 16 + if ((tintColor & 0x7FFF) == 0x7FFF) + return color; + + int tcb = tintColor & 0x1F; + int tcg = (tintColor >> 5) & 0x1F; + int tcr = (tintColor >> 10) & 0x1F; + + int cb = color & 0x1F; + int cg = (color >> 5) & 0x1F; + int cr = (color >> 10) & 0x1F; + int ca = color & 0x8000; + + cb = (cb * tcb) / 32; + cg = (cg * tcg) / 32; + cr = (cr * tcr) / 32; + return ca | cb | (cg << 5) | (cr << 10); +#elif PIXEL_SIZE == 8 + // fast but hacky + if (tintColor == 0xFF || tintColor == PXL_TRANSPARENT) + return color; + + return color & tintColor; +#endif +} + +// NOTE: alpha is between 0 and 256, NOT between 0 and 255! +FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) +{ +#if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 + // it's so insignificant here nobody will notice if we just don't... + if (alpha < 3) + return; + + // it's so significant here we might as well fill in the whole color + if (alpha > 253) + { + *dcolor = scolor; + return; + } + + int inval = 256 - alpha; +#endif + +#if PIXEL_SIZE == 32 + Color32 dc, sc; + dc.l = *dcolor; + sc.l = scolor; + + dc.p.r = (dc.p.r * inval + sc.p.r * alpha) >> 8; + dc.p.g = (dc.p.g * inval + sc.p.g * alpha) >> 8; + dc.p.b = (dc.p.b * inval + sc.p.b * alpha) >> 8; + + *dcolor = dc.l; +#elif PIXEL_SIZE == 16 + int scb = scolor & 0x1F; + int scg = (scolor >> 5) & 0x1F; + int scr = (scolor >> 10) & 0x1F; + + uintpixel_t _dcolor = *dcolor; + int dcb = _dcolor & 0x1F; + int dcg = (_dcolor >> 5) & 0x1F; + int dcr = (_dcolor >> 10) & 0x1F; + int dca = _dcolor & 0x8000; + + dcr = (dcr * inval + scr * alpha) >> 8; + dcg = (dcg * inval + scg * alpha) >> 8; + dcb = (dcb * inval + scb * alpha) >> 8; + + *dcolor = dca | dcb | (dcg << 5) | (dcr << 10); +#else + if (alpha < 240) { + static int alphaApproximationThingy = 0; + alphaApproximationThingy += 1339; + if (alphaApproximationThingy > 601000) + alphaApproximationThingy = 0; + + //gotta love that RNG + if ((alphaApproximationThingy & 0xFF) >= alpha) + return; + } + + *dcolor = scolor; +#endif +} + +FORCE_INLINE int swrIntAlpha(float alphaf) +{ + return (int)(alphaf * 256); +} + +FORCE_INLINE bool swrMustRotate(float angleDeg) +{ + int angleDegInt = (int)(angleDeg * 4); + angleDegInt %= 360*4; + + if (angleDegInt > 180*4) + angleDegInt -= 360*4; + + return swrAbs(angleDegInt) >= 1; // 0.25 degrees +} + +FORCE_INLINE bool swrMustRotateTolerant(float angleDeg) +{ + int angleDegInt = (int)(angleDeg * 16); + angleDegInt %= 360*16; + + if (angleDegInt > 180*16) + angleDegInt -= 360*16; + + return swrAbs(angleDegInt) >= 1; // 1/16 of a degree +} + +FORCE_INLINE int swrFloor(float x) +{ + int i = (int) x; + return i - (x < (float) i); +} + +FORCE_INLINE int swrCeiling(float x) +{ + int i = (int) x; + return i + (x > (float) i); +} + +static SWTexture* createTexture(const uint8_t* srcBuffer, int width, int height) +{ + SWTexture* txt = safeCalloc(1, sizeof(SWTexture)); + txt->buffer = safeCalloc(width * height, sizeof(uintpixel_t)); + + const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; + + size_t sz = width * height; + for (size_t i = 0; i < sz; i++) + txt->buffer[i] = swrConvertPixelTexture(rgbaSrc[i]); + + txt->width = (uint16_t) width; + txt->height = (uint16_t) height; + + return txt; +} + +static bool swrAddTextureIndexToLRU(SWRenderer* swr, int textureIndex) +{ + uint32_t newIndex = (swr->textureIndexLRUHead + 1) % TEXTURE_LRU_LENGTH; + if (newIndex == swr->textureIndexLRUTail) { + // about to collide with tail from the other side -- nope. + return false; + } + + swr->textureIndexLRU[swr->textureIndexLRUHead] = textureIndex; + swr->textureIndexLRUHead = newIndex; + return true; +} + +static int swrTailTextureIndexLRU(SWRenderer* swr, bool remove) +{ + if (swr->textureIndexLRUHead == swr->textureIndexLRUTail) + return -1; + + uint32_t textureIndex = swr->textureIndexLRU[swr->textureIndexLRUTail]; + + if (remove) + swr->textureIndexLRUTail = (swr->textureIndexLRUTail + 1) % TEXTURE_LRU_LENGTH; + + return textureIndex; +} + +static void swrEvictTextureFromCache(SWRenderer* swr, int textureIndex) +{ + SWTexture* texture = swr->textures[textureIndex]; + swr->textures[textureIndex] = NULL; + + free(texture->buffer); + free(texture); +} + +// Lazily decodes and uploads a TXTR page on first access. +// Returns true if the texture is ready, false if it failed to decode. +static bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) +{ + if (swr->textures[pageId]) + return true; + + DataWin* dw = swr->base.dataWin; + Texture* txtr = &dw->txtr.textures[pageId]; + + int w, h; + bool gm2022_5 = DataWin_isVersionAtLeast(dw, 2022, 5, 0, 0); + + uint8_t* pixels = NULL; + + do + { + pixels = ImageDecoder_decodeToRgba(txtr->blobData, (size_t) txtr->blobSize, gm2022_5, &w, &h); + if (pixels) + break; + + fprintf(stderr, "swr: Failed to decode TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); + + int tail = swrTailTextureIndexLRU(swr, true); + if (tail == -1) { + fprintf(stderr, "swr: Looks like we can't fit this texture in memory at all. Bummer.\n"); + break; + } + + swrEvictTextureFromCache(swr, tail); + fprintf(stderr, "swr: Evicted texture %d, trying again.\n", tail); + } + while (!pixels); + + if (pixels == nullptr) { + fprintf(stderr, "swr: Failed to decode TXTR page %u.\n", pageId); + return false; + } + + swr->textures[pageId] = createTexture(pixels, w, h); + free(pixels); + + fprintf(stderr, "SWR: Loaded TXTR page %u (%dx%d)\n", pageId, w, h); + + // add it to the LRU + do + { + bool added = swrAddTextureIndexToLRU(swr, pageId); + if (added) + break; + + int tail = swrTailTextureIndexLRU(swr, true); + if (tail == -1) { + fprintf(stderr, "swr: Come on now.\n"); + assert(tail != -1); + } + + swrEvictTextureFromCache(swr, tail); + } + while (true); + + return true; +} + +static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + renderer->dataWin = dataWin; + + //allocate frame buffer + swr->fb = safeCalloc(swr->width * swr->height, sizeof(uintpixel_t)); + swr->fbPitch = swr->width; + + //allocate texture buffer + swr->textureCount = dataWin->txtr.count; + swr->textures = safeCalloc(swr->textureCount, sizeof(SWTexture*)); + + //allocate texture LRU cache to allow for dynamic unloading of textures + swr->textureIndexLRU = safeCalloc(TEXTURE_LRU_LENGTH, sizeof(uint32_t)); + swr->textureIndexLRUHead = 0; + swr->textureIndexLRUTail = 0; + + fprintf(stderr, "SWRenderer initialized.\n"); +} + +static void SWRenderer_destroy(Renderer* renderer) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + (void) swr; + + fprintf(stderr, "SWRenderer destroyed.\n"); +} + +static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH) +{ + SWRenderer* swr = (SWRenderer*) renderer; + swr->gameW = gameW; + swr->gameH = gameH; + swr->windowW = windowW; + swr->windowH = windowH; +} + +// This used to be just one, "endFrame". Not sure what the different is. +static void SWRenderer_endFrameInit(Renderer* renderer) +{ + UNIMP(); + SWRenderer* swr = (SWRenderer*) renderer; + Runner_setNextFrame(swr->fb, swr->width, swr->height); +} + +static void SWRenderer_endFrameEnd(Renderer* renderer) +{ + UNIMP(); + SWRenderer* swr = (SWRenderer*) renderer; + Runner_setNextFrame(swr->fb, swr->width, swr->height); +} + +static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t viewY, int32_t viewW, int32_t viewH, + int32_t portX, int32_t portY, int32_t portW, int32_t portH, float viewAngle) +{ + (void)renderer; (void)viewX; (void)viewY; (void)viewW; (void)viewH; + (void)portX; (void)portY; (void)portW; (void)portH; (void)viewAngle; + UNIMP2(); + + SWRenderer* swr = (SWRenderer*) renderer; + + float xratio = (float) swr->windowW / swr->gameW; + float yratio = (float) swr->windowH / swr->gameH; + + portX = (int)(portX * xratio); + portY = (int)(portY * yratio); + portW = (int)(portW * xratio); + portH = (int)(portH * yratio); + + swr->viewActive = true; + swr->viewX = viewX; + swr->viewY = viewY; + swr->viewW = viewW; + swr->viewH = viewH; + swr->portX = portX; + swr->portY = portY; + swr->portW = portW; + swr->portH = portH; +} + +static void SWRenderer_endView(Renderer* renderer) +{ + (void)renderer; + UNIMP2(); + + SWRenderer* swr = (SWRenderer*) renderer; + swr->viewActive = false; + + swr->portX = swr->viewX = 0; + swr->portY = swr->viewY = 0; + swr->portW = swr->viewW = swr->width; + swr->portH = swr->viewH = swr->height; +} + +static void SWRenderer_beginGUI(Renderer* renderer, int32_t guiW, int32_t guiH, + int32_t portX, int32_t portY, int32_t portW, int32_t portH) +{ + (void)renderer; (void)guiW; (void)guiH; + (void)portX; (void)portY; (void)portW; (void)portH; + UNIMP2(); +} + +static void SWRenderer_endGUI(Renderer* renderer) +{ + (void)renderer; + UNIMP2(); +} + +static void swrTransformPosIfNeeded(SWRenderer* swr, float* dx, float* dy) +{ + if (!swr->viewActive) return; + + if (dx) { + float xscale = ((float)swr->portW / swr->viewW); + *dx -= swr->viewX; + *dx *= xscale; + *dx += swr->portX; + } + if (dy) { + float yscale = ((float)swr->portH / swr->viewH); + *dy -= swr->viewY; + *dy *= yscale; + *dy += swr->portY; + } +} + +static void swrTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) +{ + if (!swr->viewActive || !swr->viewW || !swr->viewH) return; + + if (dx) *dx *= ((float)swr->portW / swr->viewW); + if (dy) *dy *= ((float)swr->portH / swr->viewH); +} + +FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + if (x < 0 || y < 0) return; + if (x >= swr->width || y >= swr->height) return; + + alphaBlend(&swr->fb[y * swr->fbPitch + x], color, alpha); +} + +static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixel_t color, int alpha) +{ + SWRenderer *swr = (SWRenderer*) renderer; + + if (dy < 0) return; + if (dy >= swr->height) return; + if (dx < 0) { dw += dx; dx = 0; } + if (dx + dw >= swr->width) dw = swr->width - dx; + if (dw <= 0) return; + + uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; + for (int i = 0; i < dw; i++) + alphaBlend(&line[i], color, alpha); +} + +static void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, float alpha) +{ + SWRenderer *swr = (SWRenderer*) renderer; + float thickness = 1; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &dw, &thickness); + + // TODO: use thickness + swrDrawHLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dw), color, swrIntAlpha(alpha)); +} + +static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixel_t color, int alpha) +{ + SWRenderer *swr = (SWRenderer*) renderer; + + if (dx < 0) return; + if (dx >= swr->width) return; + if (dy < 0) { dh += dy; dy = 0; } + if (dy + dh >= swr->height) dh = swr->height - dy; + if (dh <= 0) return; + + for (int i = 0; i < dh; i++) + { + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + alphaBlend(&line[0], color, alpha); + } +} + +static void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, float alpha) +{ + SWRenderer *swr = (SWRenderer*) renderer; + float thickness = 1; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &thickness, &dh); + + // TODO: use thickness + swrDrawVLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dh), color, swrIntAlpha(alpha)); +} + +static void swrDrawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha) +{ + swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color, alpha); + swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color, alpha); + swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color, alpha); + swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color, alpha); +} + +static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, int width, uintpixel_t color, int alpha) +{ + if (x1 == x2) + { + swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color, alpha); + return; + } + if (y1 == y2) + { + swrDrawHLineInt(renderer, swrMin(x1, x2), y1, swrAbs(x1 - x2), color, alpha); + return; + } + + int dx = x2 - x1, dy = y2 - y1; + int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; + int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; + + if (dy1 <= dx1) + { + if (dx >= 0) + { + x = x1, y = y1, xe = x2; + } + else + { + x = x2, y = y2, xe = x1; + } + + swrPlotPixel(renderer, x, y, color, alpha); + + for (int i = 0; x < xe; i++) + { + x++; + if (px < 0) + { + px += 2 * dy1; + } + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y++; else y--; + px += 2 * (dy1 - dx1); + } + + swrPlotPixel(renderer, x, y, color, alpha); + } + } + else + { + if (dy >= 0) + { + x = x1, y = y1, ye = y2; + } + else + { + x = x2, y = y2, ye = y1; + } + + swrPlotPixel(renderer, x, y, color, alpha); + + for (int i = 0; y < ye; i++) + { + y++; + if (py <= 0) + { + py += 2 * dx1; + } + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x++; else x--; + py += 2 * (dx1 - dy1); + } + + swrPlotPixel(renderer, x, y, color, alpha); + } + } +} + +static void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + swrTransformSizeIfNeeded(swr, &width, NULL); + swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrCeiling(x2), swrCeiling(y2), swrCeiling(width), color, swrIntAlpha(alpha)); +} + +static void swrDrawSpriteInternal( + Renderer* renderer, int dx, int dy, int dw, int dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uintpixel_t tintColor, int alpha +) +{ + SWRenderer *swr = (SWRenderer*) renderer; + + bool flipX = false, flipY = false; + if (dw < 0) { dx += dw; dw = -dw; flipX = true; } + if (dh < 0) { dy += dh; dh = -dh; flipY = true; } + + //basic out of bound checks + if (dw == 0 || dh == 0) return; + if (sw == 0) sw = 1; + if (sh == 0) sh = 1; + if (dx + dw <= 0) return; + if (dy + dh <= 0) return; + if (dx >= swr->width) return; + if (dy >= swr->height) return; + + int odw = dw, odh = dh; + int osw = sw, osh = sh; + + int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; + + //out of bounds adjustment checks + int diffxl = 0, diffyl = 0, diffxu = 0, diffyu = 0; + if (dx < minx) { diffxl = minx - dx; dx = minx; dw -= diffxl; } + if (dy < miny) { diffyl = miny - dy; dy = miny; dh -= diffyl; } + if (dx + dw > maxx) { diffxu = dx + dw - maxx; dw -= diffxu; } + if (dy + dh > maxy) { diffyu = dy + dh - maxy; dh -= diffyu; } + + if (diffxl != 0 || diffyl != 0 || diffxu != 0 || diffyu != 0) + { + //adjust source coordinates too + diffxl = (int)((long)diffxl * osw / odw); + diffyl = (int)((long)diffyl * osh / odh); + diffxu = (int)((long)(diffxu + 1) * osw / odw); + diffyu = (int)((long)(diffyu + 1) * osh / odh); + sx += flipX ? diffxu : diffxl; + sy += flipY ? diffyu : diffyl; + sw -= diffxl + diffxu; + sh -= diffyl + diffyu; + if (sw <= 0 || sh <= 0) return; + } + + //clip the source coords into bounds too + if (sx < 0) { sw += sx; sx = 0; } + if (sy < 0) { sh += sy; sy = 0; } + if (sx + sw >= texture->width) { sw = texture->width - sx; } + if (sy + sh >= texture->height) { sh = texture->height - sy; } + + //okay, now we can finally get on with rendering + + int ixs = 0, oxs = 1, iys = 0, oys = 1; + if (flipX) ixs = dw - 1, oxs = -1; + if (flipY) iys = dh - 1, oys = -1; + + // tweak these if stuff doesn't look right + typedef int32_t fixedp_t; + const int fp_prec = 8; + + fixedp_t ystep = (sh == dh) ? (1 << fp_prec) : ((fixedp_t) osh << fp_prec) / odh; + fixedp_t xstep = (sw == dw) ? (1 << fp_prec) : ((fixedp_t) osw << fp_prec) / odw; + fixedp_t oxs2 = oxs * xstep; + fixedp_t oys2 = oys * ystep; + fixedp_t ixs2 = ixs * xstep; + fixedp_t iys2 = iys * ystep; + + if (sw == dw) + { + fixedp_t ys2 = (fixedp_t) iys2; + for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) + { + uintpixel_t* dstline; + const uintpixel_t* srcline; + dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; + if (dh == sh) + srcline = &texture->buffer[(sy + ys) * texture->width + sx]; + else + srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; + + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) + { + uintpixel_t pixel = srcline[xs]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + } + } + } + else + { + fixedp_t ys2 = iys2; + for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) + { + uintpixel_t* dstline; + const uintpixel_t* srcline; + dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; + if (dh == sh) + srcline = &texture->buffer[(sy + ys) * texture->width + sx]; + else + srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; + + fixedp_t xs2 = ixs2; + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) + { + uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + } + } + } +} + +static void swrDrawSprite( + Renderer* renderer, float dx, float dy, float dw, float dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uint32_t tintColor, float alpha +) +{ + SWRenderer *swr = (SWRenderer*) renderer; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &dw, &dh); + + swrDrawSpriteInternal( + renderer, + swrFloor(dx), + swrFloor(dy), + swrCeiling(dw), + swrCeiling(dh), + texture, + sx, sy, + sw, sh, + swrConvertPixel(tintColor), + swrIntAlpha(alpha) + ); +} + +static void swrDrawSpriteRotatedInternal( + Renderer* renderer, int dx, int dy, int dw, int dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uintpixel_t tintColor, int alpha, + float angleDeg, + float pivotX, + float pivotY +) +{ + SWRenderer* swr = (SWRenderer*) renderer; + float angleRad = -angleDeg * M_PI / 180.0f; + + bool flipX = false, flipY = false; + if (dw < 0) { dw = -dw; dx -= dw; pivotX = dw - pivotX; flipX = true; } + if (dh < 0) { dh = -dh; dy -= dh; pivotY = dh - pivotY; flipY = true; } + + float cosA = cosf(angleRad); + float sinA = sinf(angleRad); + + float cnrx[4], cnry[4]; + cnrx[0] = cnrx[3] = dx; + cnry[0] = cnry[1] = dy; + cnrx[1] = cnrx[2] = dx + dw; + cnry[2] = cnry[3] = dy + dh; + + float pxa = pivotX + dx; + float pya = pivotY + dy; + + float minXf = FLT_MAX, minYf = FLT_MAX, maxXf = -FLT_MAX, maxYf = -FLT_MAX; + for (int i = 0; i < 4; i++) + { + float cxi = cnrx[i] - pxa; + float cyi = cnry[i] - pya; + float rx = cosA * cxi - sinA * cyi + pxa; + float ry = sinA * cxi + cosA * cyi + pya; + if (minXf > rx) minXf = rx; + if (maxXf < rx) maxXf = rx; + if (minYf > ry) minYf = ry; + if (maxYf < ry) maxYf = ry; + } + + // minX, minY, maxX, maxY now represent an AABB of pixels we should loop over + int minX = swrFloor(minXf); + int minY = swrFloor(minYf); + int maxX = swrCeiling(maxXf); + int maxY = swrCeiling(maxYf); + + // basic out-of-bound checks + if (maxX < 0) return; + if (maxY < 0) return; + if (minX >= swr->width) return; + if (minY >= swr->height) return; + + // however, we'll need to clip it against out of bounds first + int minXc = minX, minYc = minY, maxXc = maxX, maxYc = maxY; + int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; + + if (minXc < minx) minXc = minx; + if (minYc < miny) minYc = miny; + if (maxXc >= maxx) maxXc = maxx; + if (maxYc >= maxy) maxYc = maxy; + + // some final clip checks + if (minXc >= maxXc || minYc >= maxYc) return; + + int sox = flipX ? sw - 1 : 0; + int soy = flipY ? sh - 1 : 0; + int six = flipX ? -1 : 1; + int siy = flipY ? -1 : 1; + + float sw_dw = (float) sw / dw; + float sh_dh = (float) sh / dh; + + for (int cy = minYc; cy < maxYc; cy++) + { + uintpixel_t *dstline = &swr->fb[cy * swr->fbPitch]; + for (int cx = minXc; cx < maxXc; cx++) + { + // we need to determine the texture-space coordinate of cx/cy + float ox = (float) cx + 0.5f - pxa; + float oy = (float) cy + 0.5f - pya; + + // "undo" the rotation + float lx = cosA * ox + sinA * oy; + float ly = -sinA * ox + cosA * oy; + + // turn it into a texture-local coordinate + lx += pxa - dx; + ly += pya - dy; + + if (lx < 0 || ly < 0 || lx >= (float) dw || ly >= (float) dh) continue; + + lx = lx * sw_dw; + ly = ly * sh_dh; + + int tx = (int)(sox + lx * six); + int ty = (int)(soy + ly * siy); + + if (tx < 0) tx = 0; + if (ty < 0) ty = 0; + if (tx >= sw) tx = sw - 1; + if (ty >= sh) ty = sh - 1; + + tx += sx; + ty += sy; + + uintpixel_t src = texture->buffer[ty * texture->width + tx]; + + if (opaque(src)) + alphaBlend(&dstline[cx], tint(tintColor, src), alpha); + } + } +} + +static void swrDrawSpriteRotated( + Renderer* renderer, float dx, float dy, float dw, float dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uint32_t tintColor, float alpha, + float angleDeg, + float pivotX, + float pivotY +) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformPosIfNeeded(swr, &pivotX, &pivotY); + swrTransformSizeIfNeeded(swr, &dw, &dh); + + swrDrawSpriteRotatedInternal( + renderer, + swrFloor(dx), + swrFloor(dy), + swrCeiling(dw), + swrCeiling(dh), + texture, + sx, sy, + sw, sh, + swrConvertPixel(tintColor), + swrIntAlpha(alpha), + angleDeg, + pivotX, + pivotY + ); +} + +static void SWRenderer_drawSprite(Renderer* renderer, int32_t tpagIndex, float x, float y, + float originX, float originY, float xscale, float yscale, + float angleDeg, uint32_t color, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + DataWin* dwin = renderer->dataWin; + + if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) return; + + TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; + int16_t pageId = tpag->texturePageId; + if (0 > pageId || swr->textureCount <= (uint32_t) pageId) return; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; + + int sx = tpag->sourceX; + int sy = tpag->sourceY; + int sw = tpag->sourceWidth; + int sh = tpag->sourceHeight; + + float dx = (float)(tpag->targetX - originX); + float dy = (float)(tpag->targetY - originY); + int dw = (int)(xscale * sw); + int dh = (int)(yscale * sh); + dx *= xscale; + dy *= yscale; + dx += x; + dy += y; + + SWTexture* texture = swr->textures[pageId]; + + if (UNLIKELY(swrMustRotate(angleDeg))) + { + float pivotX = x - dx; + float pivotY = y - dy; + swrDrawSpriteRotated(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, pivotX, pivotY); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); + } +} + +static void SWRenderer_drawSpritePart(Renderer* renderer, int32_t tpagIndex, + int32_t srcOffX, int32_t srcOffY, int32_t srcW, int32_t srcH, + float x, float y, float xscale, float yscale, float angleDeg, + float pivotX, float pivotY, uint32_t color, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + DataWin* dwin = renderer->dataWin; + + if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) return; + + bool flipX = false, flipY = false; + if (xscale < 0) flipX = true, xscale = -xscale; + if (yscale < 0) flipY = true, yscale = -yscale; + + TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; + int16_t pageId = tpag->texturePageId; + if (0 > pageId || swr->textureCount <= (uint32_t) pageId) return; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; + + int sx = tpag->sourceX + srcOffX; + int sy = tpag->sourceY + srcOffY; + int sw = srcW; + int sh = srcH; + + float dx = x; + float dy = y; + int dw = swrCeiling(xscale * sw); + int dh = swrCeiling(yscale * sh); + if (flipX) dx -= dw; + if (flipY) dy -= dh; + + SWTexture* texture = swr->textures[pageId]; + + if (UNLIKELY(swrMustRotate(angleDeg))) + { + swrDrawSpriteRotated(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, pivotX * dw, pivotY * dh); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); + } +} + +static void SWRenderer_drawSpritePos(Renderer* renderer, int32_t tpagIndex, + float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, float alpha) +{ + (void)renderer; (void)tpagIndex; + (void)x1; (void)y1; (void)x2; (void)y2; + (void)x3; (void)y3; (void)x4; (void)y4; (void)alpha; + UNIMP(); +} + +static void SWRenderer_drawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, + uint32_t color, float alpha, bool outline) +{ + uintpixel_t pxcolor = swrConvertPixel(color); + + SWRenderer* swr = (SWRenderer*) renderer; + + if (outline) + { + swrDrawRectangle(renderer, x1, y1, x2, y2, pxcolor, alpha); + } + else + { + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + + int alphaInt = swrIntAlpha(alpha); + int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); + int xd = x2i - x1i; + int yd = y2i - y1i; + if (xd <= 0 || yd <= 0) return; + + for (int y = 0; y <= yd; y++) { + swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor, alphaInt); + } + } +} + +static void SWRenderer_drawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, + uint32_t color1, uint32_t color2, uint32_t color3, uint32_t color4, + float alpha, bool outline) +{ + (void)renderer; (void)x1; (void)y1; (void)x2; (void)y2; + (void)color1; (void)color2; (void)color3; (void)color4; (void)alpha; (void)outline; + UNIMP(); +} + +static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2, float y2, + float width, uint32_t color, float alpha) +{ + (void)renderer; (void)x1; (void)y1; (void)x2; (void)y2; + (void)width; (void)color; (void)alpha; + + swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color), alpha); +} + +static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, + float x3, float y3, bool outline) +{ + (void)outline; + + uintpixel_t drawColorCvt = swrConvertPixel(renderer->drawColor); + swrDrawLine(renderer, x1, y1, x2, y2, 1, drawColorCvt, renderer->drawAlpha); + swrDrawLine(renderer, x1, y1, x3, y3, 1, drawColorCvt, renderer->drawAlpha); + swrDrawLine(renderer, x2, y2, x3, y3, 1, drawColorCvt, renderer->drawAlpha); +} + +static void SWRenderer_drawLineColor(Renderer* renderer, float x1, float y1, float x2, float y2, + float width, uint32_t color1, uint32_t color2, float alpha) +{ + (void)renderer; (void)x1; (void)y1; (void)x2; (void)y2; + (void)width; (void)color1; (void)color2; (void)alpha; + UNIMP(); +} + +typedef struct +{ + Font* font; + TexturePageItem* fontTpag; // single TPAG for regular fonts (NULL for sprite fonts) + int fontTpagIndex; + int fontPageId; + Sprite* spriteFontSprite; // source sprite for sprite fonts (NULL for regular fonts) +} +SwrFontState; + +static bool swrResolveFontState(SWRenderer* swr, DataWin* dw, Font* font, SwrFontState* state) +{ + state->font = font; + state->fontTpag = NULL; + state->fontTpagIndex = 0; + state->spriteFontSprite = NULL; + + if (font->isSpriteFont) + { + state->spriteFontSprite = &dw->sprt.sprites[font->spriteIndex]; + } + else + { + state->fontTpagIndex = font->tpagIndex; + if (state->fontTpagIndex < 0) return false; + + state->fontTpag = &dw->tpag.items[state->fontTpagIndex]; + int16_t pageId = state->fontTpag->texturePageId; + if (0 > pageId || (uint32_t) pageId >= swr->textureCount) return false; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return false; + + state->fontPageId = pageId; + } + + return true; +} + +static bool swrResolveGlyph( + SWRenderer* swr, DataWin* dw, SwrFontState* state, FontGlyph* glyph, float cursorX, float cursorY, + int* tpagIndex, int* pageId, int* sx, int* sy, int* sw, int* sh, float* dx, float* dy +) +{ + Font* font = state->font; + if (font->isSpriteFont && state->spriteFontSprite != NULL) + { + Sprite* sprite = state->spriteFontSprite; + int32_t glyphIndex = (int32_t) (glyph - font->glyphs); + if (0 > glyphIndex || glyphIndex >= (int32_t) sprite->textureCount) return false; + + int32_t tpagIdx = sprite->tpagIndices[glyphIndex]; + if (0 > tpagIdx) return false; + + TexturePageItem* glyphTpag = &dw->tpag.items[tpagIdx]; + int16_t pid = glyphTpag->texturePageId; + if (0 > pid || (uint32_t) pid >= swr->textureCount) return false; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pid)) return false; + + *tpagIndex = tpagIdx; + *pageId = glyphTpag->texturePageId; + + *sx = glyphTpag->sourceX; + *sy = glyphTpag->sourceY; + *sw = glyphTpag->sourceWidth; + *sh = glyphTpag->sourceHeight; + + *dx = cursorX + glyph->offset; + *dy = cursorY + glyphTpag->targetY - sprite->originY; + } + else + { + *tpagIndex = state->fontTpagIndex; + *pageId = state->fontPageId; + + *sx = state->fontTpag->sourceX + glyph->sourceX; + *sy = state->fontTpag->sourceY + glyph->sourceY; + *sw = glyph->sourceWidth; + *sh = glyph->sourceHeight; + + *dx = cursorX + glyph->offset; + *dy = cursorY; + } + + return true; +} + +static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, UNUSED float angleDeg, int32_t color, UNUSED float alpha) +{ + Renderer* renderer = &swr->base; + DataWin* dwin = renderer->dataWin; + + int32_t fontIndex = renderer->drawFont; + if (0 > fontIndex || dwin->font.count <= (uint32_t) fontIndex) return; + + Font* font = &dwin->font.fonts[fontIndex]; + + SwrFontState fontState; + if (!swrResolveFontState(swr, dwin, font, &fontState)) return; + + // TODO: do we need to mirror the way the text scrolls too?! + float cosA = 1.0f, sinA = 0.0f, angleRad = 0.0f; + bool mustRotate = swrMustRotateTolerant(angleDeg); + if (UNLIKELY(mustRotate)) + { + angleRad = -angleDeg * M_PI / 180.0f; + cosA = cosf(angleRad); + sinA = sinf(angleRad); + } + + int textLen = (int) strlen(text); + int lineCount = TextUtils_countLines(text, textLen); + float lineStride = TextUtils_lineStride(font); + + // Vertical alignment offset + float totalHeight = (float) lineCount * lineStride; + float valignOffset = 0; + if (renderer->drawValign == 1) valignOffset = -totalHeight / 2.0f; + else if (renderer->drawValign == 2) valignOffset = -totalHeight; + + xscale *= font->scaleX; + yscale *= font->scaleY; + + // Iterate through lines. HTML5 subtracts ascenderOffset from the per-line y offset + // (see yyFont.GR_Text_Draw), shifting glyphs up so the baseline aligns with the drawn y. + float cursorY = valignOffset - (float) font->ascenderOffset; + int32_t lineStart = 0; + + for (int32_t lineIdx = 0; lineCount > lineIdx; lineIdx++) { + // Find end of current line + int32_t lineEnd = lineStart; + while (textLen > lineEnd && !TextUtils_isNewlineChar(text[lineEnd])) { + lineEnd++; + } + int32_t lineLen = lineEnd - lineStart; + + // Horizontal alignment offset for this line + float lineWidth = TextUtils_measureLineWidth(font, text + lineStart, lineLen); + float halignOffset = 0; + if (renderer->drawHalign == 1) halignOffset = -lineWidth / 2.0f; + else if (renderer->drawHalign == 2) halignOffset = -lineWidth; + + float cursorX = halignOffset; + + // Render each glyph in the line - decode each codepoint once and carry it forward as next iteration's ch (also used for kerning) + int32_t pos = 0; + uint16_t ch = 0; + bool hasCh = false; + if (lineLen > pos) { + ch = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); + hasCh = true; + } + + while (hasCh) { + FontGlyph* glyph = TextUtils_findGlyph(font, ch); + + uint16_t nextCh = 0; + bool hasNext = lineLen > pos; + if (hasNext) nextCh = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); + + if (glyph != nullptr) { + bool drewSuccessfully = false; + if (glyph->sourceWidth != 0 && glyph->sourceHeight != 0) { + int fontTpagIndex = 0, pageId = 0; + int sx, sy, sw, sh, dw, dh; + float dx, dy; + if (swrResolveGlyph(swr, dwin, &fontState, glyph, cursorX, cursorY, + &fontTpagIndex, &pageId, &sx, &sy, &sw, &sh, &dx, &dy)) + { + dx *= xscale; dx += x; + dy *= xscale; dy += y; + dw = swrCeiling(xscale * glyph->sourceWidth); + dh = swrCeiling(yscale * glyph->sourceHeight); + + // TODO: at 640x480, for some reason, without this fixup the + // letters in the "Name the fallen human." screen don't shake + dx = round(dx * 2) / 2; + dy = round(dy * 2) / 2; + + SWTexture* texture = swr->textures[pageId]; + + if (UNLIKELY(mustRotate)) + { + float ndx = cosA * dx - sinA * dy; + float ndy = sinA * dx + cosA * dy; + swrDrawSpriteRotated(renderer, ndx, ndy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, 0.0f, 0.0f); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); + } + + drewSuccessfully = true; + } + } + + cursorX += glyph->shift; + if (drewSuccessfully && hasNext) { + cursorX += TextUtils_getKerningOffset(glyph, nextCh); + } + } + + ch = nextCh; + hasCh = hasNext; + } + + cursorY += lineStride; + // Skip past the newline, treating \r\n and \n\r as single breaks + if (textLen > lineEnd) { + lineStart = TextUtils_skipNewline(text, lineEnd, textLen); + } else { + lineStart = lineEnd; + } + } +} + +static void SWRenderer_drawText(Renderer* renderer, const char* text, float x, float y, + float xscale, float yscale, float angleDeg) +{ + SWRenderer* swr = (SWRenderer*) renderer; + swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, renderer->drawColor, renderer->drawAlpha); +} + +static void SWRenderer_drawTextColor(Renderer* renderer, const char* text, float x, float y, + float xscale, float yscale, float angleDeg, + int32_t c1, int32_t c2, int32_t c3, int32_t c4, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, c1, renderer->drawAlpha); +} + +static void SWRenderer_flush(Renderer* renderer) +{ + (void)renderer; + UNIMP(); +} + +static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alpha) +{ + (void)renderer; (void)color; (void)alpha; + UNIMP(); +} + +static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t surfaceID, + int32_t x, int32_t y, int32_t w, int32_t h, + bool removeback, bool smooth, + int32_t xorig, int32_t yorig) +{ + (void)renderer; (void)surfaceID; (void)x; (void)y; (void)w; (void)h; + (void)removeback; (void)smooth; (void)xorig; (void)yorig; + UNIMP(); + return 0; +} + +static void SWRenderer_deleteSprite(Renderer* renderer, int32_t spriteIndex) +{ + UNIMP(); + (void)renderer; (void)spriteIndex; +} + +static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) +{ + UNIMP(); + (void)renderer; (void)mode; +} + +static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, int32_t dfactor) +{ + UNIMP(); + (void)renderer; (void)sfactor; (void)dfactor; +} + +static void SWRenderer_gpuSetBlendEnable(Renderer* renderer, bool enable) +{ + UNIMP(); + (void)renderer; (void)enable; +} + +static void SWRenderer_gpuSetAlphaTestEnable(Renderer* renderer, bool enable) +{ + UNIMP(); + (void)renderer; (void)enable; +} + +static void SWRenderer_gpuSetAlphaTestRef(Renderer* renderer, uint8_t ref) +{ + UNIMP(); + (void)renderer; (void)ref; +} + +static void SWRenderer_gpuSetColorWriteEnable(Renderer* renderer, bool red, bool green, bool blue, bool alpha) +{ + UNIMP(); + (void)renderer; (void)red; (void)green; (void)blue; (void)alpha; +} + +static void SWRenderer_gpuGetColorWriteEnable(Renderer* renderer, bool* red, bool* green, bool* blue, bool* alpha) +{ + UNIMP(); + *red = false; + *green = false; + *blue = false; + *alpha = false; + (void)renderer; (void)red; (void)green; (void)blue; (void)alpha; +} + +static bool SWRenderer_gpuGetBlendEnable(Renderer* renderer) +{ + UNIMP(); + (void)renderer; + return false; +} + +static void SWRenderer_gpuSetFog(Renderer* renderer, bool enable, uint32_t color) +{ + UNIMP(); + (void)renderer; (void)enable; (void)color; +} + +static void SWRenderer_drawTiled(Renderer* renderer, int32_t tpagIndex, + float originX, float originY, float x, float y, + float xscale, float yscale, bool tileX, bool tileY, + float roomW, float roomH, uint32_t color, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + DataWin* dwin = renderer->dataWin; + + if (0 > tpagIndex || dwin->tpag.count <= (uint32_t) tpagIndex) return; + + TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; + int16_t pageId = tpag->texturePageId; + if (0 > pageId || swr->textureCount <= (uint32_t) pageId) return; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; + + float axScale = fabsf(xscale); + float ayScale = fabsf(yscale); + float tileW = (float) tpag->boundingWidth * axScale; + float tileH = (float) tpag->boundingHeight * ayScale; + if (0 >= tileW || 0 >= tileH) return; + + float startX, endX, startY, endY; + if (tileX) { + startX = fmodf(x - originX * axScale, tileW); + if (startX > 0) startX -= tileW; + endX = roomW; + } else { + startX = x - originX * axScale; + endX = startX + tileW; + } + if (tileY) { + startY = fmodf(y - originY * ayScale, tileH); + if (startY > 0) startY -= tileH; + endY = roomH; + } else { + startY = y - originY * ayScale; + endY = startY + tileH; + } + + int sx = tpag->sourceX; + int sy = tpag->sourceY; + int sw = tpag->sourceWidth; + int sh = tpag->sourceHeight; + + int localX0 = tpag->targetX - originX; + int localY0 = tpag->targetY - originY; + int localX1 = localX0 + tpag->sourceWidth; + int localY1 = localY0 + tpag->sourceHeight; + int sx0 = xscale * localX0; + int sy0 = yscale * localY0; + int sx1 = xscale * localX1; + int sy1 = yscale * localY1; + + for (int dy = startY; endY > dy; dy += tileH) { + int cy = dy + (int)(originY * ayScale); + int vy0 = cy + sy0; + int vy1 = cy + sy1; + int dh = vy1 - vy0; + + for (int dx = startX; endX > dx; dx += tileW) { + int cx = dx + (int)(originX * axScale); + int vx0 = cx + sx0; + int vx1 = cx + sx1; + int dw = vx1 - vx0; + + swrDrawSprite(renderer, vx0, vy0, dw, dh, swr->textures[pageId], sx, sy, sw, sh, color, alpha); + } + } +} + +static int32_t SWRenderer_createSurface(Renderer* renderer, int32_t width, int32_t height) +{ + UNIMP(); + (void)renderer; (void)width; (void)height; + return 0; +} + +static bool SWRenderer_surfaceExists(Renderer* renderer, int32_t surfaceID) +{ + UNIMP(); + (void)renderer; (void)surfaceID; + return false; +} + +static bool SWRenderer_setRenderTarget(Renderer* renderer, int32_t surfaceID) +{ + UNIMP(); + (void)renderer; (void)surfaceID; + return false; +} + +static float SWRenderer_getSurfaceWidth(Renderer* renderer, int32_t surfaceID) +{ + UNIMP(); + (void)renderer; (void)surfaceID; + return 0.0f; +} + +static float SWRenderer_getSurfaceHeight(Renderer* renderer, int32_t surfaceID) +{ + UNIMP(); + (void)renderer; (void)surfaceID; + return 0.0f; +} + +static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, + int32_t srcLeft, int32_t srcTop, int32_t srcWidth, int32_t srcHeight, + float x, float y, float xscale, float yscale, float angleDeg, + uint32_t color, float alpha) +{ + UNIMP(); + (void)renderer; (void)surfaceID; + (void)srcLeft; (void)srcTop; (void)srcWidth; (void)srcHeight; + (void)x; (void)y; (void)xscale; (void)yscale; (void)angleDeg; + (void)color; (void)alpha; +} + +static void SWRenderer_surfaceResize(Renderer* renderer, int32_t surfaceID, int32_t width, int32_t height) +{ + UNIMP(); + (void)renderer; (void)surfaceID; (void)width; (void)height; +} + +static void SWRenderer_surfaceFree(Renderer* renderer, int32_t surfaceID) +{ + UNIMP(); + (void)renderer; (void)surfaceID; +} + +static void SWRenderer_surfaceCopy(Renderer* renderer, + int32_t DestSurfaceID, int32_t DestX, int32_t DestY, + int32_t SrcSurfaceID, int32_t SrcX, int32_t SrcY, + int32_t SrcW, int32_t SrcH, bool part) +{ + UNIMP(); + (void)renderer; + (void)DestSurfaceID; (void)DestX; (void)DestY; + (void)SrcSurfaceID; (void)SrcX; (void)SrcY; + (void)SrcW; (void)SrcH; (void)part; +} + +static bool SWRenderer_surfaceGetPixels(Renderer* renderer, int32_t surfaceID, uint8_t* outRGBA) +{ + UNIMP(); + (void)renderer; (void)surfaceID; (void)outRGBA; + return false; +} + +static void SWRenderer_drawTiledPart(Renderer* renderer, int32_t tpagIndex, + int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, + float dstX, float dstY, float dstW, float dstH, + uint32_t color, float alpha) +{ + UNIMP(); + (void)renderer; (void)tpagIndex; + (void)srcX; (void)srcY; (void)srcW; (void)srcH; + (void)dstX; (void)dstY; (void)dstW; (void)dstH; + (void)color; (void)alpha; +} + +static RendererVtable swrVtable = +{ + .init = SWRenderer_init, + .destroy = SWRenderer_destroy, + .beginFrame = SWRenderer_beginFrame, + .endFrameInit = SWRenderer_endFrameInit, + .endFrameEnd = SWRenderer_endFrameEnd, + .beginView = SWRenderer_beginView, + .endView = SWRenderer_endView, + .beginGUI = SWRenderer_beginGUI, + .endGUI = SWRenderer_endGUI, + .drawSprite = SWRenderer_drawSprite, + .drawSpritePart = SWRenderer_drawSpritePart, + .drawSpritePos = SWRenderer_drawSpritePos, + .drawRectangle = SWRenderer_drawRectangle, + .drawRectangleColor = SWRenderer_drawRectangleColor, + .drawLine = SWRenderer_drawLine, + .drawTriangle = SWRenderer_drawTriangle, + .drawLineColor = SWRenderer_drawLineColor, + .drawText = SWRenderer_drawText, + .drawTextColor = SWRenderer_drawTextColor, + .flush = SWRenderer_flush, + .clearScreen = SWRenderer_clearScreen, + .createSpriteFromSurface = SWRenderer_createSpriteFromSurface, + .deleteSprite = SWRenderer_deleteSprite, + .gpuSetBlendMode = SWRenderer_gpuSetBlendMode, + .gpuSetBlendModeExt = SWRenderer_gpuSetBlendModeExt, + .gpuSetBlendEnable = SWRenderer_gpuSetBlendEnable, + .gpuSetAlphaTestEnable = SWRenderer_gpuSetAlphaTestEnable, + .gpuSetAlphaTestRef = SWRenderer_gpuSetAlphaTestRef, + .gpuSetColorWriteEnable = SWRenderer_gpuSetColorWriteEnable, + .gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable, + .gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable, + .gpuSetFog = SWRenderer_gpuSetFog, + .drawTile = NULL, + .drawTiled = SWRenderer_drawTiled, + .createSurface = SWRenderer_createSurface, + .surfaceExists = SWRenderer_surfaceExists, + .setRenderTarget = SWRenderer_setRenderTarget, + .getSurfaceWidth = SWRenderer_getSurfaceWidth, + .getSurfaceHeight = SWRenderer_getSurfaceHeight, + .drawSurface = SWRenderer_drawSurface, + .surfaceResize = SWRenderer_surfaceResize, + .surfaceFree = SWRenderer_surfaceFree, + .surfaceCopy = SWRenderer_surfaceCopy, + .surfaceGetPixels = SWRenderer_surfaceGetPixels, + .drawTiledPart = SWRenderer_drawTiledPart, +}; + +void SWRenderer_clearFrameBuffer(Renderer* renderer, uint32_t color) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + uintpixel_t pxcolor = swrConvertPixel(color); + + size_t fbSize = swr->fbPitch; + fbSize *= swr->height; + for (size_t i = 0; i < fbSize; i++) + { + swr->fb[i] = pxcolor; + } +} + +Renderer* SWRenderer_create(int windowWidth, int windowHeight) +{ + SWRenderer* swr = safeCalloc(1, sizeof(SWRenderer)); + swr->base.vtable = &swrVtable; + swr->base.drawColor = 0xFFFFFF; + swr->base.drawAlpha = 1.0f; + swr->base.drawFont = -1; + swr->base.drawHalign = 0; + swr->base.drawValign = 0; + swr->base.circlePrecision = 24; + + swr->width = windowWidth; + swr->height = windowHeight; + + return (Renderer*) swr; +} diff --git a/src/sw/sw_renderer.h b/src/sw/sw_renderer.h new file mode 100755 index 000000000..f8ca72267 --- /dev/null +++ b/src/sw/sw_renderer.h @@ -0,0 +1,7 @@ +#pragma once + +#include "renderer.h" + +Renderer* SWRenderer_create(int windowWidth, int windowHeight); + +void SWRenderer_clearFrameBuffer(Renderer* renderer, uint32_t color); diff --git a/src/utils.h b/src/utils.h index ef12f208f..24e78731e 100644 --- a/src/utils.h +++ b/src/utils.h @@ -25,6 +25,7 @@ do { \ if (!(condition)) { \ fprintf(stderr, "Requirement failed at %s:%d\n", __FILE__, __LINE__); \ + fflush(stderr); \ abort(); \ } \ } while (0) @@ -33,6 +34,7 @@ do { \ if (!(condition)) { \ fprintf(stderr, "Requirement failed at %s:%d: %s\n", __FILE__, __LINE__, message); \ +fflush(stderr); \ abort(); \ } \ } while (0) @@ -41,6 +43,7 @@ abort(); \ do { \ if (!(condition)) { \ fprintf(stderr, "Requirement failed at %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ +fflush(stderr); \ abort(); \ } \ } while (0) @@ -49,6 +52,7 @@ abort(); \ typeof(ptr) _val = (ptr); \ if (_val == NULL) { \ fprintf(stderr, "%s:%d: requireNotNull failed: '%s'\n", __FILE__, __LINE__, #ptr); \ +fflush(stderr); \ abort(); \ } \ _val; \ @@ -58,6 +62,7 @@ _val; \ typeof(ptr) _val = (ptr); \ if (_val == NULL) { \ fprintf(stderr, "%s:%d: requireNotNull failed: %s\n", __FILE__, __LINE__, (msg)); \ +fflush(stderr); \ abort(); \ } \ _val; \ From b813cff02580863d9a6a912b16f2d7404b2d2eee Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sun, 17 May 2026 20:45:55 +0300 Subject: [PATCH 002/130] Remove redundant src/image inclusion. --- CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e3340df1f..fed01ae26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -233,21 +233,20 @@ elseif(PLATFORM STREQUAL "sdl") if (NOT ENABLE_LEGACY_GL AND NOT ENABLE_SW_RENDERER) message(FATAL_ERROR "SDL requires legacy GL or the software renderer!") endif() - + if(ENABLE_LEGACY_GL) file(GLOB GL_SOURCES src/gl_legacy/*.c src/gl_common/*.c src/image/*.c) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_legacy) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common) target_sources(butterscotch PRIVATE ${GL_SOURCES}) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl) - target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image) elseif(ENABLE_SW_RENDERER) file(GLOB SWR_SOURCES src/sw/*.c src/image/*.c) add_compile_definitions(ENABLE_SW_RENDERER) target_sources(butterscotch PRIVATE ${SWR_SOURCES}) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/sw) - target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image) endif() + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image) # Butterscotch VM/interpreter profiler option(ENABLE_VM_GML_PROFILER "Enable Butterscotch VM/interpreter profiler" ON) From c6791e7b990d0852a3cbc92fe75d84c8aa8edeab Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sun, 17 May 2026 20:48:18 +0300 Subject: [PATCH 003/130] Add build rules for Makefile --- Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Makefile b/Makefile index 4a5d91f72..04c391bd8 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,15 @@ ifndef DISABLE_BC17 DEFINES += -DENABLE_BC17 endif +ifndef DISABLE_SW_RENDERER +ifeq ($(PLATFORM),sdl) +DEFINES += -DENABLE_SW_RENDERER +SRCS += $(wildcard src/swr/*.c) +INCLUDES += -Isrc/swr +HEADERS += $(wildcard src/swr/*.h) +endif +endif + # GNU make doesn't have a way to do OR in conditionals, stupid language for clowns ifndef DISABLE_LEGACY_GL ENABLE_GL := 1 From 8bb4bc1c84d4694c18b6ed9fdf8e0d28e1f05bb9 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sun, 17 May 2026 20:50:48 +0300 Subject: [PATCH 004/130] Fix oopsie --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 04c391bd8..3cd48e0a3 100644 --- a/Makefile +++ b/Makefile @@ -42,9 +42,9 @@ endif ifndef DISABLE_SW_RENDERER ifeq ($(PLATFORM),sdl) DEFINES += -DENABLE_SW_RENDERER -SRCS += $(wildcard src/swr/*.c) +SRCS += $(wildcard src/sw/*.c) INCLUDES += -Isrc/swr -HEADERS += $(wildcard src/swr/*.h) +HEADERS += $(wildcard src/sw/*.h) endif endif From 0fe12739fe0cdd1d8498c78dc83354a72d94f4fc Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sun, 17 May 2026 20:54:41 +0300 Subject: [PATCH 005/130] * endFrameInit/endFrameEnd no longer print "NYI" --- src/sw/sw_renderer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index ee69ef4da..a021e36f9 100755 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -372,14 +372,12 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam // This used to be just one, "endFrame". Not sure what the different is. static void SWRenderer_endFrameInit(Renderer* renderer) { - UNIMP(); SWRenderer* swr = (SWRenderer*) renderer; Runner_setNextFrame(swr->fb, swr->width, swr->height); } static void SWRenderer_endFrameEnd(Renderer* renderer) { - UNIMP(); SWRenderer* swr = (SWRenderer*) renderer; Runner_setNextFrame(swr->fb, swr->width, swr->height); } From 01b731252fe43de72cf4bd7636db165fc510a1e4 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sun, 17 May 2026 13:58:09 -0400 Subject: [PATCH 006/130] fix build --- Makefile | 2 +- src/sw/debug_utils.c | 44 ------------------ src/sw/fb_convert.c | 104 ------------------------------------------- src/sw/fb_convert.h | 13 ------ 4 files changed, 1 insertion(+), 162 deletions(-) delete mode 100755 src/sw/debug_utils.c delete mode 100755 src/sw/fb_convert.c delete mode 100755 src/sw/fb_convert.h diff --git a/Makefile b/Makefile index 3cd48e0a3..b954645f0 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ ifndef DISABLE_SW_RENDERER ifeq ($(PLATFORM),sdl) DEFINES += -DENABLE_SW_RENDERER SRCS += $(wildcard src/sw/*.c) -INCLUDES += -Isrc/swr +INCLUDES += -Isrc/sw HEADERS += $(wildcard src/sw/*.h) endif endif diff --git a/src/sw/debug_utils.c b/src/sw/debug_utils.c deleted file mode 100755 index 604dbe1ca..000000000 --- a/src/sw/debug_utils.c +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include - -int debug_fprintf(FILE* stream, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - if (stream == stderr) - { - static char buffer[4096]; - int len = vsnprintf(buffer, sizeof(buffer), fmt, args); - - OutputDebugStringA(buffer); - - FILE* stderr2 = fopen("log.txt", "a+"); - fwrite(buffer, 1, strlen(buffer), stderr2); - fclose(stderr2); - - va_end(args); - return len; - } - - int len = vfprintf(stream, fmt, args); - va_end(args); - return len; -} - -int debug_printf(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - static char buffer[4096]; - int len = vsnprintf(buffer, sizeof(buffer), fmt, args); - OutputDebugStringA(buffer); - - FILE* stderr2 = fopen("log.txt", "a+"); - fwrite(buffer, 1, strlen(buffer), stderr2); - fclose(stderr2); - - va_end(args); - return len; -} diff --git a/src/sw/fb_convert.c b/src/sw/fb_convert.c deleted file mode 100755 index 9b0ba9f7c..000000000 --- a/src/sw/fb_convert.c +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include -#include "fb_convert.h" -#include "pixel_convert.h" -#include "utils.h" - -FORCE_INLINE UNUSED -uint16_t argb8888_to_rgb1555(uint32_t xl) -{ - Pixel32ARGB x; - x.l = xl; - return (x.p.b >> 3) | ((x.p.g >> 3) << 5) | ((x.p.r >> 3) << 10); -} - -FORCE_INLINE UNUSED -uint8_t argb8888_to_rgb332(uint32_t xl) -{ - Pixel32ARGB x; - x.l = xl; - return (x.p.r >> 5) | ((x.p.g >> 5) << 3) | ((x.p.b >> 6) << 6); -} - -uint8_t* swrConvert32to24(uint8_t* dest, uint32_t* src, int width, int height) -{ - int size = width * height; - if (!dest) { - dest = safeCalloc(3, size); - } - - for (int i = 0, j = 0; i < size; i++) - { - Pixel32ARGB x; - x.l = src[i]; - - dest[j++] = x.p.b; - dest[j++] = x.p.g; - dest[j++] = x.p.r; - } - - return dest; -} - -uint16_t* swrConvert32to16(uint16_t* dest, uint32_t* src, int width, int height) -{ - int size = width * height; - if (!dest) { - dest = safeCalloc(sizeof(uint16_t), size); - } - - for (int i = 0; i < size; i++) - dest[i] = argb8888_to_rgb1555(src[i]); - - return dest; -} - -uint8_t* swrConvert32to8(uint8_t* dest, uint32_t* src, int width, int height) -{ - int size = width * height; - if (!dest) { - dest = safeCalloc(sizeof(uint8_t), size); - } - - for (int i = 0; i < size; i++) - dest[i] = argb8888_to_rgb332(src[i]); - - return dest; -} - -typedef struct -{ - BITMAPINFOHEADER bmiHeader; - RGBQUAD bmiColors[256]; -} -BITMAPINFO_8BIT; - -BITMAPINFO* swrSetup8BitBitmapInfo(int width, int height) -{ - static BITMAPINFO_8BIT* pBmi; - if (!pBmi) { - pBmi = safeCalloc(1, sizeof(BITMAPINFO_8BIT)); - - // set up palette - int i = 0; - for (int b = 0; b < 4; b++) - for (int g = 0; g < 8; g++) - for (int r = 0; r < 8; r++) - { - pBmi->bmiColors[i].rgbRed = (r << 5) + (r << 2) + (r >> 1); - pBmi->bmiColors[i].rgbGreen = (g << 5) + (g << 2) + (g >> 1); - pBmi->bmiColors[i].rgbBlue = (b << 6) + (b << 4) + (b << 2) + b; - pBmi->bmiColors[i].rgbReserved = 255; - i++; - } - } - - pBmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - pBmi->bmiHeader.biWidth = width; - pBmi->bmiHeader.biHeight = -height; - pBmi->bmiHeader.biPlanes = 1; - pBmi->bmiHeader.biCompression = BI_RGB; - pBmi->bmiHeader.biBitCount = 8; - - return (BITMAPINFO*) pBmi; -} diff --git a/src/sw/fb_convert.h b/src/sw/fb_convert.h deleted file mode 100755 index dad12c663..000000000 --- a/src/sw/fb_convert.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct tagBITMAPINFO BITMAPINFO; - -uint8_t* swrConvert32to24(uint8_t* dest, uint32_t* src, int width, int height); - -uint16_t* swrConvert32to16(uint16_t* dest, uint32_t* src, int width, int height); - -uint8_t* swrConvert32to8(uint8_t* dest, uint32_t* src, int width, int height); - -BITMAPINFO* swrSetup8BitBitmapInfo(int width, int height); From 9a017401921690c29fb1e789a605cec9b60a2549 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sun, 17 May 2026 21:10:06 +0300 Subject: [PATCH 007/130] Prepare for surface work --- src/sdl/main.c | 2 +- src/sw/sw_renderer.c | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/sdl/main.c b/src/sdl/main.c index 2828bcb71..d76c42a74 100644 --- a/src/sdl/main.c +++ b/src/sdl/main.c @@ -1216,7 +1216,7 @@ int main(int argc, char* argv[]) { if (!args.headless && runner->currentRoom->speed > 0) { static bool fastForwardActive = false; static bool fastForwardTabPrev = false; - bool fastForwardTabNow = false; + bool fastForwardTabNow = RunnerKeyboard_checkPressed(runner->keyboard, '\t'); if (args.fastForwardSpeed > 0.0 && fastForwardTabNow && !fastForwardTabPrev) { fastForwardActive = !fastForwardActive; lastFrameTime = (SDL_GetTicks()/1000.0f); diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index a021e36f9..7d398e64b 100755 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -18,6 +18,8 @@ #define TEXTURE_LRU_LENGTH 16 +#define SURFACE_MAX_COUNT 16 + typedef struct { uintpixel_t* buffer; @@ -36,12 +38,21 @@ typedef struct uintpixel_t* fb; uint16_t fbPitch; // in sizeof(uintpixel_t) units, NOT in bytes! + uintpixel_t* mainFb; + uint16_t mainWidth; + uint16_t mainHeight; + uint16_t mainPitch; + bool drawingToSurface; + SWTexture** textures; uint32_t* textureIndexLRU; uint32_t textureIndexLRUHead; uint32_t textureIndexLRUTail; size_t textureCount; + SWTexture** surfaces; + size_t surfaceCount; + bool viewActive; int viewX, viewY, viewW, viewH; int portX, portY, portW, portH; @@ -339,6 +350,10 @@ static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) swr->fb = safeCalloc(swr->width * swr->height, sizeof(uintpixel_t)); swr->fbPitch = swr->width; + //allocate surface buffer + swr->surfaces = safeCalloc(SURFACE_MAX_COUNT, sizeof(SWTexture*)); + swr->surfaceCount = SURFACE_MAX_COUNT; + //allocate texture buffer swr->textureCount = dataWin->txtr.count; swr->textures = safeCalloc(swr->textureCount, sizeof(SWTexture*)); @@ -367,18 +382,21 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam swr->gameH = gameH; swr->windowW = windowW; swr->windowH = windowH; + swr->drawingToSurface = false; } // This used to be just one, "endFrame". Not sure what the different is. static void SWRenderer_endFrameInit(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; + assert(!swr->drawingToSurface); Runner_setNextFrame(swr->fb, swr->width, swr->height); } static void SWRenderer_endFrameEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; + assert(!swr->drawingToSurface); Runner_setNextFrame(swr->fb, swr->width, swr->height); } @@ -391,8 +409,17 @@ static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t view SWRenderer* swr = (SWRenderer*) renderer; - float xratio = (float) swr->windowW / swr->gameW; - float yratio = (float) swr->windowH / swr->gameH; + float xratio, yratio; + + if (swr->drawingToSurface) { + UNIMP(); + xratio = 1.0f; + yratio = 1.0f; + } + else { + xratio = (float) swr->windowW / swr->gameW; + yratio = (float) swr->windowH / swr->gameH; + } portX = (int)(portX * xratio); portY = (int)(portY * yratio); From a443be3c5ccc3d58d96a29bb541e2823eb6b0c57 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sun, 17 May 2026 21:15:10 +0300 Subject: [PATCH 008/130] Relocate a function --- src/sw/sw_renderer.c | 174 +++++++++++++++++++++---------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 7d398e64b..f039ed877 100755 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1326,33 +1326,86 @@ static void SWRenderer_drawTextColor(Renderer* renderer, const char* text, float swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, c1, renderer->drawAlpha); } -static void SWRenderer_flush(Renderer* renderer) +static void SWRenderer_drawTiled(Renderer* renderer, int32_t tpagIndex, + float originX, float originY, float x, float y, + float xscale, float yscale, bool tileX, bool tileY, + float roomW, float roomH, uint32_t color, float alpha) { - (void)renderer; - UNIMP(); -} + SWRenderer* swr = (SWRenderer*) renderer; + DataWin* dwin = renderer->dataWin; -static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alpha) -{ - (void)renderer; (void)color; (void)alpha; - UNIMP(); + if (0 > tpagIndex || dwin->tpag.count <= (uint32_t) tpagIndex) return; + + TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; + int16_t pageId = tpag->texturePageId; + if (0 > pageId || swr->textureCount <= (uint32_t) pageId) return; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; + + float axScale = fabsf(xscale); + float ayScale = fabsf(yscale); + float tileW = (float) tpag->boundingWidth * axScale; + float tileH = (float) tpag->boundingHeight * ayScale; + if (0 >= tileW || 0 >= tileH) return; + + float startX, endX, startY, endY; + if (tileX) { + startX = fmodf(x - originX * axScale, tileW); + if (startX > 0) startX -= tileW; + endX = roomW; + } else { + startX = x - originX * axScale; + endX = startX + tileW; + } + if (tileY) { + startY = fmodf(y - originY * ayScale, tileH); + if (startY > 0) startY -= tileH; + endY = roomH; + } else { + startY = y - originY * ayScale; + endY = startY + tileH; + } + + int sx = tpag->sourceX; + int sy = tpag->sourceY; + int sw = tpag->sourceWidth; + int sh = tpag->sourceHeight; + + int localX0 = tpag->targetX - originX; + int localY0 = tpag->targetY - originY; + int localX1 = localX0 + tpag->sourceWidth; + int localY1 = localY0 + tpag->sourceHeight; + int sx0 = xscale * localX0; + int sy0 = yscale * localY0; + int sx1 = xscale * localX1; + int sy1 = yscale * localY1; + + for (int dy = startY; endY > dy; dy += tileH) { + int cy = dy + (int)(originY * ayScale); + int vy0 = cy + sy0; + int vy1 = cy + sy1; + int dh = vy1 - vy0; + + for (int dx = startX; endX > dx; dx += tileW) { + int cx = dx + (int)(originX * axScale); + int vx0 = cx + sx0; + int vx1 = cx + sx1; + int dw = vx1 - vx0; + + swrDrawSprite(renderer, vx0, vy0, dw, dh, swr->textures[pageId], sx, sy, sw, sh, color, alpha); + } + } } -static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t surfaceID, - int32_t x, int32_t y, int32_t w, int32_t h, - bool removeback, bool smooth, - int32_t xorig, int32_t yorig) +static void SWRenderer_flush(Renderer* renderer) { - (void)renderer; (void)surfaceID; (void)x; (void)y; (void)w; (void)h; - (void)removeback; (void)smooth; (void)xorig; (void)yorig; + (void)renderer; UNIMP(); - return 0; } -static void SWRenderer_deleteSprite(Renderer* renderer, int32_t spriteIndex) +static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alpha) { + (void)renderer; (void)color; (void)alpha; UNIMP(); - (void)renderer; (void)spriteIndex; } static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) @@ -1414,76 +1467,6 @@ static void SWRenderer_gpuSetFog(Renderer* renderer, bool enable, uint32_t color (void)renderer; (void)enable; (void)color; } -static void SWRenderer_drawTiled(Renderer* renderer, int32_t tpagIndex, - float originX, float originY, float x, float y, - float xscale, float yscale, bool tileX, bool tileY, - float roomW, float roomH, uint32_t color, float alpha) -{ - SWRenderer* swr = (SWRenderer*) renderer; - DataWin* dwin = renderer->dataWin; - - if (0 > tpagIndex || dwin->tpag.count <= (uint32_t) tpagIndex) return; - - TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; - int16_t pageId = tpag->texturePageId; - if (0 > pageId || swr->textureCount <= (uint32_t) pageId) return; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; - - float axScale = fabsf(xscale); - float ayScale = fabsf(yscale); - float tileW = (float) tpag->boundingWidth * axScale; - float tileH = (float) tpag->boundingHeight * ayScale; - if (0 >= tileW || 0 >= tileH) return; - - float startX, endX, startY, endY; - if (tileX) { - startX = fmodf(x - originX * axScale, tileW); - if (startX > 0) startX -= tileW; - endX = roomW; - } else { - startX = x - originX * axScale; - endX = startX + tileW; - } - if (tileY) { - startY = fmodf(y - originY * ayScale, tileH); - if (startY > 0) startY -= tileH; - endY = roomH; - } else { - startY = y - originY * ayScale; - endY = startY + tileH; - } - - int sx = tpag->sourceX; - int sy = tpag->sourceY; - int sw = tpag->sourceWidth; - int sh = tpag->sourceHeight; - - int localX0 = tpag->targetX - originX; - int localY0 = tpag->targetY - originY; - int localX1 = localX0 + tpag->sourceWidth; - int localY1 = localY0 + tpag->sourceHeight; - int sx0 = xscale * localX0; - int sy0 = yscale * localY0; - int sx1 = xscale * localX1; - int sy1 = yscale * localY1; - - for (int dy = startY; endY > dy; dy += tileH) { - int cy = dy + (int)(originY * ayScale); - int vy0 = cy + sy0; - int vy1 = cy + sy1; - int dh = vy1 - vy0; - - for (int dx = startX; endX > dx; dx += tileW) { - int cx = dx + (int)(originX * axScale); - int vx0 = cx + sx0; - int vx1 = cx + sx1; - int dw = vx1 - vx0; - - swrDrawSprite(renderer, vx0, vy0, dw, dh, swr->textures[pageId], sx, sy, sw, sh, color, alpha); - } - } -} - static int32_t SWRenderer_createSurface(Renderer* renderer, int32_t width, int32_t height) { UNIMP(); @@ -1562,6 +1545,23 @@ static bool SWRenderer_surfaceGetPixels(Renderer* renderer, int32_t surfaceID, u return false; } +static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t surfaceID, + int32_t x, int32_t y, int32_t w, int32_t h, + bool removeback, bool smooth, + int32_t xorig, int32_t yorig) +{ + (void)renderer; (void)surfaceID; (void)x; (void)y; (void)w; (void)h; + (void)removeback; (void)smooth; (void)xorig; (void)yorig; + UNIMP(); + return 0; +} + +static void SWRenderer_deleteSprite(Renderer* renderer, int32_t spriteIndex) +{ + UNIMP(); + (void)renderer; (void)spriteIndex; +} + static void SWRenderer_drawTiledPart(Renderer* renderer, int32_t tpagIndex, int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, float dstX, float dstY, float dstW, float dstH, From 0e1ccaceace0c6a3b1b38d0cec33d6e785edbc2c Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sun, 17 May 2026 18:14:11 -0400 Subject: [PATCH 009/130] chmod -x --- src/sw/defines.h | 0 src/sw/pixel_convert.h | 0 src/sw/sw_renderer.c | 0 src/sw/sw_renderer.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/sw/defines.h mode change 100755 => 100644 src/sw/pixel_convert.h mode change 100755 => 100644 src/sw/sw_renderer.c mode change 100755 => 100644 src/sw/sw_renderer.h diff --git a/src/sw/defines.h b/src/sw/defines.h old mode 100755 new mode 100644 diff --git a/src/sw/pixel_convert.h b/src/sw/pixel_convert.h old mode 100755 new mode 100644 diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c old mode 100755 new mode 100644 diff --git a/src/sw/sw_renderer.h b/src/sw/sw_renderer.h old mode 100755 new mode 100644 From c2a71bd73078c53207c019aae33de7dc53644fca Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sun, 17 May 2026 19:21:58 -0400 Subject: [PATCH 010/130] no binary litterals in C99 --- src/sw/pixel_convert.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sw/pixel_convert.h b/src/sw/pixel_convert.h index 08d592011..b2853c7e7 100644 --- a/src/sw/pixel_convert.h +++ b/src/sw/pixel_convert.h @@ -17,7 +17,7 @@ typedef uint32_t uintpixel_t; typedef uint16_t uintpixel_t; #elif PIXEL_SIZE == 8 typedef uint8_t uintpixel_t; -#define PXL_TRANSPARENT (0b10101010) +#define PXL_TRANSPARENT (0xAA) #else #error "Unknown pixel size!" #endif From d7249d7eabe67daa1e8c899ca4f64b54dbc648c9 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sun, 17 May 2026 19:58:56 -0400 Subject: [PATCH 011/130] likely macro compat --- src/sw/defines.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sw/defines.h b/src/sw/defines.h index 9160d57e7..a0377fc9a 100644 --- a/src/sw/defines.h +++ b/src/sw/defines.h @@ -1,7 +1,12 @@ #pragma once +#if defined(__GNUC__) && (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)) #define LIKELY(cond) __builtin_expect(!!(cond), 1) #define UNLIKELY(cond) __builtin_expect(!!(cond), 0) +#else +#define LIKELY(cond) (cond) +#define UNLIKELY(cond) (cond) +#endif #define UNUSED __attribute__ ((unused)) #define FORCE_INLINE static inline __attribute__((always_inline)) From 1032617b2c2dd5f27883923b422b11dc9e7072e9 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 18 May 2026 19:41:36 +0300 Subject: [PATCH 012/130] Attempt to fix a big-endian issue by byte-swapping the color channel masks in SDL. --- src/sdl/main.c | 14 ++++++++++---- src/sw/defines.h | 11 +++++++++++ src/sw/pixel_convert.h | 12 +++--------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/sdl/main.c b/src/sdl/main.c index d76c42a74..37833856f 100644 --- a/src/sdl/main.c +++ b/src/sdl/main.c @@ -615,10 +615,16 @@ void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) height, 32, width * 4, - 0xff0000, - 0x00ff00, - 0x0000ff, - 0 +#ifdef IS_BIG_ENDIAN + 0x0000ff00, // Rmask + 0x00ff0000, // Gmask + 0xff000000, // Bmask +#else + 0x00ff0000, // Rmask + 0x0000ff00, // Gmask + 0x000000ff, // Bmask +#endif + 0x00000000 ); } diff --git a/src/sw/defines.h b/src/sw/defines.h index a0377fc9a..1cfd49d88 100644 --- a/src/sw/defines.h +++ b/src/sw/defines.h @@ -1,5 +1,16 @@ #pragma once +#include "common.h" + +// CONFIG: Change the size of a pixel. +// +// 32-bit: 0xAARRGGBB +// 16-bit: 0b0RRRRRGGGGGBBBBB +// 8-bit: 0bBBGGGRRR +#define PIXEL_SIZE 32 +//#define PIXEL_SIZE 16 +//#define PIXEL_SIZE 8 + #if defined(__GNUC__) && (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)) #define LIKELY(cond) __builtin_expect(!!(cond), 1) #define UNLIKELY(cond) __builtin_expect(!!(cond), 0) diff --git a/src/sw/pixel_convert.h b/src/sw/pixel_convert.h index b2853c7e7..0d61da67b 100644 --- a/src/sw/pixel_convert.h +++ b/src/sw/pixel_convert.h @@ -2,15 +2,6 @@ #include "defines.h" -// CONFIG: Change the size of a pixel. -// -// 32-bit: 0xAARRGGBB -// 16-bit: 0b0RRRRRGGGGGBBBBB -// 8-bit: 0bBBGGGRRR -#define PIXEL_SIZE 32 -//#define PIXEL_SIZE 16 -//#define PIXEL_SIZE 8 - #if PIXEL_SIZE == 32 typedef uint32_t uintpixel_t; #elif PIXEL_SIZE == 16 @@ -22,6 +13,8 @@ typedef uint8_t uintpixel_t; #error "Unknown pixel size!" #endif +// The native format coming out of GameMaker Studio's assets. +// Bytes are in the correct order even in big endian mode. typedef union { struct { @@ -31,6 +24,7 @@ typedef union } Pixel32ABGR; +// This is the format the renderer knows how to work with. typedef union { struct { From deb760dfe8c8414696f0b1bc4b0e9c191609d9c0 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 18 May 2026 21:17:47 +0300 Subject: [PATCH 013/130] Don't call Runner_setNextFrame twice in a frame. --- src/sw/sw_renderer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index f039ed877..731aa267e 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -388,9 +388,9 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam // This used to be just one, "endFrame". Not sure what the different is. static void SWRenderer_endFrameInit(Renderer* renderer) { - SWRenderer* swr = (SWRenderer*) renderer; - assert(!swr->drawingToSurface); - Runner_setNextFrame(swr->fb, swr->width, swr->height); + (void) renderer; + + //this is kinda useless to do twice isn't it? } static void SWRenderer_endFrameEnd(Renderer* renderer) From e72eb28b6b20635eef42d976fc9e33ba78a18180 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Mon, 18 May 2026 14:20:07 -0400 Subject: [PATCH 014/130] fix --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 214742585..2563e3c09 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,9 @@ endif ifdef DISABLE_LEGACY_GL ifeq ($(PLATFORM),sdl) +ifdef DISABLE_SW_RENDERER $(error must enable at least 1 renderer) +endif else ifdef DISABLE_MODERN_GL $(error must enable at least 1 renderer) From 1bddaaf79b78aa8e6251d28de38c6d7df36b2f3a Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 19 May 2026 00:07:40 +0300 Subject: [PATCH 015/130] Implement big endian mode for the software renderer. This allows the software renderer to work correctly on big-endian systems like PowerPC Macs. Note that the framebuffer will be byteswapped from little-endian mode. --- src/sdl/main.c | 8 +------- src/sw/pixel_convert.h | 29 +++++++++++++++++++++++++---- src/sw/sw_renderer.c | 19 +++++-------------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/sdl/main.c b/src/sdl/main.c index 37833856f..4fac77cb0 100644 --- a/src/sdl/main.c +++ b/src/sdl/main.c @@ -615,16 +615,10 @@ void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) height, 32, width * 4, -#ifdef IS_BIG_ENDIAN - 0x0000ff00, // Rmask - 0x00ff0000, // Gmask - 0xff000000, // Bmask -#else 0x00ff0000, // Rmask 0x0000ff00, // Gmask 0x000000ff, // Bmask -#endif - 0x00000000 + 0x00000000 // Amask ); } diff --git a/src/sw/pixel_convert.h b/src/sw/pixel_convert.h index 0d61da67b..7eee4c2f9 100644 --- a/src/sw/pixel_convert.h +++ b/src/sw/pixel_convert.h @@ -1,6 +1,7 @@ #pragma once #include "defines.h" +#include "binary_utils.h" #if PIXEL_SIZE == 32 typedef uint32_t uintpixel_t; @@ -14,7 +15,6 @@ typedef uint8_t uintpixel_t; #endif // The native format coming out of GameMaker Studio's assets. -// Bytes are in the correct order even in big endian mode. typedef union { struct { @@ -28,7 +28,11 @@ Pixel32ABGR; typedef union { struct { +#ifdef IS_BIG_ENDIAN + uint8_t a, r, g, b; +#else uint8_t b, g, r, a; +#endif } p; uint32_t l; } @@ -66,7 +70,7 @@ uint8_t abgr8888_to_rgb332(uint32_t xl) } FORCE_INLINE UNUSED -uintpixel_t swrConvertPixelTexture(uint32_t gmPixel) +uintpixel_t swrConvertPixelBase(uint32_t gmPixel) { #if PIXEL_SIZE == 32 return (gmPixel & 0xFF00FF00) | ((gmPixel & 0xFF) << 16) | ((gmPixel >> 16) & 0xFF); @@ -77,8 +81,25 @@ uintpixel_t swrConvertPixelTexture(uint32_t gmPixel) #endif } +#if PIXEL_SIZE == 32 +#define TRANSPARENT_MASK 0xFF000000 +#elif PIXEL_SIZE == 16 +#define TRANSPARENT_MASK 0x8000 +#endif + #if PIXEL_SIZE == 8 -#define swrConvertPixel(x) swrConvertPixelTexture((x) | 0xFF000000) + +#define swrConvertPixel(x) swrConvertPixelBase((x) | 0xFF000000) +#define swrConvertPixelTexture(x) swrConvertPixelBase(x) + +#elif defined IS_BIG_ENDIAN + +#define swrConvertPixel(x) swrConvertPixelBase(x) +#define swrConvertPixelTexture(x) swrConvertPixelBase(BinaryUtils_bswap32(x)) + #else -#define swrConvertPixel swrConvertPixelTexture + +#define swrConvertPixel swrConvertPixelBase +#define swrConvertPixelTexture swrConvertPixelBase + #endif diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 731aa267e..46f02118e 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -68,26 +68,17 @@ FORCE_INLINE int swrAbs(int x) { return x < 0 ? -x : x; } FORCE_INLINE bool opaque(uintpixel_t color) { -#if PIXEL_SIZE == 32 - return (color & 0xFF000000) != 0; -#elif PIXEL_SIZE == 16 - return (color & 0x8000) != 0; -#else +#if PIXEL_SIZE == 8 return (color != PXL_TRANSPARENT); +#else + return (color & TRANSPARENT_MASK) != 0; #endif } -typedef union -{ - struct { uint8_t b, g, r, a; } p; - uint32_t l; -} -Color32; - FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) { #if PIXEL_SIZE == 32 - Color32 x, y; + Pixel32ARGB x, y; if ((tintColor & 0xFFFFFF) == 0xFFFFFF) return color; @@ -144,7 +135,7 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) #endif #if PIXEL_SIZE == 32 - Color32 dc, sc; + Pixel32ARGB dc, sc; dc.l = *dcolor; sc.l = scolor; From 05d57f1e64da86b77bf0592fbd27e9a1d86fd1de Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 19 May 2026 22:38:06 +0300 Subject: [PATCH 016/130] * Add initial support for sprite surfaces. --- src/sw/sw_renderer.c | 226 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 199 insertions(+), 27 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 46f02118e..024cf3775 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -18,7 +18,7 @@ #define TEXTURE_LRU_LENGTH 16 -#define SURFACE_MAX_COUNT 16 +#define SURFACE_MAX_COUNT 64 typedef struct { @@ -49,9 +49,10 @@ typedef struct uint32_t textureIndexLRUHead; uint32_t textureIndexLRUTail; size_t textureCount; - - SWTexture** surfaces; size_t surfaceCount; + size_t totalTextureCount; + size_t originalTPagCount; + size_t originalSpriteCount; bool viewActive; int viewX, viewY, viewW, viewH; @@ -215,7 +216,7 @@ FORCE_INLINE int swrCeiling(float x) return i + (x > (float) i); } -static SWTexture* createTexture(const uint8_t* srcBuffer, int width, int height) +static SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height) { SWTexture* txt = safeCalloc(1, sizeof(SWTexture)); txt->buffer = safeCalloc(width * height, sizeof(uintpixel_t)); @@ -223,8 +224,12 @@ static SWTexture* createTexture(const uint8_t* srcBuffer, int width, int height) const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; size_t sz = width * height; - for (size_t i = 0; i < sz; i++) - txt->buffer[i] = swrConvertPixelTexture(rgbaSrc[i]); + + if (srcBuffer) + { + for (size_t i = 0; i < sz; i++) + txt->buffer[i] = swrConvertPixelTexture(rgbaSrc[i]); + } txt->width = (uint16_t) width; txt->height = (uint16_t) height; @@ -232,6 +237,12 @@ static SWTexture* createTexture(const uint8_t* srcBuffer, int width, int height) return txt; } +static void swrFreeTexture(SWTexture* texture) +{ + free(texture->buffer); + free(texture); +} + static bool swrAddTextureIndexToLRU(SWRenderer* swr, int textureIndex) { uint32_t newIndex = (swr->textureIndexLRUHead + 1) % TEXTURE_LRU_LENGTH; @@ -263,8 +274,7 @@ static void swrEvictTextureFromCache(SWRenderer* swr, int textureIndex) SWTexture* texture = swr->textures[textureIndex]; swr->textures[textureIndex] = NULL; - free(texture->buffer); - free(texture); + swrFreeTexture(texture); } // Lazily decodes and uploads a TXTR page on first access. @@ -306,7 +316,7 @@ static bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) return false; } - swr->textures[pageId] = createTexture(pixels, w, h); + swr->textures[pageId] = swrCreateTexture(pixels, w, h); free(pixels); fprintf(stderr, "SWR: Loaded TXTR page %u (%dx%d)\n", pageId, w, h); @@ -341,19 +351,31 @@ static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) swr->fb = safeCalloc(swr->width * swr->height, sizeof(uintpixel_t)); swr->fbPitch = swr->width; - //allocate surface buffer - swr->surfaces = safeCalloc(SURFACE_MAX_COUNT, sizeof(SWTexture*)); - swr->surfaceCount = SURFACE_MAX_COUNT; - //allocate texture buffer swr->textureCount = dataWin->txtr.count; - swr->textures = safeCalloc(swr->textureCount, sizeof(SWTexture*)); + swr->surfaceCount = SURFACE_MAX_COUNT; + swr->totalTextureCount = swr->textureCount + swr->surfaceCount; + swr->textures = safeCalloc(swr->totalTextureCount, sizeof(SWTexture*)); //allocate texture LRU cache to allow for dynamic unloading of textures swr->textureIndexLRU = safeCalloc(TEXTURE_LRU_LENGTH, sizeof(uint32_t)); swr->textureIndexLRUHead = 0; swr->textureIndexLRUTail = 0; + //HACK: this isn't good, really. This should seriously be refactored. + //expand datawin's tpag items list to include our surface count. + swr->originalTPagCount = dataWin->tpag.count; + dataWin->tpag.items = safeRealloc(dataWin->tpag.items, sizeof(TexturePageItem) * (dataWin->tpag.count + swr->surfaceCount)); + dataWin->tpag.count += swr->surfaceCount; + + swr->originalSpriteCount = dataWin->sprt.count; + + for (size_t i = swr->originalTPagCount; i < dataWin->tpag.count; i++) + { + memset(&dataWin->tpag.items[i], 0, sizeof(TexturePageItem)); + dataWin->tpag.items[i].texturePageId = -1; + } + fprintf(stderr, "SWRenderer initialized.\n"); } @@ -456,6 +478,34 @@ static void SWRenderer_endGUI(Renderer* renderer) UNIMP2(); } +// TODO[MrPowerGamerBR]: This is supposed to be refactored, not to modify data.win structs directly. +static int32_t swrFindSurfaceTextureSlot(SWRenderer* swr) +{ + // NOTE: dynamic textures are not enrolled into the eviction cache for + // hopefully obvious reasons ... + for (size_t i = swr->textureCount; i != swr->totalTextureCount; i++) + { + if (swr->textures[i] == NULL) { + return (int32_t) i; + } + } + + return -1; +} + +static int32_t swrFindSurfaceTPagSlot(SWRenderer* swr) +{ + DataWin* dw = swr->base.dataWin; + for (size_t i = swr->originalTPagCount; i != dw->tpag.count; i++) + { + if (dw->tpag.items[i].texturePageId == -1) { + return (int32_t) i; + } + } + + return -1; +} + static void swrTransformPosIfNeeded(SWRenderer* swr, float* dx, float* dy) { if (!swr->viewActive) return; @@ -935,12 +985,21 @@ static void SWRenderer_drawSprite(Renderer* renderer, int32_t tpagIndex, float x SWRenderer* swr = (SWRenderer*) renderer; DataWin* dwin = renderer->dataWin; - if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) return; + if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) { + fprintf(stderr, "%s: tpagIndex of %d is invalid\n", __func__, tpagIndex); + return; + } TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; int16_t pageId = tpag->texturePageId; - if (0 > pageId || swr->textureCount <= (uint32_t) pageId) return; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; + if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) { + fprintf(stderr, "%s: tpagIndex of %d is invalid, as pageId of %d is invalid\n", __func__, tpagIndex, pageId); + return; + } + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) { + fprintf(stderr, "%s: could not ensure texture is loaded, tpagIndex: %d, pageId: %d\n", __func__, tpagIndex, pageId); + return; + } int sx = tpag->sourceX; int sy = tpag->sourceY; @@ -986,7 +1045,7 @@ static void SWRenderer_drawSpritePart(Renderer* renderer, int32_t tpagIndex, TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; int16_t pageId = tpag->texturePageId; - if (0 > pageId || swr->textureCount <= (uint32_t) pageId) return; + if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) return; if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; int sx = tpag->sourceX + srcOffX; @@ -1116,7 +1175,7 @@ static bool swrResolveFontState(SWRenderer* swr, DataWin* dw, Font* font, SwrFon state->fontTpag = &dw->tpag.items[state->fontTpagIndex]; int16_t pageId = state->fontTpag->texturePageId; - if (0 > pageId || (uint32_t) pageId >= swr->textureCount) return false; + if (0 > pageId || (uint32_t) pageId >= swr->totalTextureCount) return false; if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return false; state->fontPageId = pageId; @@ -1142,7 +1201,7 @@ static bool swrResolveGlyph( TexturePageItem* glyphTpag = &dw->tpag.items[tpagIdx]; int16_t pid = glyphTpag->texturePageId; - if (0 > pid || (uint32_t) pid >= swr->textureCount) return false; + if (0 > pid || (uint32_t) pid >= swr->totalTextureCount) return false; if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pid)) return false; *tpagIndex = tpagIdx; @@ -1329,7 +1388,7 @@ static void SWRenderer_drawTiled(Renderer* renderer, int32_t tpagIndex, TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; int16_t pageId = tpag->texturePageId; - if (0 > pageId || swr->textureCount <= (uint32_t) pageId) return; + if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) return; if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; float axScale = fabsf(xscale); @@ -1541,16 +1600,129 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su bool removeback, bool smooth, int32_t xorig, int32_t yorig) { - (void)renderer; (void)surfaceID; (void)x; (void)y; (void)w; (void)h; - (void)removeback; (void)smooth; (void)xorig; (void)yorig; - UNIMP(); - return 0; + SWRenderer* swr = (SWRenderer*) renderer; + + (void) removeback; + (void) smooth; + + if (surfaceID != -1) { + fprintf(stderr, "%s: Surfaces other than application_surface aren't supported yet!\n", __func__); + return 0; + } + + int32_t texturePageId = swrFindSurfaceTextureSlot(swr); + int32_t tpagIndex = swrFindSurfaceTPagSlot(swr); + if (texturePageId == -1 || tpagIndex == -1) { + fprintf(stderr, "%s: Surface overflow!!\n", __func__); + return 0; + } + + SWTexture* tex = swrCreateTexture(NULL, w, h); + + // grab the pixels. + for (int iy = 0; iy < h; iy++) + { + uintpixel_t* dstline = &tex->buffer[iy * tex->width]; + if ((iy + y) < 0 || (iy + y) >= swr->height) + { + for (int ix = 0; ix < w; ix++) + dstline[ix] = 0; + + continue; + } + + uintpixel_t* srcline = &swr->fb[(iy + y) * swr->width + x]; + + int ix = 0, sx = x; + // left edge + for (; sx < 0 && ix < w; sx++, ix++) + dstline[ix] = 0; + + // in-bounds + for (; sx < swr->width && ix < w; sx++, ix++) + dstline[ix] = srcline[ix] | TRANSPARENT_MASK; + + // right edge + for (; ix < w; ix++) + dstline[ix] = 0; + } + + int32_t spriteW = w; + int32_t spriteH = h; + + swr->textures[texturePageId] = tex; + + // TODO[MrPowerGamerBR]: This is supposed to be refactored, not to modify data.win structs directly. + DataWin* dw = swr->base.dataWin; + TexturePageItem* tpag = &dw->tpag.items[tpagIndex]; + tpag->sourceX = 0; + tpag->sourceY = 0; + tpag->sourceWidth = (uint16_t) spriteW; + tpag->sourceHeight = (uint16_t) spriteH; + tpag->targetX = 0; + tpag->targetY = 0; + tpag->targetWidth = (uint16_t) spriteW; + tpag->targetHeight = (uint16_t) spriteH; + tpag->boundingWidth = (uint16_t) spriteW; + tpag->boundingHeight = (uint16_t) spriteH; + tpag->texturePageId = texturePageId; + + uint32_t spriteIndex = DataWin_allocSpriteSlot(dw, swr->originalSpriteCount); + Sprite* sprite = &dw->sprt.sprites[spriteIndex]; + // name was set by DataWin_allocSpriteSlot ("__newsprite"); don't overwrite it here + sprite->width = (uint32_t) w; + sprite->height = (uint32_t) h; + sprite->originX = xorig; + sprite->originY = yorig; + sprite->textureCount = 1; + sprite->tpagIndices = safeMalloc(sizeof(int32_t)); + sprite->tpagIndices[0] = (int32_t) tpagIndex; + sprite->maskCount = 0; + sprite->masks = nullptr; + + fprintf(stderr, "%s: Allocated surface sprite with ID %d\n", __func__, spriteIndex); + return spriteIndex; } static void SWRenderer_deleteSprite(Renderer* renderer, int32_t spriteIndex) { - UNIMP(); - (void)renderer; (void)spriteIndex; + SWRenderer* swr = (SWRenderer*) renderer; + + DataWin* dw = renderer->dataWin; + if (0 > spriteIndex || dw->sprt.count <= (uint32_t) spriteIndex) return; + + // Refuse to delete original data.win sprites + if (swr->originalSpriteCount > (uint32_t) spriteIndex) { + fprintf(stderr, "%s: Sprite index %d is invalid.\n", __func__, spriteIndex); + return; + } + + Sprite* sprite = &dw->sprt.sprites[spriteIndex]; + if (sprite->textureCount == 0) return; // already deleted + + for (uint32_t i = 0; i < sprite->textureCount; i++) + { + int32_t tpagIdx = sprite->tpagIndices[i]; + if (tpagIdx >= 0 && (uint32_t) tpagIdx >= swr->originalTPagCount) { + TexturePageItem* tpag = &dw->tpag.items[tpagIdx]; + int16_t pageId = tpag->texturePageId; + if (pageId >= 0 && swr->totalTextureCount > (uint32_t) pageId) { + swrFreeTexture(swr->textures[pageId]); + swr->textures[pageId] = NULL; + } + // Mark TPAG slot as free for reuse + tpag->texturePageId = -1; + } + } + + free(sprite->tpagIndices); + sprite->tpagIndices = NULL; + + const char* keepName = sprite->name; + memset(sprite, 0, sizeof(Sprite)); + sprite->name = keepName; + + fprintf(stderr, "SWR: Deleted sprite %d\n", spriteIndex); } static void SWRenderer_drawTiledPart(Renderer* renderer, int32_t tpagIndex, From a70e78605b6c6d9f5d29558c9a7e3774bd3f92be Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 19 May 2026 23:08:25 +0300 Subject: [PATCH 017/130] * Fix yet another rotation pivot issue. This fixes Muffet's teapot in her right hand rotating around the wrong point. --- src/sw/sw_renderer.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 024cf3775..f4751e484 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -204,6 +204,12 @@ FORCE_INLINE bool swrMustRotateTolerant(float angleDeg) return swrAbs(angleDegInt) >= 1; // 1/16 of a degree } +FORCE_INLINE int swrSgn(float x) +{ + if (x < 0) return -1; + return 1; +} + FORCE_INLINE int swrFloor(float x) { int i = (int) x; @@ -1019,8 +1025,8 @@ static void SWRenderer_drawSprite(Renderer* renderer, int32_t tpagIndex, float x if (UNLIKELY(swrMustRotate(angleDeg))) { - float pivotX = x - dx; - float pivotY = y - dy; + float pivotX = (x - dx) * swrSgn(xscale); + float pivotY = (y - dy) * swrSgn(yscale); swrDrawSpriteRotated(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, pivotX, pivotY); } else From 83c3c178068e07d2a92321bf88ff7b9deec64620 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 19 May 2026 23:54:23 +0300 Subject: [PATCH 018/130] * Fix a bug where createSpriteFromSurface completely ignored screen scale. --- src/sw/sw_renderer.c | 48 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index f4751e484..7a02a571c 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -538,6 +538,38 @@ static void swrTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) if (dy) *dy *= ((float)swr->portH / swr->viewH); } +static void swrTransformPosIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) +{ + if (!swr->viewActive) return; + + if (dx) { + *dx -= swr->viewX; + *dx = *dx * swr->portW / swr->viewW; + *dx += swr->portX; + } + if (dy) { + *dy -= swr->viewY; + *dy = *dy * swr->portH / swr->viewH; + *dy += swr->portY; + } +} + +static void swrTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) +{ + if (!swr->viewActive || !swr->viewW || !swr->viewH) return; + + if (dx) *dx = *dx * swr->portW / swr->viewW; + if (dy) *dy = *dy * swr->portH / swr->viewH; +} + +static void swrReverseTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) +{ + if (!swr->viewActive) return; + + if (dx) *dx = *dx * swr->viewW / swr->portW; + if (dy) *dy = *dy * swr->viewH / swr->portH; +} + FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int alpha) { SWRenderer* swr = (SWRenderer*) renderer; @@ -1014,8 +1046,8 @@ static void SWRenderer_drawSprite(Renderer* renderer, int32_t tpagIndex, float x float dx = (float)(tpag->targetX - originX); float dy = (float)(tpag->targetY - originY); - int dw = (int)(xscale * sw); - int dh = (int)(yscale * sh); + int dw = (int)(xscale * tpag->targetWidth); + int dh = (int)(yscale * tpag->targetHeight); dx *= xscale; dy *= yscale; dx += x; @@ -1608,6 +1640,10 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su { SWRenderer* swr = (SWRenderer*) renderer; + swrTransformPosIntIfNeeded(swr, &x, &y); + swrTransformSizeIntIfNeeded(swr, &w, &h); + swrTransformSizeIntIfNeeded(swr, &xorig, &yorig); + (void) removeback; (void) smooth; @@ -1655,6 +1691,10 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su int32_t spriteW = w; int32_t spriteH = h; + + int32_t targetW = spriteW; + int32_t targetH = spriteH; + swrReverseTransformSizeIntIfNeeded(swr, &targetW, &targetH); swr->textures[texturePageId] = tex; @@ -1667,8 +1707,8 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su tpag->sourceHeight = (uint16_t) spriteH; tpag->targetX = 0; tpag->targetY = 0; - tpag->targetWidth = (uint16_t) spriteW; - tpag->targetHeight = (uint16_t) spriteH; + tpag->targetWidth = (uint16_t) (spriteW * swr->viewW / swr->portW); + tpag->targetHeight = (uint16_t) (spriteH * swr->viewH / swr->portH); tpag->boundingWidth = (uint16_t) spriteW; tpag->boundingHeight = (uint16_t) spriteH; tpag->texturePageId = texturePageId; From c2aac3224a3777b372e46abd7b2127394547ff89 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 20 May 2026 00:00:28 +0300 Subject: [PATCH 019/130] * Fix a bug where pivotX/Y didn't take into account the diff between sourceWidth/Height and targetWidth/Height --- src/sw/sw_renderer.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 7a02a571c..41b41e578 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1059,6 +1059,12 @@ static void SWRenderer_drawSprite(Renderer* renderer, int32_t tpagIndex, float x { float pivotX = (x - dx) * swrSgn(xscale); float pivotY = (y - dy) * swrSgn(yscale); + + if (tpag->targetWidth != tpag->sourceWidth) + pivotX *= (float)tpag->targetWidth / tpag->sourceWidth; + if (tpag->targetHeight != tpag->sourceHeight) + pivotY *= (float)tpag->targetHeight/ tpag->sourceHeight; + swrDrawSpriteRotated(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, pivotX, pivotY); } else From 5e43ec904f880251e736a609f772daecab56e1be Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 20 May 2026 00:00:56 +0300 Subject: [PATCH 020/130] * SDL Frontend: Use fbWidth/fbHeight instead of reqW/reqH for the SDL video mode. --- src/sdl/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/main.c b/src/sdl/main.c index 4fac77cb0..5461aeb0b 100644 --- a/src/sdl/main.c +++ b/src/sdl/main.c @@ -844,7 +844,7 @@ int main(int argc, char* argv[]) { fbWidth = reqW; fbHeight = reqH; if(!args.headless) { - scr = SDL_SetVideoMode(reqW, reqH, 0, useSWRend ? 0 : (SDL_OPENGL | SDL_RESIZABLE)); + scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, useSWRend ? 0 : (SDL_OPENGL | SDL_RESIZABLE)); if (!scr && useSWRend) { SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN); if (modes && modes != (SDL_Rect**) -1 && modes[0]) { From 66d1235cbc9316c6473a73544dddd4174948c752 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 20 May 2026 00:04:01 +0300 Subject: [PATCH 021/130] Revert "* SDL Frontend: Use fbWidth/fbHeight instead of reqW/reqH for the SDL video mode." This reverts commit 5e43ec904f880251e736a609f772daecab56e1be. --- src/sdl/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdl/main.c b/src/sdl/main.c index 5461aeb0b..4fac77cb0 100644 --- a/src/sdl/main.c +++ b/src/sdl/main.c @@ -844,7 +844,7 @@ int main(int argc, char* argv[]) { fbWidth = reqW; fbHeight = reqH; if(!args.headless) { - scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, useSWRend ? 0 : (SDL_OPENGL | SDL_RESIZABLE)); + scr = SDL_SetVideoMode(reqW, reqH, 0, useSWRend ? 0 : (SDL_OPENGL | SDL_RESIZABLE)); if (!scr && useSWRend) { SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN); if (modes && modes != (SDL_Rect**) -1 && modes[0]) { From cb5c0be5d6cdca742549add30bec1d6d3b3cdd0e Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 20 May 2026 22:43:29 +0300 Subject: [PATCH 022/130] Add enableApplicationSurface. --- src/sw/sw_renderer.c | 110 +++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 45 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 41b41e578..0584109a7 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1584,6 +1584,11 @@ static bool SWRenderer_setRenderTarget(Renderer* renderer, int32_t surfaceID) static float SWRenderer_getSurfaceWidth(Renderer* renderer, int32_t surfaceID) { + SWRenderer* swr = (SWRenderer*) renderer; + if (surfaceID == APPLICATION_SURFACE_ID) { + return (float) swr->width; + } + UNIMP(); (void)renderer; (void)surfaceID; return 0.0f; @@ -1591,6 +1596,11 @@ static float SWRenderer_getSurfaceWidth(Renderer* renderer, int32_t surfaceID) static float SWRenderer_getSurfaceHeight(Renderer* renderer, int32_t surfaceID) { + SWRenderer* swr = (SWRenderer*) renderer; + if (surfaceID == APPLICATION_SURFACE_ID) { + return (float) swr->height; + } + UNIMP(); (void)renderer; (void)surfaceID; return 0.0f; @@ -1789,53 +1799,63 @@ static void SWRenderer_drawTiledPart(Renderer* renderer, int32_t tpagIndex, (void)color; (void)alpha; } +static int32_t SWRenderer_ensureApplicationSurface(Renderer* renderer, int32_t width, int32_t height) +{ + UNIMP(); + (void) renderer; + (void) width; + (void) height; + return -1; +} + static RendererVtable swrVtable = { - .init = SWRenderer_init, - .destroy = SWRenderer_destroy, - .beginFrame = SWRenderer_beginFrame, - .endFrameInit = SWRenderer_endFrameInit, - .endFrameEnd = SWRenderer_endFrameEnd, - .beginView = SWRenderer_beginView, - .endView = SWRenderer_endView, - .beginGUI = SWRenderer_beginGUI, - .endGUI = SWRenderer_endGUI, - .drawSprite = SWRenderer_drawSprite, - .drawSpritePart = SWRenderer_drawSpritePart, - .drawSpritePos = SWRenderer_drawSpritePos, - .drawRectangle = SWRenderer_drawRectangle, - .drawRectangleColor = SWRenderer_drawRectangleColor, - .drawLine = SWRenderer_drawLine, - .drawTriangle = SWRenderer_drawTriangle, - .drawLineColor = SWRenderer_drawLineColor, - .drawText = SWRenderer_drawText, - .drawTextColor = SWRenderer_drawTextColor, - .flush = SWRenderer_flush, - .clearScreen = SWRenderer_clearScreen, - .createSpriteFromSurface = SWRenderer_createSpriteFromSurface, - .deleteSprite = SWRenderer_deleteSprite, - .gpuSetBlendMode = SWRenderer_gpuSetBlendMode, - .gpuSetBlendModeExt = SWRenderer_gpuSetBlendModeExt, - .gpuSetBlendEnable = SWRenderer_gpuSetBlendEnable, - .gpuSetAlphaTestEnable = SWRenderer_gpuSetAlphaTestEnable, - .gpuSetAlphaTestRef = SWRenderer_gpuSetAlphaTestRef, - .gpuSetColorWriteEnable = SWRenderer_gpuSetColorWriteEnable, - .gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable, - .gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable, - .gpuSetFog = SWRenderer_gpuSetFog, - .drawTile = NULL, - .drawTiled = SWRenderer_drawTiled, - .createSurface = SWRenderer_createSurface, - .surfaceExists = SWRenderer_surfaceExists, - .setRenderTarget = SWRenderer_setRenderTarget, - .getSurfaceWidth = SWRenderer_getSurfaceWidth, - .getSurfaceHeight = SWRenderer_getSurfaceHeight, - .drawSurface = SWRenderer_drawSurface, - .surfaceResize = SWRenderer_surfaceResize, - .surfaceFree = SWRenderer_surfaceFree, - .surfaceCopy = SWRenderer_surfaceCopy, - .surfaceGetPixels = SWRenderer_surfaceGetPixels, - .drawTiledPart = SWRenderer_drawTiledPart, + .init = SWRenderer_init, + .destroy = SWRenderer_destroy, + .beginFrame = SWRenderer_beginFrame, + .endFrameInit = SWRenderer_endFrameInit, + .endFrameEnd = SWRenderer_endFrameEnd, + .beginView = SWRenderer_beginView, + .endView = SWRenderer_endView, + .beginGUI = SWRenderer_beginGUI, + .endGUI = SWRenderer_endGUI, + .drawSprite = SWRenderer_drawSprite, + .drawSpritePart = SWRenderer_drawSpritePart, + .drawSpritePos = SWRenderer_drawSpritePos, + .drawRectangle = SWRenderer_drawRectangle, + .drawRectangleColor = SWRenderer_drawRectangleColor, + .drawLine = SWRenderer_drawLine, + .drawTriangle = SWRenderer_drawTriangle, + .drawLineColor = SWRenderer_drawLineColor, + .drawText = SWRenderer_drawText, + .drawTextColor = SWRenderer_drawTextColor, + .flush = SWRenderer_flush, + .clearScreen = SWRenderer_clearScreen, + .createSpriteFromSurface = SWRenderer_createSpriteFromSurface, + .deleteSprite = SWRenderer_deleteSprite, + .gpuSetBlendMode = SWRenderer_gpuSetBlendMode, + .gpuSetBlendModeExt = SWRenderer_gpuSetBlendModeExt, + .gpuSetBlendEnable = SWRenderer_gpuSetBlendEnable, + .gpuSetAlphaTestEnable = SWRenderer_gpuSetAlphaTestEnable, + .gpuSetAlphaTestRef = SWRenderer_gpuSetAlphaTestRef, + .gpuSetColorWriteEnable = SWRenderer_gpuSetColorWriteEnable, + .gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable, + .gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable, + .gpuSetFog = SWRenderer_gpuSetFog, + .drawTile = NULL, + .drawTiled = SWRenderer_drawTiled, + .createSurface = SWRenderer_createSurface, + .surfaceExists = SWRenderer_surfaceExists, + .setRenderTarget = SWRenderer_setRenderTarget, + .ensureApplicationSurface = SWRenderer_ensureApplicationSurface, + .getSurfaceWidth = SWRenderer_getSurfaceWidth, + .getSurfaceHeight = SWRenderer_getSurfaceHeight, + .drawSurface = SWRenderer_drawSurface, + .surfaceResize = SWRenderer_surfaceResize, + .surfaceFree = SWRenderer_surfaceFree, + .surfaceCopy = SWRenderer_surfaceCopy, + .surfaceGetPixels = SWRenderer_surfaceGetPixels, + .drawTiledPart = SWRenderer_drawTiledPart, }; void SWRenderer_clearFrameBuffer(Renderer* renderer, uint32_t color) From c951bd660c479a471cbbbc0e043cf91709a1b6f1 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sat, 23 May 2026 20:04:03 +0300 Subject: [PATCH 023/130] * Update drawText / drawTextColor definitions --- src/sw/sw_renderer.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 0584109a7..14dbb3ae1 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1276,7 +1276,7 @@ static bool swrResolveGlyph( return true; } -static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, UNUSED float angleDeg, int32_t color, UNUSED float alpha) +static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation) { Renderer* renderer = &swr->base; DataWin* dwin = renderer->dataWin; @@ -1301,7 +1301,7 @@ static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, flo int textLen = (int) strlen(text); int lineCount = TextUtils_countLines(text, textLen); - float lineStride = TextUtils_lineStride(font); + float lineStride = (0.0f > lineSeparation) ? TextUtils_lineStride(font) : (lineSeparation / (font->scaleY != 0.0f ? font->scaleY : 1.0f)); // Vertical alignment offset float totalHeight = (float) lineCount * lineStride; @@ -1406,18 +1406,19 @@ static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, flo } static void SWRenderer_drawText(Renderer* renderer, const char* text, float x, float y, - float xscale, float yscale, float angleDeg) + float xscale, float yscale, float angleDeg, float lineSeparation) { SWRenderer* swr = (SWRenderer*) renderer; - swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, renderer->drawColor, renderer->drawAlpha); + swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, renderer->drawColor, renderer->drawAlpha, lineSeparation); } static void SWRenderer_drawTextColor(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, - int32_t c1, int32_t c2, int32_t c3, int32_t c4, float alpha) + int32_t c1, int32_t c2, int32_t c3, int32_t c4, float alpha, + float lineSeparation) { SWRenderer* swr = (SWRenderer*) renderer; - swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, c1, renderer->drawAlpha); + swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, c1, renderer->drawAlpha, lineSeparation); } static void SWRenderer_drawTiled(Renderer* renderer, int32_t tpagIndex, From e8eef6da1de301d048110b4eab6c739973d497ba Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sat, 23 May 2026 22:54:23 +0300 Subject: [PATCH 024/130] * Fix a bug with rotations Instead of scaling the position, scale the size. This fixes the smoke in Hotland's booster pad sections being all weird. --- src/sw/sw_renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 14dbb3ae1..81b11fb70 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -996,7 +996,7 @@ static void swrDrawSpriteRotated( SWRenderer* swr = (SWRenderer*) renderer; swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformPosIfNeeded(swr, &pivotX, &pivotY); + swrTransformSizeIfNeeded(swr, &pivotX, &pivotY); swrTransformSizeIfNeeded(swr, &dw, &dh); swrDrawSpriteRotatedInternal( From 1024353b6c7c7010945528f498de8479e9aac8eb Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sat, 23 May 2026 23:04:23 +0300 Subject: [PATCH 025/130] * Add support for drawLineColor. Right now, it's incredibly bare-bones. --- src/sw/sw_renderer.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 81b11fb70..70404b264 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1178,6 +1178,8 @@ static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, floa (void)outline; uintpixel_t drawColorCvt = swrConvertPixel(renderer->drawColor); + + // TODO: draw triangle properly. swrDrawLine(renderer, x1, y1, x2, y2, 1, drawColorCvt, renderer->drawAlpha); swrDrawLine(renderer, x1, y1, x3, y3, 1, drawColorCvt, renderer->drawAlpha); swrDrawLine(renderer, x2, y2, x3, y3, 1, drawColorCvt, renderer->drawAlpha); @@ -1186,9 +1188,9 @@ static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, floa static void SWRenderer_drawLineColor(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uint32_t color1, uint32_t color2, float alpha) { - (void)renderer; (void)x1; (void)y1; (void)x2; (void)y2; - (void)width; (void)color1; (void)color2; (void)alpha; - UNIMP(); + // TODO: implement gradient properly + uintpixel_t actualColor = tint(swrConvertPixel(color1), swrConvertPixel(color2)); + swrDrawLine(renderer, x1, y1, x2, y2, width, actualColor, alpha); } typedef struct From a81e9c04ab43de94f8312c88c54457f854ae2bf6 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Sat, 23 May 2026 23:24:12 +0300 Subject: [PATCH 026/130] * Add support for gradient lines. Also, partial support for drawRectangleColor --- src/sw/sw_renderer.c | 229 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 197 insertions(+), 32 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 70404b264..9b3c0db14 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -580,7 +580,7 @@ FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t col alphaBlend(&swr->fb[y * swr->fbPitch + x], color, alpha); } -static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixel_t color, int alpha) +static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) { SWRenderer *swr = (SWRenderer*) renderer; @@ -590,12 +590,48 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe if (dx + dw >= swr->width) dw = swr->width - dx; if (dw <= 0) return; - uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; - for (int i = 0; i < dw; i++) - alphaBlend(&line[i], color, alpha); +#if PIXEL_SIZE == 32 + if (color == color2) +#endif + { + uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; + for (int i = 0; i < dw; i++) + alphaBlend(&line[i], color, alpha); + } +#if PIXEL_SIZE == 32 + else + { + Pixel32ARGB clr1, clr2; + clr1.l = color; + clr2.l = color2; + + uint32_t rinit = clr1.p.r << 20; + uint32_t ginit = clr1.p.g << 20; + uint32_t binit = clr1.p.b << 20; + int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; + int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; + int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; + rstep /= dw; + gstep /= dw; + bstep /= dw; + + uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; + for (int i = 0; i < dw; i++) + { + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + alphaBlend(&line[i], resultPixel.l, alpha); + } + } +#endif } -static void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, float alpha) +static void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha) { SWRenderer *swr = (SWRenderer*) renderer; float thickness = 1; @@ -604,10 +640,10 @@ static void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintp swrTransformSizeIfNeeded(swr, &dw, &thickness); // TODO: use thickness - swrDrawHLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dw), color, swrIntAlpha(alpha)); + swrDrawHLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dw), color, color2, swrIntAlpha(alpha)); } -static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixel_t color, int alpha) +static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) { SWRenderer *swr = (SWRenderer*) renderer; @@ -617,14 +653,50 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe if (dy + dh >= swr->height) dh = swr->height - dy; if (dh <= 0) return; - for (int i = 0; i < dh; i++) +#if PIXEL_SIZE == 32 + if (color == color2) +#endif + { + for (int i = 0; i < dh; i++) + { + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + alphaBlend(&line[0], color, alpha); + } + } +#if PIXEL_SIZE == 32 + else { - uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - alphaBlend(&line[0], color, alpha); + Pixel32ARGB clr1, clr2; + clr1.l = color; + clr2.l = color2; + + uint32_t rinit = clr1.p.r << 20; + uint32_t ginit = clr1.p.g << 20; + uint32_t binit = clr1.p.b << 20; + int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; + int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; + int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; + rstep /= dh; + gstep /= dh; + bstep /= dh; + + for (int i = 0; i < dh; i++) + { + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + alphaBlend(&line[0], resultPixel.l, alpha); + } } +#endif } -static void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, float alpha) +static void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, uintpixel_t color2, float alpha) { SWRenderer *swr = (SWRenderer*) renderer; float thickness = 1; @@ -633,27 +705,35 @@ static void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintp swrTransformSizeIfNeeded(swr, &thickness, &dh); // TODO: use thickness - swrDrawVLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dh), color, swrIntAlpha(alpha)); + swrDrawVLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dh), color, color2, swrIntAlpha(alpha)); } static void swrDrawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha) { - swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color, alpha); - swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color, alpha); - swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color, alpha); - swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color, alpha); + swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color, color, alpha); + swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color, color, alpha); + swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color, color, alpha); + swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color, color, alpha); +} + +static void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uintpixel_t color4, float alpha) +{ + swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color1, color2, alpha); + swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color3, color4, alpha); + swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color1, color3, alpha); + swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color2, color4, alpha); } -static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, int width, uintpixel_t color, int alpha) +static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, int width, uintpixel_t color1, uintpixel_t color2, int alpha) { if (x1 == x2) { - swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color, alpha); + swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); return; } if (y1 == y2) { - swrDrawHLineInt(renderer, swrMin(x1, x2), y1, swrAbs(x1 - x2), color, alpha); + swrDrawHLineInt(renderer, swrMin(x1, x2), y1, swrAbs(x1 - x2), color1, color2, alpha); return; } @@ -661,6 +741,20 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; + uintpixel_t color = color1; +#if PIXEL_SIZE == 32 + Pixel32ARGB clr1, clr2; + clr1.l = color1; + clr2.l = color2; + + uint32_t rinit = clr1.p.r << 20; + uint32_t ginit = clr1.p.g << 20; + uint32_t binit = clr1.p.b << 20; + int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; + int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; + int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; +#endif + if (dy1 <= dx1) { if (dx >= 0) @@ -672,6 +766,16 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i x = x2, y = y2, xe = x1; } +#if PIXEL_SIZE == 32 + if (dx1 > 0) { + rstep /= dx1; + gstep /= dx1; + bstep /= dx1; + } else { + rstep = gstep = bstep = 0; + } +#endif + swrPlotPixel(renderer, x, y, color, alpha); for (int i = 0; x < xe; i++) @@ -687,6 +791,17 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i px += 2 * (dy1 - dx1); } +#if PIXEL_SIZE == 32 + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + color = resultPixel.l; +#endif + swrPlotPixel(renderer, x, y, color, alpha); } } @@ -701,6 +816,16 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i x = x2, y = y2, ye = y1; } +#if PIXEL_SIZE == 32 + if (dy1 > 0) { + rstep /= dy1; + gstep /= dy1; + bstep /= dy1; + } else { + rstep = gstep = bstep = 0; + } +#endif + swrPlotPixel(renderer, x, y, color, alpha); for (int i = 0; y < ye; i++) @@ -716,18 +841,29 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i py += 2 * (dx1 - dy1); } +#if PIXEL_SIZE == 32 + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + color = resultPixel.l; +#endif + swrPlotPixel(renderer, x, y, color, alpha); } } } -static void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, float alpha) +static void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha) { SWRenderer* swr = (SWRenderer*) renderer; swrTransformPosIfNeeded(swr, &x1, &y1); swrTransformPosIfNeeded(swr, &x2, &y2); swrTransformSizeIfNeeded(swr, &width, NULL); - swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrCeiling(x2), swrCeiling(y2), swrCeiling(width), color, swrIntAlpha(alpha)); + swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrCeiling(x2), swrCeiling(y2), swrCeiling(width), color, color2, swrIntAlpha(alpha)); } static void swrDrawSpriteInternal( @@ -1149,7 +1285,7 @@ static void SWRenderer_drawRectangle(Renderer* renderer, float x1, float y1, flo if (xd <= 0 || yd <= 0) return; for (int y = 0; y <= yd; y++) { - swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor, alphaInt); + swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor, pxcolor, alphaInt); } } } @@ -1158,9 +1294,33 @@ static void SWRenderer_drawRectangleColor(Renderer* renderer, float x1, float y1 uint32_t color1, uint32_t color2, uint32_t color3, uint32_t color4, float alpha, bool outline) { - (void)renderer; (void)x1; (void)y1; (void)x2; (void)y2; - (void)color1; (void)color2; (void)color3; (void)color4; (void)alpha; (void)outline; - UNIMP(); + uintpixel_t pxcolor1 = swrConvertPixel(color1); + uintpixel_t pxcolor2 = swrConvertPixel(color2); + uintpixel_t pxcolor3 = swrConvertPixel(color3); + uintpixel_t pxcolor4 = swrConvertPixel(color4); + + SWRenderer* swr = (SWRenderer*) renderer; + + if (outline) + { + swrDrawRectangleColor(renderer, x1, y1, x2, y2, pxcolor1, pxcolor2, pxcolor3, pxcolor4, alpha); + } + else + { + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + + int alphaInt = swrIntAlpha(alpha); + int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); + int xd = x2i - x1i; + int yd = y2i - y1i; + if (xd <= 0 || yd <= 0) return; + + // TODO: blending vertically + for (int y = 0; y <= yd; y++) { + swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor1, pxcolor2, alphaInt); + } + } } static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2, float y2, @@ -1169,7 +1329,8 @@ static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2 (void)renderer; (void)x1; (void)y1; (void)x2; (void)y2; (void)width; (void)color; (void)alpha; - swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color), alpha); + uintpixel_t colorCvt = swrConvertPixel(color); + swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha); } static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, @@ -1180,17 +1341,15 @@ static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, floa uintpixel_t drawColorCvt = swrConvertPixel(renderer->drawColor); // TODO: draw triangle properly. - swrDrawLine(renderer, x1, y1, x2, y2, 1, drawColorCvt, renderer->drawAlpha); - swrDrawLine(renderer, x1, y1, x3, y3, 1, drawColorCvt, renderer->drawAlpha); - swrDrawLine(renderer, x2, y2, x3, y3, 1, drawColorCvt, renderer->drawAlpha); + swrDrawLine(renderer, x1, y1, x2, y2, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); + swrDrawLine(renderer, x1, y1, x3, y3, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); + swrDrawLine(renderer, x2, y2, x3, y3, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); } static void SWRenderer_drawLineColor(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uint32_t color1, uint32_t color2, float alpha) { - // TODO: implement gradient properly - uintpixel_t actualColor = tint(swrConvertPixel(color1), swrConvertPixel(color2)); - swrDrawLine(renderer, x1, y1, x2, y2, width, actualColor, alpha); + swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color1), swrConvertPixel(color2), alpha); } typedef struct @@ -1420,6 +1579,12 @@ static void SWRenderer_drawTextColor(Renderer* renderer, const char* text, float float lineSeparation) { SWRenderer* swr = (SWRenderer*) renderer; + + // TODO: allow c2, c3, c4 + (void) c2; + (void) c3; + (void) c4; + swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, c1, renderer->drawAlpha, lineSeparation); } From cea76f115eec8d80c1318bc7586e062276897eac Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 25 May 2026 17:26:19 +0300 Subject: [PATCH 027/130] * Add initial implementation of the triangle render function. This function renders a triangle. Obviously. --- src/sw/sw_renderer.c | 135 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 8 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 9b3c0db14..73ee33b04 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -412,10 +412,23 @@ static void SWRenderer_endFrameInit(Renderer* renderer) //this is kinda useless to do twice isn't it? } +static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, + float x3, float y3, bool outline); + static void SWRenderer_endFrameEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; assert(!swr->drawingToSurface); + + static int i = 0; + i += 4; + i = i % 480; + + renderer->drawColor = 0xFFFFFF; + SWRenderer_drawTriangle(renderer, 320, 480 - i, 620, 240, 20, i, false); + //renderer->drawColor = 0xFFFF00; + //SWRenderer_drawTriangle(renderer, 320, 40, 20, 440, 620, 360, true); + Runner_setNextFrame(swr->fb, swr->width, swr->height); } @@ -1333,17 +1346,123 @@ static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2 swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha); } -static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, - float x3, float y3, bool outline) +static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uint32_t color, int alpha) { - (void)outline; + // Figure out the maximum Y extent of the triangle. + // (Note that we know yup is the minimum.) + int xmid, ymid, xmid2, xmax, ymax; + if (yleft < yright) { + xmax = xright, ymax = yright; + xmid = xleft, ymid = yleft; + if (yright == yup) + xmid2 = xup; + else + xmid2 = xup + (xright - xup) * (ymid - yup) / (yright - yup); + } else { + xmax = xleft, ymax = yleft; + xmid = xright, ymid = yright; + if (yleft == yup) + xmid2 = xup; + else + xmid2 = xup + (xleft - xup) * (ymid - yup) / (yleft - yup); + } - uintpixel_t drawColorCvt = swrConvertPixel(renderer->drawColor); + for (int y = yup; y <= ymax; y++) + { + if (y < 0) continue; + if (y >= swr->height) break; + + int x1 = xup, x2 = xup; + if (y < ymid) + { + // Lines: between up and mid, and between up and max + if (ymid != yup) + x1 = xup + (xmid - xup + 1) * (y - yup) / (ymid - yup); + + if (ymid != yup) + x2 = xup + (xmid2 - xup + 1) * (y - yup) / (ymid - yup); + } + else + { + // Lines: between mid and max, and between up and max + if (ymax != yup) + x1 = xup + (xmax - xup + 1) * (y - yup) / (ymax - yup); + + if (ymax != ymid) + x2 = xmid + (xmax - xmid + 1) * (y - ymid) / (ymax - ymid); + } + + if (x1 >= x2) { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + + if (x1 < 0) x1 = 0; + if (x1 >= swr->width) return; + if (x2 < 0) return; + if (x2 >= swr->width) x2 = swr->width - 1; + if (x1 > x2) return; + + uintpixel_t* line = &swr->fb[y * swr->width]; + for (int x = x1; x <= x2; x++) { + alphaBlend(&line[x], color, alpha); + } + } +} + +static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color, float alpha) +{ + float xup, yup, xleft, yleft, xright, yright; + + //which vertex is higher? + xup = x1, yup = y1; + xleft = x2, yleft = y2; + xright = x3, yright = y3; + if (yup > y2) { + xup = x2, yup = y2; + xleft = x1, yleft = y1; + //xright = x3, yright = y3; + } + if (yup > y3) { + xup = x3, yup = y3; + xleft = x1, yleft = y1; + xright = x2, yright = y2; + } + + if (xleft > xright) { + float tmp = xleft; + xleft = xright; + xright = tmp; + tmp = yleft; + yleft = yright; + yright = tmp; + } - // TODO: draw triangle properly. - swrDrawLine(renderer, x1, y1, x2, y2, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); - swrDrawLine(renderer, x1, y1, x3, y3, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); - swrDrawLine(renderer, x2, y2, x3, y3, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); + swrDrawTriangleInternal( + (SWRenderer*) renderer, + swrFloor(xup), swrFloor(yup), + swrFloor(xleft), swrCeiling(yleft), + swrFloor(xright), swrCeiling(yright), + swrConvertPixel(color), + swrIntAlpha(alpha) + ); +} + +static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, + float x3, float y3, bool outline) +{ + if (outline) + { + uintpixel_t drawColorCvt = swrConvertPixel(renderer->drawColor); + swrDrawLine(renderer, x1, y1, x2, y2, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); + swrDrawLine(renderer, x1, y1, x3, y3, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); + swrDrawLine(renderer, x2, y2, x3, y3, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); + } + else + { + swrDrawTriangle(renderer, x1, y1, x2, y2, x3, y3, renderer->drawColor, renderer->drawAlpha); + } } static void SWRenderer_drawLineColor(Renderer* renderer, float x1, float y1, float x2, float y2, From 3f3b3d090b3cc8677f7c501db9ae74a9b9c0a809 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 25 May 2026 17:27:16 +0300 Subject: [PATCH 028/130] * Actually apply transforms to this triangle + remove the test code. --- src/sw/sw_renderer.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 73ee33b04..5d86cebec 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -412,23 +412,10 @@ static void SWRenderer_endFrameInit(Renderer* renderer) //this is kinda useless to do twice isn't it? } -static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, - float x3, float y3, bool outline); - static void SWRenderer_endFrameEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; assert(!swr->drawingToSurface); - - static int i = 0; - i += 4; - i = i % 480; - - renderer->drawColor = 0xFFFFFF; - SWRenderer_drawTriangle(renderer, 320, 480 - i, 620, 240, 20, i, false); - //renderer->drawColor = 0xFFFF00; - //SWRenderer_drawTriangle(renderer, 320, 40, 20, 440, 620, 360, true); - Runner_setNextFrame(swr->fb, swr->width, swr->height); } @@ -1415,6 +1402,11 @@ static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, fl { float xup, yup, xleft, yleft, xright, yright; + SWRenderer* swr = (SWRenderer*) renderer; + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + swrTransformPosIfNeeded(swr, &x3, &y3); + //which vertex is higher? xup = x1, yup = y1; xleft = x2, yleft = y2; @@ -1440,7 +1432,7 @@ static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, fl } swrDrawTriangleInternal( - (SWRenderer*) renderer, + swr, swrFloor(xup), swrFloor(yup), swrFloor(xleft), swrCeiling(yleft), swrFloor(xright), swrCeiling(yright), From 140304e44ac99faec2f3e25e8c3615ff42810030 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 25 May 2026 18:00:25 +0300 Subject: [PATCH 029/130] * Bugfixes with the triangle renderer --- src/sw/sw_renderer.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 5d86cebec..37e52d127 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1337,20 +1337,16 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft { // Figure out the maximum Y extent of the triangle. // (Note that we know yup is the minimum.) - int xmid, ymid, xmid2, xmax, ymax; + int xmid, ymid, xmid2 = xup, xmax, ymax; if (yleft < yright) { xmax = xright, ymax = yright; xmid = xleft, ymid = yleft; - if (yright == yup) - xmid2 = xup; - else + if (yright != yup) xmid2 = xup + (xright - xup) * (ymid - yup) / (yright - yup); } else { xmax = xleft, ymax = yleft; xmid = xright, ymid = yright; - if (yleft == yup) - xmid2 = xup; - else + if (yleft != yup) xmid2 = xup + (xleft - xup) * (ymid - yup) / (yleft - yup); } @@ -1360,23 +1356,23 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft if (y >= swr->height) break; int x1 = xup, x2 = xup; - if (y < ymid) + if (y <= ymid) { // Lines: between up and mid, and between up and max if (ymid != yup) - x1 = xup + (xmid - xup + 1) * (y - yup) / (ymid - yup); + x1 = xup + (xmid - xup) * (y - yup) / (ymid - yup); if (ymid != yup) - x2 = xup + (xmid2 - xup + 1) * (y - yup) / (ymid - yup); + x2 = xup + (xmid2 - xup) * (y - yup) / (ymid - yup); } else { // Lines: between mid and max, and between up and max if (ymax != yup) - x1 = xup + (xmax - xup + 1) * (y - yup) / (ymax - yup); + x1 = xup + (xmax - xup) * (y - yup) / (ymax - yup); if (ymax != ymid) - x2 = xmid + (xmax - xmid + 1) * (y - ymid) / (ymax - ymid); + x2 = xmid + (xmax - xmid) * (y - ymid) / (ymax - ymid); } if (x1 >= x2) { @@ -1386,10 +1382,10 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft } if (x1 < 0) x1 = 0; - if (x1 >= swr->width) return; - if (x2 < 0) return; + if (x1 >= swr->width) continue; + if (x2 < 0) continue; if (x2 >= swr->width) x2 = swr->width - 1; - if (x1 > x2) return; + if (x1 > x2) continue; uintpixel_t* line = &swr->fb[y * swr->width]; for (int x = x1; x <= x2; x++) { From 23bd43d09315c9f5b7dc7966f977fb6fd12e5e66 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 25 May 2026 18:30:34 +0300 Subject: [PATCH 030/130] * Fix a bug where drawSpritePart was doubly handling sprite flipping. This results in sprites not being flipped when they should be, e.g. Asriel's background in his battle --- src/sw/sw_renderer.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 37e52d127..4d5fc1165 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1219,10 +1219,6 @@ static void SWRenderer_drawSpritePart(Renderer* renderer, int32_t tpagIndex, if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) return; - bool flipX = false, flipY = false; - if (xscale < 0) flipX = true, xscale = -xscale; - if (yscale < 0) flipY = true, yscale = -yscale; - TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; int16_t pageId = tpag->texturePageId; if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) return; @@ -1237,8 +1233,6 @@ static void SWRenderer_drawSpritePart(Renderer* renderer, int32_t tpagIndex, float dy = y; int dw = swrCeiling(xscale * sw); int dh = swrCeiling(yscale * sh); - if (flipX) dx -= dw; - if (flipY) dy -= dh; SWTexture* texture = swr->textures[pageId]; From 08090dc81c7db5d1d90ba0ef729ec4e895ff15e9 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 25 May 2026 20:11:07 +0300 Subject: [PATCH 031/130] * Small 8bpp renderer fix --- src/sw/sw_renderer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 4d5fc1165..faf0e2714 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1967,8 +1967,12 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su // in-bounds for (; sx < swr->width && ix < w; sx++, ix++) +#if PIXEL_SIZE == 8 + dstline[ix] = srcline[ix]; +#else dstline[ix] = srcline[ix] | TRANSPARENT_MASK; - +#endif + // right edge for (; ix < w; ix++) dstline[ix] = 0; From 29bd1d30ceb8932b426bbd67d4901e5695cc3b95 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 27 May 2026 23:26:41 +0300 Subject: [PATCH 032/130] * Attempt at optimization that I'll revert I'll find a better way... --- src/sw/sw_renderer.c | 145 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 27 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index faf0e2714..b6db17cb8 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -76,14 +76,35 @@ FORCE_INLINE bool opaque(uintpixel_t color) #endif } +FORCE_INLINE bool swrMustTint(uintpixel_t tintColor) +{ +#if PIXEL_SIZE == 8 + return tintColor != 0xFF && tintColor != PXL_TRANSPARENT; +#elif PIXEL_SIZE == 16 + return (tintColor & 0x7FFF) != 0x7FFF; +#else + return (tintColor & 0xFFFFFF) != 0xFFFFFF; +#endif +} + +FORCE_INLINE bool swrIsAlphaInsignificant(int alpha) +{ + return alpha <= 3; +} + +FORCE_INLINE bool swrMustAlphaBlend(int alpha) +{ + return alpha > 3 && alpha < 252; +} + FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) { + if (LIKELY(!swrMustTint(tintColor))) + return color; + #if PIXEL_SIZE == 32 Pixel32ARGB x, y; - if ((tintColor & 0xFFFFFF) == 0xFFFFFF) - return color; - x.l = color; y.l = tintColor; @@ -92,9 +113,6 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) x.p.r = (int)x.p.r * y.p.r / 255; return x.l; #elif PIXEL_SIZE == 16 - if ((tintColor & 0x7FFF) == 0x7FFF) - return color; - int tcb = tintColor & 0x1F; int tcg = (tintColor >> 5) & 0x1F; int tcr = (tintColor >> 10) & 0x1F; @@ -109,10 +127,7 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) cr = (cr * tcr) / 32; return ca | cb | (cg << 5) | (cr << 10); #elif PIXEL_SIZE == 8 - // fast but hacky - if (tintColor == 0xFF || tintColor == PXL_TRANSPARENT) - return color; - + // fast but VERY inaccurate and hacky return color & tintColor; #endif } @@ -120,13 +135,13 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) // NOTE: alpha is between 0 and 256, NOT between 0 and 255! FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) { -#if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 // it's so insignificant here nobody will notice if we just don't... - if (alpha < 3) + if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; +#if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 // it's so significant here we might as well fill in the whole color - if (alpha > 253) + if (LIKELY(alpha > 253)) { *dcolor = scolor; return; @@ -162,7 +177,8 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) *dcolor = dca | dcb | (dcg << 5) | (dcr << 10); #else - if (alpha < 240) { + if (UNLIKELY(alpha < 250)) + { static int alphaApproximationThingy = 0; alphaApproximationThingy += 1339; if (alphaApproximationThingy > 601000) @@ -589,14 +605,24 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe if (dx < 0) { dw += dx; dx = 0; } if (dx + dw >= swr->width) dw = swr->width - dx; if (dw <= 0) return; + if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; + + bool mustAlphaBlend = swrMustAlphaBlend(alpha); #if PIXEL_SIZE == 32 if (color == color2) #endif { uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; - for (int i = 0; i < dw; i++) - alphaBlend(&line[i], color, alpha); + + if (mustAlphaBlend) { + for (int i = 0; i < dw; i++) + alphaBlend(&line[i], color, alpha); + } + else { + for (int i = 0; i < dw; i++) + line[i] = color; + } } #if PIXEL_SIZE == 32 else @@ -652,15 +678,29 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe if (dy < 0) { dh += dy; dy = 0; } if (dy + dh >= swr->height) dh = swr->height - dy; if (dh <= 0) return; + if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; + + bool mustAlphaBlend = swrMustAlphaBlend(alpha); #if PIXEL_SIZE == 32 if (color == color2) #endif { - for (int i = 0; i < dh; i++) + if (mustAlphaBlend) { - uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - alphaBlend(&line[0], color, alpha); + for (int i = 0; i < dh; i++) + { + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + alphaBlend(&line[0], color, alpha); + } + } + else + { + for (int i = 0; i < dh; i++) + { + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + *line = color; + } } } #if PIXEL_SIZE == 32 @@ -726,6 +766,8 @@ static void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, int width, uintpixel_t color1, uintpixel_t color2, int alpha) { + if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; + if (x1 == x2) { swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); @@ -874,6 +916,8 @@ static void swrDrawSpriteInternal( { SWRenderer *swr = (SWRenderer*) renderer; + if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; + bool flipX = false, flipY = false; if (dw < 0) { dx += dw; dw = -dw; flipX = true; } if (dh < 0) { dy += dh; dh = -dh; flipY = true; } @@ -936,6 +980,9 @@ static void swrDrawSpriteInternal( fixedp_t ixs2 = ixs * xstep; fixedp_t iys2 = iys * ystep; + bool mustAlphaBlend = swrMustAlphaBlend(alpha); + bool mustTint = swrMustTint(tintColor); + if (sw == dw) { fixedp_t ys2 = (fixedp_t) iys2; @@ -949,11 +996,32 @@ static void swrDrawSpriteInternal( else srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) + if (UNLIKELY(mustTint)) { - uintpixel_t pixel = srcline[xs]; - if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) + { + uintpixel_t pixel = srcline[xs]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + } + } + else if (UNLIKELY(mustAlphaBlend)) + { + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) + { + uintpixel_t pixel = srcline[xs]; + if (opaque(pixel)) + alphaBlend(&dstline[x], pixel, alpha); + } + } + else + { + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) + { + uintpixel_t pixel = srcline[xs]; + if (opaque(pixel)) + dstline[x] = pixel; + } } } } @@ -971,11 +1039,32 @@ static void swrDrawSpriteInternal( srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; fixedp_t xs2 = ixs2; - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) + if (UNLIKELY(mustTint)) + { + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) + { + uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + } + } + else if (UNLIKELY(mustAlphaBlend)) { - uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; - if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) + { + uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; + if (opaque(pixel)) + alphaBlend(&dstline[x], pixel, alpha); + } + } + else + { + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) + { + uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + } } } } @@ -1018,6 +1107,8 @@ static void swrDrawSpriteRotatedInternal( SWRenderer* swr = (SWRenderer*) renderer; float angleRad = -angleDeg * M_PI / 180.0f; + if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; + bool flipX = false, flipY = false; if (dw < 0) { dw = -dw; dx -= dw; pivotX = dw - pivotX; flipX = true; } if (dh < 0) { dh = -dh; dy -= dh; pivotY = dh - pivotY; flipY = true; } From 30150cbab2ead988c3d3660e232426a8ef9fcdbb Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 27 May 2026 23:27:14 +0300 Subject: [PATCH 033/130] Revert "* Attempt at optimization that I'll revert" This reverts commit 29bd1d30ceb8932b426bbd67d4901e5695cc3b95. --- src/sw/sw_renderer.c | 145 ++++++++----------------------------------- 1 file changed, 27 insertions(+), 118 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index b6db17cb8..faf0e2714 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -76,35 +76,14 @@ FORCE_INLINE bool opaque(uintpixel_t color) #endif } -FORCE_INLINE bool swrMustTint(uintpixel_t tintColor) -{ -#if PIXEL_SIZE == 8 - return tintColor != 0xFF && tintColor != PXL_TRANSPARENT; -#elif PIXEL_SIZE == 16 - return (tintColor & 0x7FFF) != 0x7FFF; -#else - return (tintColor & 0xFFFFFF) != 0xFFFFFF; -#endif -} - -FORCE_INLINE bool swrIsAlphaInsignificant(int alpha) -{ - return alpha <= 3; -} - -FORCE_INLINE bool swrMustAlphaBlend(int alpha) -{ - return alpha > 3 && alpha < 252; -} - FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) { - if (LIKELY(!swrMustTint(tintColor))) - return color; - #if PIXEL_SIZE == 32 Pixel32ARGB x, y; + if ((tintColor & 0xFFFFFF) == 0xFFFFFF) + return color; + x.l = color; y.l = tintColor; @@ -113,6 +92,9 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) x.p.r = (int)x.p.r * y.p.r / 255; return x.l; #elif PIXEL_SIZE == 16 + if ((tintColor & 0x7FFF) == 0x7FFF) + return color; + int tcb = tintColor & 0x1F; int tcg = (tintColor >> 5) & 0x1F; int tcr = (tintColor >> 10) & 0x1F; @@ -127,7 +109,10 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) cr = (cr * tcr) / 32; return ca | cb | (cg << 5) | (cr << 10); #elif PIXEL_SIZE == 8 - // fast but VERY inaccurate and hacky + // fast but hacky + if (tintColor == 0xFF || tintColor == PXL_TRANSPARENT) + return color; + return color & tintColor; #endif } @@ -135,13 +120,13 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) // NOTE: alpha is between 0 and 256, NOT between 0 and 255! FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) { +#if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 // it's so insignificant here nobody will notice if we just don't... - if (UNLIKELY(swrIsAlphaInsignificant(alpha))) + if (alpha < 3) return; -#if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 // it's so significant here we might as well fill in the whole color - if (LIKELY(alpha > 253)) + if (alpha > 253) { *dcolor = scolor; return; @@ -177,8 +162,7 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) *dcolor = dca | dcb | (dcg << 5) | (dcr << 10); #else - if (UNLIKELY(alpha < 250)) - { + if (alpha < 240) { static int alphaApproximationThingy = 0; alphaApproximationThingy += 1339; if (alphaApproximationThingy > 601000) @@ -605,24 +589,14 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe if (dx < 0) { dw += dx; dx = 0; } if (dx + dw >= swr->width) dw = swr->width - dx; if (dw <= 0) return; - if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; - - bool mustAlphaBlend = swrMustAlphaBlend(alpha); #if PIXEL_SIZE == 32 if (color == color2) #endif { uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; - - if (mustAlphaBlend) { - for (int i = 0; i < dw; i++) - alphaBlend(&line[i], color, alpha); - } - else { - for (int i = 0; i < dw; i++) - line[i] = color; - } + for (int i = 0; i < dw; i++) + alphaBlend(&line[i], color, alpha); } #if PIXEL_SIZE == 32 else @@ -678,29 +652,15 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe if (dy < 0) { dh += dy; dy = 0; } if (dy + dh >= swr->height) dh = swr->height - dy; if (dh <= 0) return; - if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; - - bool mustAlphaBlend = swrMustAlphaBlend(alpha); #if PIXEL_SIZE == 32 if (color == color2) #endif { - if (mustAlphaBlend) - { - for (int i = 0; i < dh; i++) - { - uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - alphaBlend(&line[0], color, alpha); - } - } - else + for (int i = 0; i < dh; i++) { - for (int i = 0; i < dh; i++) - { - uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - *line = color; - } + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + alphaBlend(&line[0], color, alpha); } } #if PIXEL_SIZE == 32 @@ -766,8 +726,6 @@ static void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, int width, uintpixel_t color1, uintpixel_t color2, int alpha) { - if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; - if (x1 == x2) { swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); @@ -916,8 +874,6 @@ static void swrDrawSpriteInternal( { SWRenderer *swr = (SWRenderer*) renderer; - if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; - bool flipX = false, flipY = false; if (dw < 0) { dx += dw; dw = -dw; flipX = true; } if (dh < 0) { dy += dh; dh = -dh; flipY = true; } @@ -980,9 +936,6 @@ static void swrDrawSpriteInternal( fixedp_t ixs2 = ixs * xstep; fixedp_t iys2 = iys * ystep; - bool mustAlphaBlend = swrMustAlphaBlend(alpha); - bool mustTint = swrMustTint(tintColor); - if (sw == dw) { fixedp_t ys2 = (fixedp_t) iys2; @@ -996,32 +949,11 @@ static void swrDrawSpriteInternal( else srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; - if (UNLIKELY(mustTint)) + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) { - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) - { - uintpixel_t pixel = srcline[xs]; - if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); - } - } - else if (UNLIKELY(mustAlphaBlend)) - { - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) - { - uintpixel_t pixel = srcline[xs]; - if (opaque(pixel)) - alphaBlend(&dstline[x], pixel, alpha); - } - } - else - { - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) - { - uintpixel_t pixel = srcline[xs]; - if (opaque(pixel)) - dstline[x] = pixel; - } + uintpixel_t pixel = srcline[xs]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); } } } @@ -1039,32 +971,11 @@ static void swrDrawSpriteInternal( srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; fixedp_t xs2 = ixs2; - if (UNLIKELY(mustTint)) - { - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) - { - uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; - if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); - } - } - else if (UNLIKELY(mustAlphaBlend)) + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) { - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) - { - uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; - if (opaque(pixel)) - alphaBlend(&dstline[x], pixel, alpha); - } - } - else - { - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) - { - uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; - if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); - } + uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); } } } @@ -1107,8 +1018,6 @@ static void swrDrawSpriteRotatedInternal( SWRenderer* swr = (SWRenderer*) renderer; float angleRad = -angleDeg * M_PI / 180.0f; - if (UNLIKELY(swrIsAlphaInsignificant(alpha))) return; - bool flipX = false, flipY = false; if (dw < 0) { dw = -dw; dx -= dw; pivotX = dw - pivotX; flipX = true; } if (dh < 0) { dh = -dh; dy -= dh; pivotY = dh - pivotY; flipY = true; } From 7e09706675cccc9cd38732684b5b52b09d190527 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Wed, 27 May 2026 16:30:21 -0400 Subject: [PATCH 034/130] Merge pull request #6 from Un1q32/sw-pr Support window resizing --- src/sdl/main.c | 12 ++++-------- src/sw/sw_renderer.c | 30 +++++++++++++++--------------- src/sw/sw_renderer.h | 2 +- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/sdl/main.c b/src/sdl/main.c index 458bd334f..82c5dfd6d 100644 --- a/src/sdl/main.c +++ b/src/sdl/main.c @@ -585,12 +585,10 @@ static bool getSDLWindowSize(void *window, int32_t *outW, int32_t *outH) { static void setSDLWindowSize(void *window, int32_t width, int32_t height) { (void)window; - if (useSWRend) - return; if (width <= 0 || height <= 0) return; fbWidth = width; fbHeight = height; - scr = SDL_SetVideoMode(width, height, 0, useSWRend ? 0 : (SDL_OPENGL | SDL_RESIZABLE)); + scr = SDL_SetVideoMode(width, height, 0, (useSWRend ? 0 : SDL_OPENGL) | SDL_RESIZABLE); } static bool getSDLWindowFocus(void *window) { @@ -843,7 +841,7 @@ int main(int argc, char* argv[]) { fbWidth = reqW; fbHeight = reqH; if(!args.headless) { - scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, useSWRend ? 0 : (SDL_OPENGL | SDL_RESIZABLE)); + scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, (useSWRend ? 0 : SDL_OPENGL) | SDL_RESIZABLE); if (!scr && useSWRend) { SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN); if (modes && modes != (SDL_Rect**) -1 && modes[0]) { @@ -882,13 +880,13 @@ int main(int argc, char* argv[]) { Renderer* renderer; #if defined(ENABLE_LEGACY_GL) && defined(ENABLE_SW_RENDERER) if(useSWRend) - renderer = SWRenderer_create(fbWidth, fbHeight); + renderer = SWRenderer_create(); else renderer = GLLegacyRenderer_create(); #elif defined(ENABLE_LEGACY_GL) renderer = GLLegacyRenderer_create(); #else - renderer = SWRenderer_create(fbWidth, fbHeight); + renderer = SWRenderer_create(); #endif // Initialize the audio system @@ -977,8 +975,6 @@ int main(int argc, char* argv[]) { RunnerKeyboard_onKeyUp(runner->keyboard, SDLKeyToGml(e.key.keysym.sym)); break; case SDL_VIDEORESIZE: - if (useSWRend) - break; fbWidth = e.resize.w; fbHeight = e.resize.h; scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, (useSWRend ? 0 : SDL_OPENGL) | SDL_RESIZABLE); diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index faf0e2714..586ac5b90 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -57,7 +57,7 @@ typedef struct bool viewActive; int viewX, viewY, viewW, viewH; int portX, portY, portW, portH; - int gameW, gameH, windowW, windowH; + int gameW, gameH; } SWRenderer; @@ -224,8 +224,8 @@ FORCE_INLINE int swrCeiling(float x) static SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height) { - SWTexture* txt = safeCalloc(1, sizeof(SWTexture)); - txt->buffer = safeCalloc(width * height, sizeof(uintpixel_t)); + SWTexture* txt = safeMalloc(sizeof(SWTexture)); + txt->buffer = safeMalloc(width * height * sizeof(uintpixel_t)); const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; @@ -353,10 +353,6 @@ static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) renderer->dataWin = dataWin; - //allocate frame buffer - swr->fb = safeCalloc(swr->width * swr->height, sizeof(uintpixel_t)); - swr->fbPitch = swr->width; - //allocate texture buffer swr->textureCount = dataWin->txtr.count; swr->surfaceCount = SURFACE_MAX_COUNT; @@ -399,9 +395,16 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam SWRenderer* swr = (SWRenderer*) renderer; swr->gameW = gameW; swr->gameH = gameH; - swr->windowW = windowW; - swr->windowH = windowH; swr->drawingToSurface = false; + if (swr->width != windowW || swr->height != windowH) + { + //allocate frame buffer + free(swr->fb); + swr->fb = safeMalloc(windowW * windowH * sizeof(uintpixel_t)); + swr->fbPitch = windowW; + swr->width = windowW; + swr->height = windowH; + } } // This used to be just one, "endFrame". Not sure what the different is. @@ -436,8 +439,8 @@ static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t view yratio = 1.0f; } else { - xratio = (float) swr->windowW / swr->gameW; - yratio = (float) swr->windowH / swr->gameH; + xratio = (float) swr->width / swr->gameW; + yratio = (float) swr->height / swr->gameH; } portX = (int)(portX * xratio); @@ -2145,7 +2148,7 @@ void SWRenderer_clearFrameBuffer(Renderer* renderer, uint32_t color) } } -Renderer* SWRenderer_create(int windowWidth, int windowHeight) +Renderer* SWRenderer_create(void) { SWRenderer* swr = safeCalloc(1, sizeof(SWRenderer)); swr->base.vtable = &swrVtable; @@ -2155,9 +2158,6 @@ Renderer* SWRenderer_create(int windowWidth, int windowHeight) swr->base.drawHalign = 0; swr->base.drawValign = 0; swr->base.circlePrecision = 24; - - swr->width = windowWidth; - swr->height = windowHeight; return (Renderer*) swr; } diff --git a/src/sw/sw_renderer.h b/src/sw/sw_renderer.h index f8ca72267..1b2618779 100644 --- a/src/sw/sw_renderer.h +++ b/src/sw/sw_renderer.h @@ -2,6 +2,6 @@ #include "renderer.h" -Renderer* SWRenderer_create(int windowWidth, int windowHeight); +Renderer* SWRenderer_create(void); void SWRenderer_clearFrameBuffer(Renderer* renderer, uint32_t color); From 7b961d925b58d37f233eabd5adec2e6544a2f484 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Wed, 27 May 2026 16:54:52 -0400 Subject: [PATCH 035/130] add letterboxing and pillarboxing instead of streching (#7) --- src/sw/sw_renderer.c | 69 ++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 586ac5b90..c01f1f927 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -57,7 +57,7 @@ typedef struct bool viewActive; int viewX, viewY, viewW, viewH; int portX, portY, portW, portH; - int gameW, gameH; + int gameW, gameH, maxX, maxY; } SWRenderer; @@ -437,14 +437,25 @@ static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t view UNIMP(); xratio = 1.0f; yratio = 1.0f; + portX = (int)(portX * xratio); + portY = (int)(portY * yratio); } else { - xratio = (float) swr->width / swr->gameW; - yratio = (float) swr->height / swr->gameH; - } + float scaleX = (float) swr->width / swr->gameW; + float scaleY = (float) swr->height / swr->gameH; + float scale = (scaleX < scaleY) ? scaleX : scaleY; + + int32_t scaledW = (int32_t)(swr->gameW * scale); + int32_t scaledH = (int32_t)(swr->gameH * scale); + int32_t offsetX = (swr->width - scaledW) / 2; + int32_t offsetY = (swr->height - scaledH) / 2; - portX = (int)(portX * xratio); - portY = (int)(portY * yratio); + xratio = scale; + yratio = scale; + + portX = (int)(portX * xratio) + offsetX; + portY = (int)(portY * yratio) + offsetY; + } portW = (int)(portW * xratio); portH = (int)(portH * yratio); @@ -457,6 +468,8 @@ static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t view swr->portY = portY; swr->portW = portW; swr->portH = portH; + swr->maxX = portX + portW; + swr->maxY = portY + portH; } static void SWRenderer_endView(Renderer* renderer) @@ -577,8 +590,8 @@ FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t col { SWRenderer* swr = (SWRenderer*) renderer; - if (x < 0 || y < 0) return; - if (x >= swr->width || y >= swr->height) return; + if (x < swr->portX || y < swr->portY) return; + if (x >= swr->maxX || y >= swr->maxY) return; alphaBlend(&swr->fb[y * swr->fbPitch + x], color, alpha); } @@ -587,10 +600,10 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe { SWRenderer *swr = (SWRenderer*) renderer; - if (dy < 0) return; - if (dy >= swr->height) return; - if (dx < 0) { dw += dx; dx = 0; } - if (dx + dw >= swr->width) dw = swr->width - dx; + if (dy < swr->portY) return; + if (dy >= swr->maxY) return; + if (dx < swr->portX) { dw += dx; dx = swr->portX; } + if (dx + dw >= swr->maxX) dw = swr->maxX - dx; if (dw <= 0) return; #if PIXEL_SIZE == 32 @@ -650,10 +663,10 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe { SWRenderer *swr = (SWRenderer*) renderer; - if (dx < 0) return; - if (dx >= swr->width) return; - if (dy < 0) { dh += dy; dy = 0; } - if (dy + dh >= swr->height) dh = swr->height - dy; + if (dx < swr->portX) return; + if (dx >= swr->maxX) return; + if (dy < swr->portY) { dh += dy; dy = swr->portY; } + if (dy + dh >= swr->maxY) dh = swr->maxY - dy; if (dh <= 0) return; #if PIXEL_SIZE == 32 @@ -885,10 +898,10 @@ static void swrDrawSpriteInternal( if (dw == 0 || dh == 0) return; if (sw == 0) sw = 1; if (sh == 0) sh = 1; - if (dx + dw <= 0) return; - if (dy + dh <= 0) return; - if (dx >= swr->width) return; - if (dy >= swr->height) return; + if (dx + dw <= swr->portX) return; + if (dy + dh <= swr->portY) return; + if (dx >= swr->maxX) return; + if (dy >= swr->maxY) return; int odw = dw, odh = dh; int osw = sw, osh = sh; @@ -1057,10 +1070,10 @@ static void swrDrawSpriteRotatedInternal( int maxY = swrCeiling(maxYf); // basic out-of-bound checks - if (maxX < 0) return; - if (maxY < 0) return; - if (minX >= swr->width) return; - if (minY >= swr->height) return; + if (maxX < swr->portX) return; + if (maxY < swr->portY) return; + if (minX >= swr->maxX) return; + if (minY >= swr->maxY) return; // however, we'll need to clip it against out of bounds first int minXc = minX, minYc = minY, maxXc = maxX, maxYc = maxY; @@ -1378,10 +1391,10 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft x2 = tmp; } - if (x1 < 0) x1 = 0; - if (x1 >= swr->width) continue; - if (x2 < 0) continue; - if (x2 >= swr->width) x2 = swr->width - 1; + if (x1 < swr->portX) x1 = swr->portX; + if (x1 >= swr->maxX) continue; + if (x2 < swr->portX) continue; + if (x2 >= swr->maxX) x2 = swr->maxX - 1; if (x1 > x2) continue; uintpixel_t* line = &swr->fb[y * swr->width]; From ad8ee620b510fcdde2af3aa85650349f3c51c9f8 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Thu, 28 May 2026 00:11:29 +0300 Subject: [PATCH 036/130] * Increase precision of fixed point increment when scaling sprites. This fixes an issue where the error could build up at weird screen resolutions like 1068x800. --- src/sw/sw_renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index c01f1f927..f39b31e41 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -943,7 +943,7 @@ static void swrDrawSpriteInternal( // tweak these if stuff doesn't look right typedef int32_t fixedp_t; - const int fp_prec = 8; + const int fp_prec = 14; fixedp_t ystep = (sh == dh) ? (1 << fp_prec) : ((fixedp_t) osh << fp_prec) / odh; fixedp_t xstep = (sw == dw) ? (1 << fp_prec) : ((fixedp_t) osw << fp_prec) / odw; From d3de48042a49d098e7cd45baaf05d4705f6ed609 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 28 May 2026 22:27:32 -0400 Subject: [PATCH 037/130] cmake sw renderer --- CMakeLists.txt | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b37721e8..0508b8a34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ option(ENABLE_WAD16 "Enable support for WAD Version 15/16" ON) option(ENABLE_WAD17 "Enable support for WAD Version 17" ON) option(ENABLE_LEGACY_GL "Enable the legacy OpenGL renderer" ON) option(ENABLE_MODERN_GL "Enable the modern OpenGL renderer" ON) +option(ENABLE_SW_RENDERER "Enable the software renderer" ON) if(NOT ENABLE_WAD14 AND NOT ENABLE_WAD16 AND NOT ENABLE_WAD17) message(FATAL_ERROR "You need to build Butterscotch with at least one WAD version enabled!") endif() @@ -151,10 +152,13 @@ add_library(bzip2 STATIC target_include_directories(bzip2 PUBLIC vendor/bzip2) if(PLATFORM STREQUAL "desktop") - if(NOT ENABLE_LEGACY_GL AND NOT ENABLE_MODERN_GL) + if(NOT ENABLE_LEGACY_GL AND NOT ENABLE_MODERN_GL AND NOT ENABLE_SW_RENDERER) message(FATAL_ERROR "You must enable at least one renderer!") endif() - file(GLOB GL_SOURCES src/image/*.c src/gl_common/*.c) + file(GLOB GL_SOURCES src/image/*.c) + if(ENABLE_LEGACY_GL OR ENABLE_MODERN_GL) + file(GLOB GL_SOURCES src/gl_common/*.c) + endif() if(ENABLE_LEGACY_GL) file(GLOB GL_SOURCES ${GL_SOURCES} src/gl_legacy/*.c) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_legacy) @@ -162,6 +166,9 @@ if(PLATFORM STREQUAL "desktop") if(ENABLE_MODERN_GL) file(GLOB GL_SOURCES ${GL_SOURCES} src/gl/*.c) endif() + if(ENABLE_SW_RENDERER) + file(GLOB GL_SOURCES ${GL_SOURCES} src/sw/*.c) + endif() target_sources(butterscotch PRIVATE ${GL_SOURCES}) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common) @@ -198,13 +205,16 @@ if(PLATFORM STREQUAL "desktop") endif() # GLAD - if(ENABLE_GLES) - add_compile_definitions(ENABLE_GLES) - add_library(glad STATIC vendor/glad-gles/src/glad.c) - target_include_directories(glad PUBLIC vendor/glad-gles/include) - else() - add_library(glad STATIC vendor/glad/src/glad.c) - target_include_directories(glad PUBLIC vendor/glad/include) + if(ENABLE_LEGACY_GL OR ENABLE_MODERN_GL OR DESKTOP_BACKEND STREQUAL "glfw3" OR DESKTOP_BACKEND STREQUAL "glfw2") + if(ENABLE_GLES) + add_compile_definitions(ENABLE_GLES) + add_library(glad STATIC vendor/glad-gles/src/glad.c) + target_include_directories(glad PUBLIC vendor/glad-gles/include) + else() + add_library(glad STATIC vendor/glad/src/glad.c) + target_include_directories(glad PUBLIC vendor/glad/include) + endif() + set(PLATFORM_LIBRARIES glad) endif() # stb_image @@ -213,7 +223,7 @@ if(PLATFORM STREQUAL "desktop") # stb_vorbis target_include_directories(butterscotch PUBLIC vendor/stb/vorbis) - set(PLATFORM_LIBRARIES glad m bzip2) + set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} m bzip2) if(MINGW) set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} opengl32 gdi32 winmm) elseif(NOT CMAKE_SYSTEM_NAME STREQUAL "Haiku") From 80a831e9ff97fa167479164655c5939601d6bd08 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 28 May 2026 22:27:55 -0400 Subject: [PATCH 038/130] remove fflush --- src/utils.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/utils.h b/src/utils.h index 24e78731e..42396c43a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -25,7 +25,6 @@ do { \ if (!(condition)) { \ fprintf(stderr, "Requirement failed at %s:%d\n", __FILE__, __LINE__); \ - fflush(stderr); \ abort(); \ } \ } while (0) @@ -34,7 +33,6 @@ do { \ if (!(condition)) { \ fprintf(stderr, "Requirement failed at %s:%d: %s\n", __FILE__, __LINE__, message); \ -fflush(stderr); \ abort(); \ } \ } while (0) @@ -43,7 +41,6 @@ abort(); \ do { \ if (!(condition)) { \ fprintf(stderr, "Requirement failed at %s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ -fflush(stderr); \ abort(); \ } \ } while (0) @@ -52,7 +49,6 @@ abort(); \ typeof(ptr) _val = (ptr); \ if (_val == NULL) { \ fprintf(stderr, "%s:%d: requireNotNull failed: '%s'\n", __FILE__, __LINE__, #ptr); \ -fflush(stderr); \ abort(); \ } \ _val; \ @@ -62,7 +58,6 @@ _val; \ typeof(ptr) _val = (ptr); \ if (_val == NULL) { \ fprintf(stderr, "%s:%d: requireNotNull failed: %s\n", __FILE__, __LINE__, (msg)); \ -fflush(stderr); \ abort(); \ } \ _val; \ @@ -145,4 +140,4 @@ shput((dst), (src)[i].key, (src)[i].value); \ typedef struct { char* key; bool value; -} StringBooleanEntry; \ No newline at end of file +} StringBooleanEntry; From 00fa71d956467725d087a59ddab629cbef901d62 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 28 May 2026 22:41:38 -0400 Subject: [PATCH 039/130] include --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0508b8a34..cb54aabbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,8 @@ if(PLATFORM STREQUAL "desktop") file(GLOB GL_SOURCES src/image/*.c) if(ENABLE_LEGACY_GL OR ENABLE_MODERN_GL) file(GLOB GL_SOURCES src/gl_common/*.c) + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl) + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common) endif() if(ENABLE_LEGACY_GL) file(GLOB GL_SOURCES ${GL_SOURCES} src/gl_legacy/*.c) @@ -168,10 +170,9 @@ if(PLATFORM STREQUAL "desktop") endif() if(ENABLE_SW_RENDERER) file(GLOB GL_SOURCES ${GL_SOURCES} src/sw/*.c) + target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/sw) endif() target_sources(butterscotch PRIVATE ${GL_SOURCES}) - target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl) - target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/image) # Butterscotch VM/interpreter profiler From c21237b026482a5f587aaa28f02c140dfa8b428d Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 28 May 2026 22:45:02 -0400 Subject: [PATCH 040/130] fix --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb54aabbf..9216bfdf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,7 +157,7 @@ if(PLATFORM STREQUAL "desktop") endif() file(GLOB GL_SOURCES src/image/*.c) if(ENABLE_LEGACY_GL OR ENABLE_MODERN_GL) - file(GLOB GL_SOURCES src/gl_common/*.c) + file(GLOB GL_SOURCES ${GL_SOURCES} src/gl_common/*.c) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl) target_include_directories(butterscotch PRIVATE ${CMAKE_SOURCE_DIR}/src/gl_common) endif() From 8092689dc35bdd1e463b3142d5994f405d7f26bb Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 28 May 2026 22:46:10 -0400 Subject: [PATCH 041/130] fix --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9216bfdf0..69245ec54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,9 @@ endif() if(ENABLE_MODERN_GL) add_compile_definitions(ENABLE_MODERN_GL) endif() +if(ENABLE_SW_RENDERER) + add_compile_definitions(ENABLE_SW_RENDERER) +endif() if(PLATFORM STREQUAL "web-meta") # Metadata-only build: just the data.win parser and its direct deps. No VM, no runner, no I/O subsystems. From af77e150101f1156134a50863383c78a124870f4 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sat, 30 May 2026 05:31:15 -0400 Subject: [PATCH 042/130] no more sdl --- src/sdl/main.c | 1262 ------------------------------------------------ 1 file changed, 1262 deletions(-) delete mode 100644 src/sdl/main.c diff --git a/src/sdl/main.c b/src/sdl/main.c deleted file mode 100644 index 82c5dfd6d..000000000 --- a/src/sdl/main.c +++ /dev/null @@ -1,1262 +0,0 @@ -#include "data_win.h" -#include "vm.h" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef _WIN32 -#include -#endif -#ifdef __GLIBC__ -#include -#ifdef __GLIBC_PREREQ -#if __GLIBC_PREREQ(2, 33) -#define HAVE_MALLINFO2 -#endif -#endif -#endif - -#include "runner_keyboard.h" -#include "runner.h" -#include "input_recording.h" -#include "debug_overlay.h" -#ifdef ENABLE_LEGACY_GL -#include "gl_legacy_renderer.h" -#endif -#ifdef ENABLE_SW_RENDERER -#include "sw_renderer.h" -#endif -#include "overlay_file_system.h" -#if defined(USE_OPENAL) -#include "al_audio_system.h" -#elif defined(USE_MINIAUDIO) -#include "ma_audio_system.h" -#endif -#include "noop_audio_system.h" -#include "stb_ds.h" -#include "stb_image_write.h" - -#include "utils.h" -#include "profiler.h" - -// ===[ COMMAND LINE ARGUMENTS ]=== -typedef struct { - int key; - // We need this dummy value, think that the ds_map is like a Java HashMap NOT a HashSet - // (Which is funny, because in Java HashSets are backed by HashMaps lol) - bool value; -} FrameSetEntry; - -typedef struct { - const char* dataWinPath; - const char* saveFolder; // null = default to the directory containing dataWinPath - const char* screenshotPattern; - FrameSetEntry* screenshotFrames; - const char* screenshotSurfacesPattern; - FrameSetEntry* screenshotSurfacesFrames; - FrameSetEntry* dumpFrames; - FrameSetEntry* dumpJsonFrames; - const char* dumpJsonFilePattern; - StringBooleanEntry* varReadsToBeTraced; - StringBooleanEntry* varWritesToBeTraced; - StringBooleanEntry* functionCallsToBeTraced; - StringBooleanEntry* alarmsToBeTraced; - StringBooleanEntry* instanceLifecyclesToBeTraced; - StringBooleanEntry* eventsToBeTraced; - StringBooleanEntry* collisionsToBeTraced; - StringBooleanEntry* opcodesToBeTraced; - StringBooleanEntry* stackToBeTraced; - StringBooleanEntry* disassemble; - StringBooleanEntry* tilesToBeTraced; - bool alwaysLogUnknownFunctions; - bool alwaysLogStubbedFunctions; - bool headless; - bool traceFrames; - bool printRooms; - bool printDeclaredFunctions; - int exitAtFrame; - int traceBytecodeAfterFrame; - double speedMultiplier; - double fastForwardSpeed; - int seed; - bool hasSeed; - bool debug; - bool traceEventInherited; - const char* recordInputsPath; - const char* playbackInputsPath; - const char* renderer; - YoYoOperatingSystem osType; - bool lazyRooms; - StringBooleanEntry* eagerRooms; // stb_ds string-keyed set of room names - int profilerFramesBetween; // 0 = disabled -#ifdef ENABLE_VM_OPCODE_PROFILER - bool opcodeProfiler; -#endif -} CommandLineArgs; - -static int fbWidth, fbHeight; -static SDL_Surface* scr; -static bool useSWRend; - -typedef struct { const char* name; YoYoOperatingSystem value; } OsTypeNameEntry; - -static const OsTypeNameEntry OS_TYPE_NAMES[] = { - {"unknown", OS_UNKNOWN}, - {"windows", OS_WINDOWS}, - {"win32", OS_WINDOWS}, - {"macosx", OS_MACOSX}, - {"macos", OS_MACOSX}, - {"psp", OS_PSP}, - {"ios", OS_IOS}, - {"android", OS_ANDROID}, - {"symbian", OS_SYMBIAN}, - {"linux", OS_LINUX}, - {"winphone", OS_WINPHONE}, - {"tizen", OS_TIZEN}, - {"win8native", OS_WIN8NATIVE}, - {"wiiu", OS_WIIU}, - {"3ds", OS_3DS}, - {"psvita", OS_PSVITA}, - {"bb10", OS_BB10}, - {"ps4", OS_PS4}, - {"xboxone", OS_XBOXONE}, - {"ps3", OS_PS3}, - {"xbox360", OS_XBOX360}, - {"uwp", OS_UWP}, - {"amazon", OS_AMAZON}, - {"switch", OS_SWITCH}, -}; -#define OS_TYPE_NAMES_COUNT (sizeof(OS_TYPE_NAMES)/sizeof(OS_TYPE_NAMES[0])) - -static bool parseOsTypeArg(const char* s, YoYoOperatingSystem* out) { - forEach(const OsTypeNameEntry, entry, OS_TYPE_NAMES, OS_TYPE_NAMES_COUNT) { - if (strcmp(s, entry->name) == 0) { - *out = entry->value; - return true; - } - } - return false; -} - -static void printOsTypeNames(FILE* out) { - forEachIndexed(const OsTypeNameEntry, entry, i, OS_TYPE_NAMES, OS_TYPE_NAMES_COUNT) { - fprintf(out, "%s%s", i > 0 ? ", " : "", entry->name); - } -} - -static void parseCommandLineArgs(CommandLineArgs* args, int argc, char* argv[]) { - memset(args, 0, sizeof(CommandLineArgs)); - - static struct option longOptions[] = { - {"screenshot", required_argument, nullptr, 's'}, - {"screenshot-at-frame", required_argument, nullptr, 'f'}, - {"screenshot-surfaces", required_argument, nullptr, 'U'}, - {"screenshot-surfaces-at-frame", required_argument, nullptr, 'V'}, - {"headless", no_argument, nullptr, 'h'}, - {"print-rooms", no_argument, nullptr, 'r'}, - {"print-declared-functions", no_argument, nullptr, 'p'}, - {"trace-variable-reads", required_argument, nullptr, 'R'}, - {"trace-variable-writes", required_argument, nullptr, 'W'}, - {"trace-function-calls", required_argument, nullptr, 'c'}, - {"trace-alarms", required_argument, nullptr, 'a'}, - {"trace-instance-lifecycles", required_argument, nullptr, 'l'}, - {"trace-events", required_argument, nullptr, 'e'}, - {"trace-collisions", required_argument, nullptr, 'C'}, - {"trace-event-inherited", no_argument, nullptr, 'E'}, - {"trace-tiles", required_argument, nullptr, 'T'}, - {"trace-opcodes", required_argument, nullptr, 'o'}, - {"trace-stack", required_argument, nullptr, 'S'}, - {"trace-frames", no_argument, nullptr, 'k'}, - {"always-log-unknown-functions", no_argument, nullptr, 'y'}, - {"always-log-stubbed-functions", no_argument, nullptr, 'Y'}, - {"exit-at-frame", required_argument, nullptr, 'x'}, - {"trace-bytecode-after-frame", required_argument, nullptr, 'F'}, - {"dump-frame", required_argument, nullptr, 'd'}, - {"dump-frame-json", required_argument, nullptr, 'j'}, - {"dump-frame-json-file", required_argument, nullptr, 'J'}, - {"speed", required_argument, nullptr, 'M'}, - {"fast-forward-speed", required_argument, nullptr, 'X'}, - {"seed", required_argument, nullptr, 'Z'}, - {"debug", no_argument, nullptr, 'D'}, - {"disassemble", required_argument, nullptr, 'A'}, - {"record-inputs", required_argument, nullptr, 'I'}, - {"playback-inputs", required_argument, nullptr, 'P'}, - {"renderer", required_argument, nullptr, 'g'}, - {"lazy-rooms", no_argument, nullptr, 'z'}, - {"eager-room", required_argument, nullptr, 'G'}, - {"os-type", required_argument, nullptr, 'O'}, - {"profile-gml-scripts", required_argument, nullptr, 'q'}, - {"save-folder", required_argument, nullptr, 'B'}, -#ifdef ENABLE_VM_OPCODE_PROFILER - {"profile-opcodes", no_argument, nullptr, 'Q'}, -#endif - {nullptr, 0, nullptr, 0 } - }; - - args->screenshotFrames = nullptr; - args->exitAtFrame = -1; - args->traceBytecodeAfterFrame = 0; - args->speedMultiplier = 1.0; - args->fastForwardSpeed = 0.0; -#ifdef ENABLE_LEGACY_GL - args->renderer = "legacy-gl"; -#else - args->renderer = "software"; -#endif - args->osType = OS_WINDOWS; - args->profilerFramesBetween = 0; - - int opt; - while ((opt = getopt_long(argc, argv, "", longOptions, nullptr)) != -1) { - switch (opt) { - case 's': - args->screenshotPattern = optarg; - break; - case 'f': { - char* endPtr; - long frame = strtol(optarg, &endPtr, 10); - if (*endPtr != '\0' || 0 > frame) { - fprintf(stderr, "Error: Invalid frame number '%s'\n", optarg); - exit(1); - } - - hmput(args->screenshotFrames, (int) frame, true); - break; - } - case 'U': - args->screenshotSurfacesPattern = optarg; - break; - case 'V': { - char* endPtr; - long frame = strtol(optarg, &endPtr, 10); - if (*endPtr != '\0' || 0 > frame) { - fprintf(stderr, "Error: Invalid frame number '%s' for --screenshot-surfaces-at-frame\n", optarg); - exit(1); - } - hmput(args->screenshotSurfacesFrames, (int) frame, true); - break; - } - case 'h': - args->headless = true; - break; - case 'r': - args->printRooms = true; - break; - case 'p': - args->printDeclaredFunctions = true; - break; - case 'R': - shput(args->varReadsToBeTraced, optarg, true); - break; - case 'W': - shput(args->varWritesToBeTraced, optarg, true); - break; - case 'c': - shput(args->functionCallsToBeTraced, optarg, true); - break; - case 'a': - shput(args->alarmsToBeTraced, optarg, true); - break; - case 'l': - shput(args->instanceLifecyclesToBeTraced, optarg, true); - break; - case 'e': - shput(args->eventsToBeTraced, optarg, true); - break; - case 'C': - shput(args->collisionsToBeTraced, optarg, true); - break; - case 'o': - shput(args->opcodesToBeTraced, optarg, true); - break; - case 'S': - shput(args->stackToBeTraced, optarg, true); - break; - case 'k': - args->traceFrames = true; - break; - case 'y': - args->alwaysLogUnknownFunctions = true; - break; - case 'Y': - args->alwaysLogStubbedFunctions = true; - break; - case 'x': { - char* endPtr; - long frame = strtol(optarg, &endPtr, 10); - if (*endPtr != '\0' || 0 > frame) { - fprintf(stderr, "Error: Invalid frame number '%s' for --exit-at-frame\n", optarg); - exit(1); - } - args->exitAtFrame = (int) frame; - break; - } - case 'F': { - char* endPtr; - long frame = strtol(optarg, &endPtr, 10); - if (*endPtr != '\0' || 0 > frame) { - fprintf(stderr, "Error: Invalid frame number '%s' for --trace-bytecode-after-frame\n", optarg); - exit(1); - } - args->traceBytecodeAfterFrame = (int) frame; - break; - } - case 'd': { - char* endPtr; - long frame = strtol(optarg, &endPtr, 10); - if (*endPtr != '\0' || 0 > frame) { - fprintf(stderr, "Error: Invalid frame number '%s' for --dump-frame\n", optarg); - exit(1); - } - hmput(args->dumpFrames, (int) frame, true); - break; - } - case 'j': { - char* endPtr; - long frame = strtol(optarg, &endPtr, 10); - if (*endPtr != '\0' || 0 > frame) { - fprintf(stderr, "Error: Invalid frame number '%s' for --dump-frame-json\n", optarg); - exit(1); - } - hmput(args->dumpJsonFrames, (int) frame, true); - break; - } - case 'J': - args->dumpJsonFilePattern = optarg; - break; - case 'M': { - char* endPtr; - double speed = strtod(optarg, &endPtr); - if (*endPtr != '\0' || speed <= 0.0) { - fprintf(stderr, "Error: Invalid speed multiplier '%s' for --speed (must be > 0)\n", optarg); - exit(1); - } - args->speedMultiplier = speed; - break; - } - case 'X': { - char* endPtr; - double speed = strtod(optarg, &endPtr); - if (*endPtr != '\0' || speed <= 0.0) { - fprintf(stderr, "Error: Invalid speed '%s' for --fast-forward-speed (must be > 0)\n", optarg); - exit(1); - } - args->fastForwardSpeed = speed; - break; - } - case 'D': - args->debug = true; - break; - case 'g': - args->renderer = optarg; - break; - case 'z': - args->lazyRooms = true; - break; - case 'G': - shput(args->eagerRooms, optarg, true); - break; - case 'A': - shput(args->disassemble, optarg, true); - break; - case 'T': - shput(args->tilesToBeTraced, optarg, true); - break; - case 'E': - args->traceEventInherited = true; - break; - case 'Z': { - char* endPtr; - long seedVal = strtol(optarg, &endPtr, 10); - if (*endPtr != '\0') { - fprintf(stderr, "Error: Invalid seed value '%s' for --seed\n", optarg); - exit(1); - } - args->seed = (int) seedVal; - args->hasSeed = true; - break; - } - case 'I': - args->recordInputsPath = optarg; - break; - case 'P': - args->playbackInputsPath = optarg; - break; - case 'q': { - char* endPtr; - long framesBetween = strtol(optarg, &endPtr, 10); - if (*endPtr != '\0' || framesBetween <= 0) { - fprintf(stderr, "Error: Invalid frame count '%s' for --profile-gml-scripts (must be > 0)\n", optarg); - exit(1); - } - args->profilerFramesBetween = (int) framesBetween; - break; - } - case 'B': - args->saveFolder = optarg; - break; -#ifdef ENABLE_VM_OPCODE_PROFILER - case 'Q': - args->opcodeProfiler = true; - break; -#endif - case 'O': - if (!parseOsTypeArg(optarg, &args->osType)) { - fprintf(stderr, "Error: Invalid --os-type value '%s' (expected: ", optarg); - printOsTypeNames(stderr); - fprintf(stderr, ")\n"); - exit(1); - } - break; - default: - fprintf(stderr, "Usage: %s " -#ifdef ENABLE_SW_RENDERER - "[--headless] " -#endif - "[--screenshot=PATTERN] [--screenshot-at-frame=N ...] \n", argv[0]); - exit(1); - } - } - - if (optind >= argc) { - fprintf(stderr, "Usage: %s " -#ifdef ENABLE_SW_RENDERER - "[--headless] " -#endif - "[--screenshot=PATTERN] [--screenshot-at-frame=N ...] \n", argv[0]); - exit(1); - } - - if (args->headless) { -#ifdef ENABLE_SW_RENDERER - args->renderer = "software"; - fprintf(stderr, "Warning: forcing software rendering in headless mode!\n"); -#else - fprintf(stderr, "Error: headless mode requires the software renderer, but it is not enabled!\n"); - exit(1); -#endif - } - - args->dataWinPath = argv[optind]; - - if (hmlen(args->screenshotFrames) > 0 && args->screenshotPattern == nullptr) { - fprintf(stderr, "Error: --screenshot-at-frame requires --screenshot to be set\n"); - exit(1); - } - - if (hmlen(args->screenshotSurfacesFrames) > 0 && args->screenshotSurfacesPattern == nullptr) { - fprintf(stderr, "Error: --screenshot-surfaces-at-frame requires --screenshot-surfaces to be set\n"); - exit(1); - } - - if (args->headless && args->speedMultiplier != 1.0) { - fprintf(stderr, "You can't set the speed multiplier while running in headless mode! Headless mode always run in real time\n"); - exit(1); - } -} - -static void freeCommandLineArgs(CommandLineArgs* args) { - hmfree(args->screenshotFrames); - hmfree(args->screenshotSurfacesFrames); - hmfree(args->dumpFrames); - hmfree(args->dumpJsonFrames); - shfree(args->varReadsToBeTraced); - shfree(args->varWritesToBeTraced); - shfree(args->functionCallsToBeTraced); - shfree(args->alarmsToBeTraced); - shfree(args->instanceLifecyclesToBeTraced); - shfree(args->eventsToBeTraced); - shfree(args->collisionsToBeTraced); - shfree(args->opcodesToBeTraced); - shfree(args->stackToBeTraced); - shfree(args->disassemble); - shfree(args->tilesToBeTraced); -} - -// ===[ KEYBOARD INPUT ]=== - -static int32_t SDLKeyToGml(int sdlkey) { - // Letters and numbers are the same as GML - if (sdlkey >= 'a' && sdlkey <= 'z') return toupper(sdlkey); - if (sdlkey >= '0' && sdlkey <= '9') return sdlkey; - // Special keys need mapping - switch (sdlkey) { - case SDLK_ESCAPE: return VK_ESCAPE; - case SDLK_RETURN: return VK_ENTER; - case SDLK_TAB: return VK_TAB; - case SDLK_BACKSPACE: return VK_BACKSPACE; - case SDLK_SPACE: return VK_SPACE; - case SDLK_LSHIFT: - case SDLK_RSHIFT: return VK_SHIFT; - case SDLK_LCTRL: - case SDLK_RCTRL: return VK_CONTROL; - case SDLK_LALT: - case SDLK_RALT: return VK_ALT; - case SDLK_UP: return VK_UP; - case SDLK_DOWN: return VK_DOWN; - case SDLK_LEFT: return VK_LEFT; - case SDLK_RIGHT: return VK_RIGHT; - case SDLK_F1: return VK_F1; - case SDLK_F2: return VK_F2; - case SDLK_F3: return VK_F3; - case SDLK_F4: return VK_F4; - case SDLK_F5: return VK_F5; - case SDLK_F6: return VK_F6; - case SDLK_F7: return VK_F7; - case SDLK_F8: return VK_F8; - case SDLK_F9: return VK_F9; - case SDLK_F10: return VK_F10; - case SDLK_F11: return VK_F11; - case SDLK_F12: return VK_F12; - case SDLK_INSERT: return VK_INSERT; - case SDLK_DELETE: return VK_DELETE; - case SDLK_HOME: return VK_HOME; - case SDLK_END: return VK_END; - case SDLK_PAGEUP: return VK_PAGEUP; - case SDLK_PAGEDOWN: return VK_PAGEDOWN; - default: return -1; // Unknown - } -} - -static InputRecording* globalInputRecording = nullptr; - -#if defined(__has_feature) - #if __has_feature(address_sanitizer) - #define BUTTERSCOTCH_HAS_ASAN 1 - #endif -#endif -#if defined(__SANITIZE_ADDRESS__) - #define BUTTERSCOTCH_HAS_ASAN 1 -#endif - -#if BUTTERSCOTCH_HAS_ASAN -void __asan_set_death_callback(void (*callback)(void)); -#endif - -static volatile sig_atomic_t crashSaveInProgress = 0; - -static void saveRecordingOnCrash(void) { - if (crashSaveInProgress) return; - crashSaveInProgress = 1; - if (globalInputRecording != nullptr && globalInputRecording->isRecording) { - InputRecording_save(globalInputRecording); - } -} - -static void crashSignalHandler(int sig) { - saveRecordingOnCrash(); - signal(sig, SIG_DFL); - raise(sig); -} - -static void installCrashHandlers(void) { -#if BUTTERSCOTCH_HAS_ASAN - __asan_set_death_callback(saveRecordingOnCrash); -#endif - signal(SIGSEGV, crashSignalHandler); - signal(SIGABRT, crashSignalHandler); -#ifdef SIGBUS - signal(SIGBUS, crashSignalHandler); -#endif - signal(SIGFPE, crashSignalHandler); - signal(SIGILL, crashSignalHandler); -} - - -static void setSDLWindowTitle(void* window, const char* title) { - char windowTitle[256]; - snprintf(windowTitle, sizeof(windowTitle), "Butterscotch - %s", title); - SDL_WM_SetCaption(windowTitle, NULL); -} - -static bool getSDLWindowSize(void *window, int32_t *outW, int32_t *outH) { - (void)window; - if (outW == nullptr || outH == nullptr) return false; - *outW = fbWidth; - *outH = fbHeight; - return true; -} - -static void setSDLWindowSize(void *window, int32_t width, int32_t height) { - (void)window; - if (width <= 0 || height <= 0) return; - fbWidth = width; - fbHeight = height; - scr = SDL_SetVideoMode(width, height, 0, (useSWRend ? 0 : SDL_OPENGL) | SDL_RESIZABLE); -} - -static bool getSDLWindowFocus(void *window) { - (void)window; - return true; -} - -static SDL_Surface* nextFb = NULL; -void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) -{ - if (nextFb != NULL) { - SDL_FreeSurface(nextFb); - nextFb = NULL; - } - - nextFb = SDL_CreateRGBSurfaceFrom( - framebuffer, - width, - height, - 32, - width * 4, - 0x00ff0000, // Rmask - 0x0000ff00, // Gmask - 0x000000ff, // Bmask - 0x00000000 // Amask - ); -} - -void saveInputRecording() { - // Save input recording if active, then free - if (globalInputRecording != nullptr) { - if (globalInputRecording->isRecording) { - InputRecording_save(globalInputRecording); - } - InputRecording_free(globalInputRecording); - globalInputRecording = nullptr; - } -} - -#ifndef _WIN32 -typedef struct { int key; struct sigaction value; } PreviousSignalActionEntry; -static PreviousSignalActionEntry* previousSignalActions = nullptr; - -static void onCrashSignal(int sig) { - saveInputRecording(); - // Restore the previous handler (ASAN) and re-raise so it can report the fault - sigaction(sig, &previousSignalActions[hmgeti(previousSignalActions, sig)].value, nullptr); - raise(sig); -} -#endif - -// ===[ MAIN ]=== -static bool shouldExit = false; -int main(int argc, char* argv[]) { - setbuf(stderr, NULL); - - CommandLineArgs args; - parseCommandLineArgs(&args, argc, argv); - - printf("Loading %s...\n", args.dataWinPath); - - DataWin* dataWin = DataWin_parse( - args.dataWinPath, - (DataWinParserOptions) { - .parseGen8 = true, - .parseOptn = true, - .parseLang = true, - .parseExtn = false, - .parseSond = true, - .parseAgrp = true, - .parseSprt = true, - .parseBgnd = true, - .parsePath = true, - .parseScpt = true, - .parseGlob = true, - .parseShdr = true, - .parseFont = true, - .parseTmln = true, - .parseObjt = true, - .parseRoom = true, - .parseTpag = true, - .parseCode = true, - .parseVari = true, - .parseFunc = true, - .parseStrg = true, - .parseTxtr = true, - .parseAudo = true, - .skipLoadingPreciseMasksForNonPreciseSprites = true, - .lazyLoadRooms = args.lazyRooms, - .eagerlyLoadedRooms = args.eagerRooms - } - ); - - Gen8* gen8 = &dataWin->gen8; - printf("Loaded \"%s\" (%d) successfully! [Bytecode Version %u / GameMaker version %u.%u.%u.%u]\n", gen8->name, gen8->gameID, gen8->bytecodeVersion, dataWin->detectedFormat.major, dataWin->detectedFormat.minor, dataWin->detectedFormat.release, dataWin->detectedFormat.build); - - #ifdef HAVE_MALLINFO2 - { - struct mallinfo2 mi = mallinfo2(); - printf("Memory after data.win parsing: used=%zu bytes (%.1f KB)\n", mi.uordblks, mi.uordblks / 1024.0f); - } - #endif - - // Build window title - char windowTitle[256]; - snprintf(windowTitle, sizeof(windowTitle), "Butterscotch - %s", gen8->displayName); - - // Initialize VM - VMContext* vm = VM_create(dataWin); - - Profiler_setEnabled(&vm->profiler, args.profilerFramesBetween > 0); -#ifdef ENABLE_VM_OPCODE_PROFILER - vm->opcodeProfilerEnabled = args.opcodeProfiler; - if (vm->opcodeProfilerEnabled) { - vm->opcodeVariantCounts = safeCalloc(256 * 256, sizeof(uint64_t)); - vm->opcodeRValueTypeCounts = safeCalloc(256 * 256, sizeof(uint64_t)); - } -#endif - - if (args.hasSeed) { - srand((unsigned int) args.seed); - vm->hasFixedSeed = true; - printf("Using fixed RNG seed: %d\n", args.seed); - } - - if (args.printRooms) { - // Under --lazy-rooms we load each room for display and then free it again so the dump - // reflects what each room contains without keeping all of them resident simultaneously. - forEachIndexed(Room, room, idx, dataWin->room.rooms, dataWin->room.count) { - bool loadedHere = false; - if (!room->payloadLoaded) { - DataWin_loadRoomPayload(dataWin, (int32_t) idx); - loadedHere = true; - } - - printf("[%d] %s ()\n", idx, room->name); - - forEachIndexed(RoomGameObject, roomGameObject, idx2, room->gameObjects, room->gameObjectCount) { - GameObject* gameObject = &dataWin->objt.objects[roomGameObject->objectDefinition]; - printf( - " [%d] %s (x=%d,y=%d,persistent=%d,solid=%d,spriteId=%d,preCreateCode=%d,creationCode=%d)\n", - idx2, - gameObject->name, - roomGameObject->x, - roomGameObject->y, - gameObject->persistent, - gameObject->solid, - gameObject->spriteId, - roomGameObject->preCreateCode, - roomGameObject->creationCode - ); - } - - if (loadedHere && !room->eagerlyLoaded) { - DataWin_freeRoomPayload(room); - } - } - VM_free(vm); - DataWin_free(dataWin); - return 0; - } - - if (args.printDeclaredFunctions) { - repeat(hmlen(vm->codeIndexByName), i) { - printf("[%d] %s\n", vm->codeIndexByName[i].value, vm->codeIndexByName[i].key); - } - VM_free(vm); - DataWin_free(dataWin); - return 0; - } - - if (shlen(args.disassemble) > 0) { - VM_buildCrossReferences(vm); - if (shgeti(args.disassemble, "*") >= 0) { - repeat(dataWin->code.count, i) { - VM_disassemble(vm, (int32_t) i); - } - } else { - for (ptrdiff_t i = 0; shlen(args.disassemble) > i; i++) { - const char* name = args.disassemble[i].key; - ptrdiff_t idx = shgeti(vm->codeIndexByName, (char*) name); - if (idx >= 0) { - VM_disassemble(vm, vm->codeIndexByName[idx].value); - } else { - fprintf(stderr, "Error: Script '%s' not found in funcMap\n", name); - } - } - } - VM_free(vm); - DataWin_free(dataWin); - freeCommandLineArgs(&args); - return 0; - } - - // Initialize the file system - char* dataWinDir = nullptr; - { - const char* lastSlash = strrchr(args.dataWinPath, '/'); - const char* lastBackslash = strrchr(args.dataWinPath, '\\'); - if (lastBackslash != nullptr && (lastSlash == nullptr || lastBackslash > lastSlash)) - lastSlash = lastBackslash; - if (lastSlash != nullptr) { - size_t len = (size_t) (lastSlash - args.dataWinPath + 1); - dataWinDir = safeMalloc(len + 1); - memcpy(dataWinDir, args.dataWinPath, len); - dataWinDir[len] = '\0'; - } else { - dataWinDir = safeStrdup("./"); - } - } - const char* savePath = args.saveFolder != nullptr ? args.saveFolder : dataWinDir; - OverlayFileSystem* overlayFs = OverlayFileSystem_create(dataWinDir, savePath); - free(dataWinDir); - - // Init SDL - if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)) { - fprintf(stderr, "Failed to initialize SDL\n"); - DataWin_free(dataWin); - freeCommandLineArgs(&args); - return 1; - } - - useSWRend = strcmp(args.renderer, "software") == 0; - bool useLegacyGL = strcmp(args.renderer, "legacy-gl") == 0; -#ifndef ENABLE_LEGACY_GL - if (useLegacyGL) { - fprintf(stderr, "The legacy-gl renderer is not available in this build!\n"); - DataWin_free(dataWin); - freeCommandLineArgs(&args); - return 1; - } -#endif -#ifndef ENABLE_SW_RENDERER - if (useSWRend) { - fprintf(stderr, "The software renderer is not available in this build!\n"); - DataWin_free(dataWin); - freeCommandLineArgs(&args); - return 1; - } -#endif - if (!useSWRend && !useLegacyGL) { - fprintf(stderr, "Unknown renderer: %s!\n", args.renderer); - DataWin_free(dataWin); - freeCommandLineArgs(&args); - return 1; - } - - int reqW = (int) gen8->defaultWindowWidth; - int reqH = (int) gen8->defaultWindowHeight; - fbWidth = reqW; - fbHeight = reqH; - if(!args.headless) { - scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, (useSWRend ? 0 : SDL_OPENGL) | SDL_RESIZABLE); - if (!scr && useSWRend) { - SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN); - if (modes && modes != (SDL_Rect**) -1 && modes[0]) { - fprintf(stderr, "Warning: %dx%d unavailable, falling back to %dx%d: %s\n", - reqW, reqH, modes[0]->w, modes[0]->h, SDL_GetError()); - scr = SDL_SetVideoMode(modes[0]->w, modes[0]->h, 0, 0); - fbWidth = modes[0]->w; - fbHeight = modes[0]->h; - } - } - if (!scr) { - fprintf(stderr, "Fatal: Could not set any video mode: %s\n", SDL_GetError()); - SDL_Quit(); - DataWin_free(dataWin); - freeCommandLineArgs(&args); - return 1; - } - } - - SDL_EnableKeyRepeat(0, 0); - -#ifdef ENABLE_LEGACY_GL - if(!useSWRend) { - // Load OpenGL function pointers via GLAD - if (!gladLoadGLLoader((GLADloadproc) SDL_GL_GetProcAddress)) { - fprintf(stderr, "Failed to initialize GLAD\n"); - SDL_Quit(); - DataWin_free(dataWin); - freeCommandLineArgs(&args); - return 1; - } - } -#endif - - // Initialize the renderer - Renderer* renderer; -#if defined(ENABLE_LEGACY_GL) && defined(ENABLE_SW_RENDERER) - if(useSWRend) - renderer = SWRenderer_create(); - else - renderer = GLLegacyRenderer_create(); -#elif defined(ENABLE_LEGACY_GL) - renderer = GLLegacyRenderer_create(); -#else - renderer = SWRenderer_create(); -#endif - - // Initialize the audio system - AudioSystem* audioSystem; - if (args.headless) { - audioSystem = (AudioSystem*) NoopAudioSystem_create(); - } else { -#if defined(USE_OPENAL) - audioSystem = (AudioSystem*) AlAudioSystem_create(); -#elif defined(USE_MINIAUDIO) - audioSystem = (AudioSystem*) MaAudioSystem_create(); -#else - audioSystem = (AudioSystem*) NoopAudioSystem_create(); -#endif - } - - // Initialize the runner - Runner* runner = Runner_create(dataWin, vm, renderer, (FileSystem*) overlayFs, audioSystem); - runner->debugMode = args.debug; - runner->osType = args.osType; - runner->setWindowTitle = setSDLWindowTitle; - runner->getWindowSize = getSDLWindowSize; - runner->setWindowSize = setSDLWindowSize; - runner->windowHasFocus = getSDLWindowFocus; - runner->nativeWindow = (void*)0xDEADBEEF; - - // Set up input recording/playback (both can be active: playback then continue recording) - if (args.playbackInputsPath != nullptr) { - globalInputRecording = InputRecording_createPlayer(args.playbackInputsPath, args.recordInputsPath); - } else if (args.recordInputsPath != nullptr) { - globalInputRecording = InputRecording_createRecorder(args.recordInputsPath); - } - if (globalInputRecording != nullptr) { - installCrashHandlers(); - } - shcopyFromTo(args.varReadsToBeTraced, runner->vmContext->varReadsToBeTraced); - shcopyFromTo(args.varWritesToBeTraced, runner->vmContext->varWritesToBeTraced); - shcopyFromTo(args.functionCallsToBeTraced, runner->vmContext->functionCallsToBeTraced); - shcopyFromTo(args.alarmsToBeTraced, runner->vmContext->alarmsToBeTraced); - shcopyFromTo(args.instanceLifecyclesToBeTraced, runner->vmContext->instanceLifecyclesToBeTraced); - shcopyFromTo(args.eventsToBeTraced, runner->vmContext->eventsToBeTraced); - shcopyFromTo(args.collisionsToBeTraced, runner->vmContext->collisionsToBeTraced); - shcopyFromTo(args.opcodesToBeTraced, runner->vmContext->opcodesToBeTraced); - shcopyFromTo(args.stackToBeTraced, runner->vmContext->stackToBeTraced); - shcopyFromTo(args.tilesToBeTraced, runner->vmContext->tilesToBeTraced); - runner->vmContext->traceBytecodeAfterFrame = args.traceBytecodeAfterFrame; - runner->vmContext->alwaysLogUnknownFunctions = args.alwaysLogUnknownFunctions; - runner->vmContext->alwaysLogStubbedFunctions = args.alwaysLogStubbedFunctions; - runner->vmContext->traceEventInherited = args.traceEventInherited; - - -#ifndef _WIN32 - struct sigaction sa = { .sa_handler = onCrashSignal }; - sigemptyset(&sa.sa_mask); - struct sigaction prev; - sigaction(SIGABRT, &sa, &prev); - PreviousSignalActionEntry p; - p.key = SIGABRT; - p.value = prev; - hmputs(previousSignalActions, p); - sigaction(SIGSEGV, &sa, &prev); - PreviousSignalActionEntry p2; - p.key = SIGSEGV; - p.value = prev; - hmputs(previousSignalActions, p2); -#endif - - // Initialize the first room and fire Game Start / Room Start events - Runner_initFirstRoom(runner); - - // Main loop - bool debugPaused = false; - bool debugShowCollisionMasks = false; - double lastFrameTime = (SDL_GetTicks()/1000.0f); - SDL_Event e; - while (!runner->shouldExit && !shouldExit) { - // Clear last frame's pressed/released state, then poll new input events - RunnerKeyboard_beginFrame(runner->keyboard); - RunnerGamepad_beginFrame(runner->gamepads); - while (SDL_PollEvent(&e)) { - switch(e.type) { - case SDL_KEYDOWN: - RunnerKeyboard_onKeyDown(runner->keyboard, SDLKeyToGml(e.key.keysym.sym)); - break; - case SDL_KEYUP: - RunnerKeyboard_onKeyUp(runner->keyboard, SDLKeyToGml(e.key.keysym.sym)); - break; - case SDL_VIDEORESIZE: - fbWidth = e.resize.w; - fbHeight = e.resize.h; - scr = SDL_SetVideoMode(fbWidth, fbHeight, 0, (useSWRend ? 0 : SDL_OPENGL) | SDL_RESIZABLE); - break; - case SDL_QUIT: - shouldExit = true; - break; - default: - break; - } - } - - // Process input recording/playback (must happen after SDL_PollEvents, before Runner_step) - InputRecording_processFrame(globalInputRecording, runner->keyboard, runner->frameCount); - - // Debug key bindings - if (runner->debugMode) { - // Pause - if (RunnerKeyboard_checkPressed(runner->keyboard, 'P')) { - debugPaused = !debugPaused; - fprintf(stderr, "Debug: %s\n", debugPaused ? "Paused" : "Resumed"); - } - - // Go to next room - if (RunnerKeyboard_checkPressed(runner->keyboard, VK_PAGEUP)) { - DataWin* dw = runner->dataWin; - if ((int32_t) dw->gen8.roomOrderCount > runner->currentRoomOrderPosition + 1) { - int32_t nextIdx = dw->gen8.roomOrder[runner->currentRoomOrderPosition + 1]; - runner->pendingRoom = nextIdx; - runner->audioSystem->vtable->stopAll(runner->audioSystem); - fprintf(stderr, "Debug: Going to next room -> %s\n", dw->room.rooms[nextIdx].name); - } - } - - // Go to previous room - if (RunnerKeyboard_checkPressed(runner->keyboard, VK_PAGEDOWN)) { - DataWin* dw = runner->dataWin; - if (runner->currentRoomOrderPosition > 0) { - int32_t prevIdx = dw->gen8.roomOrder[runner->currentRoomOrderPosition - 1]; - runner->pendingRoom = prevIdx; - runner->audioSystem->vtable->stopAll(runner->audioSystem); - fprintf(stderr, "Debug: Going to previous room -> %s\n", dw->room.rooms[prevIdx].name); - } - } - - // Dump runner state to console - if (RunnerKeyboard_checkPressed(runner->keyboard, VK_F12)) { - fprintf(stderr, "Debug: Dumping runner state at frame %d\n", runner->frameCount); - Runner_dumpState(runner); - } - - if (RunnerKeyboard_checkPressed(runner->keyboard, VK_F11)) { - fprintf(stderr, "Debug: Dumping runner state at frame %d\n", runner->frameCount); - char* json = Runner_dumpStateJson(runner); - - if (args.dumpJsonFilePattern != nullptr) { - char filename[512]; - snprintf(filename, sizeof(filename), args.dumpJsonFilePattern, runner->frameCount); - FILE* f = fopen(filename, "w"); - if (f != nullptr) { - fwrite(json, 1, strlen(json), f); - fputc('\n', f); - fclose(f); - printf("JSON dump saved: %s\n", filename); - } else { - fprintf(stderr, "Error: Could not write JSON dump to '%s'\n", filename); - } - } else { - printf("%s\n", json); - } - - free(json); - } - - // Toggle the collision mask debug overlay - if (RunnerKeyboard_checkPressed(runner->keyboard, VK_F2)) { - debugShowCollisionMasks = !debugShowCollisionMasks; - fprintf(stderr, "Debug: Collision mask overlay %s!\n", debugShowCollisionMasks ? "enabled" : "disabled"); - } - - // Reset global interact state because I HATE when I get stuck while moving through rooms - if (RunnerKeyboard_checkPressed(runner->keyboard, VK_F10)) { - int32_t interactVarId = shget(runner->vmContext->globalVarNameMap, "interact"); - - runner->vmContext->globalVars[interactVarId] = RValue_makeInt32(0); - printf("Changed global.interact [%d] value!\n", interactVarId); - } - } - - // Run the game step if the game is paused - bool shouldStep = true; - if (runner->debugMode && debugPaused) { - shouldStep = RunnerKeyboard_checkPressed(runner->keyboard, 'O'); - if (shouldStep) fprintf(stderr, "Debug: Frame advance (frame %d)\n", runner->frameCount); - } - - double frameStartTime = 0; - - if (shouldStep) { - if (args.traceFrames) { - frameStartTime = (SDL_GetTicks()/1000.0f); - fprintf(stderr, "Frame %d (Start)\n", runner->frameCount); - } - - // Run one game step (Begin Step, Keyboard, Alarms, Step, End Step, room transitions) - Runner_step(runner); - - if (args.profilerFramesBetween > 0 && runner->frameCount > 0 && runner->frameCount % args.profilerFramesBetween == 0) { - char* profilerReport = Profiler_createReport(vm->profiler, 20, args.profilerFramesBetween); - if (profilerReport != nullptr) { - fprintf(stderr, "%s\n", profilerReport); - free(profilerReport); - } - Profiler_reset(vm->profiler); - } - - // Update audio system (gain fading, cleanup ended sounds) - float dt = (float) ((SDL_GetTicks()/1000.0f) - lastFrameTime); - if (0.0f > dt) dt = 0.0f; - if (dt > 0.1f) dt = 0.1f; // cap delta to avoid huge fades on lag spikes - runner->audioSystem->vtable->update(runner->audioSystem, dt); - - // Dump full runner state if this frame was requested - if (hmget(args.dumpFrames, runner->frameCount)) { - Runner_dumpState(runner); - } - - // Dump runner state as JSON if this frame was requested - if (hmget(args.dumpJsonFrames, runner->frameCount)) { - char* json = Runner_dumpStateJson(runner); - if (args.dumpJsonFilePattern != nullptr) { - char filename[512]; - snprintf(filename, sizeof(filename), args.dumpJsonFilePattern, runner->frameCount); - FILE* f = fopen(filename, "w"); - if (f != nullptr) { - fwrite(json, 1, strlen(json), f); - fputc('\n', f); - fclose(f); - printf("JSON dump saved: %s\n", filename); - } else { - fprintf(stderr, "Error: Could not write JSON dump to '%s'\n", filename); - } - } else { - printf("%s\n", json); - } - free(json); - } - } - - // Clear the default framebuffer (window background) to black - -#if defined(ENABLE_SW_RENDERER) && defined(ENABLE_LEGACY_GL) - if(useSWRend) - SWRenderer_clearFrameBuffer(renderer, 0); - else - glClear(GL_COLOR_BUFFER_BIT); -#elif defined(ENABLE_LEGACY_GL) - glClear(GL_COLOR_BUFFER_BIT); -#else - SWRenderer_clearFrameBuffer(renderer, 0); -#endif - - // The application surface (FBO) is sized to defaultWindowWidth x defaultWindowHeight. - // It is a bit hard to understand, but here's how it works: - // The Port X/Port Y controls the position of the game viewport within the application surface. - // The Port W/Port H controls the size of the game viewport within the application surface. - // Think of it like if you had an image (or... well, a framebuffer) and you are "pasting" it over the application surface. - // And the Port W/Port H are scaled by the window size too (set by the GEN8 chunk) - float displayScaleX; - float displayScaleY; - - Runner_computeViewDisplayScale(runner, reqW, reqH, &displayScaleX, &displayScaleY); - - Runner_beginFrame(runner, reqW, reqH, fbWidth, fbHeight); - - // Clear FBO with room background color - if (runner->drawBackgroundColor) { -#if defined(ENABLE_SW_RENDERER) && defined(ENABLE_LEGACY_GL) - if(!useSWRend) { - int rInt = BGR_R(runner->backgroundColor); - int gInt = BGR_G(runner->backgroundColor); - int bInt = BGR_B(runner->backgroundColor); - glClearColor(rInt / 255.0f, gInt / 255.0f, bInt / 255.0f, 1.0f); - } else - SWRenderer_clearFrameBuffer(renderer, runner->backgroundColor); - } else { - if(!useSWRend) - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - else - SWRenderer_clearFrameBuffer(renderer, 0); -#elif defined(ENABLE_LEGACY_GL) - int rInt = BGR_R(runner->backgroundColor); - int gInt = BGR_G(runner->backgroundColor); - int bInt = BGR_B(runner->backgroundColor); - glClearColor(rInt / 255.0f, gInt / 255.0f, bInt / 255.0f, 1.0f); - } else { - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); -#else - SWRenderer_clearFrameBuffer(renderer, runner->backgroundColor); - } else { - SWRenderer_clearFrameBuffer(renderer, 0); -#endif - } -#ifdef ENABLE_LEGACY_GL - if(!useSWRend) - glClear(GL_COLOR_BUFFER_BIT); -#endif - - Runner_drawViews(runner, reqW, reqH, displayScaleX, displayScaleY, debugShowCollisionMasks); - renderer->vtable->endFrameInit(renderer); - Runner_drawPost(runner, fbWidth, fbHeight); - renderer->vtable->endFrameEnd(renderer); - Runner_drawGUI(runner, fbWidth, fbHeight, reqW, reqH); - - if (shouldStep && args.traceFrames) { - double frameElapsedMs = ((SDL_GetTicks()/1000.0f) - frameStartTime) * 1000.0; - fprintf(stderr, "Frame %d (End, %.2f ms)\n", runner->frameCount, frameElapsedMs); - } - - // Only swap when there isn't a room change to match the original runner. - if (runner->pendingRoom == -1) { - if(!args.headless) { - if(!useSWRend) - SDL_GL_SwapBuffers(); - else { - SDL_BlitSurface(nextFb, NULL, scr, NULL); - SDL_Flip(scr); - } - } - } - Runner_handlePendingRoomChange(runner); - - // Limit frame rate to room speed (skip in headless mode for max speed!!) - if (!args.headless && runner->currentRoom->speed > 0) { - static bool fastForwardActive = false; - static bool fastForwardTabPrev = false; - bool fastForwardTabNow = RunnerKeyboard_checkPressed(runner->keyboard, '\t'); - if (args.fastForwardSpeed > 0.0 && fastForwardTabNow && !fastForwardTabPrev) { - fastForwardActive = !fastForwardActive; - lastFrameTime = (SDL_GetTicks()/1000.0f); - } - fastForwardTabPrev = fastForwardTabNow; - double effectiveSpeed = (args.fastForwardSpeed > 0.0 && fastForwardActive) ? args.fastForwardSpeed : args.speedMultiplier; - double targetFrameTime = 1.0 / (runner->currentRoom->speed * effectiveSpeed); - double nextFrameTime = lastFrameTime + targetFrameTime; - // Sleep for most of the remaining time, then spin-wait for precision - double remaining = nextFrameTime - (SDL_GetTicks()/1000.0f); - if (remaining > 0.002) { - #ifdef _WIN32 - Sleep((DWORD) ((remaining - 0.001) * 1000)); - #else - struct timespec ts = { - .tv_sec = 0, - .tv_nsec = (long) ((remaining - 0.001) * 1e9) - }; - nanosleep(&ts, nullptr); - #endif - } - while ((SDL_GetTicks()/1000.0f) < nextFrameTime) { - // Spin-wait for the remaining sub-millisecond - } - lastFrameTime = nextFrameTime; - } else { - lastFrameTime = (SDL_GetTicks()/1000.0f); - } - } - - saveInputRecording(); - - // Cleanup - runner->audioSystem->vtable->destroy(runner->audioSystem); - runner->audioSystem = nullptr; - renderer->vtable->destroy(renderer); - - - Runner_free(runner); - OverlayFileSystem_destroy(overlayFs); - VM_free(vm); - DataWin_free(dataWin); - - freeCommandLineArgs(&args); - - printf("Bye! :3\n"); - return 0; -} From 29f1b9e01335ef7ef634b37bd74f15e20bfe2796 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sat, 30 May 2026 05:37:29 -0400 Subject: [PATCH 043/130] remove newline to reduce diff --- src/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.h b/src/utils.h index 42396c43a..ef12f208f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -140,4 +140,4 @@ shput((dst), (src)[i].key, (src)[i].value); \ typedef struct { char* key; bool value; -} StringBooleanEntry; +} StringBooleanEntry; \ No newline at end of file From 41ca2400a5226cbed1a1ef9a3b79b80db17914cb Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Mon, 8 Jun 2026 22:22:42 -0400 Subject: [PATCH 044/130] gcc 2.95 --- src/sw/sw_renderer.c | 96 ++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index f39b31e41..f1ce6edff 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -2097,55 +2097,7 @@ static int32_t SWRenderer_ensureApplicationSurface(Renderer* renderer, int32_t w return -1; } -static RendererVtable swrVtable = -{ - .init = SWRenderer_init, - .destroy = SWRenderer_destroy, - .beginFrame = SWRenderer_beginFrame, - .endFrameInit = SWRenderer_endFrameInit, - .endFrameEnd = SWRenderer_endFrameEnd, - .beginView = SWRenderer_beginView, - .endView = SWRenderer_endView, - .beginGUI = SWRenderer_beginGUI, - .endGUI = SWRenderer_endGUI, - .drawSprite = SWRenderer_drawSprite, - .drawSpritePart = SWRenderer_drawSpritePart, - .drawSpritePos = SWRenderer_drawSpritePos, - .drawRectangle = SWRenderer_drawRectangle, - .drawRectangleColor = SWRenderer_drawRectangleColor, - .drawLine = SWRenderer_drawLine, - .drawTriangle = SWRenderer_drawTriangle, - .drawLineColor = SWRenderer_drawLineColor, - .drawText = SWRenderer_drawText, - .drawTextColor = SWRenderer_drawTextColor, - .flush = SWRenderer_flush, - .clearScreen = SWRenderer_clearScreen, - .createSpriteFromSurface = SWRenderer_createSpriteFromSurface, - .deleteSprite = SWRenderer_deleteSprite, - .gpuSetBlendMode = SWRenderer_gpuSetBlendMode, - .gpuSetBlendModeExt = SWRenderer_gpuSetBlendModeExt, - .gpuSetBlendEnable = SWRenderer_gpuSetBlendEnable, - .gpuSetAlphaTestEnable = SWRenderer_gpuSetAlphaTestEnable, - .gpuSetAlphaTestRef = SWRenderer_gpuSetAlphaTestRef, - .gpuSetColorWriteEnable = SWRenderer_gpuSetColorWriteEnable, - .gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable, - .gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable, - .gpuSetFog = SWRenderer_gpuSetFog, - .drawTile = NULL, - .drawTiled = SWRenderer_drawTiled, - .createSurface = SWRenderer_createSurface, - .surfaceExists = SWRenderer_surfaceExists, - .setRenderTarget = SWRenderer_setRenderTarget, - .ensureApplicationSurface = SWRenderer_ensureApplicationSurface, - .getSurfaceWidth = SWRenderer_getSurfaceWidth, - .getSurfaceHeight = SWRenderer_getSurfaceHeight, - .drawSurface = SWRenderer_drawSurface, - .surfaceResize = SWRenderer_surfaceResize, - .surfaceFree = SWRenderer_surfaceFree, - .surfaceCopy = SWRenderer_surfaceCopy, - .surfaceGetPixels = SWRenderer_surfaceGetPixels, - .drawTiledPart = SWRenderer_drawTiledPart, -}; +static RendererVtable swrVtable; void SWRenderer_clearFrameBuffer(Renderer* renderer, uint32_t color) { @@ -2165,6 +2117,52 @@ Renderer* SWRenderer_create(void) { SWRenderer* swr = safeCalloc(1, sizeof(SWRenderer)); swr->base.vtable = &swrVtable; + swrVtable.init = SWRenderer_init; + swrVtable.destroy = SWRenderer_destroy; + swrVtable.beginFrame = SWRenderer_beginFrame; + swrVtable.endFrameInit = SWRenderer_endFrameInit; + swrVtable.endFrameEnd = SWRenderer_endFrameEnd; + swrVtable.beginView = SWRenderer_beginView; + swrVtable.endView = SWRenderer_endView; + swrVtable.beginGUI = SWRenderer_beginGUI; + swrVtable.endGUI = SWRenderer_endGUI; + swrVtable.drawSprite = SWRenderer_drawSprite; + swrVtable.drawSpritePart = SWRenderer_drawSpritePart; + swrVtable.drawSpritePos = SWRenderer_drawSpritePos; + swrVtable.drawRectangle = SWRenderer_drawRectangle; + swrVtable.drawRectangleColor = SWRenderer_drawRectangleColor; + swrVtable.drawLine = SWRenderer_drawLine; + swrVtable.drawTriangle = SWRenderer_drawTriangle; + swrVtable.drawLineColor = SWRenderer_drawLineColor; + swrVtable.drawText = SWRenderer_drawText; + swrVtable.drawTextColor = SWRenderer_drawTextColor; + swrVtable.flush = SWRenderer_flush; + swrVtable.clearScreen = SWRenderer_clearScreen; + swrVtable.createSpriteFromSurface = SWRenderer_createSpriteFromSurface; + swrVtable.deleteSprite = SWRenderer_deleteSprite; + swrVtable.gpuSetBlendMode = SWRenderer_gpuSetBlendMode; + swrVtable.gpuSetBlendModeExt = SWRenderer_gpuSetBlendModeExt; + swrVtable.gpuSetBlendEnable = SWRenderer_gpuSetBlendEnable; + swrVtable.gpuSetAlphaTestEnable = SWRenderer_gpuSetAlphaTestEnable; + swrVtable.gpuSetAlphaTestRef = SWRenderer_gpuSetAlphaTestRef; + swrVtable.gpuSetColorWriteEnable = SWRenderer_gpuSetColorWriteEnable; + swrVtable.gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable; + swrVtable.gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable; + swrVtable.gpuSetFog = SWRenderer_gpuSetFog; + swrVtable.drawTile = NULL; + swrVtable.drawTiled = SWRenderer_drawTiled; + swrVtable.createSurface = SWRenderer_createSurface; + swrVtable.surfaceExists = SWRenderer_surfaceExists; + swrVtable.setRenderTarget = SWRenderer_setRenderTarget; + swrVtable.ensureApplicationSurface = SWRenderer_ensureApplicationSurface; + swrVtable.getSurfaceWidth = SWRenderer_getSurfaceWidth; + swrVtable.getSurfaceHeight = SWRenderer_getSurfaceHeight; + swrVtable.drawSurface = SWRenderer_drawSurface; + swrVtable.surfaceResize = SWRenderer_surfaceResize; + swrVtable.surfaceFree = SWRenderer_surfaceFree; + swrVtable.surfaceCopy = SWRenderer_surfaceCopy; + swrVtable.surfaceGetPixels = SWRenderer_surfaceGetPixels; + swrVtable.drawTiledPart = SWRenderer_drawTiledPart; swr->base.drawColor = 0xFFFFFF; swr->base.drawAlpha = 1.0f; swr->base.drawFont = -1; From 7ebcc2219e2814f6c2d9bcfbf37029112cecfc63 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Wed, 10 Jun 2026 04:05:41 -0400 Subject: [PATCH 045/130] use the float version --- src/sw/sw_renderer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index f1ce6edff..6d9897830 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1643,8 +1643,8 @@ static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, flo // TODO: at 640x480, for some reason, without this fixup the // letters in the "Name the fallen human." screen don't shake - dx = round(dx * 2) / 2; - dy = round(dy * 2) / 2; + dx = roundf(dx * 2) / 2; + dy = roundf(dy * 2) / 2; SWTexture* texture = swr->textures[pageId]; From f26e0b188637ba3bcaf8dcbbfa3ada9dd13637e3 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 16 Jun 2026 16:18:43 +0300 Subject: [PATCH 046/130] * Expand texture LRU length. We really don't need it to be only 16 entries long. This fixes a performance regression in Undertale in the scene before Asriel appears. --- src/sw/sw_renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index f39b31e41..c5097eef5 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -16,7 +16,7 @@ #define M_PI 3.1415926535897932384626 #endif -#define TEXTURE_LRU_LENGTH 16 +#define TEXTURE_LRU_LENGTH 64 #define SURFACE_MAX_COUNT 64 From 04c5ebe567d646000be63b08b721eb96fb22a04b Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 16 Jun 2026 16:46:07 +0300 Subject: [PATCH 047/130] Fix warnings (marked as errors) in the swrenderer code --- src/sw/sw_renderer.c | 74 ++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 891041550..51e26bd7a 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -487,13 +487,23 @@ static void SWRenderer_endView(Renderer* renderer) } static void SWRenderer_beginGUI(Renderer* renderer, int32_t guiW, int32_t guiH, - int32_t portX, int32_t portY, int32_t portW, int32_t portH) + int32_t portX, int32_t portY, int32_t portW, int32_t portH, int32_t targetSurfaceId) { (void)renderer; (void)guiW; (void)guiH; (void)portX; (void)portY; (void)portW; (void)portH; + (void)targetSurfaceId; UNIMP2(); } +static void SWRenderer_setGuiProjection(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t portW, int32_t portH, bool renderingToUserSurface) +{ + (void) renderer; + (void) guiW; (void) guiH; + (void) portW; (void) portH; + (void) renderingToUserSurface; + UNIMP(); +} + static void SWRenderer_endGUI(Renderer* renderer) { (void)renderer; @@ -740,7 +750,7 @@ static void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color2, color4, alpha); } -static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, int width, uintpixel_t color1, uintpixel_t color2, int alpha) +static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, MAYBE_UNUSED int width, uintpixel_t color1, uintpixel_t color2, int alpha) { if (x1 == x2) { @@ -1343,8 +1353,12 @@ static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2 swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha); } -static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uint32_t color, int alpha) +static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uint32_t color1, uint32_t color2, uint32_t color3, int alpha) { + // TODO: update this + (void) color2; + (void) color3; + // Figure out the maximum Y extent of the triangle. // (Note that we know yup is the minimum.) int xmid, ymid, xmid2 = xup, xmax, ymax; @@ -1399,14 +1413,15 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft uintpixel_t* line = &swr->fb[y * swr->width]; for (int x = x1; x <= x2; x++) { - alphaBlend(&line[x], color, alpha); + alphaBlend(&line[x], color1, alpha); } } } -static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color, float alpha) +static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color1, uint32_t color2, uint32_t color3, float alpha) { float xup, yup, xleft, yleft, xright, yright; + uint32_t colorup, colorleft, colorright; SWRenderer* swr = (SWRenderer*) renderer; swrTransformPosIfNeeded(swr, &x1, &y1); @@ -1414,18 +1429,18 @@ static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, fl swrTransformPosIfNeeded(swr, &x3, &y3); //which vertex is higher? - xup = x1, yup = y1; - xleft = x2, yleft = y2; - xright = x3, yright = y3; + xup = x1, yup = y1; colorup = color1; + xleft = x2, yleft = y2; colorleft = color2; + xright = x3, yright = y3; colorright = color3; if (yup > y2) { - xup = x2, yup = y2; - xleft = x1, yleft = y1; + xup = x2, yup = y2, colorup = color2; + xleft = x1, yleft = y1, colorleft = color1; //xright = x3, yright = y3; } if (yup > y3) { - xup = x3, yup = y3; - xleft = x1, yleft = y1; - xright = x2, yright = y2; + xup = x3, yup = y3, colorup = color3; + xleft = x1, yleft = y1, colorleft = color1; + xright = x2, yright = y2, colorright = color2; } if (xleft > xright) { @@ -1435,6 +1450,9 @@ static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, fl tmp = yleft; yleft = yright; yright = tmp; + uint32_t tmp2 = colorleft; + colorleft = colorright; + colorright = tmp2; } swrDrawTriangleInternal( @@ -1442,29 +1460,35 @@ static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, fl swrFloor(xup), swrFloor(yup), swrFloor(xleft), swrCeiling(yleft), swrFloor(xright), swrCeiling(yright), - swrConvertPixel(color), + swrConvertPixel(colorup), + swrConvertPixel(colorleft), + swrConvertPixel(colorright), swrIntAlpha(alpha) ); } -static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, - float x3, float y3, bool outline) +static void SWRenderer_drawTriangle(Renderer* renderer, + float x1, float y1, float x2, float y2, float x3, float y3, + uint32_t color1, uint32_t color2, uint32_t color3, + float alpha, bool outline) { if (outline) { - uintpixel_t drawColorCvt = swrConvertPixel(renderer->drawColor); - swrDrawLine(renderer, x1, y1, x2, y2, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); - swrDrawLine(renderer, x1, y1, x3, y3, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); - swrDrawLine(renderer, x2, y2, x3, y3, 1, drawColorCvt, drawColorCvt, renderer->drawAlpha); + uintpixel_t color1cvt = swrConvertPixel(color1); + uintpixel_t color2cvt = swrConvertPixel(color2); + uintpixel_t color3cvt = swrConvertPixel(color3); + swrDrawLine(renderer, x1, y1, x2, y2, 1, color1cvt, color2cvt, renderer->drawAlpha); + swrDrawLine(renderer, x1, y1, x3, y3, 1, color1cvt, color3cvt, renderer->drawAlpha); + swrDrawLine(renderer, x2, y2, x3, y3, 1, color3cvt, color3cvt, renderer->drawAlpha); } else { - swrDrawTriangle(renderer, x1, y1, x2, y2, x3, y3, renderer->drawColor, renderer->drawAlpha); + swrDrawTriangle(renderer, x1, y1, x2, y2, x3, y3, color1, color2, color3, alpha); } } static void SWRenderer_drawLineColor(Renderer* renderer, float x1, float y1, float x2, float y2, - float width, uint32_t color1, uint32_t color2, float alpha) + float width, uint32_t color1, uint32_t color2, float alpha) { swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color1), swrConvertPixel(color2), alpha); } @@ -1692,7 +1716,7 @@ static void SWRenderer_drawText(Renderer* renderer, const char* text, float x, f static void SWRenderer_drawTextColor(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, - int32_t c1, int32_t c2, int32_t c3, int32_t c4, float alpha, + int32_t c1, int32_t c2, int32_t c3, int32_t c4, MAYBE_UNUSED float alpha, float lineSeparation) { SWRenderer* swr = (SWRenderer*) renderer; @@ -1860,10 +1884,11 @@ static bool SWRenderer_surfaceExists(Renderer* renderer, int32_t surfaceID) return false; } -static bool SWRenderer_setRenderTarget(Renderer* renderer, int32_t surfaceID) +static bool SWRenderer_setRenderTarget(Renderer* renderer, int32_t surfaceID, bool implicitApplicationSurface) { UNIMP(); (void)renderer; (void)surfaceID; + (void)implicitApplicationSurface; return false; } @@ -2125,6 +2150,7 @@ Renderer* SWRenderer_create(void) swrVtable.beginView = SWRenderer_beginView; swrVtable.endView = SWRenderer_endView; swrVtable.beginGUI = SWRenderer_beginGUI; + swrVtable.setGuiProjection = SWRenderer_setGuiProjection; swrVtable.endGUI = SWRenderer_endGUI; swrVtable.drawSprite = SWRenderer_drawSprite; swrVtable.drawSpritePart = SWRenderer_drawSpritePart; From ecdab993e67306b20561a7b969da087704dc58b1 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 16 Jun 2026 17:00:07 +0300 Subject: [PATCH 048/130] Implement no-op "requireNonnull" functions --- src/sw/sw_renderer.c | 125 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 51e26bd7a..ef1d1a039 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -2138,6 +2138,117 @@ void SWRenderer_clearFrameBuffer(Renderer* renderer, uint32_t color) } } +static uint32_t SWRenderer_spriteGetTexture(Renderer* renderer, int32_t tpagIndex) +{ + (void) renderer; + + return (uint32_t) tpagIndex + 1; +} + +static uint32_t SWRenderer_surfaceGetTexture(Renderer* renderer, int32_t surfaceID) +{ + (void) renderer; + (void) surfaceID; + + return (uint32_t) -1; +} + +static float SWRenderer_textureGetTexelWidth(Renderer* renderer, uint32_t texID) +{ + (void) renderer; + (void) texID; + + return 1.0f; +} + +static float SWRenderer_textureGetTexelHeight(Renderer* renderer, uint32_t texID) +{ + (void) renderer; + (void) texID; + + return 1.0f; +} + +static bool SWRenderer_textureGetUVs(Renderer* renderer, uint32_t texID, float* outUVs) +{ + (void) renderer; + (void) texID; + (void) outUVs; + + return false; +} + +static void SWRenderer_textureSetStage(Renderer* renderer, int32_t slot, uint32_t texID) +{ + (void) renderer; + (void) slot; + (void) texID; +} + +static bool SWRenderer_shaderIsCompiled(Renderer* renderer, int32_t shader) +{ + (void) renderer; + (void) shader; + + return false; +} + +static bool SWRenderer_shadersSupported(void) +{ + return false; +} + +static void SWRenderer_gpuSetShader(Renderer* renderer, int32_t shaderIndex) +{ + (void) renderer; + (void) shaderIndex; +} + +static void SWRenderer_gpuResetShader(Renderer* renderer) +{ + (void) renderer; +} + +static int32_t SWRenderer_shaderGetUniform(Renderer* renderer, int32_t shaderIndex, char* uniform) +{ + (void) renderer; + (void) shaderIndex; + (void) uniform; + + return 0; +} + +static int32_t SWRenderer_shaderGetSamplerIndex(Renderer* renderer, int32_t shaderIndex, char* uniform) +{ + (void) renderer; + (void) shaderIndex; + (void) uniform; + + return 0; +} + +static void SWRenderer_shaderSetUniformF(Renderer* renderer, int32_t handle, int32_t count, float value1, float value2, float value3, float value4) +{ + (void) renderer; + (void) handle; + (void) count; + (void) value1; + (void) value2; + (void) value3; + (void) value4; +} + +static void SWRenderer_shaderSetUniformI(Renderer* renderer, int32_t handle, int32_t count, int32_t value1, int32_t value2, int32_t value3, int32_t value4) +{ + (void) renderer; + (void) handle; + (void) count; + (void) value1; + (void) value2; + (void) value3; + (void) value4; +} + Renderer* SWRenderer_create(void) { SWRenderer* swr = safeCalloc(1, sizeof(SWRenderer)); @@ -2189,6 +2300,20 @@ Renderer* SWRenderer_create(void) swrVtable.surfaceCopy = SWRenderer_surfaceCopy; swrVtable.surfaceGetPixels = SWRenderer_surfaceGetPixels; swrVtable.drawTiledPart = SWRenderer_drawTiledPart; + swrVtable.spriteGetTexture = SWRenderer_spriteGetTexture; + swrVtable.surfaceGetTexture = SWRenderer_surfaceGetTexture; + swrVtable.textureGetTexelWidth = SWRenderer_textureGetTexelWidth; + swrVtable.textureGetTexelHeight = SWRenderer_textureGetTexelHeight; + swrVtable.textureGetUVs = SWRenderer_textureGetUVs; + swrVtable.textureSetStage = SWRenderer_textureSetStage; + swrVtable.gpuSetShader = SWRenderer_gpuSetShader; + swrVtable.gpuResetShader = SWRenderer_gpuResetShader; + swrVtable.shaderGetUniform = SWRenderer_shaderGetUniform; + swrVtable.shaderGetSamplerIndex = SWRenderer_shaderGetSamplerIndex; + swrVtable.shaderSetUniformF = SWRenderer_shaderSetUniformF; + swrVtable.shaderSetUniformI = SWRenderer_shaderSetUniformI; + swrVtable.shaderIsCompiled = SWRenderer_shaderIsCompiled; + swrVtable.shadersSupported = SWRenderer_shadersSupported; swr->base.drawColor = 0xFFFFFF; swr->base.drawAlpha = 1.0f; swr->base.drawFont = -1; From c6bcacb65159e7cd012e7a570b2da063b375cd0c Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 16 Jun 2026 17:17:35 +0300 Subject: [PATCH 049/130] * Fix a bug where text rotation positions were incorrectly computed. --- src/sw/sw_renderer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index ef1d1a039..3bb3568a1 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1582,7 +1582,7 @@ static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, flo { Renderer* renderer = &swr->base; DataWin* dwin = renderer->dataWin; - + int32_t fontIndex = renderer->drawFont; if (0 > fontIndex || dwin->font.count <= (uint32_t) fontIndex) return; @@ -1674,8 +1674,12 @@ static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, flo if (UNLIKELY(mustRotate)) { + dx -= x; + dy -= y; float ndx = cosA * dx - sinA * dy; float ndy = sinA * dx + cosA * dy; + ndx += x; + ndy += y; swrDrawSpriteRotated(renderer, ndx, ndy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, 0.0f, 0.0f); } else From de45fdfb183228d12fcefc375dbcbfbaf4744f05 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Fri, 3 Jul 2026 19:46:37 +0300 Subject: [PATCH 050/130] Update sw renderer to new definitions --- src/sw/sw_renderer.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 3bb3568a1..36ed96a4f 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1733,10 +1733,10 @@ static void SWRenderer_drawTextColor(Renderer* renderer, const char* text, float swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, c1, renderer->drawAlpha, lineSeparation); } -static void SWRenderer_drawTiled(Renderer* renderer, int32_t tpagIndex, - float originX, float originY, float x, float y, - float xscale, float yscale, bool tileX, bool tileY, - float roomW, float roomH, uint32_t color, float alpha) +static void SWRenderer_drawSpriteTiled(Renderer* renderer, int32_t tpagIndex, + float originX, float originY, float x, float y, + float xscale, float yscale, bool tileX, bool tileY, + float roomW, float roomH, uint32_t color, float alpha) { SWRenderer* swr = (SWRenderer*) renderer; DataWin* dwin = renderer->dataWin; @@ -1803,6 +1803,18 @@ static void SWRenderer_drawTiled(Renderer* renderer, int32_t tpagIndex, } } +static void SWRenderer_drawSurfaceTiled(Renderer* renderer, int32_t surfaceID, float x, float y, float xscale, float yscale, float roomW, float roomH, uint32_t color, float alpha) +{ + (void) renderer; + (void) surfaceID; + (void) x; (void) y; + (void) xscale; (void) yscale; + (void) roomW; (void) roomH; + (void) color; (void) alpha; + + UNIMP2(); +} + static void SWRenderer_flush(Renderer* renderer) { (void)renderer; @@ -1821,10 +1833,10 @@ static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) (void)renderer; (void)mode; } -static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, int32_t dfactor) +static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, int32_t dfactor, int32_t sfactor_alpha, int32_t dfactor_alpha) { UNIMP(); - (void)renderer; (void)sfactor; (void)dfactor; + (void)renderer; (void)sfactor; (void)dfactor; (void)sfactor_alpha; (void)dfactor_alpha; } static void SWRenderer_gpuSetBlendEnable(Renderer* renderer, bool enable) @@ -2290,8 +2302,8 @@ Renderer* SWRenderer_create(void) swrVtable.gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable; swrVtable.gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable; swrVtable.gpuSetFog = SWRenderer_gpuSetFog; - swrVtable.drawTile = NULL; - swrVtable.drawTiled = SWRenderer_drawTiled; + swrVtable.drawSpriteTiled = SWRenderer_drawSpriteTiled; + swrVtable.drawSurfaceTiled = SWRenderer_drawSurfaceTiled; swrVtable.createSurface = SWRenderer_createSurface; swrVtable.surfaceExists = SWRenderer_surfaceExists; swrVtable.setRenderTarget = SWRenderer_setRenderTarget; @@ -2318,6 +2330,9 @@ Renderer* SWRenderer_create(void) swrVtable.shaderSetUniformI = SWRenderer_shaderSetUniformI; swrVtable.shaderIsCompiled = SWRenderer_shaderIsCompiled; swrVtable.shadersSupported = SWRenderer_shadersSupported; + + swrVtable.drawTile = NULL; + swr->base.drawColor = 0xFFFFFF; swr->base.drawAlpha = 1.0f; swr->base.drawFont = -1; From 458865649f0c4bd6cd84f12b563e2988c8ecfba7 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Fri, 3 Jul 2026 20:05:05 +0300 Subject: [PATCH 051/130] Fix a bug where lines weren't clipped properly. --- src/sw/sw_renderer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 36ed96a4f..3fae545a6 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -612,7 +612,7 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe if (dy < swr->portY) return; if (dy >= swr->maxY) return; - if (dx < swr->portX) { dw += dx; dx = swr->portX; } + if (dx < swr->portX) { dw += dx - swr->portX; dx = swr->portX; } if (dx + dw >= swr->maxX) dw = swr->maxX - dx; if (dw <= 0) return; @@ -675,7 +675,7 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe if (dx < swr->portX) return; if (dx >= swr->maxX) return; - if (dy < swr->portY) { dh += dy; dy = swr->portY; } + if (dy < swr->portY) { dh += dy - swr->portY; dy = swr->portY; } if (dy + dh >= swr->maxY) dh = swr->maxY - dy; if (dh <= 0) return; From 9abdd1a1cd3a9d6cb880216187964cfc626ecb24 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Fri, 3 Jul 2026 20:21:58 +0300 Subject: [PATCH 052/130] Fix build errors (-_^) --- src/desktop/backends/sdl1.c | 4 ++++ src/sw/sw_renderer.c | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/desktop/backends/sdl1.c b/src/desktop/backends/sdl1.c index 81c237dc4..6c1270832 100644 --- a/src/desktop/backends/sdl1.c +++ b/src/desktop/backends/sdl1.c @@ -382,6 +382,10 @@ static int32_t SDLKeyToGml(int sdlkey) { case SDLK_DOWN: return VK_DOWN; case SDLK_LEFT: return VK_LEFT; case SDLK_RIGHT: return VK_RIGHT; + case SDLK_KP2: case SDLK_KP5: return VK_DOWN; + case SDLK_KP4: return VK_LEFT; + case SDLK_KP6: return VK_RIGHT; + case SDLK_KP8: return VK_UP; case SDLK_F1: return VK_F1; case SDLK_F2: return VK_F2; case SDLK_F3: return VK_F3; diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 3fae545a6..181b65867 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -224,8 +224,8 @@ FORCE_INLINE int swrCeiling(float x) static SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height) { - SWTexture* txt = safeMalloc(sizeof(SWTexture)); - txt->buffer = safeMalloc(width * height * sizeof(uintpixel_t)); + SWTexture* txt = (SWTexture*) safeMalloc(sizeof(SWTexture)); + txt->buffer = (uintpixel_t*) safeMalloc(width * height * sizeof(uintpixel_t)); const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; @@ -357,17 +357,17 @@ static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) swr->textureCount = dataWin->txtr.count; swr->surfaceCount = SURFACE_MAX_COUNT; swr->totalTextureCount = swr->textureCount + swr->surfaceCount; - swr->textures = safeCalloc(swr->totalTextureCount, sizeof(SWTexture*)); + swr->textures = (SWTexture**) safeCalloc(swr->totalTextureCount, sizeof(SWTexture*)); //allocate texture LRU cache to allow for dynamic unloading of textures - swr->textureIndexLRU = safeCalloc(TEXTURE_LRU_LENGTH, sizeof(uint32_t)); + swr->textureIndexLRU = (uint32_t*) safeCalloc(TEXTURE_LRU_LENGTH, sizeof(uint32_t)); swr->textureIndexLRUHead = 0; swr->textureIndexLRUTail = 0; //HACK: this isn't good, really. This should seriously be refactored. //expand datawin's tpag items list to include our surface count. swr->originalTPagCount = dataWin->tpag.count; - dataWin->tpag.items = safeRealloc(dataWin->tpag.items, sizeof(TexturePageItem) * (dataWin->tpag.count + swr->surfaceCount)); + dataWin->tpag.items = (TexturePageItem*) safeRealloc(dataWin->tpag.items, sizeof(TexturePageItem) * (dataWin->tpag.count + swr->surfaceCount)); dataWin->tpag.count += swr->surfaceCount; swr->originalSpriteCount = dataWin->sprt.count; @@ -400,7 +400,7 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam { //allocate frame buffer free(swr->fb); - swr->fb = safeMalloc(windowW * windowH * sizeof(uintpixel_t)); + swr->fb = (uintpixel_t*) safeMalloc(windowW * windowH * sizeof(uintpixel_t)); swr->fbPitch = windowW; swr->width = windowW; swr->height = windowH; @@ -1589,6 +1589,8 @@ static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, flo Font* font = &dwin->font.fonts[fontIndex]; SwrFontState fontState; + memset(&fontState, 0, sizeof fontState); // silence warning treated as error + if (!swrResolveFontState(swr, dwin, font, &fontState)) return; // TODO: do we need to mirror the way the text scrolls too?! @@ -2067,7 +2069,7 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su sprite->originX = xorig; sprite->originY = yorig; sprite->textureCount = 1; - sprite->tpagIndices = safeMalloc(sizeof(int32_t)); + sprite->tpagIndices = (int32_t*) safeMalloc(sizeof(int32_t)); sprite->tpagIndices[0] = (int32_t) tpagIndex; sprite->maskCount = 0; sprite->masks = nullptr; @@ -2267,7 +2269,7 @@ static void SWRenderer_shaderSetUniformI(Renderer* renderer, int32_t handle, int Renderer* SWRenderer_create(void) { - SWRenderer* swr = safeCalloc(1, sizeof(SWRenderer)); + SWRenderer* swr = (SWRenderer*) safeCalloc(1, sizeof(SWRenderer)); swr->base.vtable = &swrVtable; swrVtable.init = SWRenderer_init; swrVtable.destroy = SWRenderer_destroy; From ed8823afa3da7703af227e902553d037433dbe6a Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Fri, 3 Jul 2026 20:23:14 +0300 Subject: [PATCH 053/130] this wasn't meant to go into the PR ('_')" --- src/desktop/backends/sdl1.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/desktop/backends/sdl1.c b/src/desktop/backends/sdl1.c index 6c1270832..81c237dc4 100644 --- a/src/desktop/backends/sdl1.c +++ b/src/desktop/backends/sdl1.c @@ -382,10 +382,6 @@ static int32_t SDLKeyToGml(int sdlkey) { case SDLK_DOWN: return VK_DOWN; case SDLK_LEFT: return VK_LEFT; case SDLK_RIGHT: return VK_RIGHT; - case SDLK_KP2: case SDLK_KP5: return VK_DOWN; - case SDLK_KP4: return VK_LEFT; - case SDLK_KP6: return VK_RIGHT; - case SDLK_KP8: return VK_UP; case SDLK_F1: return VK_F1; case SDLK_F2: return VK_F2; case SDLK_F3: return VK_F3; From b39a32061b5a00567f9ee3dcbe89ecdac3b5186b Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Fri, 3 Jul 2026 20:30:08 +0300 Subject: [PATCH 054/130] Update triangle raster functions to "fix" some overlapping geometry. Namely, triangles rendered before Undyne's fight were overlapping causing weird double-opacity lines to appear --- src/sw/sw_renderer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 181b65867..4490355a3 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1374,7 +1374,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft xmid2 = xup + (xleft - xup) * (ymid - yup) / (yleft - yup); } - for (int y = yup; y <= ymax; y++) + for (int y = yup; y < ymax; y++) { if (y < 0) continue; if (y >= swr->height) break; @@ -1412,7 +1412,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft if (x1 > x2) continue; uintpixel_t* line = &swr->fb[y * swr->width]; - for (int x = x1; x <= x2; x++) { + for (int x = x1; x < x2; x++) { alphaBlend(&line[x], color1, alpha); } } From 04bdc1d18e6fb8b855317f2dc6dfaa8c19767d44 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Thu, 9 Jul 2026 16:58:25 +0300 Subject: [PATCH 055/130] * Update the software renderer to use lazy textures correctly. --- src/sw/sw_renderer.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 4490355a3..43dc93859 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -300,11 +300,22 @@ static bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) do { - pixels = ImageDecoder_decodeToRgba(txtr->blobData, (size_t) txtr->blobSize, gm2022_5, &w, &h); - if (pixels) - break; - - fprintf(stderr, "swr: Failed to decode TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); + if (!txtr->blobData) { + DataWin_loadTxtrIfNeeded(dw, pageId); + } + + if (txtr->blobData) { + pixels = ImageDecoder_decodeToRgba(txtr->blobData, (size_t) txtr->blobSize, gm2022_5, &w, &h); + if (pixels) { + free(txtr->blobData); + txtr->blobData = NULL; + break; + } + + fprintf(stderr, "swr: Failed to decode TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); + } else { + fprintf(stderr, "swr: Failed to load TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); + } int tail = swrTailTextureIndexLRU(swr, true); if (tail == -1) { From 1a717bcf10cddac405f1c7bee6d176d8334cd158 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Fri, 10 Jul 2026 19:55:01 +0300 Subject: [PATCH 056/130] Add initial surface support. This is largely untested, though, so it might have bugs. --- src/sw/sw_renderer.c | 301 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 252 insertions(+), 49 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 43dc93859..6feb507ad 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -38,13 +38,17 @@ typedef struct uintpixel_t* fb; uint16_t fbPitch; // in sizeof(uintpixel_t) units, NOT in bytes! + bool drawingToSurface; uintpixel_t* mainFb; uint16_t mainWidth; uint16_t mainHeight; uint16_t mainPitch; - bool drawingToSurface; + int lastViewX, lastViewY, lastViewW, lastViewH; + int lastPortX, lastPortY, lastPortW, lastPortH; + int lastGameW, lastGameH, lastMaxX, lastMaxY; SWTexture** textures; + SWTexture** surfaces; uint32_t* textureIndexLRU; uint32_t textureIndexLRUHead; uint32_t textureIndexLRUTail; @@ -369,6 +373,7 @@ static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) swr->surfaceCount = SURFACE_MAX_COUNT; swr->totalTextureCount = swr->textureCount + swr->surfaceCount; swr->textures = (SWTexture**) safeCalloc(swr->totalTextureCount, sizeof(SWTexture*)); + swr->surfaces = (SWTexture**) safeCalloc(swr->surfaceCount, sizeof(SWTexture*)); //allocate texture LRU cache to allow for dynamic unloading of textures swr->textureIndexLRU = (uint32_t*) safeCalloc(TEXTURE_LRU_LENGTH, sizeof(uint32_t)); @@ -396,6 +401,7 @@ static void SWRenderer_destroy(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; + // TODO: why didn't I implement this. (void) swr; fprintf(stderr, "SWRenderer destroyed.\n"); @@ -418,7 +424,7 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam } } -// This used to be just one, "endFrame". Not sure what the different is. +// This used to be just one, "endFrame". Not sure what the difference is. static void SWRenderer_endFrameInit(Renderer* renderer) { (void) renderer; @@ -497,10 +503,84 @@ static void SWRenderer_endView(Renderer* renderer) swr->portH = swr->viewH = swr->height; } +static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + if (targetSurfaceId == RENDER_TARGET_HOST_FRAMEBUFFER) + { + if (!swr->drawingToSurface) + return true; + + // restore the original framebuffer + swr->fb = swr->mainFb; + swr->width = swr->mainWidth; + swr->height = swr->mainHeight; + swr->fbPitch = swr->mainPitch; + + if (restoreOldView) { + // restore the old transform, if needed + swr->viewX = swr->lastViewX; + swr->viewY = swr->lastViewY; + swr->viewW = swr->lastViewW; + swr->viewH = swr->lastViewH; + swr->portX = swr->lastPortX; + swr->portY = swr->lastPortY; + swr->portW = swr->lastPortW; + swr->portH = swr->lastPortH; + swr->gameW = swr->lastGameW; + swr->gameH = swr->lastGameH; + swr->maxX = swr->lastMaxX; + swr->maxY = swr->lastMaxY; + } + return true; + } + + if (targetSurfaceId < 0 || (size_t) targetSurfaceId >= swr->surfaceCount || swr->surfaces[targetSurfaceId] == NULL) { + fprintf(stderr, "swr: Invalid surface id %d\n", targetSurfaceId); + return false; + } + + if (!swr->drawingToSurface) + { + // back up the original framebuffer + swr->drawingToSurface = true; + swr->mainFb = swr->fb; + swr->mainWidth = swr->width; + swr->mainHeight = swr->height; + swr->mainPitch = swr->fbPitch; + + // and the old transform + swr->lastViewX = swr->viewX; + swr->lastViewY = swr->viewY; + swr->lastViewW = swr->viewW; + swr->lastViewH = swr->viewH; + swr->lastPortX = swr->portX; + swr->lastPortY = swr->portY; + swr->lastPortW = swr->portW; + swr->lastPortH = swr->portH; + swr->lastGameW = swr->gameW; + swr->lastGameH = swr->gameH; + swr->lastMaxX = swr->maxX; + swr->lastMaxY = swr->maxY; + } + + SWTexture* surface = swr->surfaces[targetSurfaceId]; + swr->fb = surface->buffer; + swr->width = surface->width; + swr->height = surface->height; + swr->fbPitch = surface->width; + swr->drawingToSurface = true; + + return true; +} + static void SWRenderer_beginGUI(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t portX, int32_t portY, int32_t portW, int32_t portH, int32_t targetSurfaceId) { - (void)renderer; (void)guiW; (void)guiH; + swrSwitchToSurface(renderer, targetSurfaceId, false); + + (void)guiW; (void)guiH; (void)portX; (void)portY; (void)portW; (void)portH; (void)targetSurfaceId; UNIMP2(); @@ -1818,14 +1898,57 @@ static void SWRenderer_drawSpriteTiled(Renderer* renderer, int32_t tpagIndex, static void SWRenderer_drawSurfaceTiled(Renderer* renderer, int32_t surfaceID, float x, float y, float xscale, float yscale, float roomW, float roomH, uint32_t color, float alpha) { - (void) renderer; - (void) surfaceID; - (void) x; (void) y; - (void) xscale; (void) yscale; - (void) roomW; (void) roomH; - (void) color; (void) alpha; - - UNIMP2(); + SWRenderer* swr = (SWRenderer*) renderer; + + if (0 > surfaceID || swr->surfaceCount <= (size_t) surfaceID) return; + + SWTexture* surface = swr->surfaces[surfaceID]; + if (!surface) return; + + float axScale = fabsf(xscale); + float ayScale = fabsf(yscale); + float tileW = (float) surface->width * axScale; + float tileH = (float) surface->height * ayScale; + if (0 >= tileW || 0 >= tileH) return; + + float originX = 0, originY = 0; + + float startX, endX, startY, endY; + startX = fmodf(x - originX * axScale, tileW); + if (startX > 0) startX -= tileW; + endX = roomW; + startY = fmodf(y - originY * ayScale, tileH); + if (startY > 0) startY -= tileH; + endY = roomH; + + int sx = 0, sy = 0; + int sw = surface->width; + int sh = surface->height; + + int localX0 = -originX; + int localY0 = -originY; + int localX1 = localX0 + surface->width; + int localY1 = localY0 + surface->height; + int sx0 = xscale * localX0; + int sy0 = yscale * localY0; + int sx1 = xscale * localX1; + int sy1 = yscale * localY1; + + for (int dy = startY; endY > dy; dy += tileH) { + int cy = dy + (int)(originY * ayScale); + int vy0 = cy + sy0; + int vy1 = cy + sy1; + int dh = vy1 - vy0; + + for (int dx = startX; endX > dx; dx += tileW) { + int cx = dx + (int)(originX * axScale); + int vx0 = cx + sx0; + int vx1 = cx + sx1; + int dw = vx1 - vx0; + + swrDrawSprite(renderer, vx0, vy0, dw, dh, surface, sx, sy, sw, sh, color, alpha); + } + } } static void SWRenderer_flush(Renderer* renderer) @@ -1901,48 +2024,62 @@ static void SWRenderer_gpuSetFog(Renderer* renderer, bool enable, uint32_t color static int32_t SWRenderer_createSurface(Renderer* renderer, int32_t width, int32_t height) { - UNIMP(); - (void)renderer; (void)width; (void)height; - return 0; + SWRenderer* swr = (SWRenderer*) renderer; + + int32_t slot = -1; + for (size_t i = 0; i < swr->surfaceCount; i++) + { + if (swr->surfaces[i] == NULL) { + slot = (int32_t) i; + break; + } + } + + if (slot < 0) { + fprintf(stderr, "swr: Could not create surface, too many exist at once.\n"); + return slot; + } + + swr->surfaces[slot] = swrCreateTexture(NULL, width, height); + return slot; } static bool SWRenderer_surfaceExists(Renderer* renderer, int32_t surfaceID) { - UNIMP(); - (void)renderer; (void)surfaceID; - return false; + SWRenderer* swr = (SWRenderer*) renderer; + if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount) + return false; + + return swr->surfaces[surfaceID] != NULL; } static bool SWRenderer_setRenderTarget(Renderer* renderer, int32_t surfaceID, bool implicitApplicationSurface) { - UNIMP(); - (void)renderer; (void)surfaceID; - (void)implicitApplicationSurface; - return false; + return swrSwitchToSurface(renderer, surfaceID, implicitApplicationSurface); } static float SWRenderer_getSurfaceWidth(Renderer* renderer, int32_t surfaceID) { SWRenderer* swr = (SWRenderer*) renderer; - if (surfaceID == APPLICATION_SURFACE_ID) { - return (float) swr->width; - } - - UNIMP(); - (void)renderer; (void)surfaceID; - return 0.0f; + if (surfaceID == APPLICATION_SURFACE_ID) + return (float)(swr->drawingToSurface ? swr->mainWidth : swr->width); + + if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) + return 0.0f; + + return (float) swr->surfaces[surfaceID]->width; } static float SWRenderer_getSurfaceHeight(Renderer* renderer, int32_t surfaceID) { SWRenderer* swr = (SWRenderer*) renderer; - if (surfaceID == APPLICATION_SURFACE_ID) { - return (float) swr->height; - } + if (surfaceID == APPLICATION_SURFACE_ID) + return (float)(swr->drawingToSurface ? swr->mainHeight : swr->height); + + if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) + return 0.0f; - UNIMP(); - (void)renderer; (void)surfaceID; - return 0.0f; + return (float) swr->surfaces[surfaceID]->height; } static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, @@ -1950,23 +2087,87 @@ static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, float x, float y, float xscale, float yscale, float angleDeg, uint32_t color, float alpha) { - UNIMP(); - (void)renderer; (void)surfaceID; - (void)srcLeft; (void)srcTop; (void)srcWidth; (void)srcHeight; - (void)x; (void)y; (void)xscale; (void)yscale; (void)angleDeg; - (void)color; (void)alpha; + SWRenderer* swr = (SWRenderer*) renderer; + SWTexture* surface, localSurface; + if (surfaceID == APPLICATION_SURFACE_ID) { + localSurface.buffer = swr->drawingToSurface ? swr->mainFb : swr->fb; + localSurface.width = swr->drawingToSurface ? swr->mainWidth : swr->width; + localSurface.height = swr->drawingToSurface ? swr->mainHeight : swr->height; + surface = &localSurface; + } else { + if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) { + fprintf(stderr, "swr: Invalid surface id %d for drawSurface\n", surfaceID); + return; + } + + surface = swr->surfaces[surfaceID]; + } + + int sx = srcLeft; + int sy = srcTop; + int sw = srcWidth; + int sh = srcHeight; + + int tw = srcWidth * swr->viewW / swr->portW; + int th = srcHeight * swr->viewH / swr->portH; + + float dx = x; + float dy = y; + int dw = (int)(xscale * tw); + int dh = (int)(yscale * th); + + if (UNLIKELY(swrMustRotate(angleDeg))) + { + float pivotX = (x - dx) * swrSgn(xscale); + float pivotY = (y - dy) * swrSgn(yscale); + + if (tw != sw) + pivotX *= (float)tw / sw; + if (th != sh) + pivotY *= (float)th / sh; + + swrDrawSpriteRotated(renderer, dx, dy, dw, dh, surface, sx, sy, sw, sh, color, alpha, angleDeg, pivotX, pivotY); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, surface, sx, sy, sw, sh, color, alpha); + } } static void SWRenderer_surfaceResize(Renderer* renderer, int32_t surfaceID, int32_t width, int32_t height) { - UNIMP(); - (void)renderer; (void)surfaceID; (void)width; (void)height; + SWRenderer* swr = (SWRenderer*) renderer; + + if (surfaceID == APPLICATION_SURFACE_ID) { + fprintf(stderr, "swr: Don't support resizing the application window with this. There must be another way!\n"); + return; + } + + if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) { + fprintf(stderr, "swr: Cannot resize surface id %d, it's invalid\n", surfaceID); + return; + } + + swrFreeTexture(swr->surfaces[surfaceID]); + swr->surfaces[surfaceID] = swrCreateTexture(NULL, width, height); } static void SWRenderer_surfaceFree(Renderer* renderer, int32_t surfaceID) { - UNIMP(); - (void)renderer; (void)surfaceID; + SWRenderer* swr = (SWRenderer*) renderer; + + if (surfaceID == APPLICATION_SURFACE_ID) { + fprintf(stderr, "swr: Don't support resizing the application window with this. There must be another way!\n"); + return; + } + + if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) { + fprintf(stderr, "swr: Cannot resize surface id %d, it's invalid\n", surfaceID); + return; + } + + swrFreeTexture(swr->surfaces[surfaceID]); + swr->surfaces[surfaceID] = NULL; } static void SWRenderer_surfaceCopy(Renderer* renderer, @@ -2098,7 +2299,7 @@ static void SWRenderer_deleteSprite(Renderer* renderer, int32_t spriteIndex) // Refuse to delete original data.win sprites if (swr->originalSpriteCount > (uint32_t) spriteIndex) { - fprintf(stderr, "%s: Sprite index %d is invalid.\n", __func__, spriteIndex); + fprintf(stderr, "%s: Cannot delete sprite with index %d, it's invalid.\n", __func__, spriteIndex); return; } @@ -2144,11 +2345,13 @@ static void SWRenderer_drawTiledPart(Renderer* renderer, int32_t tpagIndex, static int32_t SWRenderer_ensureApplicationSurface(Renderer* renderer, int32_t width, int32_t height) { - UNIMP(); - (void) renderer; - (void) width; - (void) height; - return -1; + // We don't evict surfaces, and especially not the primary framebuffer, + // but if we did, this is where we would restore it. + (void) renderer; + (void) width; + (void) height; + + return APPLICATION_SURFACE_ID; } static RendererVtable swrVtable; From 7f3f6a8754c9f3a8c81dd3caae8f011bbd196b0a Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Fri, 10 Jul 2026 19:55:32 +0300 Subject: [PATCH 057/130] * Normalize to 4 space indentation. --- src/sw/sw_renderer.c | 3728 +++++++++++++++++++++--------------------- 1 file changed, 1864 insertions(+), 1864 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 6feb507ad..7bb25181f 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -22,46 +22,46 @@ typedef struct { - uintpixel_t* buffer; - uint16_t width, height; + uintpixel_t* buffer; + uint16_t width, height; } SWTexture; typedef struct { - Renderer base; - - // Window Properties - uint16_t width; - uint16_t height; - // Framebuffer - uintpixel_t* fb; - uint16_t fbPitch; // in sizeof(uintpixel_t) units, NOT in bytes! - - bool drawingToSurface; - uintpixel_t* mainFb; - uint16_t mainWidth; - uint16_t mainHeight; - uint16_t mainPitch; - int lastViewX, lastViewY, lastViewW, lastViewH; - int lastPortX, lastPortY, lastPortW, lastPortH; - int lastGameW, lastGameH, lastMaxX, lastMaxY; - - SWTexture** textures; + Renderer base; + + // Window Properties + uint16_t width; + uint16_t height; + // Framebuffer + uintpixel_t* fb; + uint16_t fbPitch; // in sizeof(uintpixel_t) units, NOT in bytes! + + bool drawingToSurface; + uintpixel_t* mainFb; + uint16_t mainWidth; + uint16_t mainHeight; + uint16_t mainPitch; + int lastViewX, lastViewY, lastViewW, lastViewH; + int lastPortX, lastPortY, lastPortW, lastPortH; + int lastGameW, lastGameH, lastMaxX, lastMaxY; + + SWTexture** textures; SWTexture** surfaces; - uint32_t* textureIndexLRU; - uint32_t textureIndexLRUHead; - uint32_t textureIndexLRUTail; - size_t textureCount; - size_t surfaceCount; - size_t totalTextureCount; - size_t originalTPagCount; - size_t originalSpriteCount; - - bool viewActive; - int viewX, viewY, viewW, viewH; - int portX, portY, portW, portH; - int gameW, gameH, maxX, maxY; + uint32_t* textureIndexLRU; + uint32_t textureIndexLRUHead; + uint32_t textureIndexLRUTail; + size_t textureCount; + size_t surfaceCount; + size_t totalTextureCount; + size_t originalTPagCount; + size_t originalSpriteCount; + + bool viewActive; + int viewX, viewY, viewW, viewH; + int portX, portY, portW, portH; + int gameW, gameH, maxX, maxY; } SWRenderer; @@ -74,50 +74,50 @@ FORCE_INLINE int swrAbs(int x) { return x < 0 ? -x : x; } FORCE_INLINE bool opaque(uintpixel_t color) { #if PIXEL_SIZE == 8 - return (color != PXL_TRANSPARENT); + return (color != PXL_TRANSPARENT); #else - return (color & TRANSPARENT_MASK) != 0; + return (color & TRANSPARENT_MASK) != 0; #endif } FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) { #if PIXEL_SIZE == 32 - Pixel32ARGB x, y; - - if ((tintColor & 0xFFFFFF) == 0xFFFFFF) - return color; - - x.l = color; - y.l = tintColor; - - x.p.b = (int)x.p.b * y.p.b / 255; - x.p.g = (int)x.p.g * y.p.g / 255; - x.p.r = (int)x.p.r * y.p.r / 255; - return x.l; + Pixel32ARGB x, y; + + if ((tintColor & 0xFFFFFF) == 0xFFFFFF) + return color; + + x.l = color; + y.l = tintColor; + + x.p.b = (int)x.p.b * y.p.b / 255; + x.p.g = (int)x.p.g * y.p.g / 255; + x.p.r = (int)x.p.r * y.p.r / 255; + return x.l; #elif PIXEL_SIZE == 16 - if ((tintColor & 0x7FFF) == 0x7FFF) - return color; - - int tcb = tintColor & 0x1F; - int tcg = (tintColor >> 5) & 0x1F; - int tcr = (tintColor >> 10) & 0x1F; - - int cb = color & 0x1F; - int cg = (color >> 5) & 0x1F; - int cr = (color >> 10) & 0x1F; - int ca = color & 0x8000; - - cb = (cb * tcb) / 32; - cg = (cg * tcg) / 32; - cr = (cr * tcr) / 32; - return ca | cb | (cg << 5) | (cr << 10); + if ((tintColor & 0x7FFF) == 0x7FFF) + return color; + + int tcb = tintColor & 0x1F; + int tcg = (tintColor >> 5) & 0x1F; + int tcr = (tintColor >> 10) & 0x1F; + + int cb = color & 0x1F; + int cg = (color >> 5) & 0x1F; + int cr = (color >> 10) & 0x1F; + int ca = color & 0x8000; + + cb = (cb * tcb) / 32; + cg = (cg * tcg) / 32; + cr = (cr * tcr) / 32; + return ca | cb | (cg << 5) | (cr << 10); #elif PIXEL_SIZE == 8 - // fast but hacky - if (tintColor == 0xFF || tintColor == PXL_TRANSPARENT) - return color; - - return color & tintColor; + // fast but hacky + if (tintColor == 0xFF || tintColor == PXL_TRANSPARENT) + return color; + + return color & tintColor; #endif } @@ -125,185 +125,185 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) { #if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 - // it's so insignificant here nobody will notice if we just don't... - if (alpha < 3) - return; - - // it's so significant here we might as well fill in the whole color - if (alpha > 253) - { - *dcolor = scolor; - return; - } - - int inval = 256 - alpha; + // it's so insignificant here nobody will notice if we just don't... + if (alpha < 3) + return; + + // it's so significant here we might as well fill in the whole color + if (alpha > 253) + { + *dcolor = scolor; + return; + } + + int inval = 256 - alpha; #endif #if PIXEL_SIZE == 32 - Pixel32ARGB dc, sc; - dc.l = *dcolor; - sc.l = scolor; - - dc.p.r = (dc.p.r * inval + sc.p.r * alpha) >> 8; - dc.p.g = (dc.p.g * inval + sc.p.g * alpha) >> 8; - dc.p.b = (dc.p.b * inval + sc.p.b * alpha) >> 8; - - *dcolor = dc.l; + Pixel32ARGB dc, sc; + dc.l = *dcolor; + sc.l = scolor; + + dc.p.r = (dc.p.r * inval + sc.p.r * alpha) >> 8; + dc.p.g = (dc.p.g * inval + sc.p.g * alpha) >> 8; + dc.p.b = (dc.p.b * inval + sc.p.b * alpha) >> 8; + + *dcolor = dc.l; #elif PIXEL_SIZE == 16 - int scb = scolor & 0x1F; - int scg = (scolor >> 5) & 0x1F; - int scr = (scolor >> 10) & 0x1F; - - uintpixel_t _dcolor = *dcolor; - int dcb = _dcolor & 0x1F; - int dcg = (_dcolor >> 5) & 0x1F; - int dcr = (_dcolor >> 10) & 0x1F; - int dca = _dcolor & 0x8000; - - dcr = (dcr * inval + scr * alpha) >> 8; - dcg = (dcg * inval + scg * alpha) >> 8; - dcb = (dcb * inval + scb * alpha) >> 8; - - *dcolor = dca | dcb | (dcg << 5) | (dcr << 10); + int scb = scolor & 0x1F; + int scg = (scolor >> 5) & 0x1F; + int scr = (scolor >> 10) & 0x1F; + + uintpixel_t _dcolor = *dcolor; + int dcb = _dcolor & 0x1F; + int dcg = (_dcolor >> 5) & 0x1F; + int dcr = (_dcolor >> 10) & 0x1F; + int dca = _dcolor & 0x8000; + + dcr = (dcr * inval + scr * alpha) >> 8; + dcg = (dcg * inval + scg * alpha) >> 8; + dcb = (dcb * inval + scb * alpha) >> 8; + + *dcolor = dca | dcb | (dcg << 5) | (dcr << 10); #else - if (alpha < 240) { - static int alphaApproximationThingy = 0; - alphaApproximationThingy += 1339; - if (alphaApproximationThingy > 601000) - alphaApproximationThingy = 0; - - //gotta love that RNG - if ((alphaApproximationThingy & 0xFF) >= alpha) - return; - } - - *dcolor = scolor; + if (alpha < 240) { + static int alphaApproximationThingy = 0; + alphaApproximationThingy += 1339; + if (alphaApproximationThingy > 601000) + alphaApproximationThingy = 0; + + //gotta love that RNG + if ((alphaApproximationThingy & 0xFF) >= alpha) + return; + } + + *dcolor = scolor; #endif } FORCE_INLINE int swrIntAlpha(float alphaf) { - return (int)(alphaf * 256); + return (int)(alphaf * 256); } FORCE_INLINE bool swrMustRotate(float angleDeg) { - int angleDegInt = (int)(angleDeg * 4); - angleDegInt %= 360*4; - - if (angleDegInt > 180*4) - angleDegInt -= 360*4; - - return swrAbs(angleDegInt) >= 1; // 0.25 degrees + int angleDegInt = (int)(angleDeg * 4); + angleDegInt %= 360*4; + + if (angleDegInt > 180*4) + angleDegInt -= 360*4; + + return swrAbs(angleDegInt) >= 1; // 0.25 degrees } FORCE_INLINE bool swrMustRotateTolerant(float angleDeg) { - int angleDegInt = (int)(angleDeg * 16); - angleDegInt %= 360*16; - - if (angleDegInt > 180*16) - angleDegInt -= 360*16; - - return swrAbs(angleDegInt) >= 1; // 1/16 of a degree + int angleDegInt = (int)(angleDeg * 16); + angleDegInt %= 360*16; + + if (angleDegInt > 180*16) + angleDegInt -= 360*16; + + return swrAbs(angleDegInt) >= 1; // 1/16 of a degree } FORCE_INLINE int swrSgn(float x) { - if (x < 0) return -1; - return 1; + if (x < 0) return -1; + return 1; } FORCE_INLINE int swrFloor(float x) { - int i = (int) x; - return i - (x < (float) i); + int i = (int) x; + return i - (x < (float) i); } FORCE_INLINE int swrCeiling(float x) { - int i = (int) x; - return i + (x > (float) i); + int i = (int) x; + return i + (x > (float) i); } static SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height) { - SWTexture* txt = (SWTexture*) safeMalloc(sizeof(SWTexture)); - txt->buffer = (uintpixel_t*) safeMalloc(width * height * sizeof(uintpixel_t)); - - const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; - - size_t sz = width * height; - - if (srcBuffer) - { - for (size_t i = 0; i < sz; i++) - txt->buffer[i] = swrConvertPixelTexture(rgbaSrc[i]); - } - - txt->width = (uint16_t) width; - txt->height = (uint16_t) height; - - return txt; + SWTexture* txt = (SWTexture*) safeMalloc(sizeof(SWTexture)); + txt->buffer = (uintpixel_t*) safeMalloc(width * height * sizeof(uintpixel_t)); + + const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; + + size_t sz = width * height; + + if (srcBuffer) + { + for (size_t i = 0; i < sz; i++) + txt->buffer[i] = swrConvertPixelTexture(rgbaSrc[i]); + } + + txt->width = (uint16_t) width; + txt->height = (uint16_t) height; + + return txt; } static void swrFreeTexture(SWTexture* texture) { - free(texture->buffer); - free(texture); + free(texture->buffer); + free(texture); } static bool swrAddTextureIndexToLRU(SWRenderer* swr, int textureIndex) { - uint32_t newIndex = (swr->textureIndexLRUHead + 1) % TEXTURE_LRU_LENGTH; - if (newIndex == swr->textureIndexLRUTail) { - // about to collide with tail from the other side -- nope. - return false; - } - - swr->textureIndexLRU[swr->textureIndexLRUHead] = textureIndex; - swr->textureIndexLRUHead = newIndex; - return true; + uint32_t newIndex = (swr->textureIndexLRUHead + 1) % TEXTURE_LRU_LENGTH; + if (newIndex == swr->textureIndexLRUTail) { + // about to collide with tail from the other side -- nope. + return false; + } + + swr->textureIndexLRU[swr->textureIndexLRUHead] = textureIndex; + swr->textureIndexLRUHead = newIndex; + return true; } static int swrTailTextureIndexLRU(SWRenderer* swr, bool remove) { - if (swr->textureIndexLRUHead == swr->textureIndexLRUTail) - return -1; - - uint32_t textureIndex = swr->textureIndexLRU[swr->textureIndexLRUTail]; - - if (remove) - swr->textureIndexLRUTail = (swr->textureIndexLRUTail + 1) % TEXTURE_LRU_LENGTH; - - return textureIndex; + if (swr->textureIndexLRUHead == swr->textureIndexLRUTail) + return -1; + + uint32_t textureIndex = swr->textureIndexLRU[swr->textureIndexLRUTail]; + + if (remove) + swr->textureIndexLRUTail = (swr->textureIndexLRUTail + 1) % TEXTURE_LRU_LENGTH; + + return textureIndex; } static void swrEvictTextureFromCache(SWRenderer* swr, int textureIndex) { - SWTexture* texture = swr->textures[textureIndex]; - swr->textures[textureIndex] = NULL; - - swrFreeTexture(texture); + SWTexture* texture = swr->textures[textureIndex]; + swr->textures[textureIndex] = NULL; + + swrFreeTexture(texture); } // Lazily decodes and uploads a TXTR page on first access. // Returns true if the texture is ready, false if it failed to decode. static bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) { - if (swr->textures[pageId]) - return true; + if (swr->textures[pageId]) + return true; - DataWin* dw = swr->base.dataWin; - Texture* txtr = &dw->txtr.textures[pageId]; + DataWin* dw = swr->base.dataWin; + Texture* txtr = &dw->txtr.textures[pageId]; - int w, h; - bool gm2022_5 = DataWin_isVersionAtLeast(dw, 2022, 5, 0, 0); - - uint8_t* pixels = NULL; - - do - { + int w, h; + bool gm2022_5 = DataWin_isVersionAtLeast(dw, 2022, 5, 0, 0); + + uint8_t* pixels = NULL; + + do + { if (!txtr->blobData) { DataWin_loadTxtrIfNeeded(dw, pageId); } @@ -320,187 +320,187 @@ static bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) } else { fprintf(stderr, "swr: Failed to load TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); } - - int tail = swrTailTextureIndexLRU(swr, true); - if (tail == -1) { - fprintf(stderr, "swr: Looks like we can't fit this texture in memory at all. Bummer.\n"); - break; - } - - swrEvictTextureFromCache(swr, tail); - fprintf(stderr, "swr: Evicted texture %d, trying again.\n", tail); - } - while (!pixels); - - if (pixels == nullptr) { - fprintf(stderr, "swr: Failed to decode TXTR page %u.\n", pageId); - return false; - } - - swr->textures[pageId] = swrCreateTexture(pixels, w, h); - free(pixels); - - fprintf(stderr, "SWR: Loaded TXTR page %u (%dx%d)\n", pageId, w, h); - - // add it to the LRU - do - { - bool added = swrAddTextureIndexToLRU(swr, pageId); - if (added) - break; - - int tail = swrTailTextureIndexLRU(swr, true); - if (tail == -1) { - fprintf(stderr, "swr: Come on now.\n"); - assert(tail != -1); - } - - swrEvictTextureFromCache(swr, tail); - } - while (true); - - return true; + + int tail = swrTailTextureIndexLRU(swr, true); + if (tail == -1) { + fprintf(stderr, "swr: Looks like we can't fit this texture in memory at all. Bummer.\n"); + break; + } + + swrEvictTextureFromCache(swr, tail); + fprintf(stderr, "swr: Evicted texture %d, trying again.\n", tail); + } + while (!pixels); + + if (pixels == nullptr) { + fprintf(stderr, "swr: Failed to decode TXTR page %u.\n", pageId); + return false; + } + + swr->textures[pageId] = swrCreateTexture(pixels, w, h); + free(pixels); + + fprintf(stderr, "SWR: Loaded TXTR page %u (%dx%d)\n", pageId, w, h); + + // add it to the LRU + do + { + bool added = swrAddTextureIndexToLRU(swr, pageId); + if (added) + break; + + int tail = swrTailTextureIndexLRU(swr, true); + if (tail == -1) { + fprintf(stderr, "swr: Come on now.\n"); + assert(tail != -1); + } + + swrEvictTextureFromCache(swr, tail); + } + while (true); + + return true; } static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) { - SWRenderer* swr = (SWRenderer*) renderer; - - renderer->dataWin = dataWin; - - //allocate texture buffer - swr->textureCount = dataWin->txtr.count; - swr->surfaceCount = SURFACE_MAX_COUNT; - swr->totalTextureCount = swr->textureCount + swr->surfaceCount; - swr->textures = (SWTexture**) safeCalloc(swr->totalTextureCount, sizeof(SWTexture*)); + SWRenderer* swr = (SWRenderer*) renderer; + + renderer->dataWin = dataWin; + + //allocate texture buffer + swr->textureCount = dataWin->txtr.count; + swr->surfaceCount = SURFACE_MAX_COUNT; + swr->totalTextureCount = swr->textureCount + swr->surfaceCount; + swr->textures = (SWTexture**) safeCalloc(swr->totalTextureCount, sizeof(SWTexture*)); swr->surfaces = (SWTexture**) safeCalloc(swr->surfaceCount, sizeof(SWTexture*)); - - //allocate texture LRU cache to allow for dynamic unloading of textures - swr->textureIndexLRU = (uint32_t*) safeCalloc(TEXTURE_LRU_LENGTH, sizeof(uint32_t)); - swr->textureIndexLRUHead = 0; - swr->textureIndexLRUTail = 0; - - //HACK: this isn't good, really. This should seriously be refactored. - //expand datawin's tpag items list to include our surface count. - swr->originalTPagCount = dataWin->tpag.count; - dataWin->tpag.items = (TexturePageItem*) safeRealloc(dataWin->tpag.items, sizeof(TexturePageItem) * (dataWin->tpag.count + swr->surfaceCount)); - dataWin->tpag.count += swr->surfaceCount; - - swr->originalSpriteCount = dataWin->sprt.count; - - for (size_t i = swr->originalTPagCount; i < dataWin->tpag.count; i++) - { - memset(&dataWin->tpag.items[i], 0, sizeof(TexturePageItem)); - dataWin->tpag.items[i].texturePageId = -1; - } - - fprintf(stderr, "SWRenderer initialized.\n"); + + //allocate texture LRU cache to allow for dynamic unloading of textures + swr->textureIndexLRU = (uint32_t*) safeCalloc(TEXTURE_LRU_LENGTH, sizeof(uint32_t)); + swr->textureIndexLRUHead = 0; + swr->textureIndexLRUTail = 0; + + //HACK: this isn't good, really. This should seriously be refactored. + //expand datawin's tpag items list to include our surface count. + swr->originalTPagCount = dataWin->tpag.count; + dataWin->tpag.items = (TexturePageItem*) safeRealloc(dataWin->tpag.items, sizeof(TexturePageItem) * (dataWin->tpag.count + swr->surfaceCount)); + dataWin->tpag.count += swr->surfaceCount; + + swr->originalSpriteCount = dataWin->sprt.count; + + for (size_t i = swr->originalTPagCount; i < dataWin->tpag.count; i++) + { + memset(&dataWin->tpag.items[i], 0, sizeof(TexturePageItem)); + dataWin->tpag.items[i].texturePageId = -1; + } + + fprintf(stderr, "SWRenderer initialized.\n"); } static void SWRenderer_destroy(Renderer* renderer) { - SWRenderer* swr = (SWRenderer*) renderer; - + SWRenderer* swr = (SWRenderer*) renderer; + // TODO: why didn't I implement this. - (void) swr; - - fprintf(stderr, "SWRenderer destroyed.\n"); + (void) swr; + + fprintf(stderr, "SWRenderer destroyed.\n"); } static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH) { - SWRenderer* swr = (SWRenderer*) renderer; - swr->gameW = gameW; - swr->gameH = gameH; - swr->drawingToSurface = false; - if (swr->width != windowW || swr->height != windowH) - { - //allocate frame buffer - free(swr->fb); - swr->fb = (uintpixel_t*) safeMalloc(windowW * windowH * sizeof(uintpixel_t)); - swr->fbPitch = windowW; - swr->width = windowW; - swr->height = windowH; - } + SWRenderer* swr = (SWRenderer*) renderer; + swr->gameW = gameW; + swr->gameH = gameH; + swr->drawingToSurface = false; + if (swr->width != windowW || swr->height != windowH) + { + //allocate frame buffer + free(swr->fb); + swr->fb = (uintpixel_t*) safeMalloc(windowW * windowH * sizeof(uintpixel_t)); + swr->fbPitch = windowW; + swr->width = windowW; + swr->height = windowH; + } } // This used to be just one, "endFrame". Not sure what the difference is. static void SWRenderer_endFrameInit(Renderer* renderer) { - (void) renderer; - - //this is kinda useless to do twice isn't it? + (void) renderer; + + //this is kinda useless to do twice isn't it? } static void SWRenderer_endFrameEnd(Renderer* renderer) { - SWRenderer* swr = (SWRenderer*) renderer; - assert(!swr->drawingToSurface); - Runner_setNextFrame(swr->fb, swr->width, swr->height); + SWRenderer* swr = (SWRenderer*) renderer; + assert(!swr->drawingToSurface); + Runner_setNextFrame(swr->fb, swr->width, swr->height); } static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t viewY, int32_t viewW, int32_t viewH, - int32_t portX, int32_t portY, int32_t portW, int32_t portH, float viewAngle) -{ - (void)renderer; (void)viewX; (void)viewY; (void)viewW; (void)viewH; - (void)portX; (void)portY; (void)portW; (void)portH; (void)viewAngle; - UNIMP2(); - - SWRenderer* swr = (SWRenderer*) renderer; - - float xratio, yratio; - - if (swr->drawingToSurface) { - UNIMP(); - xratio = 1.0f; - yratio = 1.0f; - portX = (int)(portX * xratio); - portY = (int)(portY * yratio); - } - else { - float scaleX = (float) swr->width / swr->gameW; - float scaleY = (float) swr->height / swr->gameH; - float scale = (scaleX < scaleY) ? scaleX : scaleY; - - int32_t scaledW = (int32_t)(swr->gameW * scale); - int32_t scaledH = (int32_t)(swr->gameH * scale); - int32_t offsetX = (swr->width - scaledW) / 2; - int32_t offsetY = (swr->height - scaledH) / 2; - - xratio = scale; - yratio = scale; - - portX = (int)(portX * xratio) + offsetX; - portY = (int)(portY * yratio) + offsetY; - } - portW = (int)(portW * xratio); - portH = (int)(portH * yratio); - - swr->viewActive = true; - swr->viewX = viewX; - swr->viewY = viewY; - swr->viewW = viewW; - swr->viewH = viewH; - swr->portX = portX; - swr->portY = portY; - swr->portW = portW; - swr->portH = portH; - swr->maxX = portX + portW; - swr->maxY = portY + portH; + int32_t portX, int32_t portY, int32_t portW, int32_t portH, float viewAngle) +{ + (void)renderer; (void)viewX; (void)viewY; (void)viewW; (void)viewH; + (void)portX; (void)portY; (void)portW; (void)portH; (void)viewAngle; + UNIMP2(); + + SWRenderer* swr = (SWRenderer*) renderer; + + float xratio, yratio; + + if (swr->drawingToSurface) { + UNIMP(); + xratio = 1.0f; + yratio = 1.0f; + portX = (int)(portX * xratio); + portY = (int)(portY * yratio); + } + else { + float scaleX = (float) swr->width / swr->gameW; + float scaleY = (float) swr->height / swr->gameH; + float scale = (scaleX < scaleY) ? scaleX : scaleY; + + int32_t scaledW = (int32_t)(swr->gameW * scale); + int32_t scaledH = (int32_t)(swr->gameH * scale); + int32_t offsetX = (swr->width - scaledW) / 2; + int32_t offsetY = (swr->height - scaledH) / 2; + + xratio = scale; + yratio = scale; + + portX = (int)(portX * xratio) + offsetX; + portY = (int)(portY * yratio) + offsetY; + } + portW = (int)(portW * xratio); + portH = (int)(portH * yratio); + + swr->viewActive = true; + swr->viewX = viewX; + swr->viewY = viewY; + swr->viewW = viewW; + swr->viewH = viewH; + swr->portX = portX; + swr->portY = portY; + swr->portW = portW; + swr->portH = portH; + swr->maxX = portX + portW; + swr->maxY = portY + portH; } static void SWRenderer_endView(Renderer* renderer) { - (void)renderer; - UNIMP2(); - - SWRenderer* swr = (SWRenderer*) renderer; - swr->viewActive = false; - - swr->portX = swr->viewX = 0; - swr->portY = swr->viewY = 0; - swr->portW = swr->viewW = swr->width; - swr->portH = swr->viewH = swr->height; + (void)renderer; + UNIMP2(); + + SWRenderer* swr = (SWRenderer*) renderer; + swr->viewActive = false; + + swr->portX = swr->viewX = 0; + swr->portY = swr->viewY = 0; + swr->portW = swr->viewW = swr->width; + swr->portH = swr->viewH = swr->height; } static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView) @@ -576,986 +576,986 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool } static void SWRenderer_beginGUI(Renderer* renderer, int32_t guiW, int32_t guiH, - int32_t portX, int32_t portY, int32_t portW, int32_t portH, int32_t targetSurfaceId) + int32_t portX, int32_t portY, int32_t portW, int32_t portH, int32_t targetSurfaceId) { swrSwitchToSurface(renderer, targetSurfaceId, false); (void)guiW; (void)guiH; - (void)portX; (void)portY; (void)portW; (void)portH; - (void)targetSurfaceId; - UNIMP2(); + (void)portX; (void)portY; (void)portW; (void)portH; + (void)targetSurfaceId; + UNIMP2(); } static void SWRenderer_setGuiProjection(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t portW, int32_t portH, bool renderingToUserSurface) { - (void) renderer; - (void) guiW; (void) guiH; - (void) portW; (void) portH; - (void) renderingToUserSurface; - UNIMP(); + (void) renderer; + (void) guiW; (void) guiH; + (void) portW; (void) portH; + (void) renderingToUserSurface; + UNIMP(); } static void SWRenderer_endGUI(Renderer* renderer) { - (void)renderer; - UNIMP2(); + (void)renderer; + UNIMP2(); } // TODO[MrPowerGamerBR]: This is supposed to be refactored, not to modify data.win structs directly. static int32_t swrFindSurfaceTextureSlot(SWRenderer* swr) { - // NOTE: dynamic textures are not enrolled into the eviction cache for - // hopefully obvious reasons ... - for (size_t i = swr->textureCount; i != swr->totalTextureCount; i++) - { - if (swr->textures[i] == NULL) { - return (int32_t) i; - } - } - - return -1; + // NOTE: dynamic textures are not enrolled into the eviction cache for + // hopefully obvious reasons ... + for (size_t i = swr->textureCount; i != swr->totalTextureCount; i++) + { + if (swr->textures[i] == NULL) { + return (int32_t) i; + } + } + + return -1; } static int32_t swrFindSurfaceTPagSlot(SWRenderer* swr) { - DataWin* dw = swr->base.dataWin; - for (size_t i = swr->originalTPagCount; i != dw->tpag.count; i++) - { - if (dw->tpag.items[i].texturePageId == -1) { - return (int32_t) i; - } - } - - return -1; + DataWin* dw = swr->base.dataWin; + for (size_t i = swr->originalTPagCount; i != dw->tpag.count; i++) + { + if (dw->tpag.items[i].texturePageId == -1) { + return (int32_t) i; + } + } + + return -1; } static void swrTransformPosIfNeeded(SWRenderer* swr, float* dx, float* dy) { - if (!swr->viewActive) return; - - if (dx) { - float xscale = ((float)swr->portW / swr->viewW); - *dx -= swr->viewX; - *dx *= xscale; - *dx += swr->portX; - } - if (dy) { - float yscale = ((float)swr->portH / swr->viewH); - *dy -= swr->viewY; - *dy *= yscale; - *dy += swr->portY; - } + if (!swr->viewActive) return; + + if (dx) { + float xscale = ((float)swr->portW / swr->viewW); + *dx -= swr->viewX; + *dx *= xscale; + *dx += swr->portX; + } + if (dy) { + float yscale = ((float)swr->portH / swr->viewH); + *dy -= swr->viewY; + *dy *= yscale; + *dy += swr->portY; + } } static void swrTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) { - if (!swr->viewActive || !swr->viewW || !swr->viewH) return; - - if (dx) *dx *= ((float)swr->portW / swr->viewW); - if (dy) *dy *= ((float)swr->portH / swr->viewH); + if (!swr->viewActive || !swr->viewW || !swr->viewH) return; + + if (dx) *dx *= ((float)swr->portW / swr->viewW); + if (dy) *dy *= ((float)swr->portH / swr->viewH); } static void swrTransformPosIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { - if (!swr->viewActive) return; - - if (dx) { - *dx -= swr->viewX; - *dx = *dx * swr->portW / swr->viewW; - *dx += swr->portX; - } - if (dy) { - *dy -= swr->viewY; - *dy = *dy * swr->portH / swr->viewH; - *dy += swr->portY; - } + if (!swr->viewActive) return; + + if (dx) { + *dx -= swr->viewX; + *dx = *dx * swr->portW / swr->viewW; + *dx += swr->portX; + } + if (dy) { + *dy -= swr->viewY; + *dy = *dy * swr->portH / swr->viewH; + *dy += swr->portY; + } } static void swrTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { - if (!swr->viewActive || !swr->viewW || !swr->viewH) return; - - if (dx) *dx = *dx * swr->portW / swr->viewW; - if (dy) *dy = *dy * swr->portH / swr->viewH; + if (!swr->viewActive || !swr->viewW || !swr->viewH) return; + + if (dx) *dx = *dx * swr->portW / swr->viewW; + if (dy) *dy = *dy * swr->portH / swr->viewH; } static void swrReverseTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { - if (!swr->viewActive) return; - - if (dx) *dx = *dx * swr->viewW / swr->portW; - if (dy) *dy = *dy * swr->viewH / swr->portH; + if (!swr->viewActive) return; + + if (dx) *dx = *dx * swr->viewW / swr->portW; + if (dy) *dy = *dy * swr->viewH / swr->portH; } FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int alpha) { - SWRenderer* swr = (SWRenderer*) renderer; - - if (x < swr->portX || y < swr->portY) return; - if (x >= swr->maxX || y >= swr->maxY) return; - - alphaBlend(&swr->fb[y * swr->fbPitch + x], color, alpha); + SWRenderer* swr = (SWRenderer*) renderer; + + if (x < swr->portX || y < swr->portY) return; + if (x >= swr->maxX || y >= swr->maxY) return; + + alphaBlend(&swr->fb[y * swr->fbPitch + x], color, alpha); } static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) { - SWRenderer *swr = (SWRenderer*) renderer; - - if (dy < swr->portY) return; - if (dy >= swr->maxY) return; - if (dx < swr->portX) { dw += dx - swr->portX; dx = swr->portX; } - if (dx + dw >= swr->maxX) dw = swr->maxX - dx; - if (dw <= 0) return; - + SWRenderer *swr = (SWRenderer*) renderer; + + if (dy < swr->portY) return; + if (dy >= swr->maxY) return; + if (dx < swr->portX) { dw += dx - swr->portX; dx = swr->portX; } + if (dx + dw >= swr->maxX) dw = swr->maxX - dx; + if (dw <= 0) return; + #if PIXEL_SIZE == 32 - if (color == color2) + if (color == color2) #endif - { - uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; - for (int i = 0; i < dw; i++) - alphaBlend(&line[i], color, alpha); - } + { + uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; + for (int i = 0; i < dw; i++) + alphaBlend(&line[i], color, alpha); + } #if PIXEL_SIZE == 32 - else - { - Pixel32ARGB clr1, clr2; - clr1.l = color; - clr2.l = color2; - - uint32_t rinit = clr1.p.r << 20; - uint32_t ginit = clr1.p.g << 20; - uint32_t binit = clr1.p.b << 20; - int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; - int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; - int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; - rstep /= dw; - gstep /= dw; - bstep /= dw; - - uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; - for (int i = 0; i < dw; i++) - { - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - alphaBlend(&line[i], resultPixel.l, alpha); - } - } + else + { + Pixel32ARGB clr1, clr2; + clr1.l = color; + clr2.l = color2; + + uint32_t rinit = clr1.p.r << 20; + uint32_t ginit = clr1.p.g << 20; + uint32_t binit = clr1.p.b << 20; + int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; + int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; + int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; + rstep /= dw; + gstep /= dw; + bstep /= dw; + + uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; + for (int i = 0; i < dw; i++) + { + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + alphaBlend(&line[i], resultPixel.l, alpha); + } + } #endif } static void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha) { - SWRenderer *swr = (SWRenderer*) renderer; - float thickness = 1; + SWRenderer *swr = (SWRenderer*) renderer; + float thickness = 1; - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &dw, &thickness); + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &dw, &thickness); - // TODO: use thickness - swrDrawHLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dw), color, color2, swrIntAlpha(alpha)); + // TODO: use thickness + swrDrawHLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dw), color, color2, swrIntAlpha(alpha)); } static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) { - SWRenderer *swr = (SWRenderer*) renderer; - - if (dx < swr->portX) return; - if (dx >= swr->maxX) return; - if (dy < swr->portY) { dh += dy - swr->portY; dy = swr->portY; } - if (dy + dh >= swr->maxY) dh = swr->maxY - dy; - if (dh <= 0) return; - + SWRenderer *swr = (SWRenderer*) renderer; + + if (dx < swr->portX) return; + if (dx >= swr->maxX) return; + if (dy < swr->portY) { dh += dy - swr->portY; dy = swr->portY; } + if (dy + dh >= swr->maxY) dh = swr->maxY - dy; + if (dh <= 0) return; + #if PIXEL_SIZE == 32 - if (color == color2) + if (color == color2) #endif - { - for (int i = 0; i < dh; i++) - { - uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - alphaBlend(&line[0], color, alpha); - } - } + { + for (int i = 0; i < dh; i++) + { + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + alphaBlend(&line[0], color, alpha); + } + } #if PIXEL_SIZE == 32 - else - { - Pixel32ARGB clr1, clr2; - clr1.l = color; - clr2.l = color2; - - uint32_t rinit = clr1.p.r << 20; - uint32_t ginit = clr1.p.g << 20; - uint32_t binit = clr1.p.b << 20; - int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; - int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; - int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; - rstep /= dh; - gstep /= dh; - bstep /= dh; - - for (int i = 0; i < dh; i++) - { - uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - alphaBlend(&line[0], resultPixel.l, alpha); - } - } + else + { + Pixel32ARGB clr1, clr2; + clr1.l = color; + clr2.l = color2; + + uint32_t rinit = clr1.p.r << 20; + uint32_t ginit = clr1.p.g << 20; + uint32_t binit = clr1.p.b << 20; + int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; + int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; + int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; + rstep /= dh; + gstep /= dh; + bstep /= dh; + + for (int i = 0; i < dh; i++) + { + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + alphaBlend(&line[0], resultPixel.l, alpha); + } + } #endif } static void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, uintpixel_t color2, float alpha) { - SWRenderer *swr = (SWRenderer*) renderer; - float thickness = 1; + SWRenderer *swr = (SWRenderer*) renderer; + float thickness = 1; - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &thickness, &dh); - - // TODO: use thickness - swrDrawVLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dh), color, color2, swrIntAlpha(alpha)); + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &thickness, &dh); + + // TODO: use thickness + swrDrawVLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dh), color, color2, swrIntAlpha(alpha)); } static void swrDrawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha) { - swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color, color, alpha); - swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color, color, alpha); - swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color, color, alpha); - swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color, color, alpha); + swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color, color, alpha); + swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color, color, alpha); + swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color, color, alpha); + swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color, color, alpha); } static void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uintpixel_t color4, float alpha) { - swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color1, color2, alpha); - swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color3, color4, alpha); - swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color1, color3, alpha); - swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color2, color4, alpha); + swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color1, color2, alpha); + swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color3, color4, alpha); + swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color1, color3, alpha); + swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color2, color4, alpha); } static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, MAYBE_UNUSED int width, uintpixel_t color1, uintpixel_t color2, int alpha) { - if (x1 == x2) - { - swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); - return; - } - if (y1 == y2) - { - swrDrawHLineInt(renderer, swrMin(x1, x2), y1, swrAbs(x1 - x2), color1, color2, alpha); - return; - } - - int dx = x2 - x1, dy = y2 - y1; - int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; - int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; - - uintpixel_t color = color1; + if (x1 == x2) + { + swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); + return; + } + if (y1 == y2) + { + swrDrawHLineInt(renderer, swrMin(x1, x2), y1, swrAbs(x1 - x2), color1, color2, alpha); + return; + } + + int dx = x2 - x1, dy = y2 - y1; + int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; + int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; + + uintpixel_t color = color1; #if PIXEL_SIZE == 32 - Pixel32ARGB clr1, clr2; - clr1.l = color1; - clr2.l = color2; - - uint32_t rinit = clr1.p.r << 20; - uint32_t ginit = clr1.p.g << 20; - uint32_t binit = clr1.p.b << 20; - int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; - int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; - int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; + Pixel32ARGB clr1, clr2; + clr1.l = color1; + clr2.l = color2; + + uint32_t rinit = clr1.p.r << 20; + uint32_t ginit = clr1.p.g << 20; + uint32_t binit = clr1.p.b << 20; + int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; + int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; + int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; #endif - - if (dy1 <= dx1) - { - if (dx >= 0) - { - x = x1, y = y1, xe = x2; - } - else - { - x = x2, y = y2, xe = x1; - } - + + if (dy1 <= dx1) + { + if (dx >= 0) + { + x = x1, y = y1, xe = x2; + } + else + { + x = x2, y = y2, xe = x1; + } + #if PIXEL_SIZE == 32 - if (dx1 > 0) { - rstep /= dx1; - gstep /= dx1; - bstep /= dx1; - } else { - rstep = gstep = bstep = 0; - } + if (dx1 > 0) { + rstep /= dx1; + gstep /= dx1; + bstep /= dx1; + } else { + rstep = gstep = bstep = 0; + } #endif - - swrPlotPixel(renderer, x, y, color, alpha); - - for (int i = 0; x < xe; i++) - { - x++; - if (px < 0) - { - px += 2 * dy1; - } - else - { - if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y++; else y--; - px += 2 * (dy1 - dx1); - } - + + swrPlotPixel(renderer, x, y, color, alpha); + + for (int i = 0; x < xe; i++) + { + x++; + if (px < 0) + { + px += 2 * dy1; + } + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y++; else y--; + px += 2 * (dy1 - dx1); + } + #if PIXEL_SIZE == 32 - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - color = resultPixel.l; + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + color = resultPixel.l; #endif - - swrPlotPixel(renderer, x, y, color, alpha); - } - } - else - { - if (dy >= 0) - { - x = x1, y = y1, ye = y2; - } - else - { - x = x2, y = y2, ye = y1; - } - + + swrPlotPixel(renderer, x, y, color, alpha); + } + } + else + { + if (dy >= 0) + { + x = x1, y = y1, ye = y2; + } + else + { + x = x2, y = y2, ye = y1; + } + #if PIXEL_SIZE == 32 - if (dy1 > 0) { - rstep /= dy1; - gstep /= dy1; - bstep /= dy1; - } else { - rstep = gstep = bstep = 0; - } + if (dy1 > 0) { + rstep /= dy1; + gstep /= dy1; + bstep /= dy1; + } else { + rstep = gstep = bstep = 0; + } #endif - - swrPlotPixel(renderer, x, y, color, alpha); - - for (int i = 0; y < ye; i++) - { - y++; - if (py <= 0) - { - py += 2 * dx1; - } - else - { - if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x++; else x--; - py += 2 * (dx1 - dy1); - } - + + swrPlotPixel(renderer, x, y, color, alpha); + + for (int i = 0; y < ye; i++) + { + y++; + if (py <= 0) + { + py += 2 * dx1; + } + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x++; else x--; + py += 2 * (dx1 - dy1); + } + #if PIXEL_SIZE == 32 - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - color = resultPixel.l; + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + color = resultPixel.l; #endif - - swrPlotPixel(renderer, x, y, color, alpha); - } - } + + swrPlotPixel(renderer, x, y, color, alpha); + } + } } static void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha) { - SWRenderer* swr = (SWRenderer*) renderer; - swrTransformPosIfNeeded(swr, &x1, &y1); - swrTransformPosIfNeeded(swr, &x2, &y2); - swrTransformSizeIfNeeded(swr, &width, NULL); - swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrCeiling(x2), swrCeiling(y2), swrCeiling(width), color, color2, swrIntAlpha(alpha)); + SWRenderer* swr = (SWRenderer*) renderer; + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + swrTransformSizeIfNeeded(swr, &width, NULL); + swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrCeiling(x2), swrCeiling(y2), swrCeiling(width), color, color2, swrIntAlpha(alpha)); } static void swrDrawSpriteInternal( - Renderer* renderer, int dx, int dy, int dw, int dh, - SWTexture* texture, int sx, int sy, int sw, int sh, - uintpixel_t tintColor, int alpha + Renderer* renderer, int dx, int dy, int dw, int dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uintpixel_t tintColor, int alpha ) { - SWRenderer *swr = (SWRenderer*) renderer; - - bool flipX = false, flipY = false; - if (dw < 0) { dx += dw; dw = -dw; flipX = true; } - if (dh < 0) { dy += dh; dh = -dh; flipY = true; } - - //basic out of bound checks - if (dw == 0 || dh == 0) return; - if (sw == 0) sw = 1; - if (sh == 0) sh = 1; - if (dx + dw <= swr->portX) return; - if (dy + dh <= swr->portY) return; - if (dx >= swr->maxX) return; - if (dy >= swr->maxY) return; - - int odw = dw, odh = dh; - int osw = sw, osh = sh; - - int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; - - //out of bounds adjustment checks - int diffxl = 0, diffyl = 0, diffxu = 0, diffyu = 0; - if (dx < minx) { diffxl = minx - dx; dx = minx; dw -= diffxl; } - if (dy < miny) { diffyl = miny - dy; dy = miny; dh -= diffyl; } - if (dx + dw > maxx) { diffxu = dx + dw - maxx; dw -= diffxu; } - if (dy + dh > maxy) { diffyu = dy + dh - maxy; dh -= diffyu; } - - if (diffxl != 0 || diffyl != 0 || diffxu != 0 || diffyu != 0) - { - //adjust source coordinates too - diffxl = (int)((long)diffxl * osw / odw); - diffyl = (int)((long)diffyl * osh / odh); - diffxu = (int)((long)(diffxu + 1) * osw / odw); - diffyu = (int)((long)(diffyu + 1) * osh / odh); - sx += flipX ? diffxu : diffxl; - sy += flipY ? diffyu : diffyl; - sw -= diffxl + diffxu; - sh -= diffyl + diffyu; - if (sw <= 0 || sh <= 0) return; - } - - //clip the source coords into bounds too - if (sx < 0) { sw += sx; sx = 0; } - if (sy < 0) { sh += sy; sy = 0; } - if (sx + sw >= texture->width) { sw = texture->width - sx; } - if (sy + sh >= texture->height) { sh = texture->height - sy; } - - //okay, now we can finally get on with rendering - - int ixs = 0, oxs = 1, iys = 0, oys = 1; - if (flipX) ixs = dw - 1, oxs = -1; - if (flipY) iys = dh - 1, oys = -1; - - // tweak these if stuff doesn't look right - typedef int32_t fixedp_t; - const int fp_prec = 14; - - fixedp_t ystep = (sh == dh) ? (1 << fp_prec) : ((fixedp_t) osh << fp_prec) / odh; - fixedp_t xstep = (sw == dw) ? (1 << fp_prec) : ((fixedp_t) osw << fp_prec) / odw; - fixedp_t oxs2 = oxs * xstep; - fixedp_t oys2 = oys * ystep; - fixedp_t ixs2 = ixs * xstep; - fixedp_t iys2 = iys * ystep; - - if (sw == dw) - { - fixedp_t ys2 = (fixedp_t) iys2; - for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) - { - uintpixel_t* dstline; - const uintpixel_t* srcline; - dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; - if (dh == sh) - srcline = &texture->buffer[(sy + ys) * texture->width + sx]; - else - srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; - - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) - { - uintpixel_t pixel = srcline[xs]; - if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); - } - } - } - else - { - fixedp_t ys2 = iys2; - for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) - { - uintpixel_t* dstline; - const uintpixel_t* srcline; - dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; - if (dh == sh) - srcline = &texture->buffer[(sy + ys) * texture->width + sx]; - else - srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; - - fixedp_t xs2 = ixs2; - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) - { - uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; - if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); - } - } - } + SWRenderer *swr = (SWRenderer*) renderer; + + bool flipX = false, flipY = false; + if (dw < 0) { dx += dw; dw = -dw; flipX = true; } + if (dh < 0) { dy += dh; dh = -dh; flipY = true; } + + //basic out of bound checks + if (dw == 0 || dh == 0) return; + if (sw == 0) sw = 1; + if (sh == 0) sh = 1; + if (dx + dw <= swr->portX) return; + if (dy + dh <= swr->portY) return; + if (dx >= swr->maxX) return; + if (dy >= swr->maxY) return; + + int odw = dw, odh = dh; + int osw = sw, osh = sh; + + int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; + + //out of bounds adjustment checks + int diffxl = 0, diffyl = 0, diffxu = 0, diffyu = 0; + if (dx < minx) { diffxl = minx - dx; dx = minx; dw -= diffxl; } + if (dy < miny) { diffyl = miny - dy; dy = miny; dh -= diffyl; } + if (dx + dw > maxx) { diffxu = dx + dw - maxx; dw -= diffxu; } + if (dy + dh > maxy) { diffyu = dy + dh - maxy; dh -= diffyu; } + + if (diffxl != 0 || diffyl != 0 || diffxu != 0 || diffyu != 0) + { + //adjust source coordinates too + diffxl = (int)((long)diffxl * osw / odw); + diffyl = (int)((long)diffyl * osh / odh); + diffxu = (int)((long)(diffxu + 1) * osw / odw); + diffyu = (int)((long)(diffyu + 1) * osh / odh); + sx += flipX ? diffxu : diffxl; + sy += flipY ? diffyu : diffyl; + sw -= diffxl + diffxu; + sh -= diffyl + diffyu; + if (sw <= 0 || sh <= 0) return; + } + + //clip the source coords into bounds too + if (sx < 0) { sw += sx; sx = 0; } + if (sy < 0) { sh += sy; sy = 0; } + if (sx + sw >= texture->width) { sw = texture->width - sx; } + if (sy + sh >= texture->height) { sh = texture->height - sy; } + + //okay, now we can finally get on with rendering + + int ixs = 0, oxs = 1, iys = 0, oys = 1; + if (flipX) ixs = dw - 1, oxs = -1; + if (flipY) iys = dh - 1, oys = -1; + + // tweak these if stuff doesn't look right + typedef int32_t fixedp_t; + const int fp_prec = 14; + + fixedp_t ystep = (sh == dh) ? (1 << fp_prec) : ((fixedp_t) osh << fp_prec) / odh; + fixedp_t xstep = (sw == dw) ? (1 << fp_prec) : ((fixedp_t) osw << fp_prec) / odw; + fixedp_t oxs2 = oxs * xstep; + fixedp_t oys2 = oys * ystep; + fixedp_t ixs2 = ixs * xstep; + fixedp_t iys2 = iys * ystep; + + if (sw == dw) + { + fixedp_t ys2 = (fixedp_t) iys2; + for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) + { + uintpixel_t* dstline; + const uintpixel_t* srcline; + dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; + if (dh == sh) + srcline = &texture->buffer[(sy + ys) * texture->width + sx]; + else + srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; + + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) + { + uintpixel_t pixel = srcline[xs]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + } + } + } + else + { + fixedp_t ys2 = iys2; + for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) + { + uintpixel_t* dstline; + const uintpixel_t* srcline; + dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; + if (dh == sh) + srcline = &texture->buffer[(sy + ys) * texture->width + sx]; + else + srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; + + fixedp_t xs2 = ixs2; + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) + { + uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + } + } + } } static void swrDrawSprite( - Renderer* renderer, float dx, float dy, float dw, float dh, - SWTexture* texture, int sx, int sy, int sw, int sh, - uint32_t tintColor, float alpha + Renderer* renderer, float dx, float dy, float dw, float dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uint32_t tintColor, float alpha ) { - SWRenderer *swr = (SWRenderer*) renderer; - - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &dw, &dh); - - swrDrawSpriteInternal( - renderer, - swrFloor(dx), - swrFloor(dy), - swrCeiling(dw), - swrCeiling(dh), - texture, - sx, sy, - sw, sh, - swrConvertPixel(tintColor), - swrIntAlpha(alpha) - ); + SWRenderer *swr = (SWRenderer*) renderer; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &dw, &dh); + + swrDrawSpriteInternal( + renderer, + swrFloor(dx), + swrFloor(dy), + swrCeiling(dw), + swrCeiling(dh), + texture, + sx, sy, + sw, sh, + swrConvertPixel(tintColor), + swrIntAlpha(alpha) + ); } static void swrDrawSpriteRotatedInternal( - Renderer* renderer, int dx, int dy, int dw, int dh, - SWTexture* texture, int sx, int sy, int sw, int sh, - uintpixel_t tintColor, int alpha, - float angleDeg, - float pivotX, - float pivotY + Renderer* renderer, int dx, int dy, int dw, int dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uintpixel_t tintColor, int alpha, + float angleDeg, + float pivotX, + float pivotY ) { - SWRenderer* swr = (SWRenderer*) renderer; - float angleRad = -angleDeg * M_PI / 180.0f; - - bool flipX = false, flipY = false; - if (dw < 0) { dw = -dw; dx -= dw; pivotX = dw - pivotX; flipX = true; } - if (dh < 0) { dh = -dh; dy -= dh; pivotY = dh - pivotY; flipY = true; } - - float cosA = cosf(angleRad); - float sinA = sinf(angleRad); - - float cnrx[4], cnry[4]; - cnrx[0] = cnrx[3] = dx; - cnry[0] = cnry[1] = dy; - cnrx[1] = cnrx[2] = dx + dw; - cnry[2] = cnry[3] = dy + dh; - - float pxa = pivotX + dx; - float pya = pivotY + dy; - - float minXf = FLT_MAX, minYf = FLT_MAX, maxXf = -FLT_MAX, maxYf = -FLT_MAX; - for (int i = 0; i < 4; i++) - { - float cxi = cnrx[i] - pxa; - float cyi = cnry[i] - pya; - float rx = cosA * cxi - sinA * cyi + pxa; - float ry = sinA * cxi + cosA * cyi + pya; - if (minXf > rx) minXf = rx; - if (maxXf < rx) maxXf = rx; - if (minYf > ry) minYf = ry; - if (maxYf < ry) maxYf = ry; - } - - // minX, minY, maxX, maxY now represent an AABB of pixels we should loop over - int minX = swrFloor(minXf); - int minY = swrFloor(minYf); - int maxX = swrCeiling(maxXf); - int maxY = swrCeiling(maxYf); - - // basic out-of-bound checks - if (maxX < swr->portX) return; - if (maxY < swr->portY) return; - if (minX >= swr->maxX) return; - if (minY >= swr->maxY) return; - - // however, we'll need to clip it against out of bounds first - int minXc = minX, minYc = minY, maxXc = maxX, maxYc = maxY; - int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; - - if (minXc < minx) minXc = minx; - if (minYc < miny) minYc = miny; - if (maxXc >= maxx) maxXc = maxx; - if (maxYc >= maxy) maxYc = maxy; - - // some final clip checks - if (minXc >= maxXc || minYc >= maxYc) return; - - int sox = flipX ? sw - 1 : 0; - int soy = flipY ? sh - 1 : 0; - int six = flipX ? -1 : 1; - int siy = flipY ? -1 : 1; - - float sw_dw = (float) sw / dw; - float sh_dh = (float) sh / dh; - - for (int cy = minYc; cy < maxYc; cy++) - { - uintpixel_t *dstline = &swr->fb[cy * swr->fbPitch]; - for (int cx = minXc; cx < maxXc; cx++) - { - // we need to determine the texture-space coordinate of cx/cy - float ox = (float) cx + 0.5f - pxa; - float oy = (float) cy + 0.5f - pya; - - // "undo" the rotation - float lx = cosA * ox + sinA * oy; - float ly = -sinA * ox + cosA * oy; - - // turn it into a texture-local coordinate - lx += pxa - dx; - ly += pya - dy; - - if (lx < 0 || ly < 0 || lx >= (float) dw || ly >= (float) dh) continue; - - lx = lx * sw_dw; - ly = ly * sh_dh; - - int tx = (int)(sox + lx * six); - int ty = (int)(soy + ly * siy); - - if (tx < 0) tx = 0; - if (ty < 0) ty = 0; - if (tx >= sw) tx = sw - 1; - if (ty >= sh) ty = sh - 1; - - tx += sx; - ty += sy; - - uintpixel_t src = texture->buffer[ty * texture->width + tx]; - - if (opaque(src)) - alphaBlend(&dstline[cx], tint(tintColor, src), alpha); - } - } + SWRenderer* swr = (SWRenderer*) renderer; + float angleRad = -angleDeg * M_PI / 180.0f; + + bool flipX = false, flipY = false; + if (dw < 0) { dw = -dw; dx -= dw; pivotX = dw - pivotX; flipX = true; } + if (dh < 0) { dh = -dh; dy -= dh; pivotY = dh - pivotY; flipY = true; } + + float cosA = cosf(angleRad); + float sinA = sinf(angleRad); + + float cnrx[4], cnry[4]; + cnrx[0] = cnrx[3] = dx; + cnry[0] = cnry[1] = dy; + cnrx[1] = cnrx[2] = dx + dw; + cnry[2] = cnry[3] = dy + dh; + + float pxa = pivotX + dx; + float pya = pivotY + dy; + + float minXf = FLT_MAX, minYf = FLT_MAX, maxXf = -FLT_MAX, maxYf = -FLT_MAX; + for (int i = 0; i < 4; i++) + { + float cxi = cnrx[i] - pxa; + float cyi = cnry[i] - pya; + float rx = cosA * cxi - sinA * cyi + pxa; + float ry = sinA * cxi + cosA * cyi + pya; + if (minXf > rx) minXf = rx; + if (maxXf < rx) maxXf = rx; + if (minYf > ry) minYf = ry; + if (maxYf < ry) maxYf = ry; + } + + // minX, minY, maxX, maxY now represent an AABB of pixels we should loop over + int minX = swrFloor(minXf); + int minY = swrFloor(minYf); + int maxX = swrCeiling(maxXf); + int maxY = swrCeiling(maxYf); + + // basic out-of-bound checks + if (maxX < swr->portX) return; + if (maxY < swr->portY) return; + if (minX >= swr->maxX) return; + if (minY >= swr->maxY) return; + + // however, we'll need to clip it against out of bounds first + int minXc = minX, minYc = minY, maxXc = maxX, maxYc = maxY; + int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; + + if (minXc < minx) minXc = minx; + if (minYc < miny) minYc = miny; + if (maxXc >= maxx) maxXc = maxx; + if (maxYc >= maxy) maxYc = maxy; + + // some final clip checks + if (minXc >= maxXc || minYc >= maxYc) return; + + int sox = flipX ? sw - 1 : 0; + int soy = flipY ? sh - 1 : 0; + int six = flipX ? -1 : 1; + int siy = flipY ? -1 : 1; + + float sw_dw = (float) sw / dw; + float sh_dh = (float) sh / dh; + + for (int cy = minYc; cy < maxYc; cy++) + { + uintpixel_t *dstline = &swr->fb[cy * swr->fbPitch]; + for (int cx = minXc; cx < maxXc; cx++) + { + // we need to determine the texture-space coordinate of cx/cy + float ox = (float) cx + 0.5f - pxa; + float oy = (float) cy + 0.5f - pya; + + // "undo" the rotation + float lx = cosA * ox + sinA * oy; + float ly = -sinA * ox + cosA * oy; + + // turn it into a texture-local coordinate + lx += pxa - dx; + ly += pya - dy; + + if (lx < 0 || ly < 0 || lx >= (float) dw || ly >= (float) dh) continue; + + lx = lx * sw_dw; + ly = ly * sh_dh; + + int tx = (int)(sox + lx * six); + int ty = (int)(soy + ly * siy); + + if (tx < 0) tx = 0; + if (ty < 0) ty = 0; + if (tx >= sw) tx = sw - 1; + if (ty >= sh) ty = sh - 1; + + tx += sx; + ty += sy; + + uintpixel_t src = texture->buffer[ty * texture->width + tx]; + + if (opaque(src)) + alphaBlend(&dstline[cx], tint(tintColor, src), alpha); + } + } } static void swrDrawSpriteRotated( - Renderer* renderer, float dx, float dy, float dw, float dh, - SWTexture* texture, int sx, int sy, int sw, int sh, - uint32_t tintColor, float alpha, - float angleDeg, - float pivotX, - float pivotY + Renderer* renderer, float dx, float dy, float dw, float dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uint32_t tintColor, float alpha, + float angleDeg, + float pivotX, + float pivotY ) { - SWRenderer* swr = (SWRenderer*) renderer; - - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &pivotX, &pivotY); - swrTransformSizeIfNeeded(swr, &dw, &dh); - - swrDrawSpriteRotatedInternal( - renderer, - swrFloor(dx), - swrFloor(dy), - swrCeiling(dw), - swrCeiling(dh), - texture, - sx, sy, - sw, sh, - swrConvertPixel(tintColor), - swrIntAlpha(alpha), - angleDeg, - pivotX, - pivotY - ); + SWRenderer* swr = (SWRenderer*) renderer; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &pivotX, &pivotY); + swrTransformSizeIfNeeded(swr, &dw, &dh); + + swrDrawSpriteRotatedInternal( + renderer, + swrFloor(dx), + swrFloor(dy), + swrCeiling(dw), + swrCeiling(dh), + texture, + sx, sy, + sw, sh, + swrConvertPixel(tintColor), + swrIntAlpha(alpha), + angleDeg, + pivotX, + pivotY + ); } static void SWRenderer_drawSprite(Renderer* renderer, int32_t tpagIndex, float x, float y, - float originX, float originY, float xscale, float yscale, - float angleDeg, uint32_t color, float alpha) -{ - SWRenderer* swr = (SWRenderer*) renderer; - DataWin* dwin = renderer->dataWin; - - if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) { - fprintf(stderr, "%s: tpagIndex of %d is invalid\n", __func__, tpagIndex); - return; - } - - TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; - int16_t pageId = tpag->texturePageId; - if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) { - fprintf(stderr, "%s: tpagIndex of %d is invalid, as pageId of %d is invalid\n", __func__, tpagIndex, pageId); - return; - } - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) { - fprintf(stderr, "%s: could not ensure texture is loaded, tpagIndex: %d, pageId: %d\n", __func__, tpagIndex, pageId); - return; - } - - int sx = tpag->sourceX; - int sy = tpag->sourceY; - int sw = tpag->sourceWidth; - int sh = tpag->sourceHeight; - - float dx = (float)(tpag->targetX - originX); - float dy = (float)(tpag->targetY - originY); - int dw = (int)(xscale * tpag->targetWidth); - int dh = (int)(yscale * tpag->targetHeight); - dx *= xscale; - dy *= yscale; - dx += x; - dy += y; - - SWTexture* texture = swr->textures[pageId]; - - if (UNLIKELY(swrMustRotate(angleDeg))) - { - float pivotX = (x - dx) * swrSgn(xscale); - float pivotY = (y - dy) * swrSgn(yscale); - - if (tpag->targetWidth != tpag->sourceWidth) - pivotX *= (float)tpag->targetWidth / tpag->sourceWidth; - if (tpag->targetHeight != tpag->sourceHeight) - pivotY *= (float)tpag->targetHeight/ tpag->sourceHeight; - - swrDrawSpriteRotated(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, pivotX, pivotY); - } - else - { - swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); - } + float originX, float originY, float xscale, float yscale, + float angleDeg, uint32_t color, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + DataWin* dwin = renderer->dataWin; + + if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) { + fprintf(stderr, "%s: tpagIndex of %d is invalid\n", __func__, tpagIndex); + return; + } + + TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; + int16_t pageId = tpag->texturePageId; + if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) { + fprintf(stderr, "%s: tpagIndex of %d is invalid, as pageId of %d is invalid\n", __func__, tpagIndex, pageId); + return; + } + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) { + fprintf(stderr, "%s: could not ensure texture is loaded, tpagIndex: %d, pageId: %d\n", __func__, tpagIndex, pageId); + return; + } + + int sx = tpag->sourceX; + int sy = tpag->sourceY; + int sw = tpag->sourceWidth; + int sh = tpag->sourceHeight; + + float dx = (float)(tpag->targetX - originX); + float dy = (float)(tpag->targetY - originY); + int dw = (int)(xscale * tpag->targetWidth); + int dh = (int)(yscale * tpag->targetHeight); + dx *= xscale; + dy *= yscale; + dx += x; + dy += y; + + SWTexture* texture = swr->textures[pageId]; + + if (UNLIKELY(swrMustRotate(angleDeg))) + { + float pivotX = (x - dx) * swrSgn(xscale); + float pivotY = (y - dy) * swrSgn(yscale); + + if (tpag->targetWidth != tpag->sourceWidth) + pivotX *= (float)tpag->targetWidth / tpag->sourceWidth; + if (tpag->targetHeight != tpag->sourceHeight) + pivotY *= (float)tpag->targetHeight/ tpag->sourceHeight; + + swrDrawSpriteRotated(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, pivotX, pivotY); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); + } } static void SWRenderer_drawSpritePart(Renderer* renderer, int32_t tpagIndex, - int32_t srcOffX, int32_t srcOffY, int32_t srcW, int32_t srcH, - float x, float y, float xscale, float yscale, float angleDeg, - float pivotX, float pivotY, uint32_t color, float alpha) -{ - SWRenderer* swr = (SWRenderer*) renderer; - DataWin* dwin = renderer->dataWin; - - if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) return; - - TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; - int16_t pageId = tpag->texturePageId; - if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) return; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; - - int sx = tpag->sourceX + srcOffX; - int sy = tpag->sourceY + srcOffY; - int sw = srcW; - int sh = srcH; - - float dx = x; - float dy = y; - int dw = swrCeiling(xscale * sw); - int dh = swrCeiling(yscale * sh); - - SWTexture* texture = swr->textures[pageId]; - - if (UNLIKELY(swrMustRotate(angleDeg))) - { - swrDrawSpriteRotated(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, pivotX * dw, pivotY * dh); - } - else - { - swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); - } + int32_t srcOffX, int32_t srcOffY, int32_t srcW, int32_t srcH, + float x, float y, float xscale, float yscale, float angleDeg, + float pivotX, float pivotY, uint32_t color, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + DataWin* dwin = renderer->dataWin; + + if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) return; + + TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; + int16_t pageId = tpag->texturePageId; + if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) return; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; + + int sx = tpag->sourceX + srcOffX; + int sy = tpag->sourceY + srcOffY; + int sw = srcW; + int sh = srcH; + + float dx = x; + float dy = y; + int dw = swrCeiling(xscale * sw); + int dh = swrCeiling(yscale * sh); + + SWTexture* texture = swr->textures[pageId]; + + if (UNLIKELY(swrMustRotate(angleDeg))) + { + swrDrawSpriteRotated(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, pivotX * dw, pivotY * dh); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); + } } static void SWRenderer_drawSpritePos(Renderer* renderer, int32_t tpagIndex, - float x1, float y1, float x2, float y2, - float x3, float y3, float x4, float y4, float alpha) + float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, float alpha) { - (void)renderer; (void)tpagIndex; - (void)x1; (void)y1; (void)x2; (void)y2; - (void)x3; (void)y3; (void)x4; (void)y4; (void)alpha; - UNIMP(); + (void)renderer; (void)tpagIndex; + (void)x1; (void)y1; (void)x2; (void)y2; + (void)x3; (void)y3; (void)x4; (void)y4; (void)alpha; + UNIMP(); } static void SWRenderer_drawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, - uint32_t color, float alpha, bool outline) -{ - uintpixel_t pxcolor = swrConvertPixel(color); - - SWRenderer* swr = (SWRenderer*) renderer; - - if (outline) - { - swrDrawRectangle(renderer, x1, y1, x2, y2, pxcolor, alpha); - } - else - { - swrTransformPosIfNeeded(swr, &x1, &y1); - swrTransformPosIfNeeded(swr, &x2, &y2); - - int alphaInt = swrIntAlpha(alpha); - int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); - int xd = x2i - x1i; - int yd = y2i - y1i; - if (xd <= 0 || yd <= 0) return; - - for (int y = 0; y <= yd; y++) { - swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor, pxcolor, alphaInt); - } - } + uint32_t color, float alpha, bool outline) +{ + uintpixel_t pxcolor = swrConvertPixel(color); + + SWRenderer* swr = (SWRenderer*) renderer; + + if (outline) + { + swrDrawRectangle(renderer, x1, y1, x2, y2, pxcolor, alpha); + } + else + { + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + + int alphaInt = swrIntAlpha(alpha); + int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); + int xd = x2i - x1i; + int yd = y2i - y1i; + if (xd <= 0 || yd <= 0) return; + + for (int y = 0; y <= yd; y++) { + swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor, pxcolor, alphaInt); + } + } } static void SWRenderer_drawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, - uint32_t color1, uint32_t color2, uint32_t color3, uint32_t color4, - float alpha, bool outline) -{ - uintpixel_t pxcolor1 = swrConvertPixel(color1); - uintpixel_t pxcolor2 = swrConvertPixel(color2); - uintpixel_t pxcolor3 = swrConvertPixel(color3); - uintpixel_t pxcolor4 = swrConvertPixel(color4); - - SWRenderer* swr = (SWRenderer*) renderer; - - if (outline) - { - swrDrawRectangleColor(renderer, x1, y1, x2, y2, pxcolor1, pxcolor2, pxcolor3, pxcolor4, alpha); - } - else - { - swrTransformPosIfNeeded(swr, &x1, &y1); - swrTransformPosIfNeeded(swr, &x2, &y2); - - int alphaInt = swrIntAlpha(alpha); - int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); - int xd = x2i - x1i; - int yd = y2i - y1i; - if (xd <= 0 || yd <= 0) return; - - // TODO: blending vertically - for (int y = 0; y <= yd; y++) { - swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor1, pxcolor2, alphaInt); - } - } + uint32_t color1, uint32_t color2, uint32_t color3, uint32_t color4, + float alpha, bool outline) +{ + uintpixel_t pxcolor1 = swrConvertPixel(color1); + uintpixel_t pxcolor2 = swrConvertPixel(color2); + uintpixel_t pxcolor3 = swrConvertPixel(color3); + uintpixel_t pxcolor4 = swrConvertPixel(color4); + + SWRenderer* swr = (SWRenderer*) renderer; + + if (outline) + { + swrDrawRectangleColor(renderer, x1, y1, x2, y2, pxcolor1, pxcolor2, pxcolor3, pxcolor4, alpha); + } + else + { + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + + int alphaInt = swrIntAlpha(alpha); + int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); + int xd = x2i - x1i; + int yd = y2i - y1i; + if (xd <= 0 || yd <= 0) return; + + // TODO: blending vertically + for (int y = 0; y <= yd; y++) { + swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor1, pxcolor2, alphaInt); + } + } } static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2, float y2, - float width, uint32_t color, float alpha) + float width, uint32_t color, float alpha) { - (void)renderer; (void)x1; (void)y1; (void)x2; (void)y2; - (void)width; (void)color; (void)alpha; - - uintpixel_t colorCvt = swrConvertPixel(color); - swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha); + (void)renderer; (void)x1; (void)y1; (void)x2; (void)y2; + (void)width; (void)color; (void)alpha; + + uintpixel_t colorCvt = swrConvertPixel(color); + swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha); } static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uint32_t color1, uint32_t color2, uint32_t color3, int alpha) { - // TODO: update this - (void) color2; - (void) color3; - - // Figure out the maximum Y extent of the triangle. - // (Note that we know yup is the minimum.) - int xmid, ymid, xmid2 = xup, xmax, ymax; - if (yleft < yright) { - xmax = xright, ymax = yright; - xmid = xleft, ymid = yleft; - if (yright != yup) - xmid2 = xup + (xright - xup) * (ymid - yup) / (yright - yup); - } else { - xmax = xleft, ymax = yleft; - xmid = xright, ymid = yright; - if (yleft != yup) - xmid2 = xup + (xleft - xup) * (ymid - yup) / (yleft - yup); - } - - for (int y = yup; y < ymax; y++) - { - if (y < 0) continue; - if (y >= swr->height) break; - - int x1 = xup, x2 = xup; - if (y <= ymid) - { - // Lines: between up and mid, and between up and max - if (ymid != yup) - x1 = xup + (xmid - xup) * (y - yup) / (ymid - yup); - - if (ymid != yup) - x2 = xup + (xmid2 - xup) * (y - yup) / (ymid - yup); - } - else - { - // Lines: between mid and max, and between up and max - if (ymax != yup) - x1 = xup + (xmax - xup) * (y - yup) / (ymax - yup); - - if (ymax != ymid) - x2 = xmid + (xmax - xmid) * (y - ymid) / (ymax - ymid); - } - - if (x1 >= x2) { - int tmp = x1; - x1 = x2; - x2 = tmp; - } - - if (x1 < swr->portX) x1 = swr->portX; - if (x1 >= swr->maxX) continue; - if (x2 < swr->portX) continue; - if (x2 >= swr->maxX) x2 = swr->maxX - 1; - if (x1 > x2) continue; - - uintpixel_t* line = &swr->fb[y * swr->width]; - for (int x = x1; x < x2; x++) { - alphaBlend(&line[x], color1, alpha); - } - } + // TODO: update this + (void) color2; + (void) color3; + + // Figure out the maximum Y extent of the triangle. + // (Note that we know yup is the minimum.) + int xmid, ymid, xmid2 = xup, xmax, ymax; + if (yleft < yright) { + xmax = xright, ymax = yright; + xmid = xleft, ymid = yleft; + if (yright != yup) + xmid2 = xup + (xright - xup) * (ymid - yup) / (yright - yup); + } else { + xmax = xleft, ymax = yleft; + xmid = xright, ymid = yright; + if (yleft != yup) + xmid2 = xup + (xleft - xup) * (ymid - yup) / (yleft - yup); + } + + for (int y = yup; y < ymax; y++) + { + if (y < 0) continue; + if (y >= swr->height) break; + + int x1 = xup, x2 = xup; + if (y <= ymid) + { + // Lines: between up and mid, and between up and max + if (ymid != yup) + x1 = xup + (xmid - xup) * (y - yup) / (ymid - yup); + + if (ymid != yup) + x2 = xup + (xmid2 - xup) * (y - yup) / (ymid - yup); + } + else + { + // Lines: between mid and max, and between up and max + if (ymax != yup) + x1 = xup + (xmax - xup) * (y - yup) / (ymax - yup); + + if (ymax != ymid) + x2 = xmid + (xmax - xmid) * (y - ymid) / (ymax - ymid); + } + + if (x1 >= x2) { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + + if (x1 < swr->portX) x1 = swr->portX; + if (x1 >= swr->maxX) continue; + if (x2 < swr->portX) continue; + if (x2 >= swr->maxX) x2 = swr->maxX - 1; + if (x1 > x2) continue; + + uintpixel_t* line = &swr->fb[y * swr->width]; + for (int x = x1; x < x2; x++) { + alphaBlend(&line[x], color1, alpha); + } + } } static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color1, uint32_t color2, uint32_t color3, float alpha) { - float xup, yup, xleft, yleft, xright, yright; - uint32_t colorup, colorleft, colorright; - - SWRenderer* swr = (SWRenderer*) renderer; - swrTransformPosIfNeeded(swr, &x1, &y1); - swrTransformPosIfNeeded(swr, &x2, &y2); - swrTransformPosIfNeeded(swr, &x3, &y3); - - //which vertex is higher? - xup = x1, yup = y1; colorup = color1; - xleft = x2, yleft = y2; colorleft = color2; - xright = x3, yright = y3; colorright = color3; - if (yup > y2) { - xup = x2, yup = y2, colorup = color2; - xleft = x1, yleft = y1, colorleft = color1; - //xright = x3, yright = y3; - } - if (yup > y3) { - xup = x3, yup = y3, colorup = color3; - xleft = x1, yleft = y1, colorleft = color1; - xright = x2, yright = y2, colorright = color2; - } - - if (xleft > xright) { - float tmp = xleft; - xleft = xright; - xright = tmp; - tmp = yleft; - yleft = yright; - yright = tmp; - uint32_t tmp2 = colorleft; - colorleft = colorright; - colorright = tmp2; - } - - swrDrawTriangleInternal( - swr, - swrFloor(xup), swrFloor(yup), - swrFloor(xleft), swrCeiling(yleft), - swrFloor(xright), swrCeiling(yright), - swrConvertPixel(colorup), - swrConvertPixel(colorleft), - swrConvertPixel(colorright), - swrIntAlpha(alpha) - ); + float xup, yup, xleft, yleft, xright, yright; + uint32_t colorup, colorleft, colorright; + + SWRenderer* swr = (SWRenderer*) renderer; + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + swrTransformPosIfNeeded(swr, &x3, &y3); + + //which vertex is higher? + xup = x1, yup = y1; colorup = color1; + xleft = x2, yleft = y2; colorleft = color2; + xright = x3, yright = y3; colorright = color3; + if (yup > y2) { + xup = x2, yup = y2, colorup = color2; + xleft = x1, yleft = y1, colorleft = color1; + //xright = x3, yright = y3; + } + if (yup > y3) { + xup = x3, yup = y3, colorup = color3; + xleft = x1, yleft = y1, colorleft = color1; + xright = x2, yright = y2, colorright = color2; + } + + if (xleft > xright) { + float tmp = xleft; + xleft = xright; + xright = tmp; + tmp = yleft; + yleft = yright; + yright = tmp; + uint32_t tmp2 = colorleft; + colorleft = colorright; + colorright = tmp2; + } + + swrDrawTriangleInternal( + swr, + swrFloor(xup), swrFloor(yup), + swrFloor(xleft), swrCeiling(yleft), + swrFloor(xright), swrCeiling(yright), + swrConvertPixel(colorup), + swrConvertPixel(colorleft), + swrConvertPixel(colorright), + swrIntAlpha(alpha) + ); } static void SWRenderer_drawTriangle(Renderer* renderer, @@ -1563,463 +1563,463 @@ static void SWRenderer_drawTriangle(Renderer* renderer, uint32_t color1, uint32_t color2, uint32_t color3, float alpha, bool outline) { - if (outline) - { - uintpixel_t color1cvt = swrConvertPixel(color1); - uintpixel_t color2cvt = swrConvertPixel(color2); - uintpixel_t color3cvt = swrConvertPixel(color3); - swrDrawLine(renderer, x1, y1, x2, y2, 1, color1cvt, color2cvt, renderer->drawAlpha); - swrDrawLine(renderer, x1, y1, x3, y3, 1, color1cvt, color3cvt, renderer->drawAlpha); - swrDrawLine(renderer, x2, y2, x3, y3, 1, color3cvt, color3cvt, renderer->drawAlpha); - } - else - { - swrDrawTriangle(renderer, x1, y1, x2, y2, x3, y3, color1, color2, color3, alpha); - } + if (outline) + { + uintpixel_t color1cvt = swrConvertPixel(color1); + uintpixel_t color2cvt = swrConvertPixel(color2); + uintpixel_t color3cvt = swrConvertPixel(color3); + swrDrawLine(renderer, x1, y1, x2, y2, 1, color1cvt, color2cvt, renderer->drawAlpha); + swrDrawLine(renderer, x1, y1, x3, y3, 1, color1cvt, color3cvt, renderer->drawAlpha); + swrDrawLine(renderer, x2, y2, x3, y3, 1, color3cvt, color3cvt, renderer->drawAlpha); + } + else + { + swrDrawTriangle(renderer, x1, y1, x2, y2, x3, y3, color1, color2, color3, alpha); + } } static void SWRenderer_drawLineColor(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uint32_t color1, uint32_t color2, float alpha) { - swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color1), swrConvertPixel(color2), alpha); + swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color1), swrConvertPixel(color2), alpha); } typedef struct { - Font* font; - TexturePageItem* fontTpag; // single TPAG for regular fonts (NULL for sprite fonts) - int fontTpagIndex; - int fontPageId; - Sprite* spriteFontSprite; // source sprite for sprite fonts (NULL for regular fonts) + Font* font; + TexturePageItem* fontTpag; // single TPAG for regular fonts (NULL for sprite fonts) + int fontTpagIndex; + int fontPageId; + Sprite* spriteFontSprite; // source sprite for sprite fonts (NULL for regular fonts) } SwrFontState; static bool swrResolveFontState(SWRenderer* swr, DataWin* dw, Font* font, SwrFontState* state) { - state->font = font; - state->fontTpag = NULL; - state->fontTpagIndex = 0; - state->spriteFontSprite = NULL; - - if (font->isSpriteFont) - { - state->spriteFontSprite = &dw->sprt.sprites[font->spriteIndex]; - } - else - { - state->fontTpagIndex = font->tpagIndex; - if (state->fontTpagIndex < 0) return false; - - state->fontTpag = &dw->tpag.items[state->fontTpagIndex]; - int16_t pageId = state->fontTpag->texturePageId; - if (0 > pageId || (uint32_t) pageId >= swr->totalTextureCount) return false; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return false; - - state->fontPageId = pageId; - } - - return true; + state->font = font; + state->fontTpag = NULL; + state->fontTpagIndex = 0; + state->spriteFontSprite = NULL; + + if (font->isSpriteFont) + { + state->spriteFontSprite = &dw->sprt.sprites[font->spriteIndex]; + } + else + { + state->fontTpagIndex = font->tpagIndex; + if (state->fontTpagIndex < 0) return false; + + state->fontTpag = &dw->tpag.items[state->fontTpagIndex]; + int16_t pageId = state->fontTpag->texturePageId; + if (0 > pageId || (uint32_t) pageId >= swr->totalTextureCount) return false; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return false; + + state->fontPageId = pageId; + } + + return true; } static bool swrResolveGlyph( - SWRenderer* swr, DataWin* dw, SwrFontState* state, FontGlyph* glyph, float cursorX, float cursorY, - int* tpagIndex, int* pageId, int* sx, int* sy, int* sw, int* sh, float* dx, float* dy + SWRenderer* swr, DataWin* dw, SwrFontState* state, FontGlyph* glyph, float cursorX, float cursorY, + int* tpagIndex, int* pageId, int* sx, int* sy, int* sw, int* sh, float* dx, float* dy ) { - Font* font = state->font; - if (font->isSpriteFont && state->spriteFontSprite != NULL) - { - Sprite* sprite = state->spriteFontSprite; - int32_t glyphIndex = (int32_t) (glyph - font->glyphs); - if (0 > glyphIndex || glyphIndex >= (int32_t) sprite->textureCount) return false; - - int32_t tpagIdx = sprite->tpagIndices[glyphIndex]; - if (0 > tpagIdx) return false; - - TexturePageItem* glyphTpag = &dw->tpag.items[tpagIdx]; - int16_t pid = glyphTpag->texturePageId; - if (0 > pid || (uint32_t) pid >= swr->totalTextureCount) return false; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pid)) return false; - - *tpagIndex = tpagIdx; - *pageId = glyphTpag->texturePageId; - - *sx = glyphTpag->sourceX; - *sy = glyphTpag->sourceY; - *sw = glyphTpag->sourceWidth; - *sh = glyphTpag->sourceHeight; - - *dx = cursorX + glyph->offset; - *dy = cursorY + glyphTpag->targetY - sprite->originY; - } - else - { - *tpagIndex = state->fontTpagIndex; - *pageId = state->fontPageId; - - *sx = state->fontTpag->sourceX + glyph->sourceX; - *sy = state->fontTpag->sourceY + glyph->sourceY; - *sw = glyph->sourceWidth; - *sh = glyph->sourceHeight; - - *dx = cursorX + glyph->offset; - *dy = cursorY; - } - - return true; + Font* font = state->font; + if (font->isSpriteFont && state->spriteFontSprite != NULL) + { + Sprite* sprite = state->spriteFontSprite; + int32_t glyphIndex = (int32_t) (glyph - font->glyphs); + if (0 > glyphIndex || glyphIndex >= (int32_t) sprite->textureCount) return false; + + int32_t tpagIdx = sprite->tpagIndices[glyphIndex]; + if (0 > tpagIdx) return false; + + TexturePageItem* glyphTpag = &dw->tpag.items[tpagIdx]; + int16_t pid = glyphTpag->texturePageId; + if (0 > pid || (uint32_t) pid >= swr->totalTextureCount) return false; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pid)) return false; + + *tpagIndex = tpagIdx; + *pageId = glyphTpag->texturePageId; + + *sx = glyphTpag->sourceX; + *sy = glyphTpag->sourceY; + *sw = glyphTpag->sourceWidth; + *sh = glyphTpag->sourceHeight; + + *dx = cursorX + glyph->offset; + *dy = cursorY + glyphTpag->targetY - sprite->originY; + } + else + { + *tpagIndex = state->fontTpagIndex; + *pageId = state->fontPageId; + + *sx = state->fontTpag->sourceX + glyph->sourceX; + *sy = state->fontTpag->sourceY + glyph->sourceY; + *sw = glyph->sourceWidth; + *sh = glyph->sourceHeight; + + *dx = cursorX + glyph->offset; + *dy = cursorY; + } + + return true; } static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation) { - Renderer* renderer = &swr->base; - DataWin* dwin = renderer->dataWin; - - int32_t fontIndex = renderer->drawFont; - if (0 > fontIndex || dwin->font.count <= (uint32_t) fontIndex) return; - - Font* font = &dwin->font.fonts[fontIndex]; - - SwrFontState fontState; - memset(&fontState, 0, sizeof fontState); // silence warning treated as error - - if (!swrResolveFontState(swr, dwin, font, &fontState)) return; - - // TODO: do we need to mirror the way the text scrolls too?! - float cosA = 1.0f, sinA = 0.0f, angleRad = 0.0f; - bool mustRotate = swrMustRotateTolerant(angleDeg); - if (UNLIKELY(mustRotate)) - { - angleRad = -angleDeg * M_PI / 180.0f; - cosA = cosf(angleRad); - sinA = sinf(angleRad); - } - - int textLen = (int) strlen(text); - int lineCount = TextUtils_countLines(text, textLen); + Renderer* renderer = &swr->base; + DataWin* dwin = renderer->dataWin; + + int32_t fontIndex = renderer->drawFont; + if (0 > fontIndex || dwin->font.count <= (uint32_t) fontIndex) return; + + Font* font = &dwin->font.fonts[fontIndex]; + + SwrFontState fontState; + memset(&fontState, 0, sizeof fontState); // silence warning treated as error + + if (!swrResolveFontState(swr, dwin, font, &fontState)) return; + + // TODO: do we need to mirror the way the text scrolls too?! + float cosA = 1.0f, sinA = 0.0f, angleRad = 0.0f; + bool mustRotate = swrMustRotateTolerant(angleDeg); + if (UNLIKELY(mustRotate)) + { + angleRad = -angleDeg * M_PI / 180.0f; + cosA = cosf(angleRad); + sinA = sinf(angleRad); + } + + int textLen = (int) strlen(text); + int lineCount = TextUtils_countLines(text, textLen); float lineStride = (0.0f > lineSeparation) ? TextUtils_lineStride(font) : (lineSeparation / (font->scaleY != 0.0f ? font->scaleY : 1.0f)); - // Vertical alignment offset - float totalHeight = (float) lineCount * lineStride; - float valignOffset = 0; - if (renderer->drawValign == 1) valignOffset = -totalHeight / 2.0f; - else if (renderer->drawValign == 2) valignOffset = -totalHeight; - - xscale *= font->scaleX; - yscale *= font->scaleY; - - // Iterate through lines. HTML5 subtracts ascenderOffset from the per-line y offset - // (see yyFont.GR_Text_Draw), shifting glyphs up so the baseline aligns with the drawn y. - float cursorY = valignOffset - (float) font->ascenderOffset; - int32_t lineStart = 0; - - for (int32_t lineIdx = 0; lineCount > lineIdx; lineIdx++) { - // Find end of current line - int32_t lineEnd = lineStart; - while (textLen > lineEnd && !TextUtils_isNewlineChar(text[lineEnd])) { - lineEnd++; - } - int32_t lineLen = lineEnd - lineStart; - - // Horizontal alignment offset for this line - float lineWidth = TextUtils_measureLineWidth(font, text + lineStart, lineLen); - float halignOffset = 0; - if (renderer->drawHalign == 1) halignOffset = -lineWidth / 2.0f; - else if (renderer->drawHalign == 2) halignOffset = -lineWidth; - - float cursorX = halignOffset; - - // Render each glyph in the line - decode each codepoint once and carry it forward as next iteration's ch (also used for kerning) - int32_t pos = 0; - uint16_t ch = 0; - bool hasCh = false; - if (lineLen > pos) { - ch = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); - hasCh = true; - } - - while (hasCh) { - FontGlyph* glyph = TextUtils_findGlyph(font, ch); - - uint16_t nextCh = 0; - bool hasNext = lineLen > pos; - if (hasNext) nextCh = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); - - if (glyph != nullptr) { - bool drewSuccessfully = false; - if (glyph->sourceWidth != 0 && glyph->sourceHeight != 0) { - int fontTpagIndex = 0, pageId = 0; - int sx, sy, sw, sh, dw, dh; - float dx, dy; - if (swrResolveGlyph(swr, dwin, &fontState, glyph, cursorX, cursorY, - &fontTpagIndex, &pageId, &sx, &sy, &sw, &sh, &dx, &dy)) - { - dx *= xscale; dx += x; - dy *= xscale; dy += y; - dw = swrCeiling(xscale * glyph->sourceWidth); - dh = swrCeiling(yscale * glyph->sourceHeight); - - // TODO: at 640x480, for some reason, without this fixup the - // letters in the "Name the fallen human." screen don't shake - dx = roundf(dx * 2) / 2; - dy = roundf(dy * 2) / 2; - - SWTexture* texture = swr->textures[pageId]; - - if (UNLIKELY(mustRotate)) - { - dx -= x; - dy -= y; - float ndx = cosA * dx - sinA * dy; - float ndy = sinA * dx + cosA * dy; - ndx += x; - ndy += y; - swrDrawSpriteRotated(renderer, ndx, ndy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, 0.0f, 0.0f); - } - else - { - swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); - } - - drewSuccessfully = true; - } - } - - cursorX += glyph->shift; - if (drewSuccessfully && hasNext) { - cursorX += TextUtils_getKerningOffset(glyph, nextCh); - } - } - - ch = nextCh; - hasCh = hasNext; - } - - cursorY += lineStride; - // Skip past the newline, treating \r\n and \n\r as single breaks - if (textLen > lineEnd) { - lineStart = TextUtils_skipNewline(text, lineEnd, textLen); - } else { - lineStart = lineEnd; - } - } + // Vertical alignment offset + float totalHeight = (float) lineCount * lineStride; + float valignOffset = 0; + if (renderer->drawValign == 1) valignOffset = -totalHeight / 2.0f; + else if (renderer->drawValign == 2) valignOffset = -totalHeight; + + xscale *= font->scaleX; + yscale *= font->scaleY; + + // Iterate through lines. HTML5 subtracts ascenderOffset from the per-line y offset + // (see yyFont.GR_Text_Draw), shifting glyphs up so the baseline aligns with the drawn y. + float cursorY = valignOffset - (float) font->ascenderOffset; + int32_t lineStart = 0; + + for (int32_t lineIdx = 0; lineCount > lineIdx; lineIdx++) { + // Find end of current line + int32_t lineEnd = lineStart; + while (textLen > lineEnd && !TextUtils_isNewlineChar(text[lineEnd])) { + lineEnd++; + } + int32_t lineLen = lineEnd - lineStart; + + // Horizontal alignment offset for this line + float lineWidth = TextUtils_measureLineWidth(font, text + lineStart, lineLen); + float halignOffset = 0; + if (renderer->drawHalign == 1) halignOffset = -lineWidth / 2.0f; + else if (renderer->drawHalign == 2) halignOffset = -lineWidth; + + float cursorX = halignOffset; + + // Render each glyph in the line - decode each codepoint once and carry it forward as next iteration's ch (also used for kerning) + int32_t pos = 0; + uint16_t ch = 0; + bool hasCh = false; + if (lineLen > pos) { + ch = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); + hasCh = true; + } + + while (hasCh) { + FontGlyph* glyph = TextUtils_findGlyph(font, ch); + + uint16_t nextCh = 0; + bool hasNext = lineLen > pos; + if (hasNext) nextCh = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); + + if (glyph != nullptr) { + bool drewSuccessfully = false; + if (glyph->sourceWidth != 0 && glyph->sourceHeight != 0) { + int fontTpagIndex = 0, pageId = 0; + int sx, sy, sw, sh, dw, dh; + float dx, dy; + if (swrResolveGlyph(swr, dwin, &fontState, glyph, cursorX, cursorY, + &fontTpagIndex, &pageId, &sx, &sy, &sw, &sh, &dx, &dy)) + { + dx *= xscale; dx += x; + dy *= xscale; dy += y; + dw = swrCeiling(xscale * glyph->sourceWidth); + dh = swrCeiling(yscale * glyph->sourceHeight); + + // TODO: at 640x480, for some reason, without this fixup the + // letters in the "Name the fallen human." screen don't shake + dx = roundf(dx * 2) / 2; + dy = roundf(dy * 2) / 2; + + SWTexture* texture = swr->textures[pageId]; + + if (UNLIKELY(mustRotate)) + { + dx -= x; + dy -= y; + float ndx = cosA * dx - sinA * dy; + float ndy = sinA * dx + cosA * dy; + ndx += x; + ndy += y; + swrDrawSpriteRotated(renderer, ndx, ndy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, 0.0f, 0.0f); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); + } + + drewSuccessfully = true; + } + } + + cursorX += glyph->shift; + if (drewSuccessfully && hasNext) { + cursorX += TextUtils_getKerningOffset(glyph, nextCh); + } + } + + ch = nextCh; + hasCh = hasNext; + } + + cursorY += lineStride; + // Skip past the newline, treating \r\n and \n\r as single breaks + if (textLen > lineEnd) { + lineStart = TextUtils_skipNewline(text, lineEnd, textLen); + } else { + lineStart = lineEnd; + } + } } static void SWRenderer_drawText(Renderer* renderer, const char* text, float x, float y, - float xscale, float yscale, float angleDeg, float lineSeparation) + float xscale, float yscale, float angleDeg, float lineSeparation) { - SWRenderer* swr = (SWRenderer*) renderer; - swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, renderer->drawColor, renderer->drawAlpha, lineSeparation); + SWRenderer* swr = (SWRenderer*) renderer; + swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, renderer->drawColor, renderer->drawAlpha, lineSeparation); } static void SWRenderer_drawTextColor(Renderer* renderer, const char* text, float x, float y, - float xscale, float yscale, float angleDeg, - int32_t c1, int32_t c2, int32_t c3, int32_t c4, MAYBE_UNUSED float alpha, - float lineSeparation) + float xscale, float yscale, float angleDeg, + int32_t c1, int32_t c2, int32_t c3, int32_t c4, MAYBE_UNUSED float alpha, + float lineSeparation) { - SWRenderer* swr = (SWRenderer*) renderer; - - // TODO: allow c2, c3, c4 - (void) c2; - (void) c3; - (void) c4; - - swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, c1, renderer->drawAlpha, lineSeparation); + SWRenderer* swr = (SWRenderer*) renderer; + + // TODO: allow c2, c3, c4 + (void) c2; + (void) c3; + (void) c4; + + swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, c1, renderer->drawAlpha, lineSeparation); } static void SWRenderer_drawSpriteTiled(Renderer* renderer, int32_t tpagIndex, - float originX, float originY, float x, float y, - float xscale, float yscale, bool tileX, bool tileY, - float roomW, float roomH, uint32_t color, float alpha) -{ - SWRenderer* swr = (SWRenderer*) renderer; - DataWin* dwin = renderer->dataWin; - - if (0 > tpagIndex || dwin->tpag.count <= (uint32_t) tpagIndex) return; - - TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; - int16_t pageId = tpag->texturePageId; - if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) return; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; - - float axScale = fabsf(xscale); - float ayScale = fabsf(yscale); - float tileW = (float) tpag->boundingWidth * axScale; - float tileH = (float) tpag->boundingHeight * ayScale; - if (0 >= tileW || 0 >= tileH) return; - - float startX, endX, startY, endY; - if (tileX) { - startX = fmodf(x - originX * axScale, tileW); - if (startX > 0) startX -= tileW; - endX = roomW; - } else { - startX = x - originX * axScale; - endX = startX + tileW; - } - if (tileY) { - startY = fmodf(y - originY * ayScale, tileH); - if (startY > 0) startY -= tileH; - endY = roomH; - } else { - startY = y - originY * ayScale; - endY = startY + tileH; - } - - int sx = tpag->sourceX; - int sy = tpag->sourceY; - int sw = tpag->sourceWidth; - int sh = tpag->sourceHeight; - - int localX0 = tpag->targetX - originX; - int localY0 = tpag->targetY - originY; - int localX1 = localX0 + tpag->sourceWidth; - int localY1 = localY0 + tpag->sourceHeight; - int sx0 = xscale * localX0; - int sy0 = yscale * localY0; - int sx1 = xscale * localX1; - int sy1 = yscale * localY1; - - for (int dy = startY; endY > dy; dy += tileH) { - int cy = dy + (int)(originY * ayScale); - int vy0 = cy + sy0; - int vy1 = cy + sy1; - int dh = vy1 - vy0; - - for (int dx = startX; endX > dx; dx += tileW) { - int cx = dx + (int)(originX * axScale); - int vx0 = cx + sx0; - int vx1 = cx + sx1; - int dw = vx1 - vx0; - - swrDrawSprite(renderer, vx0, vy0, dw, dh, swr->textures[pageId], sx, sy, sw, sh, color, alpha); - } - } + float originX, float originY, float x, float y, + float xscale, float yscale, bool tileX, bool tileY, + float roomW, float roomH, uint32_t color, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + DataWin* dwin = renderer->dataWin; + + if (0 > tpagIndex || dwin->tpag.count <= (uint32_t) tpagIndex) return; + + TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; + int16_t pageId = tpag->texturePageId; + if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) return; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return; + + float axScale = fabsf(xscale); + float ayScale = fabsf(yscale); + float tileW = (float) tpag->boundingWidth * axScale; + float tileH = (float) tpag->boundingHeight * ayScale; + if (0 >= tileW || 0 >= tileH) return; + + float startX, endX, startY, endY; + if (tileX) { + startX = fmodf(x - originX * axScale, tileW); + if (startX > 0) startX -= tileW; + endX = roomW; + } else { + startX = x - originX * axScale; + endX = startX + tileW; + } + if (tileY) { + startY = fmodf(y - originY * ayScale, tileH); + if (startY > 0) startY -= tileH; + endY = roomH; + } else { + startY = y - originY * ayScale; + endY = startY + tileH; + } + + int sx = tpag->sourceX; + int sy = tpag->sourceY; + int sw = tpag->sourceWidth; + int sh = tpag->sourceHeight; + + int localX0 = tpag->targetX - originX; + int localY0 = tpag->targetY - originY; + int localX1 = localX0 + tpag->sourceWidth; + int localY1 = localY0 + tpag->sourceHeight; + int sx0 = xscale * localX0; + int sy0 = yscale * localY0; + int sx1 = xscale * localX1; + int sy1 = yscale * localY1; + + for (int dy = startY; endY > dy; dy += tileH) { + int cy = dy + (int)(originY * ayScale); + int vy0 = cy + sy0; + int vy1 = cy + sy1; + int dh = vy1 - vy0; + + for (int dx = startX; endX > dx; dx += tileW) { + int cx = dx + (int)(originX * axScale); + int vx0 = cx + sx0; + int vx1 = cx + sx1; + int dw = vx1 - vx0; + + swrDrawSprite(renderer, vx0, vy0, dw, dh, swr->textures[pageId], sx, sy, sw, sh, color, alpha); + } + } } static void SWRenderer_drawSurfaceTiled(Renderer* renderer, int32_t surfaceID, float x, float y, float xscale, float yscale, float roomW, float roomH, uint32_t color, float alpha) { - SWRenderer* swr = (SWRenderer*) renderer; + SWRenderer* swr = (SWRenderer*) renderer; - if (0 > surfaceID || swr->surfaceCount <= (size_t) surfaceID) return; + if (0 > surfaceID || swr->surfaceCount <= (size_t) surfaceID) return; - SWTexture* surface = swr->surfaces[surfaceID]; - if (!surface) return; + SWTexture* surface = swr->surfaces[surfaceID]; + if (!surface) return; - float axScale = fabsf(xscale); - float ayScale = fabsf(yscale); - float tileW = (float) surface->width * axScale; - float tileH = (float) surface->height * ayScale; - if (0 >= tileW || 0 >= tileH) return; + float axScale = fabsf(xscale); + float ayScale = fabsf(yscale); + float tileW = (float) surface->width * axScale; + float tileH = (float) surface->height * ayScale; + if (0 >= tileW || 0 >= tileH) return; float originX = 0, originY = 0; - float startX, endX, startY, endY; + float startX, endX, startY, endY; startX = fmodf(x - originX * axScale, tileW); if (startX > 0) startX -= tileW; endX = roomW; startY = fmodf(y - originY * ayScale, tileH); if (startY > 0) startY -= tileH; endY = roomH; - + int sx = 0, sy = 0; - int sw = surface->width; - int sh = surface->height; - - int localX0 = -originX; - int localY0 = -originY; - int localX1 = localX0 + surface->width; - int localY1 = localY0 + surface->height; - int sx0 = xscale * localX0; - int sy0 = yscale * localY0; - int sx1 = xscale * localX1; - int sy1 = yscale * localY1; - - for (int dy = startY; endY > dy; dy += tileH) { - int cy = dy + (int)(originY * ayScale); - int vy0 = cy + sy0; - int vy1 = cy + sy1; - int dh = vy1 - vy0; - - for (int dx = startX; endX > dx; dx += tileW) { - int cx = dx + (int)(originX * axScale); - int vx0 = cx + sx0; - int vx1 = cx + sx1; - int dw = vx1 - vx0; - - swrDrawSprite(renderer, vx0, vy0, dw, dh, surface, sx, sy, sw, sh, color, alpha); - } - } + int sw = surface->width; + int sh = surface->height; + + int localX0 = -originX; + int localY0 = -originY; + int localX1 = localX0 + surface->width; + int localY1 = localY0 + surface->height; + int sx0 = xscale * localX0; + int sy0 = yscale * localY0; + int sx1 = xscale * localX1; + int sy1 = yscale * localY1; + + for (int dy = startY; endY > dy; dy += tileH) { + int cy = dy + (int)(originY * ayScale); + int vy0 = cy + sy0; + int vy1 = cy + sy1; + int dh = vy1 - vy0; + + for (int dx = startX; endX > dx; dx += tileW) { + int cx = dx + (int)(originX * axScale); + int vx0 = cx + sx0; + int vx1 = cx + sx1; + int dw = vx1 - vx0; + + swrDrawSprite(renderer, vx0, vy0, dw, dh, surface, sx, sy, sw, sh, color, alpha); + } + } } static void SWRenderer_flush(Renderer* renderer) { - (void)renderer; - UNIMP(); + (void)renderer; + UNIMP(); } static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alpha) { - (void)renderer; (void)color; (void)alpha; - UNIMP(); + (void)renderer; (void)color; (void)alpha; + UNIMP(); } static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) { - UNIMP(); - (void)renderer; (void)mode; + UNIMP(); + (void)renderer; (void)mode; } static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, int32_t dfactor, int32_t sfactor_alpha, int32_t dfactor_alpha) { - UNIMP(); - (void)renderer; (void)sfactor; (void)dfactor; (void)sfactor_alpha; (void)dfactor_alpha; + UNIMP(); + (void)renderer; (void)sfactor; (void)dfactor; (void)sfactor_alpha; (void)dfactor_alpha; } static void SWRenderer_gpuSetBlendEnable(Renderer* renderer, bool enable) { - UNIMP(); - (void)renderer; (void)enable; + UNIMP(); + (void)renderer; (void)enable; } static void SWRenderer_gpuSetAlphaTestEnable(Renderer* renderer, bool enable) { - UNIMP(); - (void)renderer; (void)enable; + UNIMP(); + (void)renderer; (void)enable; } static void SWRenderer_gpuSetAlphaTestRef(Renderer* renderer, uint8_t ref) { - UNIMP(); - (void)renderer; (void)ref; + UNIMP(); + (void)renderer; (void)ref; } static void SWRenderer_gpuSetColorWriteEnable(Renderer* renderer, bool red, bool green, bool blue, bool alpha) { - UNIMP(); - (void)renderer; (void)red; (void)green; (void)blue; (void)alpha; + UNIMP(); + (void)renderer; (void)red; (void)green; (void)blue; (void)alpha; } static void SWRenderer_gpuGetColorWriteEnable(Renderer* renderer, bool* red, bool* green, bool* blue, bool* alpha) { - UNIMP(); - *red = false; - *green = false; - *blue = false; - *alpha = false; - (void)renderer; (void)red; (void)green; (void)blue; (void)alpha; + UNIMP(); + *red = false; + *green = false; + *blue = false; + *alpha = false; + (void)renderer; (void)red; (void)green; (void)blue; (void)alpha; } static bool SWRenderer_gpuGetBlendEnable(Renderer* renderer) { - UNIMP(); - (void)renderer; - return false; + UNIMP(); + (void)renderer; + return false; } static void SWRenderer_gpuSetFog(Renderer* renderer, bool enable, uint32_t color) { - UNIMP(); - (void)renderer; (void)enable; (void)color; + UNIMP(); + (void)renderer; (void)enable; (void)color; } static int32_t SWRenderer_createSurface(Renderer* renderer, int32_t width, int32_t height) @@ -2060,9 +2060,9 @@ static bool SWRenderer_setRenderTarget(Renderer* renderer, int32_t surfaceID, bo static float SWRenderer_getSurfaceWidth(Renderer* renderer, int32_t surfaceID) { - SWRenderer* swr = (SWRenderer*) renderer; - if (surfaceID == APPLICATION_SURFACE_ID) - return (float)(swr->drawingToSurface ? swr->mainWidth : swr->width); + SWRenderer* swr = (SWRenderer*) renderer; + if (surfaceID == APPLICATION_SURFACE_ID) + return (float)(swr->drawingToSurface ? swr->mainWidth : swr->width); if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) return 0.0f; @@ -2072,25 +2072,25 @@ static float SWRenderer_getSurfaceWidth(Renderer* renderer, int32_t surfaceID) static float SWRenderer_getSurfaceHeight(Renderer* renderer, int32_t surfaceID) { - SWRenderer* swr = (SWRenderer*) renderer; - if (surfaceID == APPLICATION_SURFACE_ID) - return (float)(swr->drawingToSurface ? swr->mainHeight : swr->height); + SWRenderer* swr = (SWRenderer*) renderer; + if (surfaceID == APPLICATION_SURFACE_ID) + return (float)(swr->drawingToSurface ? swr->mainHeight : swr->height); if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) return 0.0f; - + return (float) swr->surfaces[surfaceID]->height; } static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, - int32_t srcLeft, int32_t srcTop, int32_t srcWidth, int32_t srcHeight, - float x, float y, float xscale, float yscale, float angleDeg, - uint32_t color, float alpha) + int32_t srcLeft, int32_t srcTop, int32_t srcWidth, int32_t srcHeight, + float x, float y, float xscale, float yscale, float angleDeg, + uint32_t color, float alpha) { - SWRenderer* swr = (SWRenderer*) renderer; + SWRenderer* swr = (SWRenderer*) renderer; SWTexture* surface, localSurface; - if (surfaceID == APPLICATION_SURFACE_ID) { - localSurface.buffer = swr->drawingToSurface ? swr->mainFb : swr->fb; + if (surfaceID == APPLICATION_SURFACE_ID) { + localSurface.buffer = swr->drawingToSurface ? swr->mainFb : swr->fb; localSurface.width = swr->drawingToSurface ? swr->mainWidth : swr->width; localSurface.height = swr->drawingToSurface ? swr->mainHeight : swr->height; surface = &localSurface; @@ -2102,41 +2102,41 @@ static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, surface = swr->surfaces[surfaceID]; } - - int sx = srcLeft; - int sy = srcTop; - int sw = srcWidth; - int sh = srcHeight; + + int sx = srcLeft; + int sy = srcTop; + int sw = srcWidth; + int sh = srcHeight; int tw = srcWidth * swr->viewW / swr->portW; int th = srcHeight * swr->viewH / swr->portH; - - float dx = x; - float dy = y; - int dw = (int)(xscale * tw); - int dh = (int)(yscale * th); - - if (UNLIKELY(swrMustRotate(angleDeg))) - { - float pivotX = (x - dx) * swrSgn(xscale); - float pivotY = (y - dy) * swrSgn(yscale); - - if (tw != sw) - pivotX *= (float)tw / sw; - if (th != sh) - pivotY *= (float)th / sh; - - swrDrawSpriteRotated(renderer, dx, dy, dw, dh, surface, sx, sy, sw, sh, color, alpha, angleDeg, pivotX, pivotY); - } - else - { - swrDrawSprite(renderer, dx, dy, dw, dh, surface, sx, sy, sw, sh, color, alpha); - } + + float dx = x; + float dy = y; + int dw = (int)(xscale * tw); + int dh = (int)(yscale * th); + + if (UNLIKELY(swrMustRotate(angleDeg))) + { + float pivotX = (x - dx) * swrSgn(xscale); + float pivotY = (y - dy) * swrSgn(yscale); + + if (tw != sw) + pivotX *= (float)tw / sw; + if (th != sh) + pivotY *= (float)th / sh; + + swrDrawSpriteRotated(renderer, dx, dy, dw, dh, surface, sx, sy, sw, sh, color, alpha, angleDeg, pivotX, pivotY); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, surface, sx, sy, sw, sh, color, alpha); + } } static void SWRenderer_surfaceResize(Renderer* renderer, int32_t surfaceID, int32_t width, int32_t height) { - SWRenderer* swr = (SWRenderer*) renderer; + SWRenderer* swr = (SWRenderer*) renderer; if (surfaceID == APPLICATION_SURFACE_ID) { fprintf(stderr, "swr: Don't support resizing the application window with this. There must be another way!\n"); @@ -2154,7 +2154,7 @@ static void SWRenderer_surfaceResize(Renderer* renderer, int32_t surfaceID, int3 static void SWRenderer_surfaceFree(Renderer* renderer, int32_t surfaceID) { - SWRenderer* swr = (SWRenderer*) renderer; + SWRenderer* swr = (SWRenderer*) renderer; if (surfaceID == APPLICATION_SURFACE_ID) { fprintf(stderr, "swr: Don't support resizing the application window with this. There must be another way!\n"); @@ -2171,176 +2171,176 @@ static void SWRenderer_surfaceFree(Renderer* renderer, int32_t surfaceID) } static void SWRenderer_surfaceCopy(Renderer* renderer, - int32_t DestSurfaceID, int32_t DestX, int32_t DestY, - int32_t SrcSurfaceID, int32_t SrcX, int32_t SrcY, - int32_t SrcW, int32_t SrcH, bool part) + int32_t DestSurfaceID, int32_t DestX, int32_t DestY, + int32_t SrcSurfaceID, int32_t SrcX, int32_t SrcY, + int32_t SrcW, int32_t SrcH, bool part) { - UNIMP(); - (void)renderer; - (void)DestSurfaceID; (void)DestX; (void)DestY; - (void)SrcSurfaceID; (void)SrcX; (void)SrcY; - (void)SrcW; (void)SrcH; (void)part; + UNIMP(); + (void)renderer; + (void)DestSurfaceID; (void)DestX; (void)DestY; + (void)SrcSurfaceID; (void)SrcX; (void)SrcY; + (void)SrcW; (void)SrcH; (void)part; } static bool SWRenderer_surfaceGetPixels(Renderer* renderer, int32_t surfaceID, uint8_t* outRGBA) { - UNIMP(); - (void)renderer; (void)surfaceID; (void)outRGBA; - return false; + UNIMP(); + (void)renderer; (void)surfaceID; (void)outRGBA; + return false; } static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t surfaceID, - int32_t x, int32_t y, int32_t w, int32_t h, - bool removeback, bool smooth, - int32_t xorig, int32_t yorig) -{ - SWRenderer* swr = (SWRenderer*) renderer; - - swrTransformPosIntIfNeeded(swr, &x, &y); - swrTransformSizeIntIfNeeded(swr, &w, &h); - swrTransformSizeIntIfNeeded(swr, &xorig, &yorig); - - (void) removeback; - (void) smooth; - - if (surfaceID != -1) { - fprintf(stderr, "%s: Surfaces other than application_surface aren't supported yet!\n", __func__); - return 0; - } - - int32_t texturePageId = swrFindSurfaceTextureSlot(swr); - int32_t tpagIndex = swrFindSurfaceTPagSlot(swr); - if (texturePageId == -1 || tpagIndex == -1) { - fprintf(stderr, "%s: Surface overflow!!\n", __func__); - return 0; - } - - SWTexture* tex = swrCreateTexture(NULL, w, h); - - // grab the pixels. - for (int iy = 0; iy < h; iy++) - { - uintpixel_t* dstline = &tex->buffer[iy * tex->width]; - if ((iy + y) < 0 || (iy + y) >= swr->height) - { - for (int ix = 0; ix < w; ix++) - dstline[ix] = 0; - - continue; - } - - uintpixel_t* srcline = &swr->fb[(iy + y) * swr->width + x]; - - int ix = 0, sx = x; - // left edge - for (; sx < 0 && ix < w; sx++, ix++) - dstline[ix] = 0; - - // in-bounds - for (; sx < swr->width && ix < w; sx++, ix++) + int32_t x, int32_t y, int32_t w, int32_t h, + bool removeback, bool smooth, + int32_t xorig, int32_t yorig) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + swrTransformPosIntIfNeeded(swr, &x, &y); + swrTransformSizeIntIfNeeded(swr, &w, &h); + swrTransformSizeIntIfNeeded(swr, &xorig, &yorig); + + (void) removeback; + (void) smooth; + + if (surfaceID != -1) { + fprintf(stderr, "%s: Surfaces other than application_surface aren't supported yet!\n", __func__); + return 0; + } + + int32_t texturePageId = swrFindSurfaceTextureSlot(swr); + int32_t tpagIndex = swrFindSurfaceTPagSlot(swr); + if (texturePageId == -1 || tpagIndex == -1) { + fprintf(stderr, "%s: Surface overflow!!\n", __func__); + return 0; + } + + SWTexture* tex = swrCreateTexture(NULL, w, h); + + // grab the pixels. + for (int iy = 0; iy < h; iy++) + { + uintpixel_t* dstline = &tex->buffer[iy * tex->width]; + if ((iy + y) < 0 || (iy + y) >= swr->height) + { + for (int ix = 0; ix < w; ix++) + dstline[ix] = 0; + + continue; + } + + uintpixel_t* srcline = &swr->fb[(iy + y) * swr->width + x]; + + int ix = 0, sx = x; + // left edge + for (; sx < 0 && ix < w; sx++, ix++) + dstline[ix] = 0; + + // in-bounds + for (; sx < swr->width && ix < w; sx++, ix++) #if PIXEL_SIZE == 8 - dstline[ix] = srcline[ix]; + dstline[ix] = srcline[ix]; #else - dstline[ix] = srcline[ix] | TRANSPARENT_MASK; + dstline[ix] = srcline[ix] | TRANSPARENT_MASK; #endif - // right edge - for (; ix < w; ix++) - dstline[ix] = 0; - } - - int32_t spriteW = w; - int32_t spriteH = h; - - int32_t targetW = spriteW; - int32_t targetH = spriteH; - swrReverseTransformSizeIntIfNeeded(swr, &targetW, &targetH); - - swr->textures[texturePageId] = tex; - - // TODO[MrPowerGamerBR]: This is supposed to be refactored, not to modify data.win structs directly. - DataWin* dw = swr->base.dataWin; - TexturePageItem* tpag = &dw->tpag.items[tpagIndex]; - tpag->sourceX = 0; - tpag->sourceY = 0; - tpag->sourceWidth = (uint16_t) spriteW; - tpag->sourceHeight = (uint16_t) spriteH; - tpag->targetX = 0; - tpag->targetY = 0; - tpag->targetWidth = (uint16_t) (spriteW * swr->viewW / swr->portW); - tpag->targetHeight = (uint16_t) (spriteH * swr->viewH / swr->portH); - tpag->boundingWidth = (uint16_t) spriteW; - tpag->boundingHeight = (uint16_t) spriteH; - tpag->texturePageId = texturePageId; - - uint32_t spriteIndex = DataWin_allocSpriteSlot(dw, swr->originalSpriteCount); - Sprite* sprite = &dw->sprt.sprites[spriteIndex]; - // name was set by DataWin_allocSpriteSlot ("__newsprite"); don't overwrite it here - sprite->width = (uint32_t) w; - sprite->height = (uint32_t) h; - sprite->originX = xorig; - sprite->originY = yorig; - sprite->textureCount = 1; - sprite->tpagIndices = (int32_t*) safeMalloc(sizeof(int32_t)); - sprite->tpagIndices[0] = (int32_t) tpagIndex; - sprite->maskCount = 0; - sprite->masks = nullptr; - - fprintf(stderr, "%s: Allocated surface sprite with ID %d\n", __func__, spriteIndex); - return spriteIndex; + // right edge + for (; ix < w; ix++) + dstline[ix] = 0; + } + + int32_t spriteW = w; + int32_t spriteH = h; + + int32_t targetW = spriteW; + int32_t targetH = spriteH; + swrReverseTransformSizeIntIfNeeded(swr, &targetW, &targetH); + + swr->textures[texturePageId] = tex; + + // TODO[MrPowerGamerBR]: This is supposed to be refactored, not to modify data.win structs directly. + DataWin* dw = swr->base.dataWin; + TexturePageItem* tpag = &dw->tpag.items[tpagIndex]; + tpag->sourceX = 0; + tpag->sourceY = 0; + tpag->sourceWidth = (uint16_t) spriteW; + tpag->sourceHeight = (uint16_t) spriteH; + tpag->targetX = 0; + tpag->targetY = 0; + tpag->targetWidth = (uint16_t) (spriteW * swr->viewW / swr->portW); + tpag->targetHeight = (uint16_t) (spriteH * swr->viewH / swr->portH); + tpag->boundingWidth = (uint16_t) spriteW; + tpag->boundingHeight = (uint16_t) spriteH; + tpag->texturePageId = texturePageId; + + uint32_t spriteIndex = DataWin_allocSpriteSlot(dw, swr->originalSpriteCount); + Sprite* sprite = &dw->sprt.sprites[spriteIndex]; + // name was set by DataWin_allocSpriteSlot ("__newsprite"); don't overwrite it here + sprite->width = (uint32_t) w; + sprite->height = (uint32_t) h; + sprite->originX = xorig; + sprite->originY = yorig; + sprite->textureCount = 1; + sprite->tpagIndices = (int32_t*) safeMalloc(sizeof(int32_t)); + sprite->tpagIndices[0] = (int32_t) tpagIndex; + sprite->maskCount = 0; + sprite->masks = nullptr; + + fprintf(stderr, "%s: Allocated surface sprite with ID %d\n", __func__, spriteIndex); + return spriteIndex; } static void SWRenderer_deleteSprite(Renderer* renderer, int32_t spriteIndex) { - SWRenderer* swr = (SWRenderer*) renderer; - - DataWin* dw = renderer->dataWin; - if (0 > spriteIndex || dw->sprt.count <= (uint32_t) spriteIndex) return; - - // Refuse to delete original data.win sprites - if (swr->originalSpriteCount > (uint32_t) spriteIndex) { - fprintf(stderr, "%s: Cannot delete sprite with index %d, it's invalid.\n", __func__, spriteIndex); - return; - } - - Sprite* sprite = &dw->sprt.sprites[spriteIndex]; - if (sprite->textureCount == 0) return; // already deleted - - for (uint32_t i = 0; i < sprite->textureCount; i++) - { - int32_t tpagIdx = sprite->tpagIndices[i]; - if (tpagIdx >= 0 && (uint32_t) tpagIdx >= swr->originalTPagCount) { - TexturePageItem* tpag = &dw->tpag.items[tpagIdx]; - int16_t pageId = tpag->texturePageId; - if (pageId >= 0 && swr->totalTextureCount > (uint32_t) pageId) { - swrFreeTexture(swr->textures[pageId]); - swr->textures[pageId] = NULL; - } - // Mark TPAG slot as free for reuse - tpag->texturePageId = -1; - } - } - - free(sprite->tpagIndices); - sprite->tpagIndices = NULL; - - const char* keepName = sprite->name; - memset(sprite, 0, sizeof(Sprite)); - sprite->name = keepName; - - fprintf(stderr, "SWR: Deleted sprite %d\n", spriteIndex); + SWRenderer* swr = (SWRenderer*) renderer; + + DataWin* dw = renderer->dataWin; + if (0 > spriteIndex || dw->sprt.count <= (uint32_t) spriteIndex) return; + + // Refuse to delete original data.win sprites + if (swr->originalSpriteCount > (uint32_t) spriteIndex) { + fprintf(stderr, "%s: Cannot delete sprite with index %d, it's invalid.\n", __func__, spriteIndex); + return; + } + + Sprite* sprite = &dw->sprt.sprites[spriteIndex]; + if (sprite->textureCount == 0) return; // already deleted + + for (uint32_t i = 0; i < sprite->textureCount; i++) + { + int32_t tpagIdx = sprite->tpagIndices[i]; + if (tpagIdx >= 0 && (uint32_t) tpagIdx >= swr->originalTPagCount) { + TexturePageItem* tpag = &dw->tpag.items[tpagIdx]; + int16_t pageId = tpag->texturePageId; + if (pageId >= 0 && swr->totalTextureCount > (uint32_t) pageId) { + swrFreeTexture(swr->textures[pageId]); + swr->textures[pageId] = NULL; + } + // Mark TPAG slot as free for reuse + tpag->texturePageId = -1; + } + } + + free(sprite->tpagIndices); + sprite->tpagIndices = NULL; + + const char* keepName = sprite->name; + memset(sprite, 0, sizeof(Sprite)); + sprite->name = keepName; + + fprintf(stderr, "SWR: Deleted sprite %d\n", spriteIndex); } static void SWRenderer_drawTiledPart(Renderer* renderer, int32_t tpagIndex, - int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, - float dstX, float dstY, float dstW, float dstH, - uint32_t color, float alpha) + int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, + float dstX, float dstY, float dstW, float dstH, + uint32_t color, float alpha) { - UNIMP(); - (void)renderer; (void)tpagIndex; - (void)srcX; (void)srcY; (void)srcW; (void)srcH; - (void)dstX; (void)dstY; (void)dstW; (void)dstH; - (void)color; (void)alpha; + UNIMP(); + (void)renderer; (void)tpagIndex; + (void)srcX; (void)srcY; (void)srcW; (void)srcH; + (void)dstX; (void)dstY; (void)dstW; (void)dstH; + (void)color; (void)alpha; } static int32_t SWRenderer_ensureApplicationSurface(Renderer* renderer, int32_t width, int32_t height) @@ -2358,203 +2358,203 @@ static RendererVtable swrVtable; void SWRenderer_clearFrameBuffer(Renderer* renderer, uint32_t color) { - SWRenderer* swr = (SWRenderer*) renderer; - - uintpixel_t pxcolor = swrConvertPixel(color); - - size_t fbSize = swr->fbPitch; - fbSize *= swr->height; - for (size_t i = 0; i < fbSize; i++) - { - swr->fb[i] = pxcolor; - } + SWRenderer* swr = (SWRenderer*) renderer; + + uintpixel_t pxcolor = swrConvertPixel(color); + + size_t fbSize = swr->fbPitch; + fbSize *= swr->height; + for (size_t i = 0; i < fbSize; i++) + { + swr->fb[i] = pxcolor; + } } static uint32_t SWRenderer_spriteGetTexture(Renderer* renderer, int32_t tpagIndex) { - (void) renderer; + (void) renderer; - return (uint32_t) tpagIndex + 1; + return (uint32_t) tpagIndex + 1; } static uint32_t SWRenderer_surfaceGetTexture(Renderer* renderer, int32_t surfaceID) { - (void) renderer; - (void) surfaceID; + (void) renderer; + (void) surfaceID; - return (uint32_t) -1; + return (uint32_t) -1; } static float SWRenderer_textureGetTexelWidth(Renderer* renderer, uint32_t texID) { - (void) renderer; - (void) texID; - - return 1.0f; + (void) renderer; + (void) texID; + + return 1.0f; } static float SWRenderer_textureGetTexelHeight(Renderer* renderer, uint32_t texID) { - (void) renderer; - (void) texID; - - return 1.0f; + (void) renderer; + (void) texID; + + return 1.0f; } static bool SWRenderer_textureGetUVs(Renderer* renderer, uint32_t texID, float* outUVs) { - (void) renderer; - (void) texID; - (void) outUVs; - - return false; + (void) renderer; + (void) texID; + (void) outUVs; + + return false; } static void SWRenderer_textureSetStage(Renderer* renderer, int32_t slot, uint32_t texID) { - (void) renderer; - (void) slot; - (void) texID; + (void) renderer; + (void) slot; + (void) texID; } static bool SWRenderer_shaderIsCompiled(Renderer* renderer, int32_t shader) { - (void) renderer; - (void) shader; - - return false; + (void) renderer; + (void) shader; + + return false; } static bool SWRenderer_shadersSupported(void) { - return false; + return false; } static void SWRenderer_gpuSetShader(Renderer* renderer, int32_t shaderIndex) { - (void) renderer; - (void) shaderIndex; + (void) renderer; + (void) shaderIndex; } static void SWRenderer_gpuResetShader(Renderer* renderer) { - (void) renderer; + (void) renderer; } static int32_t SWRenderer_shaderGetUniform(Renderer* renderer, int32_t shaderIndex, char* uniform) { - (void) renderer; - (void) shaderIndex; - (void) uniform; - - return 0; + (void) renderer; + (void) shaderIndex; + (void) uniform; + + return 0; } static int32_t SWRenderer_shaderGetSamplerIndex(Renderer* renderer, int32_t shaderIndex, char* uniform) { - (void) renderer; - (void) shaderIndex; - (void) uniform; - - return 0; + (void) renderer; + (void) shaderIndex; + (void) uniform; + + return 0; } static void SWRenderer_shaderSetUniformF(Renderer* renderer, int32_t handle, int32_t count, float value1, float value2, float value3, float value4) { - (void) renderer; - (void) handle; - (void) count; - (void) value1; - (void) value2; - (void) value3; - (void) value4; + (void) renderer; + (void) handle; + (void) count; + (void) value1; + (void) value2; + (void) value3; + (void) value4; } static void SWRenderer_shaderSetUniformI(Renderer* renderer, int32_t handle, int32_t count, int32_t value1, int32_t value2, int32_t value3, int32_t value4) { - (void) renderer; - (void) handle; - (void) count; - (void) value1; - (void) value2; - (void) value3; - (void) value4; + (void) renderer; + (void) handle; + (void) count; + (void) value1; + (void) value2; + (void) value3; + (void) value4; } Renderer* SWRenderer_create(void) { - SWRenderer* swr = (SWRenderer*) safeCalloc(1, sizeof(SWRenderer)); - swr->base.vtable = &swrVtable; - swrVtable.init = SWRenderer_init; - swrVtable.destroy = SWRenderer_destroy; - swrVtable.beginFrame = SWRenderer_beginFrame; - swrVtable.endFrameInit = SWRenderer_endFrameInit; - swrVtable.endFrameEnd = SWRenderer_endFrameEnd; - swrVtable.beginView = SWRenderer_beginView; - swrVtable.endView = SWRenderer_endView; - swrVtable.beginGUI = SWRenderer_beginGUI; - swrVtable.setGuiProjection = SWRenderer_setGuiProjection; - swrVtable.endGUI = SWRenderer_endGUI; - swrVtable.drawSprite = SWRenderer_drawSprite; - swrVtable.drawSpritePart = SWRenderer_drawSpritePart; - swrVtable.drawSpritePos = SWRenderer_drawSpritePos; - swrVtable.drawRectangle = SWRenderer_drawRectangle; - swrVtable.drawRectangleColor = SWRenderer_drawRectangleColor; - swrVtable.drawLine = SWRenderer_drawLine; - swrVtable.drawTriangle = SWRenderer_drawTriangle; - swrVtable.drawLineColor = SWRenderer_drawLineColor; - swrVtable.drawText = SWRenderer_drawText; - swrVtable.drawTextColor = SWRenderer_drawTextColor; - swrVtable.flush = SWRenderer_flush; - swrVtable.clearScreen = SWRenderer_clearScreen; - swrVtable.createSpriteFromSurface = SWRenderer_createSpriteFromSurface; - swrVtable.deleteSprite = SWRenderer_deleteSprite; - swrVtable.gpuSetBlendMode = SWRenderer_gpuSetBlendMode; - swrVtable.gpuSetBlendModeExt = SWRenderer_gpuSetBlendModeExt; - swrVtable.gpuSetBlendEnable = SWRenderer_gpuSetBlendEnable; - swrVtable.gpuSetAlphaTestEnable = SWRenderer_gpuSetAlphaTestEnable; - swrVtable.gpuSetAlphaTestRef = SWRenderer_gpuSetAlphaTestRef; - swrVtable.gpuSetColorWriteEnable = SWRenderer_gpuSetColorWriteEnable; - swrVtable.gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable; - swrVtable.gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable; - swrVtable.gpuSetFog = SWRenderer_gpuSetFog; - swrVtable.drawSpriteTiled = SWRenderer_drawSpriteTiled; - swrVtable.drawSurfaceTiled = SWRenderer_drawSurfaceTiled; - swrVtable.createSurface = SWRenderer_createSurface; - swrVtable.surfaceExists = SWRenderer_surfaceExists; - swrVtable.setRenderTarget = SWRenderer_setRenderTarget; - swrVtable.ensureApplicationSurface = SWRenderer_ensureApplicationSurface; - swrVtable.getSurfaceWidth = SWRenderer_getSurfaceWidth; - swrVtable.getSurfaceHeight = SWRenderer_getSurfaceHeight; - swrVtable.drawSurface = SWRenderer_drawSurface; - swrVtable.surfaceResize = SWRenderer_surfaceResize; - swrVtable.surfaceFree = SWRenderer_surfaceFree; - swrVtable.surfaceCopy = SWRenderer_surfaceCopy; - swrVtable.surfaceGetPixels = SWRenderer_surfaceGetPixels; - swrVtable.drawTiledPart = SWRenderer_drawTiledPart; - swrVtable.spriteGetTexture = SWRenderer_spriteGetTexture; - swrVtable.surfaceGetTexture = SWRenderer_surfaceGetTexture; - swrVtable.textureGetTexelWidth = SWRenderer_textureGetTexelWidth; - swrVtable.textureGetTexelHeight = SWRenderer_textureGetTexelHeight; - swrVtable.textureGetUVs = SWRenderer_textureGetUVs; - swrVtable.textureSetStage = SWRenderer_textureSetStage; - swrVtable.gpuSetShader = SWRenderer_gpuSetShader; - swrVtable.gpuResetShader = SWRenderer_gpuResetShader; - swrVtable.shaderGetUniform = SWRenderer_shaderGetUniform; - swrVtable.shaderGetSamplerIndex = SWRenderer_shaderGetSamplerIndex; - swrVtable.shaderSetUniformF = SWRenderer_shaderSetUniformF; - swrVtable.shaderSetUniformI = SWRenderer_shaderSetUniformI; - swrVtable.shaderIsCompiled = SWRenderer_shaderIsCompiled; - swrVtable.shadersSupported = SWRenderer_shadersSupported; - - swrVtable.drawTile = NULL; - - swr->base.drawColor = 0xFFFFFF; - swr->base.drawAlpha = 1.0f; - swr->base.drawFont = -1; - swr->base.drawHalign = 0; - swr->base.drawValign = 0; - swr->base.circlePrecision = 24; - - return (Renderer*) swr; + SWRenderer* swr = (SWRenderer*) safeCalloc(1, sizeof(SWRenderer)); + swr->base.vtable = &swrVtable; + swrVtable.init = SWRenderer_init; + swrVtable.destroy = SWRenderer_destroy; + swrVtable.beginFrame = SWRenderer_beginFrame; + swrVtable.endFrameInit = SWRenderer_endFrameInit; + swrVtable.endFrameEnd = SWRenderer_endFrameEnd; + swrVtable.beginView = SWRenderer_beginView; + swrVtable.endView = SWRenderer_endView; + swrVtable.beginGUI = SWRenderer_beginGUI; + swrVtable.setGuiProjection = SWRenderer_setGuiProjection; + swrVtable.endGUI = SWRenderer_endGUI; + swrVtable.drawSprite = SWRenderer_drawSprite; + swrVtable.drawSpritePart = SWRenderer_drawSpritePart; + swrVtable.drawSpritePos = SWRenderer_drawSpritePos; + swrVtable.drawRectangle = SWRenderer_drawRectangle; + swrVtable.drawRectangleColor = SWRenderer_drawRectangleColor; + swrVtable.drawLine = SWRenderer_drawLine; + swrVtable.drawTriangle = SWRenderer_drawTriangle; + swrVtable.drawLineColor = SWRenderer_drawLineColor; + swrVtable.drawText = SWRenderer_drawText; + swrVtable.drawTextColor = SWRenderer_drawTextColor; + swrVtable.flush = SWRenderer_flush; + swrVtable.clearScreen = SWRenderer_clearScreen; + swrVtable.createSpriteFromSurface = SWRenderer_createSpriteFromSurface; + swrVtable.deleteSprite = SWRenderer_deleteSprite; + swrVtable.gpuSetBlendMode = SWRenderer_gpuSetBlendMode; + swrVtable.gpuSetBlendModeExt = SWRenderer_gpuSetBlendModeExt; + swrVtable.gpuSetBlendEnable = SWRenderer_gpuSetBlendEnable; + swrVtable.gpuSetAlphaTestEnable = SWRenderer_gpuSetAlphaTestEnable; + swrVtable.gpuSetAlphaTestRef = SWRenderer_gpuSetAlphaTestRef; + swrVtable.gpuSetColorWriteEnable = SWRenderer_gpuSetColorWriteEnable; + swrVtable.gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable; + swrVtable.gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable; + swrVtable.gpuSetFog = SWRenderer_gpuSetFog; + swrVtable.drawSpriteTiled = SWRenderer_drawSpriteTiled; + swrVtable.drawSurfaceTiled = SWRenderer_drawSurfaceTiled; + swrVtable.createSurface = SWRenderer_createSurface; + swrVtable.surfaceExists = SWRenderer_surfaceExists; + swrVtable.setRenderTarget = SWRenderer_setRenderTarget; + swrVtable.ensureApplicationSurface = SWRenderer_ensureApplicationSurface; + swrVtable.getSurfaceWidth = SWRenderer_getSurfaceWidth; + swrVtable.getSurfaceHeight = SWRenderer_getSurfaceHeight; + swrVtable.drawSurface = SWRenderer_drawSurface; + swrVtable.surfaceResize = SWRenderer_surfaceResize; + swrVtable.surfaceFree = SWRenderer_surfaceFree; + swrVtable.surfaceCopy = SWRenderer_surfaceCopy; + swrVtable.surfaceGetPixels = SWRenderer_surfaceGetPixels; + swrVtable.drawTiledPart = SWRenderer_drawTiledPart; + swrVtable.spriteGetTexture = SWRenderer_spriteGetTexture; + swrVtable.surfaceGetTexture = SWRenderer_surfaceGetTexture; + swrVtable.textureGetTexelWidth = SWRenderer_textureGetTexelWidth; + swrVtable.textureGetTexelHeight = SWRenderer_textureGetTexelHeight; + swrVtable.textureGetUVs = SWRenderer_textureGetUVs; + swrVtable.textureSetStage = SWRenderer_textureSetStage; + swrVtable.gpuSetShader = SWRenderer_gpuSetShader; + swrVtable.gpuResetShader = SWRenderer_gpuResetShader; + swrVtable.shaderGetUniform = SWRenderer_shaderGetUniform; + swrVtable.shaderGetSamplerIndex = SWRenderer_shaderGetSamplerIndex; + swrVtable.shaderSetUniformF = SWRenderer_shaderSetUniformF; + swrVtable.shaderSetUniformI = SWRenderer_shaderSetUniformI; + swrVtable.shaderIsCompiled = SWRenderer_shaderIsCompiled; + swrVtable.shadersSupported = SWRenderer_shadersSupported; + + swrVtable.drawTile = NULL; + + swr->base.drawColor = 0xFFFFFF; + swr->base.drawAlpha = 1.0f; + swr->base.drawFont = -1; + swr->base.drawHalign = 0; + swr->base.drawValign = 0; + swr->base.circlePrecision = 24; + + return (Renderer*) swr; } From b76d903ef7dc41b903c735f48c8a7f48aa1f0ce1 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Fri, 10 Jul 2026 20:45:08 +0300 Subject: [PATCH 058/130] * Clean up scaling code, fix a bunch of bugs Now, Alphys' lab camera feed renders properly regardless of resolution and scale. --- src/sw/sw_renderer.c | 78 +++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 7bb25181f..62951f6ff 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -46,6 +46,7 @@ typedef struct int lastViewX, lastViewY, lastViewW, lastViewH; int lastPortX, lastPortY, lastPortW, lastPortH; int lastGameW, lastGameH, lastMaxX, lastMaxY; + float lastScaleX, lastScaleY; SWTexture** textures; SWTexture** surfaces; @@ -62,6 +63,10 @@ typedef struct int viewX, viewY, viewW, viewH; int portX, portY, portW, portH; int gameW, gameH, maxX, maxY; + + int offsetX, offsetY; + float scaleX, scaleY; + float defaultScaleX, defaultScaleY; } SWRenderer; @@ -413,6 +418,7 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam swr->gameW = gameW; swr->gameH = gameH; swr->drawingToSurface = false; + if (swr->width != windowW || swr->height != windowH) { //allocate frame buffer @@ -450,6 +456,11 @@ static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t view float xratio, yratio; + float portviewX = (float) portW / viewW; + float portviewY = (float) portH / viewH; + + int offsetX = 0, offsetY = 0; + if (swr->drawingToSurface) { UNIMP(); xratio = 1.0f; @@ -464,8 +475,9 @@ static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t view int32_t scaledW = (int32_t)(swr->gameW * scale); int32_t scaledH = (int32_t)(swr->gameH * scale); - int32_t offsetX = (swr->width - scaledW) / 2; - int32_t offsetY = (swr->height - scaledH) / 2; + + offsetX = (swr->width - scaledW) / 2; + offsetY = (swr->height - scaledH) / 2; xratio = scale; yratio = scale; @@ -473,6 +485,14 @@ static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t view portX = (int)(portX * xratio) + offsetX; portY = (int)(portY * yratio) + offsetY; } + + swr->scaleX = xratio * portviewX; + swr->scaleY = yratio * portviewY; + swr->offsetX = offsetX; + swr->offsetY = offsetY; + swr->defaultScaleX = xratio; + swr->defaultScaleY = yratio; + portW = (int)(portW * xratio); portH = (int)(portH * yratio); @@ -497,10 +517,16 @@ static void SWRenderer_endView(Renderer* renderer) SWRenderer* swr = (SWRenderer*) renderer; swr->viewActive = false; - swr->portX = swr->viewX = 0; - swr->portY = swr->viewY = 0; + swr->viewX = 0; + swr->viewY = 0; + swr->portX = swr->offsetX; + swr->portY = swr->offsetY; swr->portW = swr->viewW = swr->width; swr->portH = swr->viewH = swr->height; + swr->maxX = swr->portX + swr->portW; + swr->maxY = swr->portY + swr->portH; + swr->scaleX = swr->defaultScaleX; + swr->scaleY = swr->defaultScaleY; } static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView) @@ -532,6 +558,8 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool swr->gameH = swr->lastGameH; swr->maxX = swr->lastMaxX; swr->maxY = swr->lastMaxY; + swr->scaleX = swr->lastScaleX; + swr->scaleY = swr->lastScaleY; } return true; } @@ -563,6 +591,8 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool swr->lastGameH = swr->gameH; swr->lastMaxX = swr->maxX; swr->lastMaxY = swr->maxY; + swr->lastScaleX = swr->scaleX; + swr->lastScaleY = swr->scaleY; } SWTexture* surface = swr->surfaces[targetSurfaceId]; @@ -631,60 +661,48 @@ static int32_t swrFindSurfaceTPagSlot(SWRenderer* swr) static void swrTransformPosIfNeeded(SWRenderer* swr, float* dx, float* dy) { - if (!swr->viewActive) return; - if (dx) { - float xscale = ((float)swr->portW / swr->viewW); *dx -= swr->viewX; - *dx *= xscale; + *dx *= swr->scaleX; *dx += swr->portX; } if (dy) { - float yscale = ((float)swr->portH / swr->viewH); *dy -= swr->viewY; - *dy *= yscale; + *dy *= swr->scaleY; *dy += swr->portY; } } static void swrTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) { - if (!swr->viewActive || !swr->viewW || !swr->viewH) return; - - if (dx) *dx *= ((float)swr->portW / swr->viewW); - if (dy) *dy *= ((float)swr->portH / swr->viewH); + if (dx) *dx *= swr->scaleX; + if (dy) *dy *= swr->scaleY; } static void swrTransformPosIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { - if (!swr->viewActive) return; - if (dx) { *dx -= swr->viewX; - *dx = *dx * swr->portW / swr->viewW; + *dx = (int)(*dx * swr->scaleX); *dx += swr->portX; } if (dy) { *dy -= swr->viewY; - *dy = *dy * swr->portH / swr->viewH; + *dy = (int)(*dy * swr->scaleY); *dy += swr->portY; } } static void swrTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { - if (!swr->viewActive || !swr->viewW || !swr->viewH) return; - - if (dx) *dx = *dx * swr->portW / swr->viewW; - if (dy) *dy = *dy * swr->portH / swr->viewH; + if (dx) *dx = (int)(*dx * swr->scaleX); + if (dy) *dy = (int)(*dy * swr->scaleY); } static void swrReverseTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { - if (!swr->viewActive) return; - - if (dx) *dx = *dx * swr->viewW / swr->portW; - if (dy) *dy = *dy * swr->viewH / swr->portH; + if (dx) *dx = (int)(*dx / swr->scaleX); + if (dy) *dy = (int)(*dy / swr->scaleY); } FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int alpha) @@ -2108,8 +2126,8 @@ static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, int sw = srcWidth; int sh = srcHeight; - int tw = srcWidth * swr->viewW / swr->portW; - int th = srcHeight * swr->viewH / swr->portH; + int tw = (int)(srcWidth * swr->scaleX); + int th = (int)(srcHeight * swr->scaleY); float dx = x; float dy = y; @@ -2267,8 +2285,8 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su tpag->sourceHeight = (uint16_t) spriteH; tpag->targetX = 0; tpag->targetY = 0; - tpag->targetWidth = (uint16_t) (spriteW * swr->viewW / swr->portW); - tpag->targetHeight = (uint16_t) (spriteH * swr->viewH / swr->portH); + tpag->targetWidth = (uint16_t) (spriteW / swr->scaleX); + tpag->targetHeight = (uint16_t) (spriteH / swr->scaleY); tpag->boundingWidth = (uint16_t) spriteW; tpag->boundingHeight = (uint16_t) spriteH; tpag->texturePageId = texturePageId; From 197a717f372ee77e02e5e303957f5b7509cf8644 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Fri, 10 Jul 2026 21:22:37 +0300 Subject: [PATCH 059/130] * More surface fixes. This fixes Undyne the Undying's transition cutscene. --- src/sw/sw_renderer.c | 18 ++++++++++++++---- src/vm_builtins.c | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 62951f6ff..d1d365889 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1053,6 +1053,7 @@ static void swrDrawSpriteInternal( if (sy < 0) { sh += sy; sy = 0; } if (sx + sw >= texture->width) { sw = texture->width - sx; } if (sy + sh >= texture->height) { sh = texture->height - sy; } + if (sw <= 0 || sh <= 0) return; //okay, now we can finally get on with rendering @@ -1369,6 +1370,15 @@ static void SWRenderer_drawSpritePart(Renderer* renderer, int32_t tpagIndex, int dw = swrCeiling(xscale * sw); int dh = swrCeiling(yscale * sh); + if (tpag->sourceWidth != tpag->targetWidth) { + sx = sx * tpag->sourceWidth / tpag->targetWidth; + sw = sw * tpag->sourceWidth / tpag->targetWidth; + } + if (tpag->sourceHeight != tpag->targetHeight) { + sy = sy * tpag->sourceHeight / tpag->targetHeight; + sh = sh * tpag->sourceHeight / tpag->targetHeight; + } + SWTexture* texture = swr->textures[pageId]; if (UNLIKELY(swrMustRotate(angleDeg))) @@ -2080,7 +2090,7 @@ static float SWRenderer_getSurfaceWidth(Renderer* renderer, int32_t surfaceID) { SWRenderer* swr = (SWRenderer*) renderer; if (surfaceID == APPLICATION_SURFACE_ID) - return (float)(swr->drawingToSurface ? swr->mainWidth : swr->width); + return (float)(int)((swr->drawingToSurface ? swr->mainWidth : swr->width) / swr->scaleX); if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) return 0.0f; @@ -2092,7 +2102,7 @@ static float SWRenderer_getSurfaceHeight(Renderer* renderer, int32_t surfaceID) { SWRenderer* swr = (SWRenderer*) renderer; if (surfaceID == APPLICATION_SURFACE_ID) - return (float)(swr->drawingToSurface ? swr->mainHeight : swr->height); + return (float)(int)((swr->drawingToSurface ? swr->mainHeight : swr->height) / swr->scaleY); if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) return 0.0f; @@ -2294,8 +2304,8 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su uint32_t spriteIndex = DataWin_allocSpriteSlot(dw, swr->originalSpriteCount); Sprite* sprite = &dw->sprt.sprites[spriteIndex]; // name was set by DataWin_allocSpriteSlot ("__newsprite"); don't overwrite it here - sprite->width = (uint32_t) w; - sprite->height = (uint32_t) h; + sprite->width = (uint32_t) tpag->targetWidth; + sprite->height = (uint32_t) tpag->targetHeight; sprite->originX = xorig; sprite->originY = yorig; sprite->textureCount = 1; diff --git a/src/vm_builtins.c b/src/vm_builtins.c index 07d4a389e..bd4b098be 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -1242,7 +1242,7 @@ RValue VMBuiltins_getVariable(VMContext* ctx, Instance* inst, int16_t builtinVar return RValue_makeReal((GMLReal) INSTANCE_NOONE); } case BUILTIN_VAR_FPS: - return RValue_makeReal(ctx->dataWin->gen8.gms2FPS); + return RValue_makeReal(30.0f /* ctx->dataWin->gen8.gms2FPS */); case BUILTIN_VAR_DEBUG_MODE: return RValue_makeBool(false); case BUILTIN_VAR_DELTA_TIME: From b585cb79f36b60b5fa07b09168dce10a85a145e6 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 15 Jul 2026 18:57:20 +0300 Subject: [PATCH 060/130] Fix surface switching issues --- src/sw/sw_renderer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index d1d365889..bb9f9de4a 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -539,6 +539,8 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool return true; // restore the original framebuffer + fprintf(stderr, "back to original framebuffer\n"); + swr->drawingToSurface = false; swr->fb = swr->mainFb; swr->width = swr->mainWidth; swr->height = swr->mainHeight; @@ -602,6 +604,14 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool swr->fbPitch = surface->width; swr->drawingToSurface = true; + swr->viewX = swr->portX = 0; + swr->viewY = swr->portY = 0; + swr->maxX = swr->viewW = swr->portW = surface->width; + swr->maxY = swr->viewH = swr->portH = surface->height; + swr->scaleX = swr->scaleY = 1.0f; + + fprintf(stderr, "switching to surface %p, fb %p, %dx%d\n", surface, swr->fb, swr->width, swr->height); + return true; } From 94ed1b8e688fff8bac5923dbd3b519f7b9a0b6a4 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 15 Jul 2026 19:13:45 +0300 Subject: [PATCH 061/130] Implement SWRenderer_clearScreen --- src/sw/sw_renderer.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index bb9f9de4a..bfbc4ac14 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1997,8 +1997,22 @@ static void SWRenderer_flush(Renderer* renderer) static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alpha) { - (void)renderer; (void)color; (void)alpha; - UNIMP(); + SWRenderer* swr = (SWRenderer*) renderer; + + color = swrConvertPixel(color); +#ifdef TRANSPARENT_MASK + color |= TRANSPARENT_MASK; + if (alpha < 0.5f) { + color &= ~TRANSPARENT_MASK; + } +#endif + + for (int y = 0; y < swr->height; y++) { + uintpixel_t* line = &swr->fb[y * swr->fbPitch]; + for (int x = 0; x < swr->width; x++) { + line[x] = color; + } + } } static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) From 37af64b41f0ea7351fedde9c82723430c500fb90 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 15 Jul 2026 19:19:54 +0300 Subject: [PATCH 062/130] Add better support for transparent surfaces --- src/sw/pixel_convert.h | 13 ++++--------- src/sw/sw_renderer.c | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/sw/pixel_convert.h b/src/sw/pixel_convert.h index 7eee4c2f9..6f60c677a 100644 --- a/src/sw/pixel_convert.h +++ b/src/sw/pixel_convert.h @@ -87,19 +87,14 @@ uintpixel_t swrConvertPixelBase(uint32_t gmPixel) #define TRANSPARENT_MASK 0x8000 #endif -#if PIXEL_SIZE == 8 - -#define swrConvertPixel(x) swrConvertPixelBase((x) | 0xFF000000) -#define swrConvertPixelTexture(x) swrConvertPixelBase(x) +#if defined IS_BIG_ENDIAN -#elif defined IS_BIG_ENDIAN - -#define swrConvertPixel(x) swrConvertPixelBase(x) +#define swrConvertPixel(x) swrConvertPixelBase(x | 0xFF000000) #define swrConvertPixelTexture(x) swrConvertPixelBase(BinaryUtils_bswap32(x)) #else -#define swrConvertPixel swrConvertPixelBase -#define swrConvertPixelTexture swrConvertPixelBase +#define swrConvertPixel(x) swrConvertPixelBase((x) | 0xFF000000) +#define swrConvertPixelTexture(x) swrConvertPixelBase(x) #endif diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index bfbc4ac14..0e6004731 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -152,6 +152,7 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) dc.p.r = (dc.p.r * inval + sc.p.r * alpha) >> 8; dc.p.g = (dc.p.g * inval + sc.p.g * alpha) >> 8; dc.p.b = (dc.p.b * inval + sc.p.b * alpha) >> 8; + dc.p.a = 0xFF; *dcolor = dc.l; #elif PIXEL_SIZE == 16 @@ -163,13 +164,12 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) int dcb = _dcolor & 0x1F; int dcg = (_dcolor >> 5) & 0x1F; int dcr = (_dcolor >> 10) & 0x1F; - int dca = _dcolor & 0x8000; dcr = (dcr * inval + scr * alpha) >> 8; dcg = (dcg * inval + scg * alpha) >> 8; dcb = (dcb * inval + scb * alpha) >> 8; - *dcolor = dca | dcb | (dcg << 5) | (dcr << 10); + *dcolor = 0x8000 | dcb | (dcg << 5) | (dcr << 10); #else if (alpha < 240) { static int alphaApproximationThingy = 0; @@ -2001,10 +2001,22 @@ static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alp color = swrConvertPixel(color); #ifdef TRANSPARENT_MASK - color |= TRANSPARENT_MASK; - if (alpha < 0.5f) { + if (alpha >= 0.95f) { + color |= TRANSPARENT_MASK; + } + else if (alpha < 0.01f) { color &= ~TRANSPARENT_MASK; } + else { + #if PIXEL_SIZE == 32 + int alphai = (int)(255.0f * alpha); + if (alphai < 0) alphai = 0; + if (alphai > 255) alphai = 255; + color |= (alphai << 24); + #elif PIXEL_SIZE == 16 + color |= (alpha > 0.5f); + #endif + } #endif for (int y = 0; y < swr->height; y++) { From db6e8d7cea85e23da9b3406a26ed103289472ef3 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Thu, 23 Jul 2026 09:02:54 -0400 Subject: [PATCH 063/130] msvc defines --- src/sw/defines.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/sw/defines.h b/src/sw/defines.h index 1cfd49d88..7fdfdc90a 100644 --- a/src/sw/defines.h +++ b/src/sw/defines.h @@ -19,6 +19,18 @@ #define UNLIKELY(cond) (cond) #endif -#define UNUSED __attribute__ ((unused)) +#if defined(__GNUC__) || defined(__clang__) #define FORCE_INLINE static inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define FORCE_INLINE static __forceinline +#else +#define FORCE_INLINE static inline +#endif +#if defined(__cplusplus) && __cplusplus >= 201703L +#define UNUSED [[maybe_unused]] +#elif defined(__GNUC__) || defined(__clang__) +#define UNUSED __attribute__ ((unused)) +#else +#define UNUSED +#endif From c530c7586d66d06b9975d2f2310ce8af27ae7bb7 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Fri, 24 Jul 2026 14:39:43 -0400 Subject: [PATCH 064/130] msvc 5 --- src/sw/defines.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sw/defines.h b/src/sw/defines.h index 7fdfdc90a..b74141cc5 100644 --- a/src/sw/defines.h +++ b/src/sw/defines.h @@ -21,7 +21,7 @@ #if defined(__GNUC__) || defined(__clang__) #define FORCE_INLINE static inline __attribute__((always_inline)) -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) && _MSC_VER >= 1200 #define FORCE_INLINE static __forceinline #else #define FORCE_INLINE static inline From a97e4e563e2f2bd80ab21b72f6ac14d6e16f939f Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 28 Jul 2026 17:18:05 +0300 Subject: [PATCH 065/130] * Fix rectangles with swapped coordinates and make them draw properly. This fixes the battle UI and menu in DELTARUNE --- src/sw/sw_renderer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 0e6004731..1c41f420a 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -1431,6 +1431,8 @@ static void SWRenderer_drawRectangle(Renderer* renderer, float x1, float y1, flo int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); int xd = x2i - x1i; int yd = y2i - y1i; + if (xd < 0) { x1i = x2i; xd = -xd; } + if (yd < 0) { y1i = y2i; yd = -yd; } if (xd <= 0 || yd <= 0) return; for (int y = 0; y <= yd; y++) { @@ -1463,6 +1465,8 @@ static void SWRenderer_drawRectangleColor(Renderer* renderer, float x1, float y1 int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); int xd = x2i - x1i; int yd = y2i - y1i; + if (xd < 0) { x1i = x2i; xd = -xd; } + if (yd < 0) { y1i = y2i; yd = -yd; } if (xd <= 0 || yd <= 0) return; // TODO: blending vertically From 6043a69a7071905920226b50b603ab8ba7adf9ac Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 28 Jul 2026 19:22:14 +0300 Subject: [PATCH 066/130] * Surface fixes This fixes the Queen arcade boxing battle in Deltarune Ch2 --- src/sw/sw_renderer.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 1c41f420a..085a6cae4 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -709,6 +709,12 @@ static void swrTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* d if (dy) *dy = (int)(*dy * swr->scaleY); } +static void swrReverseTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) +{ + if (dx) *dx = *dx / swr->scaleX; + if (dy) *dy = *dy / swr->scaleY; +} + static void swrReverseTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { if (dx) *dx = (int)(*dx / swr->scaleX); @@ -1483,6 +1489,9 @@ static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2 (void)width; (void)color; (void)alpha; uintpixel_t colorCvt = swrConvertPixel(color); +#ifdef TRANSPARENT_MASK + colorCvt |= TRANSPARENT_MASK; +#endif swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha); } @@ -2171,6 +2180,17 @@ static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, surface = swr->surfaces[surfaceID]; } + if (srcWidth < 0) { + srcWidth = surface->width; + swrReverseTransformSizeIfNeeded(swr, &xscale, NULL); + } + if (srcHeight < 0) { + srcHeight = surface->height; + swrReverseTransformSizeIfNeeded(swr, NULL, &yscale); + } + + fprintf(stderr,"drawSurface(%d, %d, %d, %d, %d, %f, %f, %f, %f, %f, %08x, %f) -> %p\n",surfaceID,srcLeft,srcTop,srcWidth,srcHeight,x,y,xscale,yscale,angleDeg,color,alpha,surface); + int sx = srcLeft; int sy = srcTop; int sw = srcWidth; @@ -2184,6 +2204,12 @@ static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, int dw = (int)(xscale * tw); int dh = (int)(yscale * th); + //make it opaque first +#ifdef TRANSPARENT_MASK + for (int i = 0; i < surface->width * surface->height; i++) + surface->buffer[i] |= TRANSPARENT_MASK; +#endif + if (UNLIKELY(swrMustRotate(angleDeg))) { float pivotX = (x - dx) * swrSgn(xscale); From 6ef6d4e4e7393b15a9a26ae1917b0ed0b79401cd Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 28 Jul 2026 19:31:40 +0300 Subject: [PATCH 067/130] * Oops, forgot some details on my last commit --- src/sw/sw_renderer.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 085a6cae4..4ed4566d1 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -2189,8 +2189,6 @@ static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, swrReverseTransformSizeIfNeeded(swr, NULL, &yscale); } - fprintf(stderr,"drawSurface(%d, %d, %d, %d, %d, %f, %f, %f, %f, %f, %08x, %f) -> %p\n",surfaceID,srcLeft,srcTop,srcWidth,srcHeight,x,y,xscale,yscale,angleDeg,color,alpha,surface); - int sx = srcLeft; int sy = srcTop; int sw = srcWidth; @@ -2204,12 +2202,6 @@ static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, int dw = (int)(xscale * tw); int dh = (int)(yscale * th); - //make it opaque first -#ifdef TRANSPARENT_MASK - for (int i = 0; i < surface->width * surface->height; i++) - surface->buffer[i] |= TRANSPARENT_MASK; -#endif - if (UNLIKELY(swrMustRotate(angleDeg))) { float pivotX = (x - dx) * swrSgn(xscale); From 2c0a61d28d1276f77efd192c5c6d207738488d82 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 28 Jul 2026 20:18:21 +0300 Subject: [PATCH 068/130] * Implement surfaceCopy (untested?! but doesn't crash), and noop applyProjection --- src/desktop/backends/sdl1.c | 8 ++-- src/sw/sw_renderer.c | 82 ++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/src/desktop/backends/sdl1.c b/src/desktop/backends/sdl1.c index ad9beba10..448562fce 100644 --- a/src/desktop/backends/sdl1.c +++ b/src/desktop/backends/sdl1.c @@ -378,10 +378,10 @@ static int32_t SDLKeyToGml(int sdlkey) { case SDLK_RCTRL: return VK_CONTROL; case SDLK_LALT: case SDLK_RALT: return VK_ALT; - case SDLK_UP: return VK_UP; - case SDLK_DOWN: return VK_DOWN; - case SDLK_LEFT: return VK_LEFT; - case SDLK_RIGHT: return VK_RIGHT; +case SDLK_KP8: case SDLK_UP: return VK_UP; +case SDLK_KP5:case SDLK_KP2: case SDLK_DOWN: return VK_DOWN; +case SDLK_KP4: case SDLK_LEFT: return VK_LEFT; +case SDLK_KP6: case SDLK_RIGHT: return VK_RIGHT; case SDLK_F1: return VK_F1; case SDLK_F2: return VK_F2; case SDLK_F3: return VK_F3; diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 4ed4566d1..fb894391e 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -2225,7 +2225,7 @@ static void SWRenderer_surfaceResize(Renderer* renderer, int32_t surfaceID, int3 SWRenderer* swr = (SWRenderer*) renderer; if (surfaceID == APPLICATION_SURFACE_ID) { - fprintf(stderr, "swr: Don't support resizing the application window with this. There must be another way!\n"); + fprintf(stderr, "swr: Don't support resizing the application window with this. There must be another way! (need to set to %dx%d)\n", width, height); return; } @@ -2243,7 +2243,7 @@ static void SWRenderer_surfaceFree(Renderer* renderer, int32_t surfaceID) SWRenderer* swr = (SWRenderer*) renderer; if (surfaceID == APPLICATION_SURFACE_ID) { - fprintf(stderr, "swr: Don't support resizing the application window with this. There must be another way!\n"); + fprintf(stderr, "swr: Don't support SWRenderer_surfaceCopy the application window with this. There must be another way!\n"); return; } @@ -2261,6 +2261,76 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, int32_t SrcSurfaceID, int32_t SrcX, int32_t SrcY, int32_t SrcW, int32_t SrcH, bool part) { + SWRenderer* swr = (SWRenderer*) renderer; + + SWTexture temp1, temp2; + SWTexture *dstSurf, *srcSurf; + + if (DestSurfaceID == APPLICATION_SURFACE_ID) { + dstSurf = &temp1; + temp1.width = swr->mainWidth; + temp1.height = swr->mainHeight; + temp1.buffer = swr->mainFb; + } + else if (DestSurfaceID < 0 || (size_t) DestSurfaceID >= swr->surfaceCount || swr->surfaces[DestSurfaceID] == NULL) { + fprintf(stderr, "swr: Cannot resize surface id %d, it's invalid (dest in surfaceCopy)\n", DestSurfaceID); + return; + } + else { + dstSurf = swr->surfaces[DestSurfaceID]; + } + + if (SrcSurfaceID == APPLICATION_SURFACE_ID) { + srcSurf = &temp2; + temp2.width = swr->mainWidth; + temp2.height = swr->mainHeight; + temp2.buffer = swr->mainFb; + } + else if (SrcSurfaceID < 0 || (size_t) SrcSurfaceID >= swr->surfaceCount || swr->surfaces[SrcSurfaceID] == NULL) { + fprintf(stderr, "swr: Cannot resize surface id %d, it's invalid (src in surfaceCopy)\n", SrcSurfaceID); + return; + } + else { + srcSurf = swr->surfaces[SrcSurfaceID]; + } + + if (SrcX + SrcW < 0) return; + if (SrcY + SrcH < 0) return; + if (SrcX >= srcSurf->width) return; + if (SrcY >= srcSurf->height) return; + if (DestX + SrcW < 0) return; + if (DestY + SrcH < 0) return; + if (DestX >= dstSurf->width) return; + if (DestY >= dstSurf->height) return; + + if (SrcY < 0) { + SrcH += SrcY; + DestY -= SrcY; + SrcY = 0; + } + if (SrcX < 0) { + SrcW += SrcX; + DestX -= SrcX; + SrcX = 0; + } + if (SrcX + SrcW >= srcSurf->width) + SrcW = srcSurf->width - SrcX; + if (SrcY + SrcH >= srcSurf->height) + SrcH = srcSurf->height - SrcY; + + if (DestX + SrcW >= dstSurf->width) + SrcW = dstSurf->width - DestX; + if (DestY + SrcH >= dstSurf->height) + SrcH = dstSurf->height - DestY; + + for (int dy = 0; dy < SrcH; dy++) { + /***/ uintpixel_t* dstLine = &dstSurf->buffer[(dy + DestY) * dstSurf->width]; + const uintpixel_t* srcLine = &srcSurf->buffer[(dy + SrcY) * srcSurf->width]; + for (int dx = 0; dx < SrcW; dx++) { + dstLine[dx + DestX] = srcLine[dx + SrcX]; + } + } + UNIMP(); (void)renderer; (void)DestSurfaceID; (void)DestX; (void)DestY; @@ -2567,6 +2637,13 @@ static void SWRenderer_shaderSetUniformI(Renderer* renderer, int32_t handle, int (void) value4; } +static void SWRenderer_applyProjection(Renderer* renderer, const Matrix4f* worldToClip) +{ + (void) renderer; + (void) worldToClip; + UNIMP(); +} + Renderer* SWRenderer_create(void) { SWRenderer* swr = (SWRenderer*) safeCalloc(1, sizeof(SWRenderer)); @@ -2632,6 +2709,7 @@ Renderer* SWRenderer_create(void) swrVtable.shaderSetUniformI = SWRenderer_shaderSetUniformI; swrVtable.shaderIsCompiled = SWRenderer_shaderIsCompiled; swrVtable.shadersSupported = SWRenderer_shadersSupported; + swrVtable.applyProjection = SWRenderer_applyProjection; swrVtable.drawTile = NULL; From b4cbd6ec69593604207bdabcfd68e326e8361080 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 28 Jul 2026 22:20:16 +0300 Subject: [PATCH 069/130] * Blend mode support: normal, add, and subtract. This might slow things down significantly, sadly. Maybe I should keep this as a compiler option, or whatever. --- src/sw/sw_renderer.c | 148 +++++++++++++++++++++++++++++++++---------- 1 file changed, 116 insertions(+), 32 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index fb894391e..54a505412 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -67,6 +67,8 @@ typedef struct int offsetX, offsetY; float scaleX, scaleY; float defaultScaleX, defaultScaleY; + + int blendMode; } SWRenderer; @@ -127,21 +129,26 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) } // NOTE: alpha is between 0 and 256, NOT between 0 and 255! -FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) +// +// TODO: this routine could use some optimization. Obviously I tried my best, but clearly +// it's still true that too many calculations are being performed. +// +// TODO: SIMD could be used here! however, I don't really care. +// Old computers/consoles we plan to run this on don't really have +// SIMD, do they? So I want to optimize for the unfortunate case as +// much as I can. +FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int srcalpha, int dstalpha) { #if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 - // it's so insignificant here nobody will notice if we just don't... - if (alpha < 3) - return; - // it's so significant here we might as well fill in the whole color - if (alpha > 253) - { + if (LIKELY(dstalpha < 3 && srcalpha > 253)) { *dcolor = scolor; return; } - int inval = 256 - alpha; + // it's so insignificant here nobody will notice if we just don't... + if (UNLIKELY(srcalpha == 0)) + return; #endif #if PIXEL_SIZE == 32 @@ -149,9 +156,22 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) dc.l = *dcolor; sc.l = scolor; - dc.p.r = (dc.p.r * inval + sc.p.r * alpha) >> 8; - dc.p.g = (dc.p.g * inval + sc.p.g * alpha) >> 8; - dc.p.b = (dc.p.b * inval + sc.p.b * alpha) >> 8; + int dcr = (dc.p.r * dstalpha + sc.p.r * srcalpha) >> 8; + int dcg = (dc.p.g * dstalpha + sc.p.g * srcalpha) >> 8; + int dcb = (dc.p.b * dstalpha + sc.p.b * srcalpha) >> 8; + + //clamp to 0 + dcr &= ((-dcr) >> 31); + dcg &= ((-dcg) >> 31); + dcb &= ((-dcb) >> 31); + //clamp to 255 + dcr |= ((signed char)(dcr >> 1) >> 7); + dcg |= ((signed char)(dcg >> 1) >> 7); + dcb |= ((signed char)(dcb >> 1) >> 7); + + dc.p.r = dcr; + dc.p.g = dcg; + dc.p.b = dcb; dc.p.a = 0xFF; *dcolor = dc.l; @@ -165,20 +185,29 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int alpha) int dcg = (_dcolor >> 5) & 0x1F; int dcr = (_dcolor >> 10) & 0x1F; - dcr = (dcr * inval + scr * alpha) >> 8; - dcg = (dcg * inval + scg * alpha) >> 8; - dcb = (dcb * inval + scb * alpha) >> 8; + dcr = (dcr * dstalpha + scr * srcalpha) >> 8; + dcg = (dcg * dstalpha + scg * srcalpha) >> 8; + dcb = (dcb * dstalpha + scb * srcalpha) >> 8; + + //clamp to 0 + dcr &= ((-dcr) >> 31); + dcg &= ((-dcg) >> 31); + dcb &= ((-dcb) >> 31); + //clamp to 255 + dcr |= ((signed char)(dcr >> 1) >> 7); + dcg |= ((signed char)(dcg >> 1) >> 7); + dcb |= ((signed char)(dcb >> 1) >> 7); *dcolor = 0x8000 | dcb | (dcg << 5) | (dcr << 10); #else - if (alpha < 240) { + if (srcalpha < 240) { static int alphaApproximationThingy = 0; alphaApproximationThingy += 1339; if (alphaApproximationThingy > 601000) alphaApproximationThingy = 0; //gotta love that RNG - if ((alphaApproximationThingy & 0xFF) >= alpha) + if ((alphaApproximationThingy & 0xFF) >= srcalpha) return; } @@ -191,6 +220,32 @@ FORCE_INLINE int swrIntAlpha(float alphaf) return (int)(alphaf * 256); } +FORCE_INLINE int swrCalcSrcAlpha(SWRenderer* swr, int alpha) +{ + switch (swr->blendMode) + { + default: + return alpha; + case bm_add: + return alpha; + case bm_subtract: + return -alpha; + } +} + +FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) +{ + switch (swr->blendMode) + { + default: + return 256 - alpha; + case bm_add: + return 256; + case bm_subtract: + return 256; + } +} + FORCE_INLINE bool swrMustRotate(float angleDeg) { int angleDegInt = (int)(angleDeg * 4); @@ -418,6 +473,7 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam swr->gameW = gameW; swr->gameH = gameH; swr->drawingToSurface = false; + swr->blendMode = bm_normal; if (swr->width != windowW || swr->height != windowH) { @@ -545,6 +601,7 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool swr->width = swr->mainWidth; swr->height = swr->mainHeight; swr->fbPitch = swr->mainPitch; + swr->blendMode = bm_normal; if (restoreOldView) { // restore the old transform, if needed @@ -579,6 +636,7 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool swr->mainWidth = swr->width; swr->mainHeight = swr->height; swr->mainPitch = swr->fbPitch; + swr->blendMode = bm_normal; // and the old transform swr->lastViewX = swr->viewX; @@ -603,6 +661,7 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool swr->height = surface->height; swr->fbPitch = surface->width; swr->drawingToSurface = true; + swr->blendMode = bm_normal; swr->viewX = swr->portX = 0; swr->viewY = swr->portY = 0; @@ -721,14 +780,14 @@ static void swrReverseTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int if (dy) *dy = (int)(*dy / swr->scaleY); } -FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int alpha) +FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int srcalpha, int dstalpha) { SWRenderer* swr = (SWRenderer*) renderer; if (x < swr->portX || y < swr->portY) return; if (x >= swr->maxX || y >= swr->maxY) return; - alphaBlend(&swr->fb[y * swr->fbPitch + x], color, alpha); + alphaBlend(&swr->fb[y * swr->fbPitch + x], color, srcalpha, dstalpha); } static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) @@ -741,13 +800,16 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe if (dx + dw >= swr->maxX) dw = swr->maxX - dx; if (dw <= 0) return; + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + #if PIXEL_SIZE == 32 if (color == color2) #endif { uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; for (int i = 0; i < dw; i++) - alphaBlend(&line[i], color, alpha); + alphaBlend(&line[i], color, srcalpha, invalpha); } #if PIXEL_SIZE == 32 else @@ -776,7 +838,7 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe rinit += rstep; ginit += gstep; binit += bstep; - alphaBlend(&line[i], resultPixel.l, alpha); + alphaBlend(&line[i], resultPixel.l, srcalpha, invalpha); } } #endif @@ -804,6 +866,9 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe if (dy + dh >= swr->maxY) dh = swr->maxY - dy; if (dh <= 0) return; + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + #if PIXEL_SIZE == 32 if (color == color2) #endif @@ -811,7 +876,7 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe for (int i = 0; i < dh; i++) { uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - alphaBlend(&line[0], color, alpha); + alphaBlend(&line[0], color, srcalpha, invalpha); } } #if PIXEL_SIZE == 32 @@ -841,7 +906,7 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe rinit += rstep; ginit += gstep; binit += bstep; - alphaBlend(&line[0], resultPixel.l, alpha); + alphaBlend(&line[0], resultPixel.l, srcalpha, invalpha); } } #endif @@ -891,6 +956,8 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M int dx = x2 - x1, dy = y2 - y1; int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; + int srcalpha = swrCalcSrcAlpha((SWRenderer*) renderer, alpha); + int invalpha = swrCalcDstAlpha((SWRenderer*) renderer, alpha); uintpixel_t color = color1; #if PIXEL_SIZE == 32 @@ -927,7 +994,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M } #endif - swrPlotPixel(renderer, x, y, color, alpha); + swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); for (int i = 0; x < xe; i++) { @@ -953,7 +1020,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M color = resultPixel.l; #endif - swrPlotPixel(renderer, x, y, color, alpha); + swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); } } else @@ -977,7 +1044,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M } #endif - swrPlotPixel(renderer, x, y, color, alpha); + swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); for (int i = 0; y < ye; i++) { @@ -1003,7 +1070,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M color = resultPixel.l; #endif - swrPlotPixel(renderer, x, y, color, alpha); + swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); } } } @@ -1088,6 +1155,9 @@ static void swrDrawSpriteInternal( fixedp_t ixs2 = ixs * xstep; fixedp_t iys2 = iys * ystep; + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + if (sw == dw) { fixedp_t ys2 = (fixedp_t) iys2; @@ -1105,7 +1175,7 @@ static void swrDrawSpriteInternal( { uintpixel_t pixel = srcline[xs]; if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + alphaBlend(&dstline[x], tint(tintColor, pixel), srcalpha, invalpha); } } } @@ -1127,7 +1197,7 @@ static void swrDrawSpriteInternal( { uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), alpha); + alphaBlend(&dstline[x], tint(tintColor, pixel), srcalpha, invalpha); } } } @@ -1231,6 +1301,9 @@ static void swrDrawSpriteRotatedInternal( float sw_dw = (float) sw / dw; float sh_dh = (float) sh / dh; + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + for (int cy = minYc; cy < maxYc; cy++) { uintpixel_t *dstline = &swr->fb[cy * swr->fbPitch]; @@ -1267,7 +1340,7 @@ static void swrDrawSpriteRotatedInternal( uintpixel_t src = texture->buffer[ty * texture->width + tx]; if (opaque(src)) - alphaBlend(&dstline[cx], tint(tintColor, src), alpha); + alphaBlend(&dstline[cx], tint(tintColor, src), srcalpha, invalpha); } } } @@ -1501,6 +1574,9 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft (void) color2; (void) color3; + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + // Figure out the maximum Y extent of the triangle. // (Note that we know yup is the minimum.) int xmid, ymid, xmid2 = xup, xmax, ymax; @@ -1555,7 +1631,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft uintpixel_t* line = &swr->fb[y * swr->width]; for (int x = x1; x < x2; x++) { - alphaBlend(&line[x], color1, alpha); + alphaBlend(&line[x], color1, srcalpha, invalpha); } } } @@ -2042,8 +2118,16 @@ static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alp static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) { - UNIMP(); - (void)renderer; (void)mode; + //UNIMP(); + //(void)renderer; (void)mode; + + SWRenderer* swr = (SWRenderer*) renderer; + swr->blendMode = mode; + + //if (mode != bm_normal && mode != bm_add && mode != bm_subtract) + { + fprintf(stderr, "swr: unsupported blend mode: %d\n", mode); + } } static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, int32_t dfactor, int32_t sfactor_alpha, int32_t dfactor_alpha) From fb188afa0a4aac16c0b08f3415be6badf74fa956 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 28 Jul 2026 23:05:45 +0300 Subject: [PATCH 070/130] * Attempt to implement color masking (gpuSetColorWriteEnable) for surfaces Not 100% sure if this works yet though because rory's cone doesn't draw properly --- src/sw/sw_renderer.c | 233 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 205 insertions(+), 28 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 54a505412..00984ef3a 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -27,6 +27,15 @@ typedef struct } SWTexture; +typedef struct +{ + // used for almost all intents and purposes. + SWTexture* texture; + // upon a gpuSetColorWriteEnable change, the shadow texture is used for writing instead. + SWTexture* shadowTexture; +} +SWSurface; + typedef struct { Renderer base; @@ -49,7 +58,7 @@ typedef struct float lastScaleX, lastScaleY; SWTexture** textures; - SWTexture** surfaces; + SWSurface** surfaces; uint32_t* textureIndexLRU; uint32_t textureIndexLRUHead; uint32_t textureIndexLRUTail; @@ -69,9 +78,19 @@ typedef struct float defaultScaleX, defaultScaleY; int blendMode; + + // only used for surfaces. The application surface doesn't support these at the moment. + int currentSurfaceIndex; + int writeMask; } SWRenderer; +#define WRITE_MASK_ALL (15) +#define WRITE_MASK_RED (1) +#define WRITE_MASK_GREEN (2) +#define WRITE_MASK_BLUE (4) +#define WRITE_MASK_ALPHA (8) + void Runner_setNextFrame(uintpixel_t* framebuffer, int width, int height); FORCE_INLINE int swrMin(int a, int b) { return a < b ? a : b; } @@ -286,19 +305,29 @@ FORCE_INLINE int swrCeiling(float x) return i + (x > (float) i); } -static SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height) +static SWTexture* swrCreateTextureEx(const void* srcBuffer, int width, int height, bool convert) { SWTexture* txt = (SWTexture*) safeMalloc(sizeof(SWTexture)); txt->buffer = (uintpixel_t*) safeMalloc(width * height * sizeof(uintpixel_t)); - const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; - size_t sz = width * height; if (srcBuffer) { - for (size_t i = 0; i < sz; i++) - txt->buffer[i] = swrConvertPixelTexture(rgbaSrc[i]); + if (convert) { + const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; + for (size_t i = 0; i < sz; i++) + txt->buffer[i] = swrConvertPixelTexture(rgbaSrc[i]); + } + else { + const uintpixel_t* rgbaSrc = (const uintpixel_t*) srcBuffer; + for (size_t i = 0; i < sz; i++) + txt->buffer[i] = rgbaSrc[i]; + } + } + else + { + memset(txt->buffer, 0, width * height * sizeof(uintpixel_t)); } txt->width = (uint16_t) width; @@ -307,12 +336,60 @@ static SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int heig return txt; } +static SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height) +{ + return swrCreateTextureEx(srcBuffer, width, height, true); +} + +static SWSurface* swrCreateSurface(int width, int height) +{ + SWSurface* surf = (SWSurface*) safeMalloc(sizeof(SWSurface)); + surf->texture = swrCreateTexture(NULL, width, height); + surf->shadowTexture = NULL; + return surf; +} + +static SWTexture* swrCopyTexture(SWTexture* texture) +{ + return swrCreateTextureEx(texture->buffer, texture->width, texture->height, false); +} + static void swrFreeTexture(SWTexture* texture) { + if (UNLIKELY(!texture)) + return; + free(texture->buffer); free(texture); } +static void swrFreeSurface(SWSurface* surface) +{ + if (UNLIKELY(!surface)) + return; + + swrFreeTexture(surface->texture); + swrFreeTexture(surface->shadowTexture); + free(surface); +} + +static SWTexture* swrWritableSurfaceTexture(SWRenderer* swr, int surfaceID) +{ + if (UNLIKELY(swr->surfaces[surfaceID]->shadowTexture)) { + return swr->surfaces[surfaceID]->shadowTexture; + } + + if (LIKELY(swr->writeMask == WRITE_MASK_ALL)) { + return swr->surfaces[surfaceID]->texture; + } + + if (UNLIKELY(!swr->surfaces[surfaceID]->shadowTexture)) { + swr->surfaces[surfaceID]->shadowTexture = swrCopyTexture(swr->surfaces[surfaceID]->texture); + } + + return swr->surfaces[surfaceID]->shadowTexture; +} + static bool swrAddTextureIndexToLRU(SWRenderer* swr, int textureIndex) { uint32_t newIndex = (swr->textureIndexLRUHead + 1) % TEXTURE_LRU_LENGTH; @@ -433,7 +510,7 @@ static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) swr->surfaceCount = SURFACE_MAX_COUNT; swr->totalTextureCount = swr->textureCount + swr->surfaceCount; swr->textures = (SWTexture**) safeCalloc(swr->totalTextureCount, sizeof(SWTexture*)); - swr->surfaces = (SWTexture**) safeCalloc(swr->surfaceCount, sizeof(SWTexture*)); + swr->surfaces = (SWSurface**) safeCalloc(swr->surfaceCount, sizeof(SWSurface*)); //allocate texture LRU cache to allow for dynamic unloading of textures swr->textureIndexLRU = (uint32_t*) safeCalloc(TEXTURE_LRU_LENGTH, sizeof(uint32_t)); @@ -585,10 +662,70 @@ static void SWRenderer_endView(Renderer* renderer) swr->scaleY = swr->defaultScaleY; } +static void swrCommitShadowWritesToSurfaceIfNeeded(SWRenderer* swr, SWSurface* surface) +{ + if (LIKELY(!swr->drawingToSurface)) + return; + + if (UNLIKELY(!surface)) + return; + + if (UNLIKELY(!surface->shadowTexture)) + return; + + if (LIKELY(swr->writeMask == WRITE_MASK_ALL)) { + swrFreeTexture(surface->texture); + surface->texture = surface->shadowTexture; + surface->shadowTexture = NULL; + return; + } + + if (UNLIKELY(swr->writeMask == 0)) { + swrFreeTexture(surface->shadowTexture); + surface->shadowTexture = NULL; + return; + } + + uintpixel_t mask = 0; +#if PIXEL_SIZE == 32 + Pixel32ARGB x; + x.l = 0; + if (swr->writeMask & WRITE_MASK_RED) x.p.r = 255; + if (swr->writeMask & WRITE_MASK_GREEN) x.p.g = 255; + if (swr->writeMask & WRITE_MASK_BLUE) x.p.b = 255; + if (swr->writeMask & WRITE_MASK_ALPHA) x.p.a = 255; + mask = x.l; +#elif PIXEL_SIZE == 16 + if (swr->writeMask & WRITE_MASK_RED) l |= 0x7C00; + if (swr->writeMask & WRITE_MASK_GREEN) l |= 0x03E0; + if (swr->writeMask & WRITE_MASK_BLUE) l |= 0x001F; + if (swr->writeMask & WRITE_MASK_ALPHA) l |= 0x8000; +#else + //although it DOES ues rgb332, needs special handling for ALPHA + fprintf(stderr, "swr: Unimplemented color masking for 8-bit mode TODO\n"); + swrFreeTexture(surface->texture); + surface->texture = surface->shadowTexture; + surface->shadowTexture = NULL; + return; +#endif + + uintpixel_t invmask = ~mask; + size_t max = surface->texture->width * surface->texture->height; + for (size_t i = 0; i < max; i++) + { + surface->shadowTexture->buffer[i] = + surface->texture->buffer[i] = (surface->texture->buffer[i] & invmask) | (surface->shadowTexture->buffer[i] & mask); + } +} + static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView) { SWRenderer* swr = (SWRenderer*) renderer; + if (swr->drawingToSurface) { + swrCommitShadowWritesToSurfaceIfNeeded(swr, swr->surfaces[swr->currentSurfaceIndex]); + } + if (targetSurfaceId == RENDER_TARGET_HOST_FRAMEBUFFER) { if (!swr->drawingToSurface) @@ -602,6 +739,8 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool swr->height = swr->mainHeight; swr->fbPitch = swr->mainPitch; swr->blendMode = bm_normal; + swr->currentSurfaceIndex = -1; + swr->writeMask = WRITE_MASK_ALL; if (restoreOldView) { // restore the old transform, if needed @@ -655,13 +794,15 @@ static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool swr->lastScaleY = swr->scaleY; } - SWTexture* surface = swr->surfaces[targetSurfaceId]; + SWTexture* surface = swr->surfaces[targetSurfaceId]->texture; swr->fb = surface->buffer; swr->width = surface->width; swr->height = surface->height; swr->fbPitch = surface->width; swr->drawingToSurface = true; swr->blendMode = bm_normal; + swr->currentSurfaceIndex = targetSurfaceId; + swr->writeMask = WRITE_MASK_ALL; swr->viewX = swr->portX = 0; swr->viewY = swr->portY = 0; @@ -2029,8 +2170,11 @@ static void SWRenderer_drawSurfaceTiled(Renderer* renderer, int32_t surfaceID, f if (0 > surfaceID || swr->surfaceCount <= (size_t) surfaceID) return; - SWTexture* surface = swr->surfaces[surfaceID]; - if (!surface) return; + SWSurface* surfaceP = swr->surfaces[surfaceID]; + if (!surfaceP) return; + + swrCommitShadowWritesToSurfaceIfNeeded(swr, surfaceP); + SWTexture* surface = surfaceP->texture; float axScale = fabsf(xscale); float ayScale = fabsf(yscale); @@ -2156,18 +2300,53 @@ static void SWRenderer_gpuSetAlphaTestRef(Renderer* renderer, uint8_t ref) static void SWRenderer_gpuSetColorWriteEnable(Renderer* renderer, bool red, bool green, bool blue, bool alpha) { - UNIMP(); - (void)renderer; (void)red; (void)green; (void)blue; (void)alpha; + SWRenderer* swr = (SWRenderer*) renderer; + + if (!swr->drawingToSurface) { + fprintf(stderr, "swr: gpuSetColorWriteEnable not supported for main framebuffer"); + return; + } + + SWSurface* currSurf = swr->surfaces[swr->currentSurfaceIndex]; + + swrCommitShadowWritesToSurfaceIfNeeded(swr, currSurf); + + swr->writeMask = + (red ? WRITE_MASK_RED : 0) | + (green ? WRITE_MASK_GREEN : 0) | + (blue ? WRITE_MASK_BLUE : 0) | + (alpha ? WRITE_MASK_ALPHA : 0); + + // no need to change other properties, because the width and height are the same. + // but we ALWAYS need to re-fetch the writable surface texture since the old one + // may have been freed. + swr->fb = swrWritableSurfaceTexture(swr, swr->currentSurfaceIndex)->buffer; + + if (currSurf->shadowTexture) { + assert(currSurf->texture->width == currSurf->shadowTexture->width); + assert(currSurf->texture->height == currSurf->shadowTexture->height); + assert(currSurf->texture->buffer != currSurf->shadowTexture->buffer); + } } static void SWRenderer_gpuGetColorWriteEnable(Renderer* renderer, bool* red, bool* green, bool* blue, bool* alpha) { - UNIMP(); *red = false; *green = false; *blue = false; *alpha = false; - (void)renderer; (void)red; (void)green; (void)blue; (void)alpha; + + SWRenderer* swr = (SWRenderer*) renderer; + + if (!swr->drawingToSurface) { + fprintf(stderr, "swr: gpuGetColorWriteEnable not supported for main framebuffer"); + return; + } + + *red = (swr->writeMask & WRITE_MASK_RED) != 0; + *green = (swr->writeMask & WRITE_MASK_GREEN) != 0; + *blue = (swr->writeMask & WRITE_MASK_BLUE) != 0; + *alpha = (swr->writeMask & WRITE_MASK_ALPHA) != 0; } static bool SWRenderer_gpuGetBlendEnable(Renderer* renderer) @@ -2201,7 +2380,7 @@ static int32_t SWRenderer_createSurface(Renderer* renderer, int32_t width, int32 return slot; } - swr->surfaces[slot] = swrCreateTexture(NULL, width, height); + swr->surfaces[slot] = swrCreateSurface(width, height); return slot; } @@ -2228,7 +2407,7 @@ static float SWRenderer_getSurfaceWidth(Renderer* renderer, int32_t surfaceID) if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) return 0.0f; - return (float) swr->surfaces[surfaceID]->width; + return (float) swr->surfaces[surfaceID]->texture->width; } static float SWRenderer_getSurfaceHeight(Renderer* renderer, int32_t surfaceID) @@ -2240,7 +2419,7 @@ static float SWRenderer_getSurfaceHeight(Renderer* renderer, int32_t surfaceID) if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) return 0.0f; - return (float) swr->surfaces[surfaceID]->height; + return (float) swr->surfaces[surfaceID]->texture->height; } static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, @@ -2260,8 +2439,9 @@ static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, fprintf(stderr, "swr: Invalid surface id %d for drawSurface\n", surfaceID); return; } - - surface = swr->surfaces[surfaceID]; + + swrCommitShadowWritesToSurfaceIfNeeded(swr, swr->surfaces[surfaceID]); + surface = swr->surfaces[surfaceID]->texture; } if (srcWidth < 0) { @@ -2318,8 +2498,8 @@ static void SWRenderer_surfaceResize(Renderer* renderer, int32_t surfaceID, int3 return; } - swrFreeTexture(swr->surfaces[surfaceID]); - swr->surfaces[surfaceID] = swrCreateTexture(NULL, width, height); + swrFreeSurface(swr->surfaces[surfaceID]); + swr->surfaces[surfaceID] = swrCreateSurface(width, height); } static void SWRenderer_surfaceFree(Renderer* renderer, int32_t surfaceID) @@ -2336,7 +2516,7 @@ static void SWRenderer_surfaceFree(Renderer* renderer, int32_t surfaceID) return; } - swrFreeTexture(swr->surfaces[surfaceID]); + swrFreeSurface(swr->surfaces[surfaceID]); swr->surfaces[surfaceID] = NULL; } @@ -2361,7 +2541,7 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, return; } else { - dstSurf = swr->surfaces[DestSurfaceID]; + dstSurf = swrWritableSurfaceTexture(swr, DestSurfaceID); } if (SrcSurfaceID == APPLICATION_SURFACE_ID) { @@ -2375,7 +2555,8 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, return; } else { - srcSurf = swr->surfaces[SrcSurfaceID]; + swrCommitShadowWritesToSurfaceIfNeeded(swr, swr->surfaces[SrcSurfaceID]); + srcSurf = swr->surfaces[SrcSurfaceID]->texture; } if (SrcX + SrcW < 0) return; @@ -2416,10 +2597,6 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, } UNIMP(); - (void)renderer; - (void)DestSurfaceID; (void)DestX; (void)DestY; - (void)SrcSurfaceID; (void)SrcX; (void)SrcY; - (void)SrcW; (void)SrcH; (void)part; } static bool SWRenderer_surfaceGetPixels(Renderer* renderer, int32_t surfaceID, uint8_t* outRGBA) From d7b84995ea889eeac4e9568ba93d02e973522f9a Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 28 Jul 2026 23:24:33 +0300 Subject: [PATCH 071/130] * Alright, so the Knight fight is playable now, although I'm not super confident in this code yet --- src/sw/sw_renderer.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 00984ef3a..2efb99eb6 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -2606,6 +2606,12 @@ static bool SWRenderer_surfaceGetPixels(Renderer* renderer, int32_t surfaceID, u return false; } +static int32_t SWRenderer_gpuGetBlendMode(Renderer* renderer) +{ + SWRenderer* swr = (SWRenderer*) renderer; + return swr->blendMode; +} + static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t surfaceID, int32_t x, int32_t y, int32_t w, int32_t h, bool removeback, bool smooth, @@ -2620,15 +2626,27 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su (void) removeback; (void) smooth; - if (surfaceID != -1) { - fprintf(stderr, "%s: Surfaces other than application_surface aren't supported yet!\n", __func__); - return 0; + SWTexture *srcTex, temp1; + + if (surfaceID == APPLICATION_SURFACE_ID) { + srcTex = &temp1; + temp1.width = swr->width; + temp1.height = swr->height; + temp1.buffer = swr->fb; + } else { + if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) { + fprintf(stderr, "%s: Invalid surface ID %d\n", __func__, surfaceID); + return 0; + } + SWSurface* surf = swr->surfaces[surfaceID]; + swrCommitShadowWritesToSurfaceIfNeeded(swr, surf); + srcTex = surf->texture; } int32_t texturePageId = swrFindSurfaceTextureSlot(swr); int32_t tpagIndex = swrFindSurfaceTPagSlot(swr); if (texturePageId == -1 || tpagIndex == -1) { - fprintf(stderr, "%s: Surface overflow!!\n", __func__); + fprintf(stderr, "%s: Sprite overflow!!\n", __func__); return 0; } @@ -2638,7 +2656,7 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su for (int iy = 0; iy < h; iy++) { uintpixel_t* dstline = &tex->buffer[iy * tex->width]; - if ((iy + y) < 0 || (iy + y) >= swr->height) + if ((iy + y) < 0 || (iy + y) >= srcTex->height) { for (int ix = 0; ix < w; ix++) dstline[ix] = 0; @@ -2646,7 +2664,7 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su continue; } - uintpixel_t* srcline = &swr->fb[(iy + y) * swr->width + x]; + uintpixel_t* srcline = &srcTex->buffer[(iy + y) * srcTex->width + x]; int ix = 0, sx = x; // left edge @@ -2654,7 +2672,7 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su dstline[ix] = 0; // in-bounds - for (; sx < swr->width && ix < w; sx++, ix++) + for (; sx < srcTex->width && ix < w; sx++, ix++) #if PIXEL_SIZE == 8 dstline[ix] = srcline[ix]; #else @@ -2941,6 +2959,7 @@ Renderer* SWRenderer_create(void) swrVtable.gpuSetColorWriteEnable = SWRenderer_gpuSetColorWriteEnable; swrVtable.gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable; swrVtable.gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable; + swrVtable.gpuGetBlendMode = SWRenderer_gpuGetBlendMode; swrVtable.gpuSetFog = SWRenderer_gpuSetFog; swrVtable.drawSpriteTiled = SWRenderer_drawSpriteTiled; swrVtable.drawSurfaceTiled = SWRenderer_drawSurfaceTiled; From 57b9863ea167aed01cab1580af3f984c558ff485 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Wed, 29 Jul 2026 22:38:13 -0400 Subject: [PATCH 072/130] fix --- src/sw/sw_renderer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 2efb99eb6..9b60554c5 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -2916,10 +2916,11 @@ static void SWRenderer_shaderSetUniformI(Renderer* renderer, int32_t handle, int (void) value4; } -static void SWRenderer_applyProjection(Renderer* renderer, const Matrix4f* worldToClip) +static void SWRenderer_applyProjection(Renderer* renderer, const Matrix4f* worldToClip, const Matrix4f* idk) { (void) renderer; (void) worldToClip; + (void) idk; UNIMP(); } From 885dd769b809da6a42391ef5b4b62b40c37093da Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Tue, 28 Jul 2026 19:06:28 -0400 Subject: [PATCH 073/130] fix bad free --- src/sw/sw_renderer.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 9b60554c5..93a404b79 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -448,8 +448,10 @@ static bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) if (txtr->blobData) { pixels = ImageDecoder_decodeToRgba(txtr->blobData, (size_t) txtr->blobSize, gm2022_5, &w, &h); if (pixels) { - free(txtr->blobData); - txtr->blobData = NULL; + if (!txtr->mapped) { + free(txtr->blobData); + txtr->blobData = NULL; + } break; } From 0f8cc9db4e7d9d0ed9b4b5b3ff9dbfc5e6bb6093 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 16:44:31 +0300 Subject: [PATCH 074/130] * Holy cheese and crackers did I just commit this whole thing --- CMakeLists.txt | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1cc0f85d..516fb7438 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -298,19 +298,6 @@ if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita") add_compile_definitions(ENABLE_VM_STUB_LOGS) endif() -<<<<<<< HEAD - # GLAD - if(ENABLE_LEGACY_GL OR ENABLE_MODERN_GL OR DESKTOP_BACKEND STREQUAL "glfw3" OR DESKTOP_BACKEND STREQUAL "glfw2") - add_library(glad STATIC vendor/glad/src/glad.c) - target_include_directories(glad PUBLIC vendor/glad/include) - set(PLATFORM_LIBRARIES glad) - endif() - - # stb_image - target_include_directories(butterscotch PUBLIC vendor/stb/image) - - set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} bzip2 stb_ds sha1 stb_vorbis) -======= # stb_image target_include_directories(butterscotch PUBLIC vendor/stb/image) @@ -323,7 +310,7 @@ if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita") set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} glad) endif() endif() ->>>>>>> 4c99e3f18277765511a68d6f52431f7951e3be5f + if(NOT MSVC) set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} m) endif() From d14a7898b50d372967c63a3d623b018f792c7612 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 16:45:51 +0300 Subject: [PATCH 075/130] * This shouldn't be in this PR either --- src/backends/sdl1.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backends/sdl1.c b/src/backends/sdl1.c index 74a50e978..cc65997c0 100644 --- a/src/backends/sdl1.c +++ b/src/backends/sdl1.c @@ -378,10 +378,10 @@ static int32_t SDLKeyToGml(int sdlkey) { case SDLK_RCTRL: return VK_CONTROL; case SDLK_LALT: case SDLK_RALT: return VK_ALT; -case SDLK_KP8: case SDLK_UP: return VK_UP; -case SDLK_KP5:case SDLK_KP2: case SDLK_DOWN: return VK_DOWN; -case SDLK_KP4: case SDLK_LEFT: return VK_LEFT; -case SDLK_KP6: case SDLK_RIGHT: return VK_RIGHT; + case SDLK_UP: return VK_UP; + case SDLK_DOWN: return VK_DOWN; + case SDLK_LEFT: return VK_LEFT; + case SDLK_RIGHT: return VK_RIGHT; case SDLK_F1: return VK_F1; case SDLK_F2: return VK_F2; case SDLK_F3: return VK_F3; From bca244a6bb7c29d7343cc92c620d97b743d563c3 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 16:46:34 +0300 Subject: [PATCH 076/130] * Revert this as well Note: This will break Alphys' lab camera feed in UNDERTALE, but it's not up to this PR to fix it. --- src/vm_builtins.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vm_builtins.c b/src/vm_builtins.c index 86cfceba1..51241c919 100644 --- a/src/vm_builtins.c +++ b/src/vm_builtins.c @@ -1283,7 +1283,7 @@ RValue VMBuiltins_getVariable(VMContext* ctx, Instance* inst, int16_t builtinVar return RValue_makeReal((GMLReal) INSTANCE_NOONE); } case BUILTIN_VAR_FPS: - return RValue_makeReal(30.0f /* ctx->dataWin->gen8.gms2FPS */); + return RValue_makeReal(ctx->dataWin->gen8.gms2FPS); case BUILTIN_VAR_DEBUG_MODE: return RValue_makeBool(false); case BUILTIN_VAR_DELTA_TIME: From a9db8437b72b0e1b1b79a29f12f600e288c8da81 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 16:48:32 +0300 Subject: [PATCH 077/130] * Yeah, I was not supposed to check DESKTOP_BACKEND here --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 516fb7438..d49fc9606 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -303,7 +303,7 @@ if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita") set(PLATFORM_LIBRARIES bzip2 stb_ds sha1 stb_vorbis) if(NOT PLATFORM STREQUAL "vita") - if(ENABLE_LEGACY_GL OR ENABLE_MODERN_GL OR DESKTOP_BACKEND STREQUAL "glfw3" OR DESKTOP_BACKEND STREQUAL "glfw2") + if(ENABLE_LEGACY_GL OR ENABLE_MODERN_GL OR BACKEND STREQUAL "glfw3" OR BACKEND STREQUAL "glfw2") # GLAD add_library(glad STATIC vendor/glad/src/glad.c) target_include_directories(glad PUBLIC vendor/glad/include) From ba58342a4fd76f9f8389724da0f38cc838fbfec4 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 16:51:01 +0300 Subject: [PATCH 078/130] * Fix a warning --- src/sw/sw_renderer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 93a404b79..1561cbb94 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -2527,6 +2527,8 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, int32_t SrcSurfaceID, int32_t SrcX, int32_t SrcY, int32_t SrcW, int32_t SrcH, bool part) { + (void) part; // TODO: figure out the meaning of this parameter + SWRenderer* swr = (SWRenderer*) renderer; SWTexture temp1, temp2; From 0d5df39b50049707f9bdf6216a70874726eef927 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 19:49:06 +0300 Subject: [PATCH 079/130] * First part of cleanup... --- src/sw/defines.h | 5 +- src/sw/pixel_convert.h | 5 +- src/sw/sw_drawing.c | 99 +++++ src/sw/sw_drawing.h | 7 + src/sw/sw_inlined.h | 53 +++ src/sw/sw_pixel_calc.h | 183 +++++++++ src/sw/sw_renderer.c | 729 +---------------------------------- src/sw/sw_renderer.h | 5 +- src/sw/sw_renderer_private.h | 106 +++++ src/sw/sw_surface.c | 121 ++++++ src/sw/sw_surface.h | 11 + src/sw/sw_texture.c | 52 +++ src/sw/sw_texture.h | 9 + src/sw/sw_texture_lru.c | 116 ++++++ src/sw/sw_texture_lru.h | 9 + src/sw/sw_transform.h | 56 +++ 16 files changed, 836 insertions(+), 730 deletions(-) create mode 100755 src/sw/sw_drawing.c create mode 100755 src/sw/sw_drawing.h create mode 100755 src/sw/sw_inlined.h create mode 100755 src/sw/sw_pixel_calc.h create mode 100755 src/sw/sw_renderer_private.h create mode 100755 src/sw/sw_surface.c create mode 100755 src/sw/sw_surface.h create mode 100755 src/sw/sw_texture.c create mode 100755 src/sw/sw_texture.h create mode 100755 src/sw/sw_texture_lru.c create mode 100755 src/sw/sw_texture_lru.h create mode 100755 src/sw/sw_transform.h diff --git a/src/sw/defines.h b/src/sw/defines.h index b74141cc5..8c3072aef 100644 --- a/src/sw/defines.h +++ b/src/sw/defines.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef _SW_DEFINES_H +#define _SW_DEFINES_H #include "common.h" @@ -34,3 +35,5 @@ #else #define UNUSED #endif + +#endif//_SW_DEFINES_H diff --git a/src/sw/pixel_convert.h b/src/sw/pixel_convert.h index 6f60c677a..fa0195aec 100644 --- a/src/sw/pixel_convert.h +++ b/src/sw/pixel_convert.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef _PIXEL_CONVERT_H +#define _PIXEL_CONVERT_H #include "defines.h" #include "binary_utils.h" @@ -98,3 +99,5 @@ uintpixel_t swrConvertPixelBase(uint32_t gmPixel) #define swrConvertPixelTexture(x) swrConvertPixelBase(x) #endif + +#endif//_PIXEL_CONVERT_H diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c new file mode 100755 index 000000000..ed22240d0 --- /dev/null +++ b/src/sw/sw_drawing.c @@ -0,0 +1,99 @@ +#include +#include "sw_renderer_private.h" + +bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + if (swr->drawingToSurface) { + swrCommitShadowWritesToSurfaceIfNeeded(swr, swr->surfaces[swr->currentSurfaceIndex]); + } + + if (targetSurfaceId == RENDER_TARGET_HOST_FRAMEBUFFER) + { + if (!swr->drawingToSurface) + return true; + + // restore the original framebuffer + fprintf(stderr, "back to original framebuffer\n"); + swr->drawingToSurface = false; + swr->fb = swr->mainFb; + swr->width = swr->mainWidth; + swr->height = swr->mainHeight; + swr->fbPitch = swr->mainPitch; + swr->blendMode = bm_normal; + swr->currentSurfaceIndex = -1; + swr->writeMask = WRITE_MASK_ALL; + + if (restoreOldView) { + // restore the old transform, if needed + swr->viewX = swr->lastViewX; + swr->viewY = swr->lastViewY; + swr->viewW = swr->lastViewW; + swr->viewH = swr->lastViewH; + swr->portX = swr->lastPortX; + swr->portY = swr->lastPortY; + swr->portW = swr->lastPortW; + swr->portH = swr->lastPortH; + swr->gameW = swr->lastGameW; + swr->gameH = swr->lastGameH; + swr->maxX = swr->lastMaxX; + swr->maxY = swr->lastMaxY; + swr->scaleX = swr->lastScaleX; + swr->scaleY = swr->lastScaleY; + } + return true; + } + + if (targetSurfaceId < 0 || (size_t) targetSurfaceId >= swr->surfaceCount || swr->surfaces[targetSurfaceId] == NULL) { + fprintf(stderr, "swr: Invalid surface id %d\n", targetSurfaceId); + return false; + } + + if (!swr->drawingToSurface) + { + // back up the original framebuffer + swr->drawingToSurface = true; + swr->mainFb = swr->fb; + swr->mainWidth = swr->width; + swr->mainHeight = swr->height; + swr->mainPitch = swr->fbPitch; + swr->blendMode = bm_normal; + + // and the old transform + swr->lastViewX = swr->viewX; + swr->lastViewY = swr->viewY; + swr->lastViewW = swr->viewW; + swr->lastViewH = swr->viewH; + swr->lastPortX = swr->portX; + swr->lastPortY = swr->portY; + swr->lastPortW = swr->portW; + swr->lastPortH = swr->portH; + swr->lastGameW = swr->gameW; + swr->lastGameH = swr->gameH; + swr->lastMaxX = swr->maxX; + swr->lastMaxY = swr->maxY; + swr->lastScaleX = swr->scaleX; + swr->lastScaleY = swr->scaleY; + } + + SWTexture* surface = swr->surfaces[targetSurfaceId]->texture; + swr->fb = surface->buffer; + swr->width = surface->width; + swr->height = surface->height; + swr->fbPitch = surface->width; + swr->drawingToSurface = true; + swr->blendMode = bm_normal; + swr->currentSurfaceIndex = targetSurfaceId; + swr->writeMask = WRITE_MASK_ALL; + + swr->viewX = swr->portX = 0; + swr->viewY = swr->portY = 0; + swr->maxX = swr->viewW = swr->portW = surface->width; + swr->maxY = swr->viewH = swr->portH = surface->height; + swr->scaleX = swr->scaleY = 1.0f; + + fprintf(stderr, "switching to surface %p, fb %p, %dx%d\n", surface, swr->fb, swr->width, swr->height); + + return true; +} diff --git a/src/sw/sw_drawing.h b/src/sw/sw_drawing.h new file mode 100755 index 000000000..edfc5df59 --- /dev/null +++ b/src/sw/sw_drawing.h @@ -0,0 +1,7 @@ +#ifndef _SW_DRAWING_H +#define _SW_DRAWING_H + +bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView); + + +#endif//_SW_DRAWING_H diff --git a/src/sw/sw_inlined.h b/src/sw/sw_inlined.h new file mode 100755 index 000000000..9bc8dd7ce --- /dev/null +++ b/src/sw/sw_inlined.h @@ -0,0 +1,53 @@ +#ifndef _SW_INLINED_H +#define _SW_INLINED_H + +#include "defines.h" + +FORCE_INLINE int swrMin(int a, int b) { return a < b ? a : b; } +FORCE_INLINE int swrMax(int a, int b) { return a > b ? a : b; } +FORCE_INLINE int swrAbs(int x) { return x < 0 ? -x : x; } + +FORCE_INLINE int swrSgn(float x) +{ + if (x < 0) return -1; + return 1; +} + +FORCE_INLINE int swrFloor(float x) +{ + int i = (int) x; + return i - (x < (float) i); +} + +FORCE_INLINE int swrCeiling(float x) +{ + int i = (int) x; + return i + (x > (float) i); +} + +// Checks if the "rotate" version of the sprite drawing routine should be used. +FORCE_INLINE bool swrMustRotate(float angleDeg) +{ + int angleDegInt = (int)(angleDeg * 4); + angleDegInt %= 360*4; + + if (angleDegInt > 180*4) + angleDegInt -= 360*4; + + return swrAbs(angleDegInt) >= 1; // 0.25 degrees +} + +// Checks if the "rotate" version of the sprite drawing routine should be used. +// This is a more sensitive version. +FORCE_INLINE bool swrMustRotateSensitive(float angleDeg) +{ + int angleDegInt = (int)(angleDeg * 16); + angleDegInt %= 360*16; + + if (angleDegInt > 180*16) + angleDegInt -= 360*16; + + return swrAbs(angleDegInt) >= 1; // 1/16 of a degree +} + +#endif//_SW_INLINED_H diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h new file mode 100755 index 000000000..9d55130c9 --- /dev/null +++ b/src/sw/sw_pixel_calc.h @@ -0,0 +1,183 @@ +#ifndef _SW_PIXEL_CALC_H +#define _SW_PIXEL_CALC_H + +#include "defines.h" +#include "pixel_convert.h" + +// Check if a pixel is opaque. +// +// Later, this should be changed to perform full alpha-blending +// (at least in 32-bit pixel mode) +FORCE_INLINE bool opaque(uintpixel_t color) +{ +#if PIXEL_SIZE == 8 + return (color != PXL_TRANSPARENT); +#else + return (color & TRANSPARENT_MASK) != 0; +#endif +} + +// Multiplies a color value (`color`) by another color value (`tintColor`). +FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) +{ +#if PIXEL_SIZE == 32 + Pixel32ARGB x, y; + + if ((tintColor & 0xFFFFFF) == 0xFFFFFF) + return color; + + x.l = color; + y.l = tintColor; + + x.p.b = (int)x.p.b * y.p.b / 255; + x.p.g = (int)x.p.g * y.p.g / 255; + x.p.r = (int)x.p.r * y.p.r / 255; + return x.l; +#elif PIXEL_SIZE == 16 + if ((tintColor & 0x7FFF) == 0x7FFF) + return color; + + int tcb = tintColor & 0x1F; + int tcg = (tintColor >> 5) & 0x1F; + int tcr = (tintColor >> 10) & 0x1F; + + int cb = color & 0x1F; + int cg = (color >> 5) & 0x1F; + int cr = (color >> 10) & 0x1F; + int ca = color & 0x8000; + + cb = (cb * tcb) / 32; + cg = (cg * tcg) / 32; + cr = (cr * tcr) / 32; + return ca | cb | (cg << 5) | (cr << 10); +#elif PIXEL_SIZE == 8 + // fast but hacky + if (tintColor == 0xFF || tintColor == PXL_TRANSPARENT) + return color; + + return color & tintColor; +#endif +} + +// Performs alpha blending on a pixel, with another pixel. +// +// NOTE: alpha is between 0 and 256, NOT between 0 and 255! +// +// TODO: This routine could use some optimization. Obviously I tried my best, but clearly +// it's still true that too many calculations are being performed. +// +// NOTE: Obviously I could use SIMD here, but old computers didn't have SIMD, and the code +// runs fast enough on modern computers to not need to do SIMD. +FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int srcalpha, int dstalpha) +{ +#if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 + // it's so significant here we might as well fill in the whole color + if (LIKELY(dstalpha < 3 && srcalpha > 253)) { + *dcolor = scolor; + return; + } + + // it's so insignificant here nobody will notice if we just don't... + if (UNLIKELY(srcalpha == 0)) + return; +#endif + +#if PIXEL_SIZE == 32 + Pixel32ARGB dc, sc; + dc.l = *dcolor; + sc.l = scolor; + + int dcr = (dc.p.r * dstalpha + sc.p.r * srcalpha) >> 8; + int dcg = (dc.p.g * dstalpha + sc.p.g * srcalpha) >> 8; + int dcb = (dc.p.b * dstalpha + sc.p.b * srcalpha) >> 8; + + //clamp to 0 + dcr &= ((-dcr) >> 31); + dcg &= ((-dcg) >> 31); + dcb &= ((-dcb) >> 31); + //clamp to 255 + dcr |= ((signed char)(dcr >> 1) >> 7); + dcg |= ((signed char)(dcg >> 1) >> 7); + dcb |= ((signed char)(dcb >> 1) >> 7); + + dc.p.r = dcr; + dc.p.g = dcg; + dc.p.b = dcb; + dc.p.a = 0xFF; + + *dcolor = dc.l; +#elif PIXEL_SIZE == 16 + int scb = scolor & 0x1F; + int scg = (scolor >> 5) & 0x1F; + int scr = (scolor >> 10) & 0x1F; + + uintpixel_t _dcolor = *dcolor; + int dcb = _dcolor & 0x1F; + int dcg = (_dcolor >> 5) & 0x1F; + int dcr = (_dcolor >> 10) & 0x1F; + + dcr = (dcr * dstalpha + scr * srcalpha) >> 8; + dcg = (dcg * dstalpha + scg * srcalpha) >> 8; + dcb = (dcb * dstalpha + scb * srcalpha) >> 8; + + //clamp to 0 + dcr &= ((-dcr) >> 31); + dcg &= ((-dcg) >> 31); + dcb &= ((-dcb) >> 31); + //clamp to 255 + dcr |= ((signed char)(dcr >> 1) >> 7); + dcg |= ((signed char)(dcg >> 1) >> 7); + dcb |= ((signed char)(dcb >> 1) >> 7); + + *dcolor = 0x8000 | dcb | (dcg << 5) | (dcr << 10); +#else + if (srcalpha < 240) { + static int alphaApproximationThingy = 0; + alphaApproximationThingy += 1339; + if (alphaApproximationThingy > 601000) + alphaApproximationThingy = 0; + + //gotta love that RNG + if ((alphaApproximationThingy & 0xFF) >= srcalpha) + return; + } + + *dcolor = scolor; +#endif +} + +// Calculates an internal "alpha" value from GML-provided "alpha" values. +FORCE_INLINE int swrIntAlpha(float alphaf) +{ + return (int)(alphaf * 256); +} + +// Calculates the source alpha for a pixel based on the current blend mode. +FORCE_INLINE int swrCalcSrcAlpha(SWRenderer* swr, int alpha) +{ + switch (swr->blendMode) + { + default: + return alpha; + case bm_add: + return alpha; + case bm_subtract: + return -alpha; + } +} + +// Calculates the destination alpha for a pixel based on the current blend mode. +FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) +{ + switch (swr->blendMode) + { + default: + return 256 - alpha; + case bm_add: + return 256; + case bm_subtract: + return 256; + } +} + +#endif//_SW_PIXEL_CALC_H diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 1561cbb94..4065394ee 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -2,505 +2,13 @@ #include #include #include -#include "defines.h" -#include "sw_renderer.h" #include "text_utils.h" -#include "pixel_convert.h" #include "image/image_decoder.h" -#define UNIMP() do { fprintf(stderr, "NYI %s\n", __func__); } while (0) -//#define UNIMP() do { } while (0) -#define UNIMP2() do { } while (0) - -#ifndef M_PI -#define M_PI 3.1415926535897932384626 -#endif - -#define TEXTURE_LRU_LENGTH 64 - -#define SURFACE_MAX_COUNT 64 - -typedef struct -{ - uintpixel_t* buffer; - uint16_t width, height; -} -SWTexture; - -typedef struct -{ - // used for almost all intents and purposes. - SWTexture* texture; - // upon a gpuSetColorWriteEnable change, the shadow texture is used for writing instead. - SWTexture* shadowTexture; -} -SWSurface; - -typedef struct -{ - Renderer base; - - // Window Properties - uint16_t width; - uint16_t height; - // Framebuffer - uintpixel_t* fb; - uint16_t fbPitch; // in sizeof(uintpixel_t) units, NOT in bytes! - - bool drawingToSurface; - uintpixel_t* mainFb; - uint16_t mainWidth; - uint16_t mainHeight; - uint16_t mainPitch; - int lastViewX, lastViewY, lastViewW, lastViewH; - int lastPortX, lastPortY, lastPortW, lastPortH; - int lastGameW, lastGameH, lastMaxX, lastMaxY; - float lastScaleX, lastScaleY; - - SWTexture** textures; - SWSurface** surfaces; - uint32_t* textureIndexLRU; - uint32_t textureIndexLRUHead; - uint32_t textureIndexLRUTail; - size_t textureCount; - size_t surfaceCount; - size_t totalTextureCount; - size_t originalTPagCount; - size_t originalSpriteCount; - - bool viewActive; - int viewX, viewY, viewW, viewH; - int portX, portY, portW, portH; - int gameW, gameH, maxX, maxY; - - int offsetX, offsetY; - float scaleX, scaleY; - float defaultScaleX, defaultScaleY; - - int blendMode; - - // only used for surfaces. The application surface doesn't support these at the moment. - int currentSurfaceIndex; - int writeMask; -} -SWRenderer; - -#define WRITE_MASK_ALL (15) -#define WRITE_MASK_RED (1) -#define WRITE_MASK_GREEN (2) -#define WRITE_MASK_BLUE (4) -#define WRITE_MASK_ALPHA (8) +#include "sw_renderer_private.h" void Runner_setNextFrame(uintpixel_t* framebuffer, int width, int height); -FORCE_INLINE int swrMin(int a, int b) { return a < b ? a : b; } -FORCE_INLINE int swrMax(int a, int b) { return a > b ? a : b; } -FORCE_INLINE int swrAbs(int x) { return x < 0 ? -x : x; } - -FORCE_INLINE bool opaque(uintpixel_t color) -{ -#if PIXEL_SIZE == 8 - return (color != PXL_TRANSPARENT); -#else - return (color & TRANSPARENT_MASK) != 0; -#endif -} - -FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) -{ -#if PIXEL_SIZE == 32 - Pixel32ARGB x, y; - - if ((tintColor & 0xFFFFFF) == 0xFFFFFF) - return color; - - x.l = color; - y.l = tintColor; - - x.p.b = (int)x.p.b * y.p.b / 255; - x.p.g = (int)x.p.g * y.p.g / 255; - x.p.r = (int)x.p.r * y.p.r / 255; - return x.l; -#elif PIXEL_SIZE == 16 - if ((tintColor & 0x7FFF) == 0x7FFF) - return color; - - int tcb = tintColor & 0x1F; - int tcg = (tintColor >> 5) & 0x1F; - int tcr = (tintColor >> 10) & 0x1F; - - int cb = color & 0x1F; - int cg = (color >> 5) & 0x1F; - int cr = (color >> 10) & 0x1F; - int ca = color & 0x8000; - - cb = (cb * tcb) / 32; - cg = (cg * tcg) / 32; - cr = (cr * tcr) / 32; - return ca | cb | (cg << 5) | (cr << 10); -#elif PIXEL_SIZE == 8 - // fast but hacky - if (tintColor == 0xFF || tintColor == PXL_TRANSPARENT) - return color; - - return color & tintColor; -#endif -} - -// NOTE: alpha is between 0 and 256, NOT between 0 and 255! -// -// TODO: this routine could use some optimization. Obviously I tried my best, but clearly -// it's still true that too many calculations are being performed. -// -// TODO: SIMD could be used here! however, I don't really care. -// Old computers/consoles we plan to run this on don't really have -// SIMD, do they? So I want to optimize for the unfortunate case as -// much as I can. -FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int srcalpha, int dstalpha) -{ -#if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 - // it's so significant here we might as well fill in the whole color - if (LIKELY(dstalpha < 3 && srcalpha > 253)) { - *dcolor = scolor; - return; - } - - // it's so insignificant here nobody will notice if we just don't... - if (UNLIKELY(srcalpha == 0)) - return; -#endif - -#if PIXEL_SIZE == 32 - Pixel32ARGB dc, sc; - dc.l = *dcolor; - sc.l = scolor; - - int dcr = (dc.p.r * dstalpha + sc.p.r * srcalpha) >> 8; - int dcg = (dc.p.g * dstalpha + sc.p.g * srcalpha) >> 8; - int dcb = (dc.p.b * dstalpha + sc.p.b * srcalpha) >> 8; - - //clamp to 0 - dcr &= ((-dcr) >> 31); - dcg &= ((-dcg) >> 31); - dcb &= ((-dcb) >> 31); - //clamp to 255 - dcr |= ((signed char)(dcr >> 1) >> 7); - dcg |= ((signed char)(dcg >> 1) >> 7); - dcb |= ((signed char)(dcb >> 1) >> 7); - - dc.p.r = dcr; - dc.p.g = dcg; - dc.p.b = dcb; - dc.p.a = 0xFF; - - *dcolor = dc.l; -#elif PIXEL_SIZE == 16 - int scb = scolor & 0x1F; - int scg = (scolor >> 5) & 0x1F; - int scr = (scolor >> 10) & 0x1F; - - uintpixel_t _dcolor = *dcolor; - int dcb = _dcolor & 0x1F; - int dcg = (_dcolor >> 5) & 0x1F; - int dcr = (_dcolor >> 10) & 0x1F; - - dcr = (dcr * dstalpha + scr * srcalpha) >> 8; - dcg = (dcg * dstalpha + scg * srcalpha) >> 8; - dcb = (dcb * dstalpha + scb * srcalpha) >> 8; - - //clamp to 0 - dcr &= ((-dcr) >> 31); - dcg &= ((-dcg) >> 31); - dcb &= ((-dcb) >> 31); - //clamp to 255 - dcr |= ((signed char)(dcr >> 1) >> 7); - dcg |= ((signed char)(dcg >> 1) >> 7); - dcb |= ((signed char)(dcb >> 1) >> 7); - - *dcolor = 0x8000 | dcb | (dcg << 5) | (dcr << 10); -#else - if (srcalpha < 240) { - static int alphaApproximationThingy = 0; - alphaApproximationThingy += 1339; - if (alphaApproximationThingy > 601000) - alphaApproximationThingy = 0; - - //gotta love that RNG - if ((alphaApproximationThingy & 0xFF) >= srcalpha) - return; - } - - *dcolor = scolor; -#endif -} - -FORCE_INLINE int swrIntAlpha(float alphaf) -{ - return (int)(alphaf * 256); -} - -FORCE_INLINE int swrCalcSrcAlpha(SWRenderer* swr, int alpha) -{ - switch (swr->blendMode) - { - default: - return alpha; - case bm_add: - return alpha; - case bm_subtract: - return -alpha; - } -} - -FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) -{ - switch (swr->blendMode) - { - default: - return 256 - alpha; - case bm_add: - return 256; - case bm_subtract: - return 256; - } -} - -FORCE_INLINE bool swrMustRotate(float angleDeg) -{ - int angleDegInt = (int)(angleDeg * 4); - angleDegInt %= 360*4; - - if (angleDegInt > 180*4) - angleDegInt -= 360*4; - - return swrAbs(angleDegInt) >= 1; // 0.25 degrees -} - -FORCE_INLINE bool swrMustRotateTolerant(float angleDeg) -{ - int angleDegInt = (int)(angleDeg * 16); - angleDegInt %= 360*16; - - if (angleDegInt > 180*16) - angleDegInt -= 360*16; - - return swrAbs(angleDegInt) >= 1; // 1/16 of a degree -} - -FORCE_INLINE int swrSgn(float x) -{ - if (x < 0) return -1; - return 1; -} - -FORCE_INLINE int swrFloor(float x) -{ - int i = (int) x; - return i - (x < (float) i); -} - -FORCE_INLINE int swrCeiling(float x) -{ - int i = (int) x; - return i + (x > (float) i); -} - -static SWTexture* swrCreateTextureEx(const void* srcBuffer, int width, int height, bool convert) -{ - SWTexture* txt = (SWTexture*) safeMalloc(sizeof(SWTexture)); - txt->buffer = (uintpixel_t*) safeMalloc(width * height * sizeof(uintpixel_t)); - - size_t sz = width * height; - - if (srcBuffer) - { - if (convert) { - const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; - for (size_t i = 0; i < sz; i++) - txt->buffer[i] = swrConvertPixelTexture(rgbaSrc[i]); - } - else { - const uintpixel_t* rgbaSrc = (const uintpixel_t*) srcBuffer; - for (size_t i = 0; i < sz; i++) - txt->buffer[i] = rgbaSrc[i]; - } - } - else - { - memset(txt->buffer, 0, width * height * sizeof(uintpixel_t)); - } - - txt->width = (uint16_t) width; - txt->height = (uint16_t) height; - - return txt; -} - -static SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height) -{ - return swrCreateTextureEx(srcBuffer, width, height, true); -} - -static SWSurface* swrCreateSurface(int width, int height) -{ - SWSurface* surf = (SWSurface*) safeMalloc(sizeof(SWSurface)); - surf->texture = swrCreateTexture(NULL, width, height); - surf->shadowTexture = NULL; - return surf; -} - -static SWTexture* swrCopyTexture(SWTexture* texture) -{ - return swrCreateTextureEx(texture->buffer, texture->width, texture->height, false); -} - -static void swrFreeTexture(SWTexture* texture) -{ - if (UNLIKELY(!texture)) - return; - - free(texture->buffer); - free(texture); -} - -static void swrFreeSurface(SWSurface* surface) -{ - if (UNLIKELY(!surface)) - return; - - swrFreeTexture(surface->texture); - swrFreeTexture(surface->shadowTexture); - free(surface); -} - -static SWTexture* swrWritableSurfaceTexture(SWRenderer* swr, int surfaceID) -{ - if (UNLIKELY(swr->surfaces[surfaceID]->shadowTexture)) { - return swr->surfaces[surfaceID]->shadowTexture; - } - - if (LIKELY(swr->writeMask == WRITE_MASK_ALL)) { - return swr->surfaces[surfaceID]->texture; - } - - if (UNLIKELY(!swr->surfaces[surfaceID]->shadowTexture)) { - swr->surfaces[surfaceID]->shadowTexture = swrCopyTexture(swr->surfaces[surfaceID]->texture); - } - - return swr->surfaces[surfaceID]->shadowTexture; -} - -static bool swrAddTextureIndexToLRU(SWRenderer* swr, int textureIndex) -{ - uint32_t newIndex = (swr->textureIndexLRUHead + 1) % TEXTURE_LRU_LENGTH; - if (newIndex == swr->textureIndexLRUTail) { - // about to collide with tail from the other side -- nope. - return false; - } - - swr->textureIndexLRU[swr->textureIndexLRUHead] = textureIndex; - swr->textureIndexLRUHead = newIndex; - return true; -} - -static int swrTailTextureIndexLRU(SWRenderer* swr, bool remove) -{ - if (swr->textureIndexLRUHead == swr->textureIndexLRUTail) - return -1; - - uint32_t textureIndex = swr->textureIndexLRU[swr->textureIndexLRUTail]; - - if (remove) - swr->textureIndexLRUTail = (swr->textureIndexLRUTail + 1) % TEXTURE_LRU_LENGTH; - - return textureIndex; -} - -static void swrEvictTextureFromCache(SWRenderer* swr, int textureIndex) -{ - SWTexture* texture = swr->textures[textureIndex]; - swr->textures[textureIndex] = NULL; - - swrFreeTexture(texture); -} - -// Lazily decodes and uploads a TXTR page on first access. -// Returns true if the texture is ready, false if it failed to decode. -static bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) -{ - if (swr->textures[pageId]) - return true; - - DataWin* dw = swr->base.dataWin; - Texture* txtr = &dw->txtr.textures[pageId]; - - int w, h; - bool gm2022_5 = DataWin_isVersionAtLeast(dw, 2022, 5, 0, 0); - - uint8_t* pixels = NULL; - - do - { - if (!txtr->blobData) { - DataWin_loadTxtrIfNeeded(dw, pageId); - } - - if (txtr->blobData) { - pixels = ImageDecoder_decodeToRgba(txtr->blobData, (size_t) txtr->blobSize, gm2022_5, &w, &h); - if (pixels) { - if (!txtr->mapped) { - free(txtr->blobData); - txtr->blobData = NULL; - } - break; - } - - fprintf(stderr, "swr: Failed to decode TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); - } else { - fprintf(stderr, "swr: Failed to load TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); - } - - int tail = swrTailTextureIndexLRU(swr, true); - if (tail == -1) { - fprintf(stderr, "swr: Looks like we can't fit this texture in memory at all. Bummer.\n"); - break; - } - - swrEvictTextureFromCache(swr, tail); - fprintf(stderr, "swr: Evicted texture %d, trying again.\n", tail); - } - while (!pixels); - - if (pixels == nullptr) { - fprintf(stderr, "swr: Failed to decode TXTR page %u.\n", pageId); - return false; - } - - swr->textures[pageId] = swrCreateTexture(pixels, w, h); - free(pixels); - - fprintf(stderr, "SWR: Loaded TXTR page %u (%dx%d)\n", pageId, w, h); - - // add it to the LRU - do - { - bool added = swrAddTextureIndexToLRU(swr, pageId); - if (added) - break; - - int tail = swrTailTextureIndexLRU(swr, true); - if (tail == -1) { - fprintf(stderr, "swr: Come on now.\n"); - assert(tail != -1); - } - - swrEvictTextureFromCache(swr, tail); - } - while (true); - - return true; -} - static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) { SWRenderer* swr = (SWRenderer*) renderer; @@ -664,159 +172,6 @@ static void SWRenderer_endView(Renderer* renderer) swr->scaleY = swr->defaultScaleY; } -static void swrCommitShadowWritesToSurfaceIfNeeded(SWRenderer* swr, SWSurface* surface) -{ - if (LIKELY(!swr->drawingToSurface)) - return; - - if (UNLIKELY(!surface)) - return; - - if (UNLIKELY(!surface->shadowTexture)) - return; - - if (LIKELY(swr->writeMask == WRITE_MASK_ALL)) { - swrFreeTexture(surface->texture); - surface->texture = surface->shadowTexture; - surface->shadowTexture = NULL; - return; - } - - if (UNLIKELY(swr->writeMask == 0)) { - swrFreeTexture(surface->shadowTexture); - surface->shadowTexture = NULL; - return; - } - - uintpixel_t mask = 0; -#if PIXEL_SIZE == 32 - Pixel32ARGB x; - x.l = 0; - if (swr->writeMask & WRITE_MASK_RED) x.p.r = 255; - if (swr->writeMask & WRITE_MASK_GREEN) x.p.g = 255; - if (swr->writeMask & WRITE_MASK_BLUE) x.p.b = 255; - if (swr->writeMask & WRITE_MASK_ALPHA) x.p.a = 255; - mask = x.l; -#elif PIXEL_SIZE == 16 - if (swr->writeMask & WRITE_MASK_RED) l |= 0x7C00; - if (swr->writeMask & WRITE_MASK_GREEN) l |= 0x03E0; - if (swr->writeMask & WRITE_MASK_BLUE) l |= 0x001F; - if (swr->writeMask & WRITE_MASK_ALPHA) l |= 0x8000; -#else - //although it DOES ues rgb332, needs special handling for ALPHA - fprintf(stderr, "swr: Unimplemented color masking for 8-bit mode TODO\n"); - swrFreeTexture(surface->texture); - surface->texture = surface->shadowTexture; - surface->shadowTexture = NULL; - return; -#endif - - uintpixel_t invmask = ~mask; - size_t max = surface->texture->width * surface->texture->height; - for (size_t i = 0; i < max; i++) - { - surface->shadowTexture->buffer[i] = - surface->texture->buffer[i] = (surface->texture->buffer[i] & invmask) | (surface->shadowTexture->buffer[i] & mask); - } -} - -static bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView) -{ - SWRenderer* swr = (SWRenderer*) renderer; - - if (swr->drawingToSurface) { - swrCommitShadowWritesToSurfaceIfNeeded(swr, swr->surfaces[swr->currentSurfaceIndex]); - } - - if (targetSurfaceId == RENDER_TARGET_HOST_FRAMEBUFFER) - { - if (!swr->drawingToSurface) - return true; - - // restore the original framebuffer - fprintf(stderr, "back to original framebuffer\n"); - swr->drawingToSurface = false; - swr->fb = swr->mainFb; - swr->width = swr->mainWidth; - swr->height = swr->mainHeight; - swr->fbPitch = swr->mainPitch; - swr->blendMode = bm_normal; - swr->currentSurfaceIndex = -1; - swr->writeMask = WRITE_MASK_ALL; - - if (restoreOldView) { - // restore the old transform, if needed - swr->viewX = swr->lastViewX; - swr->viewY = swr->lastViewY; - swr->viewW = swr->lastViewW; - swr->viewH = swr->lastViewH; - swr->portX = swr->lastPortX; - swr->portY = swr->lastPortY; - swr->portW = swr->lastPortW; - swr->portH = swr->lastPortH; - swr->gameW = swr->lastGameW; - swr->gameH = swr->lastGameH; - swr->maxX = swr->lastMaxX; - swr->maxY = swr->lastMaxY; - swr->scaleX = swr->lastScaleX; - swr->scaleY = swr->lastScaleY; - } - return true; - } - - if (targetSurfaceId < 0 || (size_t) targetSurfaceId >= swr->surfaceCount || swr->surfaces[targetSurfaceId] == NULL) { - fprintf(stderr, "swr: Invalid surface id %d\n", targetSurfaceId); - return false; - } - - if (!swr->drawingToSurface) - { - // back up the original framebuffer - swr->drawingToSurface = true; - swr->mainFb = swr->fb; - swr->mainWidth = swr->width; - swr->mainHeight = swr->height; - swr->mainPitch = swr->fbPitch; - swr->blendMode = bm_normal; - - // and the old transform - swr->lastViewX = swr->viewX; - swr->lastViewY = swr->viewY; - swr->lastViewW = swr->viewW; - swr->lastViewH = swr->viewH; - swr->lastPortX = swr->portX; - swr->lastPortY = swr->portY; - swr->lastPortW = swr->portW; - swr->lastPortH = swr->portH; - swr->lastGameW = swr->gameW; - swr->lastGameH = swr->gameH; - swr->lastMaxX = swr->maxX; - swr->lastMaxY = swr->maxY; - swr->lastScaleX = swr->scaleX; - swr->lastScaleY = swr->scaleY; - } - - SWTexture* surface = swr->surfaces[targetSurfaceId]->texture; - swr->fb = surface->buffer; - swr->width = surface->width; - swr->height = surface->height; - swr->fbPitch = surface->width; - swr->drawingToSurface = true; - swr->blendMode = bm_normal; - swr->currentSurfaceIndex = targetSurfaceId; - swr->writeMask = WRITE_MASK_ALL; - - swr->viewX = swr->portX = 0; - swr->viewY = swr->portY = 0; - swr->maxX = swr->viewW = swr->portW = surface->width; - swr->maxY = swr->viewH = swr->portH = surface->height; - swr->scaleX = swr->scaleY = 1.0f; - - fprintf(stderr, "switching to surface %p, fb %p, %dx%d\n", surface, swr->fb, swr->width, swr->height); - - return true; -} - static void SWRenderer_beginGUI(Renderer* renderer, int32_t guiW, int32_t guiH, int32_t portX, int32_t portY, int32_t portW, int32_t portH, int32_t targetSurfaceId) { @@ -843,86 +198,6 @@ static void SWRenderer_endGUI(Renderer* renderer) UNIMP2(); } -// TODO[MrPowerGamerBR]: This is supposed to be refactored, not to modify data.win structs directly. -static int32_t swrFindSurfaceTextureSlot(SWRenderer* swr) -{ - // NOTE: dynamic textures are not enrolled into the eviction cache for - // hopefully obvious reasons ... - for (size_t i = swr->textureCount; i != swr->totalTextureCount; i++) - { - if (swr->textures[i] == NULL) { - return (int32_t) i; - } - } - - return -1; -} - -static int32_t swrFindSurfaceTPagSlot(SWRenderer* swr) -{ - DataWin* dw = swr->base.dataWin; - for (size_t i = swr->originalTPagCount; i != dw->tpag.count; i++) - { - if (dw->tpag.items[i].texturePageId == -1) { - return (int32_t) i; - } - } - - return -1; -} - -static void swrTransformPosIfNeeded(SWRenderer* swr, float* dx, float* dy) -{ - if (dx) { - *dx -= swr->viewX; - *dx *= swr->scaleX; - *dx += swr->portX; - } - if (dy) { - *dy -= swr->viewY; - *dy *= swr->scaleY; - *dy += swr->portY; - } -} - -static void swrTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) -{ - if (dx) *dx *= swr->scaleX; - if (dy) *dy *= swr->scaleY; -} - -static void swrTransformPosIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) -{ - if (dx) { - *dx -= swr->viewX; - *dx = (int)(*dx * swr->scaleX); - *dx += swr->portX; - } - if (dy) { - *dy -= swr->viewY; - *dy = (int)(*dy * swr->scaleY); - *dy += swr->portY; - } -} - -static void swrTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) -{ - if (dx) *dx = (int)(*dx * swr->scaleX); - if (dy) *dy = (int)(*dy * swr->scaleY); -} - -static void swrReverseTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) -{ - if (dx) *dx = *dx / swr->scaleX; - if (dy) *dy = *dy / swr->scaleY; -} - -static void swrReverseTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) -{ - if (dx) *dx = (int)(*dx / swr->scaleX); - if (dy) *dy = (int)(*dy / swr->scaleY); -} - FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int srcalpha, int dstalpha) { SWRenderer* swr = (SWRenderer*) renderer; @@ -1956,7 +1231,7 @@ static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, flo // TODO: do we need to mirror the way the text scrolls too?! float cosA = 1.0f, sinA = 0.0f, angleRad = 0.0f; - bool mustRotate = swrMustRotateTolerant(angleDeg); + bool mustRotate = swrMustRotateSensitive(angleDeg); if (UNLIKELY(mustRotate)) { angleRad = -angleDeg * M_PI / 180.0f; diff --git a/src/sw/sw_renderer.h b/src/sw/sw_renderer.h index 1b2618779..e79997be3 100644 --- a/src/sw/sw_renderer.h +++ b/src/sw/sw_renderer.h @@ -1,7 +1,10 @@ -#pragma once +#ifndef _SW_RENDERER_H +#define _SW_RENDERER_H #include "renderer.h" Renderer* SWRenderer_create(void); void SWRenderer_clearFrameBuffer(Renderer* renderer, uint32_t color); + +#endif//_SW_RENDERER_H diff --git a/src/sw/sw_renderer_private.h b/src/sw/sw_renderer_private.h new file mode 100755 index 000000000..6b9b61738 --- /dev/null +++ b/src/sw/sw_renderer_private.h @@ -0,0 +1,106 @@ +#ifndef _SW_RENDERER_PRIVATE_H +#define _SW_RENDERER_PRIVATE_H + +#include "sw_renderer.h" +#include "defines.h" +#include "pixel_convert.h" + +// Unimplemented Functions +#define UNIMP() do { fprintf(stderr, "NYI %s\n", __func__); } while (0) +//#define UNIMP() do { } while (0) +#define UNIMP2() do { } while (0) + +// Provide PI if not specified +#ifndef M_PI +#define M_PI 3.1415926535897932384626 +#endif + +// Configurable Properties +#define TEXTURE_LRU_LENGTH 64 +#define SURFACE_MAX_COUNT 64 +// (NOTE: See PIXEL_SIZE in defines.h) + + +// Struct Definitions +typedef struct +{ + uintpixel_t* buffer; + uint16_t width, height; +} +SWTexture; + +typedef struct +{ + // used for almost all intents and purposes. + SWTexture* texture; + // upon a gpuSetColorWriteEnable change, the shadow texture is used for writing instead. + SWTexture* shadowTexture; +} +SWSurface; + +#define WRITE_MASK_ALL (15) +#define WRITE_MASK_RED (1) +#define WRITE_MASK_GREEN (2) +#define WRITE_MASK_BLUE (4) +#define WRITE_MASK_ALPHA (8) + +typedef struct +{ + Renderer base; + + // Window Properties + uint16_t width; + uint16_t height; + // Framebuffer + uintpixel_t* fb; + uint16_t fbPitch; // in sizeof(uintpixel_t) units, NOT in bytes! + + bool drawingToSurface; + uintpixel_t* mainFb; + uint16_t mainWidth; + uint16_t mainHeight; + uint16_t mainPitch; + int lastViewX, lastViewY, lastViewW, lastViewH; + int lastPortX, lastPortY, lastPortW, lastPortH; + int lastGameW, lastGameH, lastMaxX, lastMaxY; + float lastScaleX, lastScaleY; + + SWTexture** textures; + SWSurface** surfaces; + uint32_t* textureIndexLRU; + uint32_t textureIndexLRUHead; + uint32_t textureIndexLRUTail; + size_t textureCount; + size_t surfaceCount; + size_t totalTextureCount; + size_t originalTPagCount; + size_t originalSpriteCount; + + bool viewActive; + int viewX, viewY, viewW, viewH; + int portX, portY, portW, portH; + int gameW, gameH, maxX, maxY; + + int offsetX, offsetY; + float scaleX, scaleY; + float defaultScaleX, defaultScaleY; + + int blendMode; + + // only used for surfaces. The application surface doesn't support these at the moment. + int currentSurfaceIndex; + int writeMask; +} +SWRenderer; + +// Inlined function definitions included below +#include "sw_pixel_calc.h" +#include "sw_inlined.h" +#include "sw_transform.h" + +#include "sw_texture.h" +#include "sw_surface.h" +#include "sw_drawing.h" +#include "sw_texture_lru.h" + +#endif//_SW_RENDERER_PRIVATE_H diff --git a/src/sw/sw_surface.c b/src/sw/sw_surface.c new file mode 100755 index 000000000..50c060925 --- /dev/null +++ b/src/sw/sw_surface.c @@ -0,0 +1,121 @@ +#include +#include "sw_renderer_private.h" + +SWSurface* swrCreateSurface(int width, int height) +{ + SWSurface* surf = (SWSurface*) safeMalloc(sizeof(SWSurface)); + surf->texture = swrCreateTexture(NULL, width, height); + surf->shadowTexture = NULL; + return surf; +} + +void swrFreeSurface(SWSurface* surface) +{ + if (UNLIKELY(!surface)) + return; + + swrFreeTexture(surface->texture); + swrFreeTexture(surface->shadowTexture); + free(surface); +} + +SWTexture* swrWritableSurfaceTexture(SWRenderer* swr, int surfaceID) +{ + if (UNLIKELY(swr->surfaces[surfaceID]->shadowTexture)) { + return swr->surfaces[surfaceID]->shadowTexture; + } + + if (LIKELY(swr->writeMask == WRITE_MASK_ALL)) { + return swr->surfaces[surfaceID]->texture; + } + + if (UNLIKELY(!swr->surfaces[surfaceID]->shadowTexture)) { + swr->surfaces[surfaceID]->shadowTexture = swrCopyTexture(swr->surfaces[surfaceID]->texture); + } + + return swr->surfaces[surfaceID]->shadowTexture; +} + +void swrCommitShadowWritesToSurfaceIfNeeded(SWRenderer* swr, SWSurface* surface) +{ + if (LIKELY(!swr->drawingToSurface)) + return; + + if (UNLIKELY(!surface)) + return; + + if (UNLIKELY(!surface->shadowTexture)) + return; + + if (LIKELY(swr->writeMask == WRITE_MASK_ALL)) { + swrFreeTexture(surface->texture); + surface->texture = surface->shadowTexture; + surface->shadowTexture = NULL; + return; + } + + if (UNLIKELY(swr->writeMask == 0)) { + swrFreeTexture(surface->shadowTexture); + surface->shadowTexture = NULL; + return; + } + + uintpixel_t mask = 0; +#if PIXEL_SIZE == 32 + Pixel32ARGB x; + x.l = 0; + if (swr->writeMask & WRITE_MASK_RED) x.p.r = 255; + if (swr->writeMask & WRITE_MASK_GREEN) x.p.g = 255; + if (swr->writeMask & WRITE_MASK_BLUE) x.p.b = 255; + if (swr->writeMask & WRITE_MASK_ALPHA) x.p.a = 255; + mask = x.l; +#elif PIXEL_SIZE == 16 + if (swr->writeMask & WRITE_MASK_RED) l |= 0x7C00; + if (swr->writeMask & WRITE_MASK_GREEN) l |= 0x03E0; + if (swr->writeMask & WRITE_MASK_BLUE) l |= 0x001F; + if (swr->writeMask & WRITE_MASK_ALPHA) l |= 0x8000; +#else + //although it DOES ues rgb332, needs special handling for ALPHA + fprintf(stderr, "swr: Unimplemented color masking for 8-bit mode TODO\n"); + swrFreeTexture(surface->texture); + surface->texture = surface->shadowTexture; + surface->shadowTexture = NULL; + return; +#endif + + uintpixel_t invmask = ~mask; + size_t max = surface->texture->width * surface->texture->height; + for (size_t i = 0; i < max; i++) + { + surface->shadowTexture->buffer[i] = + surface->texture->buffer[i] = (surface->texture->buffer[i] & invmask) | (surface->shadowTexture->buffer[i] & mask); + } +} + +// TODO[MrPowerGamerBR]: This is supposed to be refactored, not to modify data.win structs directly. +int32_t swrFindSurfaceTextureSlot(SWRenderer* swr) +{ + // NOTE: dynamic textures are not enrolled into the eviction cache for + // hopefully obvious reasons ... + for (size_t i = swr->textureCount; i != swr->totalTextureCount; i++) + { + if (swr->textures[i] == NULL) { + return (int32_t) i; + } + } + + return -1; +} + +int32_t swrFindSurfaceTPagSlot(SWRenderer* swr) +{ + DataWin* dw = swr->base.dataWin; + for (size_t i = swr->originalTPagCount; i != dw->tpag.count; i++) + { + if (dw->tpag.items[i].texturePageId == -1) { + return (int32_t) i; + } + } + + return -1; +} diff --git a/src/sw/sw_surface.h b/src/sw/sw_surface.h new file mode 100755 index 000000000..c339bd929 --- /dev/null +++ b/src/sw/sw_surface.h @@ -0,0 +1,11 @@ +#ifndef _SW_SURFACE_H +#define _SW_SURFACE_H + +SWSurface* swrCreateSurface(int width, int height); +void swrFreeSurface(SWSurface* surface); +SWTexture* swrWritableSurfaceTexture(SWRenderer* swr, int surfaceID); +void swrCommitShadowWritesToSurfaceIfNeeded(SWRenderer* swr, SWSurface* surface); +int32_t swrFindSurfaceTextureSlot(SWRenderer* swr); +int32_t swrFindSurfaceTPagSlot(SWRenderer* swr); + +#endif//_SW_SURFACE_H diff --git a/src/sw/sw_texture.c b/src/sw/sw_texture.c new file mode 100755 index 000000000..6d5089e4a --- /dev/null +++ b/src/sw/sw_texture.c @@ -0,0 +1,52 @@ +#include +#include "sw_renderer_private.h" + +SWTexture* swrCreateTextureEx(const void* srcBuffer, int width, int height, bool convert) +{ + SWTexture* txt = (SWTexture*) safeMalloc(sizeof(SWTexture)); + txt->buffer = (uintpixel_t*) safeMalloc(width * height * sizeof(uintpixel_t)); + + size_t sz = width * height; + + if (srcBuffer) + { + if (convert) { + const uint32_t* rgbaSrc = (const uint32_t*) srcBuffer; + for (size_t i = 0; i < sz; i++) + txt->buffer[i] = swrConvertPixelTexture(rgbaSrc[i]); + } + else { + const uintpixel_t* rgbaSrc = (const uintpixel_t*) srcBuffer; + for (size_t i = 0; i < sz; i++) + txt->buffer[i] = rgbaSrc[i]; + } + } + else + { + memset(txt->buffer, 0, width * height * sizeof(uintpixel_t)); + } + + txt->width = (uint16_t) width; + txt->height = (uint16_t) height; + + return txt; +} + +SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height) +{ + return swrCreateTextureEx(srcBuffer, width, height, true); +} + +SWTexture* swrCopyTexture(SWTexture* texture) +{ + return swrCreateTextureEx(texture->buffer, texture->width, texture->height, false); +} + +void swrFreeTexture(SWTexture* texture) +{ + if (UNLIKELY(!texture)) + return; + + free(texture->buffer); + free(texture); +} diff --git a/src/sw/sw_texture.h b/src/sw/sw_texture.h new file mode 100755 index 000000000..7c335f201 --- /dev/null +++ b/src/sw/sw_texture.h @@ -0,0 +1,9 @@ +#ifndef _SW_TEXTURE_H +#define _SW_TEXTURE_H + +SWTexture* swrCreateTextureEx(const void* srcBuffer, int width, int height, bool convert); +SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height); +SWTexture* swrCopyTexture(SWTexture* texture); +void swrFreeTexture(SWTexture* texture); + +#endif//_SW_TEXTURE_H diff --git a/src/sw/sw_texture_lru.c b/src/sw/sw_texture_lru.c new file mode 100755 index 000000000..71b8d049d --- /dev/null +++ b/src/sw/sw_texture_lru.c @@ -0,0 +1,116 @@ +#include +#include +#include "sw_renderer_private.h" +#include "image/image_decoder.h" + +bool swrAddTextureIndexToLRU(SWRenderer* swr, int textureIndex) +{ + uint32_t newIndex = (swr->textureIndexLRUHead + 1) % TEXTURE_LRU_LENGTH; + if (newIndex == swr->textureIndexLRUTail) { + // about to collide with tail from the other side -- nope. + return false; + } + + swr->textureIndexLRU[swr->textureIndexLRUHead] = textureIndex; + swr->textureIndexLRUHead = newIndex; + return true; +} + +int swrTailTextureIndexLRU(SWRenderer* swr, bool remove) +{ + if (swr->textureIndexLRUHead == swr->textureIndexLRUTail) + return -1; + + uint32_t textureIndex = swr->textureIndexLRU[swr->textureIndexLRUTail]; + + if (remove) + swr->textureIndexLRUTail = (swr->textureIndexLRUTail + 1) % TEXTURE_LRU_LENGTH; + + return textureIndex; +} + +void swrEvictTextureFromCache(SWRenderer* swr, int textureIndex) +{ + SWTexture* texture = swr->textures[textureIndex]; + swr->textures[textureIndex] = NULL; + + swrFreeTexture(texture); +} + +// Lazily decodes and uploads a TXTR page on first access. +// Returns true if the texture is ready, false if it failed to decode. +bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) +{ + if (swr->textures[pageId]) + return true; + + DataWin* dw = swr->base.dataWin; + Texture* txtr = &dw->txtr.textures[pageId]; + + int w, h; + bool gm2022_5 = DataWin_isVersionAtLeast(dw, 2022, 5, 0, 0); + + uint8_t* pixels = NULL; + + do + { + if (!txtr->blobData) { + DataWin_loadTxtrIfNeeded(dw, pageId); + } + + if (txtr->blobData) { + pixels = ImageDecoder_decodeToRgba(txtr->blobData, (size_t) txtr->blobSize, gm2022_5, &w, &h); + if (pixels) { + if (!txtr->mapped) { + free(txtr->blobData); + txtr->blobData = NULL; + } + break; + } + + fprintf(stderr, "swr: Failed to decode TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); + } else { + fprintf(stderr, "swr: Failed to load TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); + } + + int tail = swrTailTextureIndexLRU(swr, true); + if (tail == -1) { + fprintf(stderr, "swr: Looks like we can't fit this texture in memory at all. Bummer.\n"); + break; + } + + swrEvictTextureFromCache(swr, tail); + fprintf(stderr, "swr: Evicted texture %d, trying again.\n", tail); + } + while (!pixels); + + if (pixels == nullptr) { + fprintf(stderr, "swr: Failed to decode TXTR page %u.\n", pageId); + return false; + } + + swr->textures[pageId] = swrCreateTexture(pixels, w, h); + free(pixels); + + fprintf(stderr, "SWR: Loaded TXTR page %u (%dx%d)\n", pageId, w, h); + + // add it to the LRU + do + { + bool added = swrAddTextureIndexToLRU(swr, pageId); + if (added) + break; + + int tail = swrTailTextureIndexLRU(swr, true); + if (tail == -1) { + fprintf(stderr, "swr: Come on now.\n"); + assert(tail != -1); + return false; + } + + swrEvictTextureFromCache(swr, tail); + } + while (true); + + return true; +} diff --git a/src/sw/sw_texture_lru.h b/src/sw/sw_texture_lru.h new file mode 100755 index 000000000..3b6e8507e --- /dev/null +++ b/src/sw/sw_texture_lru.h @@ -0,0 +1,9 @@ +#ifndef _SW_TEXTURE_LRU_H +#define _SW_TEXTURE_LRU_H + +bool swrAddTextureIndexToLRU(SWRenderer* swr, int textureIndex); +int swrTailTextureIndexLRU(SWRenderer* swr, bool remove); +void swrEvictTextureFromCache(SWRenderer* swr, int textureIndex); +bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId); + +#endif//_SW_TEXTURE_LRU_H diff --git a/src/sw/sw_transform.h b/src/sw/sw_transform.h new file mode 100755 index 000000000..999c499e4 --- /dev/null +++ b/src/sw/sw_transform.h @@ -0,0 +1,56 @@ +#ifndef _SW_TRANSFORM_H +#define _SW_TRANSFORM_H + +static void swrTransformPosIfNeeded(SWRenderer* swr, float* dx, float* dy) +{ + if (dx) { + *dx -= swr->viewX; + *dx *= swr->scaleX; + *dx += swr->portX; + } + if (dy) { + *dy -= swr->viewY; + *dy *= swr->scaleY; + *dy += swr->portY; + } +} + +static void swrTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) +{ + if (dx) *dx *= swr->scaleX; + if (dy) *dy *= swr->scaleY; +} + +static void swrTransformPosIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) +{ + if (dx) { + *dx -= swr->viewX; + *dx = (int)(*dx * swr->scaleX); + *dx += swr->portX; + } + if (dy) { + *dy -= swr->viewY; + *dy = (int)(*dy * swr->scaleY); + *dy += swr->portY; + } +} + +static void swrTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) +{ + if (dx) *dx = (int)(*dx * swr->scaleX); + if (dy) *dy = (int)(*dy * swr->scaleY); +} + +static void swrReverseTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) +{ + if (dx) *dx = *dx / swr->scaleX; + if (dy) *dy = *dy / swr->scaleY; +} + +static void swrReverseTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) +{ + if (dx) *dx = (int)(*dx / swr->scaleX); + if (dy) *dy = (int)(*dy / swr->scaleY); +} + +#endif//_SW_TRANSFORM_H From 08986ecf5ca8864d390ac876f131b6ce6e1f2fbc Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 19:49:41 +0300 Subject: [PATCH 080/130] * Make this FORCE_INLINE to silence unused function warnings --- src/sw/sw_transform.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sw/sw_transform.h b/src/sw/sw_transform.h index 999c499e4..fc24b8db8 100755 --- a/src/sw/sw_transform.h +++ b/src/sw/sw_transform.h @@ -1,7 +1,7 @@ #ifndef _SW_TRANSFORM_H #define _SW_TRANSFORM_H -static void swrTransformPosIfNeeded(SWRenderer* swr, float* dx, float* dy) +FORCE_INLINE void swrTransformPosIfNeeded(SWRenderer* swr, float* dx, float* dy) { if (dx) { *dx -= swr->viewX; @@ -15,13 +15,13 @@ static void swrTransformPosIfNeeded(SWRenderer* swr, float* dx, float* dy) } } -static void swrTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) +FORCE_INLINE void swrTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) { if (dx) *dx *= swr->scaleX; if (dy) *dy *= swr->scaleY; } -static void swrTransformPosIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) +FORCE_INLINE void swrTransformPosIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { if (dx) { *dx -= swr->viewX; @@ -35,19 +35,19 @@ static void swrTransformPosIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy } } -static void swrTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) +FORCE_INLINE void swrTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { if (dx) *dx = (int)(*dx * swr->scaleX); if (dy) *dy = (int)(*dy * swr->scaleY); } -static void swrReverseTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) +FORCE_INLINE void swrReverseTransformSizeIfNeeded(SWRenderer* swr, float* dx, float* dy) { if (dx) *dx = *dx / swr->scaleX; if (dy) *dy = *dy / swr->scaleY; } -static void swrReverseTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) +FORCE_INLINE void swrReverseTransformSizeIntIfNeeded(SWRenderer* swr, int32_t* dx, int32_t* dy) { if (dx) *dx = (int)(*dx / swr->scaleX); if (dy) *dy = (int)(*dy / swr->scaleY); From 167d8a1019803db341153280014aa9a8fd9316fc Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 20:01:47 +0300 Subject: [PATCH 081/130] * Move more stuff to sw_drawing.c --- src/sw/sw_drawing.c | 985 +++++++++++++++++++++++++++++++++++++++++++ src/sw/sw_drawing.h | 12 +- src/sw/sw_renderer.c | 977 +----------------------------------------- 3 files changed, 998 insertions(+), 976 deletions(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index ed22240d0..7ec362fbc 100755 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -1,6 +1,659 @@ #include +#include +#include +#include "text_utils.h" #include "sw_renderer_private.h" +// ==== Internal structures ==== + +typedef struct +{ + Font* font; + TexturePageItem* fontTpag; // single TPAG for regular fonts (NULL for sprite fonts) + int fontTpagIndex; + int fontPageId; + Sprite* spriteFontSprite; // source sprite for sprite fonts (NULL for regular fonts) +} +SwrFontState; + +// ==== Internal functions ==== + +FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int srcalpha, int dstalpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + if (x < swr->portX || y < swr->portY) return; + if (x >= swr->maxX || y >= swr->maxY) return; + + alphaBlend(&swr->fb[y * swr->fbPitch + x], color, srcalpha, dstalpha); +} + +static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) +{ + SWRenderer *swr = (SWRenderer*) renderer; + + if (dy < swr->portY) return; + if (dy >= swr->maxY) return; + if (dx < swr->portX) { dw += dx - swr->portX; dx = swr->portX; } + if (dx + dw >= swr->maxX) dw = swr->maxX - dx; + if (dw <= 0) return; + + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + +#if PIXEL_SIZE == 32 + if (color == color2) +#endif + { + uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; + for (int i = 0; i < dw; i++) + alphaBlend(&line[i], color, srcalpha, invalpha); + } +#if PIXEL_SIZE == 32 + else + { + Pixel32ARGB clr1, clr2; + clr1.l = color; + clr2.l = color2; + + uint32_t rinit = clr1.p.r << 20; + uint32_t ginit = clr1.p.g << 20; + uint32_t binit = clr1.p.b << 20; + int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; + int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; + int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; + rstep /= dw; + gstep /= dw; + bstep /= dw; + + uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; + for (int i = 0; i < dw; i++) + { + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + alphaBlend(&line[i], resultPixel.l, srcalpha, invalpha); + } + } +#endif +} + +static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) +{ + SWRenderer *swr = (SWRenderer*) renderer; + + if (dx < swr->portX) return; + if (dx >= swr->maxX) return; + if (dy < swr->portY) { dh += dy - swr->portY; dy = swr->portY; } + if (dy + dh >= swr->maxY) dh = swr->maxY - dy; + if (dh <= 0) return; + + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + +#if PIXEL_SIZE == 32 + if (color == color2) +#endif + { + for (int i = 0; i < dh; i++) + { + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + alphaBlend(&line[0], color, srcalpha, invalpha); + } + } +#if PIXEL_SIZE == 32 + else + { + Pixel32ARGB clr1, clr2; + clr1.l = color; + clr2.l = color2; + + uint32_t rinit = clr1.p.r << 20; + uint32_t ginit = clr1.p.g << 20; + uint32_t binit = clr1.p.b << 20; + int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; + int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; + int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; + rstep /= dh; + gstep /= dh; + bstep /= dh; + + for (int i = 0; i < dh; i++) + { + uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + alphaBlend(&line[0], resultPixel.l, srcalpha, invalpha); + } + } +#endif +} + +static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, MAYBE_UNUSED int width, uintpixel_t color1, uintpixel_t color2, int alpha) +{ + if (x1 == x2) + { + swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); + return; + } + if (y1 == y2) + { + swrDrawHLineInt(renderer, swrMin(x1, x2), y1, swrAbs(x1 - x2), color1, color2, alpha); + return; + } + + int dx = x2 - x1, dy = y2 - y1; + int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; + int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; + int srcalpha = swrCalcSrcAlpha((SWRenderer*) renderer, alpha); + int invalpha = swrCalcDstAlpha((SWRenderer*) renderer, alpha); + + uintpixel_t color = color1; +#if PIXEL_SIZE == 32 + Pixel32ARGB clr1, clr2; + clr1.l = color1; + clr2.l = color2; + + uint32_t rinit = clr1.p.r << 20; + uint32_t ginit = clr1.p.g << 20; + uint32_t binit = clr1.p.b << 20; + int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; + int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; + int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; +#endif + + if (dy1 <= dx1) + { + if (dx >= 0) + { + x = x1, y = y1, xe = x2; + } + else + { + x = x2, y = y2, xe = x1; + } + +#if PIXEL_SIZE == 32 + if (dx1 > 0) { + rstep /= dx1; + gstep /= dx1; + bstep /= dx1; + } else { + rstep = gstep = bstep = 0; + } +#endif + + swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); + + for (int i = 0; x < xe; i++) + { + x++; + if (px < 0) + { + px += 2 * dy1; + } + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y++; else y--; + px += 2 * (dy1 - dx1); + } + +#if PIXEL_SIZE == 32 + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + color = resultPixel.l; +#endif + + swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); + } + } + else + { + if (dy >= 0) + { + x = x1, y = y1, ye = y2; + } + else + { + x = x2, y = y2, ye = y1; + } + +#if PIXEL_SIZE == 32 + if (dy1 > 0) { + rstep /= dy1; + gstep /= dy1; + bstep /= dy1; + } else { + rstep = gstep = bstep = 0; + } +#endif + + swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); + + for (int i = 0; y < ye; i++) + { + y++; + if (py <= 0) + { + py += 2 * dx1; + } + else + { + if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x++; else x--; + py += 2 * (dx1 - dy1); + } + +#if PIXEL_SIZE == 32 + Pixel32ARGB resultPixel; + resultPixel.p.r = rinit >> 20; + resultPixel.p.g = ginit >> 20; + resultPixel.p.b = binit >> 20; + rinit += rstep; + ginit += gstep; + binit += bstep; + color = resultPixel.l; +#endif + + swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); + } + } +} + +static void swrDrawSpriteInternal( + Renderer* renderer, int dx, int dy, int dw, int dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uintpixel_t tintColor, int alpha +) +{ + SWRenderer *swr = (SWRenderer*) renderer; + + bool flipX = false, flipY = false; + if (dw < 0) { dx += dw; dw = -dw; flipX = true; } + if (dh < 0) { dy += dh; dh = -dh; flipY = true; } + + //basic out of bound checks + if (dw == 0 || dh == 0) return; + if (sw == 0) sw = 1; + if (sh == 0) sh = 1; + if (dx + dw <= swr->portX) return; + if (dy + dh <= swr->portY) return; + if (dx >= swr->maxX) return; + if (dy >= swr->maxY) return; + + int odw = dw, odh = dh; + int osw = sw, osh = sh; + + int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; + + //out of bounds adjustment checks + int diffxl = 0, diffyl = 0, diffxu = 0, diffyu = 0; + if (dx < minx) { diffxl = minx - dx; dx = minx; dw -= diffxl; } + if (dy < miny) { diffyl = miny - dy; dy = miny; dh -= diffyl; } + if (dx + dw > maxx) { diffxu = dx + dw - maxx; dw -= diffxu; } + if (dy + dh > maxy) { diffyu = dy + dh - maxy; dh -= diffyu; } + + if (diffxl != 0 || diffyl != 0 || diffxu != 0 || diffyu != 0) + { + //adjust source coordinates too + diffxl = (int)((long)diffxl * osw / odw); + diffyl = (int)((long)diffyl * osh / odh); + diffxu = (int)((long)(diffxu + 1) * osw / odw); + diffyu = (int)((long)(diffyu + 1) * osh / odh); + sx += flipX ? diffxu : diffxl; + sy += flipY ? diffyu : diffyl; + sw -= diffxl + diffxu; + sh -= diffyl + diffyu; + if (sw <= 0 || sh <= 0) return; + } + + //clip the source coords into bounds too + if (sx < 0) { sw += sx; sx = 0; } + if (sy < 0) { sh += sy; sy = 0; } + if (sx + sw >= texture->width) { sw = texture->width - sx; } + if (sy + sh >= texture->height) { sh = texture->height - sy; } + if (sw <= 0 || sh <= 0) return; + + //okay, now we can finally get on with rendering + + int ixs = 0, oxs = 1, iys = 0, oys = 1; + if (flipX) ixs = dw - 1, oxs = -1; + if (flipY) iys = dh - 1, oys = -1; + + // tweak these if stuff doesn't look right + typedef int32_t fixedp_t; + const int fp_prec = 14; + + fixedp_t ystep = (sh == dh) ? (1 << fp_prec) : ((fixedp_t) osh << fp_prec) / odh; + fixedp_t xstep = (sw == dw) ? (1 << fp_prec) : ((fixedp_t) osw << fp_prec) / odw; + fixedp_t oxs2 = oxs * xstep; + fixedp_t oys2 = oys * ystep; + fixedp_t ixs2 = ixs * xstep; + fixedp_t iys2 = iys * ystep; + + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + + if (sw == dw) + { + fixedp_t ys2 = (fixedp_t) iys2; + for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) + { + uintpixel_t* dstline; + const uintpixel_t* srcline; + dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; + if (dh == sh) + srcline = &texture->buffer[(sy + ys) * texture->width + sx]; + else + srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; + + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) + { + uintpixel_t pixel = srcline[xs]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), srcalpha, invalpha); + } + } + } + else + { + fixedp_t ys2 = iys2; + for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) + { + uintpixel_t* dstline; + const uintpixel_t* srcline; + dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; + if (dh == sh) + srcline = &texture->buffer[(sy + ys) * texture->width + sx]; + else + srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; + + fixedp_t xs2 = ixs2; + for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) + { + uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; + if (opaque(pixel)) + alphaBlend(&dstline[x], tint(tintColor, pixel), srcalpha, invalpha); + } + } + } +} + +static void swrDrawSpriteRotatedInternal( + Renderer* renderer, int dx, int dy, int dw, int dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uintpixel_t tintColor, int alpha, + float angleDeg, + float pivotX, + float pivotY +) +{ + SWRenderer* swr = (SWRenderer*) renderer; + float angleRad = -angleDeg * M_PI / 180.0f; + + bool flipX = false, flipY = false; + if (dw < 0) { dw = -dw; dx -= dw; pivotX = dw - pivotX; flipX = true; } + if (dh < 0) { dh = -dh; dy -= dh; pivotY = dh - pivotY; flipY = true; } + + float cosA = cosf(angleRad); + float sinA = sinf(angleRad); + + float cnrx[4], cnry[4]; + cnrx[0] = cnrx[3] = dx; + cnry[0] = cnry[1] = dy; + cnrx[1] = cnrx[2] = dx + dw; + cnry[2] = cnry[3] = dy + dh; + + float pxa = pivotX + dx; + float pya = pivotY + dy; + + float minXf = FLT_MAX, minYf = FLT_MAX, maxXf = -FLT_MAX, maxYf = -FLT_MAX; + for (int i = 0; i < 4; i++) + { + float cxi = cnrx[i] - pxa; + float cyi = cnry[i] - pya; + float rx = cosA * cxi - sinA * cyi + pxa; + float ry = sinA * cxi + cosA * cyi + pya; + if (minXf > rx) minXf = rx; + if (maxXf < rx) maxXf = rx; + if (minYf > ry) minYf = ry; + if (maxYf < ry) maxYf = ry; + } + + // minX, minY, maxX, maxY now represent an AABB of pixels we should loop over + int minX = swrFloor(minXf); + int minY = swrFloor(minYf); + int maxX = swrCeiling(maxXf); + int maxY = swrCeiling(maxYf); + + // basic out-of-bound checks + if (maxX < swr->portX) return; + if (maxY < swr->portY) return; + if (minX >= swr->maxX) return; + if (minY >= swr->maxY) return; + + // however, we'll need to clip it against out of bounds first + int minXc = minX, minYc = minY, maxXc = maxX, maxYc = maxY; + int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; + + if (minXc < minx) minXc = minx; + if (minYc < miny) minYc = miny; + if (maxXc >= maxx) maxXc = maxx; + if (maxYc >= maxy) maxYc = maxy; + + // some final clip checks + if (minXc >= maxXc || minYc >= maxYc) return; + + int sox = flipX ? sw - 1 : 0; + int soy = flipY ? sh - 1 : 0; + int six = flipX ? -1 : 1; + int siy = flipY ? -1 : 1; + + float sw_dw = (float) sw / dw; + float sh_dh = (float) sh / dh; + + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + + for (int cy = minYc; cy < maxYc; cy++) + { + uintpixel_t *dstline = &swr->fb[cy * swr->fbPitch]; + for (int cx = minXc; cx < maxXc; cx++) + { + // we need to determine the texture-space coordinate of cx/cy + float ox = (float) cx + 0.5f - pxa; + float oy = (float) cy + 0.5f - pya; + + // "undo" the rotation + float lx = cosA * ox + sinA * oy; + float ly = -sinA * ox + cosA * oy; + + // turn it into a texture-local coordinate + lx += pxa - dx; + ly += pya - dy; + + if (lx < 0 || ly < 0 || lx >= (float) dw || ly >= (float) dh) continue; + + lx = lx * sw_dw; + ly = ly * sh_dh; + + int tx = (int)(sox + lx * six); + int ty = (int)(soy + ly * siy); + + if (tx < 0) tx = 0; + if (ty < 0) ty = 0; + if (tx >= sw) tx = sw - 1; + if (ty >= sh) ty = sh - 1; + + tx += sx; + ty += sy; + + uintpixel_t src = texture->buffer[ty * texture->width + tx]; + + if (opaque(src)) + alphaBlend(&dstline[cx], tint(tintColor, src), srcalpha, invalpha); + } + } +} + +static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uint32_t color1, uint32_t color2, uint32_t color3, int alpha) +{ + // TODO: update this + (void) color2; + (void) color3; + + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + + // Figure out the maximum Y extent of the triangle. + // (Note that we know yup is the minimum.) + int xmid, ymid, xmid2 = xup, xmax, ymax; + if (yleft < yright) { + xmax = xright, ymax = yright; + xmid = xleft, ymid = yleft; + if (yright != yup) + xmid2 = xup + (xright - xup) * (ymid - yup) / (yright - yup); + } else { + xmax = xleft, ymax = yleft; + xmid = xright, ymid = yright; + if (yleft != yup) + xmid2 = xup + (xleft - xup) * (ymid - yup) / (yleft - yup); + } + + for (int y = yup; y < ymax; y++) + { + if (y < 0) continue; + if (y >= swr->height) break; + + int x1 = xup, x2 = xup; + if (y <= ymid) + { + // Lines: between up and mid, and between up and max + if (ymid != yup) + x1 = xup + (xmid - xup) * (y - yup) / (ymid - yup); + + if (ymid != yup) + x2 = xup + (xmid2 - xup) * (y - yup) / (ymid - yup); + } + else + { + // Lines: between mid and max, and between up and max + if (ymax != yup) + x1 = xup + (xmax - xup) * (y - yup) / (ymax - yup); + + if (ymax != ymid) + x2 = xmid + (xmax - xmid) * (y - ymid) / (ymax - ymid); + } + + if (x1 >= x2) { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + + if (x1 < swr->portX) x1 = swr->portX; + if (x1 >= swr->maxX) continue; + if (x2 < swr->portX) continue; + if (x2 >= swr->maxX) x2 = swr->maxX - 1; + if (x1 > x2) continue; + + uintpixel_t* line = &swr->fb[y * swr->width]; + for (int x = x1; x < x2; x++) { + alphaBlend(&line[x], color1, srcalpha, invalpha); + } + } +} + +static bool swrResolveFontState(SWRenderer* swr, DataWin* dw, Font* font, SwrFontState* state) +{ + state->font = font; + state->fontTpag = NULL; + state->fontTpagIndex = 0; + state->spriteFontSprite = NULL; + + if (font->isSpriteFont) + { + state->spriteFontSprite = &dw->sprt.sprites[font->spriteIndex]; + } + else + { + state->fontTpagIndex = font->tpagIndex; + if (state->fontTpagIndex < 0) return false; + + state->fontTpag = &dw->tpag.items[state->fontTpagIndex]; + int16_t pageId = state->fontTpag->texturePageId; + if (0 > pageId || (uint32_t) pageId >= swr->totalTextureCount) return false; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return false; + + state->fontPageId = pageId; + } + + return true; +} + +static bool swrResolveGlyph( + SWRenderer* swr, DataWin* dw, SwrFontState* state, FontGlyph* glyph, float cursorX, float cursorY, + int* tpagIndex, int* pageId, int* sx, int* sy, int* sw, int* sh, float* dx, float* dy +) +{ + Font* font = state->font; + if (font->isSpriteFont && state->spriteFontSprite != NULL) + { + Sprite* sprite = state->spriteFontSprite; + int32_t glyphIndex = (int32_t) (glyph - font->glyphs); + if (0 > glyphIndex || glyphIndex >= (int32_t) sprite->textureCount) return false; + + int32_t tpagIdx = sprite->tpagIndices[glyphIndex]; + if (0 > tpagIdx) return false; + + TexturePageItem* glyphTpag = &dw->tpag.items[tpagIdx]; + int16_t pid = glyphTpag->texturePageId; + if (0 > pid || (uint32_t) pid >= swr->totalTextureCount) return false; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pid)) return false; + + *tpagIndex = tpagIdx; + *pageId = glyphTpag->texturePageId; + + *sx = glyphTpag->sourceX; + *sy = glyphTpag->sourceY; + *sw = glyphTpag->sourceWidth; + *sh = glyphTpag->sourceHeight; + + *dx = cursorX + glyph->offset; + *dy = cursorY + glyphTpag->targetY - sprite->originY; + } + else + { + *tpagIndex = state->fontTpagIndex; + *pageId = state->fontPageId; + + *sx = state->fontTpag->sourceX + glyph->sourceX; + *sy = state->fontTpag->sourceY + glyph->sourceY; + *sw = glyph->sourceWidth; + *sh = glyph->sourceHeight; + + *dx = cursorX + glyph->offset; + *dy = cursorY; + } + + return true; +} + +// ==== Exposed interface ==== + bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView) { SWRenderer* swr = (SWRenderer*) renderer; @@ -97,3 +750,335 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor return true; } + +void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha) +{ + SWRenderer *swr = (SWRenderer*) renderer; + float thickness = 1; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &dw, &thickness); + + // TODO: use thickness + swrDrawHLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dw), color, color2, swrIntAlpha(alpha)); +} + +void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, uintpixel_t color2, float alpha) +{ + SWRenderer *swr = (SWRenderer*) renderer; + float thickness = 1; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &thickness, &dh); + + // TODO: use thickness + swrDrawVLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dh), color, color2, swrIntAlpha(alpha)); +} + +void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + swrTransformSizeIfNeeded(swr, &width, NULL); + swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrCeiling(x2), swrCeiling(y2), swrCeiling(width), color, color2, swrIntAlpha(alpha)); +} + +void swrDrawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha) +{ + swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color, color, alpha); + swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color, color, alpha); + swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color, color, alpha); + swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color, color, alpha); +} + +void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uintpixel_t color4, float alpha) +{ + swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color1, color2, alpha); + swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color3, color4, alpha); + swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color1, color3, alpha); + swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color2, color4, alpha); +} + +void swrFillRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t pxcolor, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + + int alphaInt = swrIntAlpha(alpha); + int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); + int xd = x2i - x1i; + int yd = y2i - y1i; + if (xd < 0) { x1i = x2i; xd = -xd; } + if (yd < 0) { y1i = y2i; yd = -yd; } + if (xd <= 0 || yd <= 0) return; + + for (int y = 0; y <= yd; y++) { + swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor, pxcolor, alphaInt); + } +} + +void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t pxcolor1, uintpixel_t pxcolor2, uintpixel_t pxcolor3, uintpixel_t pxcolor4, float alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + + int alphaInt = swrIntAlpha(alpha); + int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); + int xd = x2i - x1i; + int yd = y2i - y1i; + if (xd < 0) { x1i = x2i; xd = -xd; } + if (yd < 0) { y1i = y2i; yd = -yd; } + if (xd <= 0 || yd <= 0) return; + + // TODO: blending vertically + (void) pxcolor3; + (void) pxcolor4; + + for (int y = 0; y <= yd; y++) { + swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor1, pxcolor2, alphaInt); + } +} + +void swrDrawSprite( + Renderer* renderer, float dx, float dy, float dw, float dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uint32_t tintColor, float alpha +) +{ + SWRenderer *swr = (SWRenderer*) renderer; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &dw, &dh); + + swrDrawSpriteInternal( + renderer, + swrFloor(dx), + swrFloor(dy), + swrCeiling(dw), + swrCeiling(dh), + texture, + sx, sy, + sw, sh, + swrConvertPixel(tintColor), + swrIntAlpha(alpha) + ); +} + +void swrDrawSpriteRotated( + Renderer* renderer, float dx, float dy, float dw, float dh, + SWTexture* texture, int sx, int sy, int sw, int sh, + uint32_t tintColor, float alpha, + float angleDeg, + float pivotX, + float pivotY +) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + swrTransformPosIfNeeded(swr, &dx, &dy); + swrTransformSizeIfNeeded(swr, &pivotX, &pivotY); + swrTransformSizeIfNeeded(swr, &dw, &dh); + + swrDrawSpriteRotatedInternal( + renderer, + swrFloor(dx), + swrFloor(dy), + swrCeiling(dw), + swrCeiling(dh), + texture, + sx, sy, + sw, sh, + swrConvertPixel(tintColor), + swrIntAlpha(alpha), + angleDeg, + pivotX, + pivotY + ); +} + +void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color1, uint32_t color2, uint32_t color3, float alpha) +{ + float xup, yup, xleft, yleft, xright, yright; + uint32_t colorup, colorleft, colorright; + + SWRenderer* swr = (SWRenderer*) renderer; + swrTransformPosIfNeeded(swr, &x1, &y1); + swrTransformPosIfNeeded(swr, &x2, &y2); + swrTransformPosIfNeeded(swr, &x3, &y3); + + //which vertex is higher? + xup = x1, yup = y1; colorup = color1; + xleft = x2, yleft = y2; colorleft = color2; + xright = x3, yright = y3; colorright = color3; + if (yup > y2) { + xup = x2, yup = y2, colorup = color2; + xleft = x1, yleft = y1, colorleft = color1; + //xright = x3, yright = y3; + } + if (yup > y3) { + xup = x3, yup = y3, colorup = color3; + xleft = x1, yleft = y1, colorleft = color1; + xright = x2, yright = y2, colorright = color2; + } + + if (xleft > xright) { + float tmp = xleft; + xleft = xright; + xright = tmp; + tmp = yleft; + yleft = yright; + yright = tmp; + uint32_t tmp2 = colorleft; + colorleft = colorright; + colorright = tmp2; + } + + swrDrawTriangleInternal( + swr, + swrFloor(xup), swrFloor(yup), + swrFloor(xleft), swrCeiling(yleft), + swrFloor(xright), swrCeiling(yright), + swrConvertPixel(colorup), + swrConvertPixel(colorleft), + swrConvertPixel(colorright), + swrIntAlpha(alpha) + ); +} + +void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation) +{ + Renderer* renderer = &swr->base; + DataWin* dwin = renderer->dataWin; + + int32_t fontIndex = renderer->drawFont; + if (0 > fontIndex || dwin->font.count <= (uint32_t) fontIndex) return; + + Font* font = &dwin->font.fonts[fontIndex]; + + SwrFontState fontState; + memset(&fontState, 0, sizeof fontState); // silence warning treated as error + + if (!swrResolveFontState(swr, dwin, font, &fontState)) return; + + // TODO: do we need to mirror the way the text scrolls too?! + float cosA = 1.0f, sinA = 0.0f, angleRad = 0.0f; + bool mustRotate = swrMustRotateSensitive(angleDeg); + if (UNLIKELY(mustRotate)) + { + angleRad = -angleDeg * M_PI / 180.0f; + cosA = cosf(angleRad); + sinA = sinf(angleRad); + } + + int textLen = (int) strlen(text); + int lineCount = TextUtils_countLines(text, textLen); + float lineStride = (0.0f > lineSeparation) ? TextUtils_lineStride(font) : (lineSeparation / (font->scaleY != 0.0f ? font->scaleY : 1.0f)); + + // Vertical alignment offset + float totalHeight = (float) lineCount * lineStride; + float valignOffset = 0; + if (renderer->drawValign == 1) valignOffset = -totalHeight / 2.0f; + else if (renderer->drawValign == 2) valignOffset = -totalHeight; + + xscale *= font->scaleX; + yscale *= font->scaleY; + + // Iterate through lines. HTML5 subtracts ascenderOffset from the per-line y offset + // (see yyFont.GR_Text_Draw), shifting glyphs up so the baseline aligns with the drawn y. + float cursorY = valignOffset - (float) font->ascenderOffset; + int32_t lineStart = 0; + + for (int32_t lineIdx = 0; lineCount > lineIdx; lineIdx++) { + // Find end of current line + int32_t lineEnd = lineStart; + while (textLen > lineEnd && !TextUtils_isNewlineChar(text[lineEnd])) { + lineEnd++; + } + int32_t lineLen = lineEnd - lineStart; + + // Horizontal alignment offset for this line + float lineWidth = TextUtils_measureLineWidth(font, text + lineStart, lineLen); + float halignOffset = 0; + if (renderer->drawHalign == 1) halignOffset = -lineWidth / 2.0f; + else if (renderer->drawHalign == 2) halignOffset = -lineWidth; + + float cursorX = halignOffset; + + // Render each glyph in the line - decode each codepoint once and carry it forward as next iteration's ch (also used for kerning) + int32_t pos = 0; + uint16_t ch = 0; + bool hasCh = false; + if (lineLen > pos) { + ch = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); + hasCh = true; + } + + while (hasCh) { + FontGlyph* glyph = TextUtils_findGlyph(font, ch); + + uint16_t nextCh = 0; + bool hasNext = lineLen > pos; + if (hasNext) nextCh = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); + + if (glyph != nullptr) { + bool drewSuccessfully = false; + if (glyph->sourceWidth != 0 && glyph->sourceHeight != 0) { + int fontTpagIndex = 0, pageId = 0; + int sx, sy, sw, sh, dw, dh; + float dx, dy; + if (swrResolveGlyph(swr, dwin, &fontState, glyph, cursorX, cursorY, + &fontTpagIndex, &pageId, &sx, &sy, &sw, &sh, &dx, &dy)) + { + dx *= xscale; dx += x; + dy *= xscale; dy += y; + dw = swrCeiling(xscale * glyph->sourceWidth); + dh = swrCeiling(yscale * glyph->sourceHeight); + + // TODO: at 640x480, for some reason, without this fixup the + // letters in the "Name the fallen human." screen don't shake + dx = roundf(dx * 2) / 2; + dy = roundf(dy * 2) / 2; + + SWTexture* texture = swr->textures[pageId]; + + if (UNLIKELY(mustRotate)) + { + dx -= x; + dy -= y; + float ndx = cosA * dx - sinA * dy; + float ndy = sinA * dx + cosA * dy; + ndx += x; + ndy += y; + swrDrawSpriteRotated(renderer, ndx, ndy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, 0.0f, 0.0f); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); + } + + drewSuccessfully = true; + } + } + + cursorX += glyph->shift; + if (drewSuccessfully && hasNext) { + cursorX += TextUtils_getKerningOffset(glyph, nextCh); + } + } + + ch = nextCh; + hasCh = hasNext; + } + + cursorY += lineStride; + // Skip past the newline, treating \r\n and \n\r as single breaks + if (textLen > lineEnd) { + lineStart = TextUtils_skipNewline(text, lineEnd, textLen); + } else { + lineStart = lineEnd; + } + } +} diff --git a/src/sw/sw_drawing.h b/src/sw/sw_drawing.h index edfc5df59..5dee54f5c 100755 --- a/src/sw/sw_drawing.h +++ b/src/sw/sw_drawing.h @@ -2,6 +2,16 @@ #define _SW_DRAWING_H bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView); - +void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha); +void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, uintpixel_t color2, float alpha); +void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha); +void swrDrawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha); +void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uintpixel_t color4, float alpha); +void swrFillRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha); +void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uintpixel_t color4, float alpha); +void swrDrawSprite(Renderer* renderer, float dx, float dy, float dw, float dh, SWTexture* texture, int sx, int sy, int sw, int sh, uint32_t tintColor, float alpha); +void swrDrawSpriteRotated(Renderer* renderer, float dx, float dy, float dw, float dh, SWTexture* texture, int sx, int sy, int sw, int sh, uint32_t tintColor, float alpha, float angleDeg, float pivotX, float pivotY); +void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color1, uint32_t color2, uint32_t color3, float alpha); +void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation); #endif//_SW_DRAWING_H diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 4065394ee..01587bd44 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -198,603 +198,6 @@ static void SWRenderer_endGUI(Renderer* renderer) UNIMP2(); } -FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int srcalpha, int dstalpha) -{ - SWRenderer* swr = (SWRenderer*) renderer; - - if (x < swr->portX || y < swr->portY) return; - if (x >= swr->maxX || y >= swr->maxY) return; - - alphaBlend(&swr->fb[y * swr->fbPitch + x], color, srcalpha, dstalpha); -} - -static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) -{ - SWRenderer *swr = (SWRenderer*) renderer; - - if (dy < swr->portY) return; - if (dy >= swr->maxY) return; - if (dx < swr->portX) { dw += dx - swr->portX; dx = swr->portX; } - if (dx + dw >= swr->maxX) dw = swr->maxX - dx; - if (dw <= 0) return; - - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); - -#if PIXEL_SIZE == 32 - if (color == color2) -#endif - { - uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; - for (int i = 0; i < dw; i++) - alphaBlend(&line[i], color, srcalpha, invalpha); - } -#if PIXEL_SIZE == 32 - else - { - Pixel32ARGB clr1, clr2; - clr1.l = color; - clr2.l = color2; - - uint32_t rinit = clr1.p.r << 20; - uint32_t ginit = clr1.p.g << 20; - uint32_t binit = clr1.p.b << 20; - int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; - int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; - int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; - rstep /= dw; - gstep /= dw; - bstep /= dw; - - uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; - for (int i = 0; i < dw; i++) - { - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - alphaBlend(&line[i], resultPixel.l, srcalpha, invalpha); - } - } -#endif -} - -static void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha) -{ - SWRenderer *swr = (SWRenderer*) renderer; - float thickness = 1; - - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &dw, &thickness); - - // TODO: use thickness - swrDrawHLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dw), color, color2, swrIntAlpha(alpha)); -} - -static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) -{ - SWRenderer *swr = (SWRenderer*) renderer; - - if (dx < swr->portX) return; - if (dx >= swr->maxX) return; - if (dy < swr->portY) { dh += dy - swr->portY; dy = swr->portY; } - if (dy + dh >= swr->maxY) dh = swr->maxY - dy; - if (dh <= 0) return; - - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); - -#if PIXEL_SIZE == 32 - if (color == color2) -#endif - { - for (int i = 0; i < dh; i++) - { - uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - alphaBlend(&line[0], color, srcalpha, invalpha); - } - } -#if PIXEL_SIZE == 32 - else - { - Pixel32ARGB clr1, clr2; - clr1.l = color; - clr2.l = color2; - - uint32_t rinit = clr1.p.r << 20; - uint32_t ginit = clr1.p.g << 20; - uint32_t binit = clr1.p.b << 20; - int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; - int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; - int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; - rstep /= dh; - gstep /= dh; - bstep /= dh; - - for (int i = 0; i < dh; i++) - { - uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - alphaBlend(&line[0], resultPixel.l, srcalpha, invalpha); - } - } -#endif -} - -static void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, uintpixel_t color2, float alpha) -{ - SWRenderer *swr = (SWRenderer*) renderer; - float thickness = 1; - - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &thickness, &dh); - - // TODO: use thickness - swrDrawVLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dh), color, color2, swrIntAlpha(alpha)); -} - -static void swrDrawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha) -{ - swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color, color, alpha); - swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color, color, alpha); - swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color, color, alpha); - swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color, color, alpha); -} - -static void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uintpixel_t color4, float alpha) -{ - swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color1, color2, alpha); - swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color3, color4, alpha); - swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color1, color3, alpha); - swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color2, color4, alpha); -} - -static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, MAYBE_UNUSED int width, uintpixel_t color1, uintpixel_t color2, int alpha) -{ - if (x1 == x2) - { - swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); - return; - } - if (y1 == y2) - { - swrDrawHLineInt(renderer, swrMin(x1, x2), y1, swrAbs(x1 - x2), color1, color2, alpha); - return; - } - - int dx = x2 - x1, dy = y2 - y1; - int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; - int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; - int srcalpha = swrCalcSrcAlpha((SWRenderer*) renderer, alpha); - int invalpha = swrCalcDstAlpha((SWRenderer*) renderer, alpha); - - uintpixel_t color = color1; -#if PIXEL_SIZE == 32 - Pixel32ARGB clr1, clr2; - clr1.l = color1; - clr2.l = color2; - - uint32_t rinit = clr1.p.r << 20; - uint32_t ginit = clr1.p.g << 20; - uint32_t binit = clr1.p.b << 20; - int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; - int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; - int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; -#endif - - if (dy1 <= dx1) - { - if (dx >= 0) - { - x = x1, y = y1, xe = x2; - } - else - { - x = x2, y = y2, xe = x1; - } - -#if PIXEL_SIZE == 32 - if (dx1 > 0) { - rstep /= dx1; - gstep /= dx1; - bstep /= dx1; - } else { - rstep = gstep = bstep = 0; - } -#endif - - swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); - - for (int i = 0; x < xe; i++) - { - x++; - if (px < 0) - { - px += 2 * dy1; - } - else - { - if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y++; else y--; - px += 2 * (dy1 - dx1); - } - -#if PIXEL_SIZE == 32 - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - color = resultPixel.l; -#endif - - swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); - } - } - else - { - if (dy >= 0) - { - x = x1, y = y1, ye = y2; - } - else - { - x = x2, y = y2, ye = y1; - } - -#if PIXEL_SIZE == 32 - if (dy1 > 0) { - rstep /= dy1; - gstep /= dy1; - bstep /= dy1; - } else { - rstep = gstep = bstep = 0; - } -#endif - - swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); - - for (int i = 0; y < ye; i++) - { - y++; - if (py <= 0) - { - py += 2 * dx1; - } - else - { - if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x++; else x--; - py += 2 * (dx1 - dy1); - } - -#if PIXEL_SIZE == 32 - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - color = resultPixel.l; -#endif - - swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); - } - } -} - -static void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha) -{ - SWRenderer* swr = (SWRenderer*) renderer; - swrTransformPosIfNeeded(swr, &x1, &y1); - swrTransformPosIfNeeded(swr, &x2, &y2); - swrTransformSizeIfNeeded(swr, &width, NULL); - swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrCeiling(x2), swrCeiling(y2), swrCeiling(width), color, color2, swrIntAlpha(alpha)); -} - -static void swrDrawSpriteInternal( - Renderer* renderer, int dx, int dy, int dw, int dh, - SWTexture* texture, int sx, int sy, int sw, int sh, - uintpixel_t tintColor, int alpha -) -{ - SWRenderer *swr = (SWRenderer*) renderer; - - bool flipX = false, flipY = false; - if (dw < 0) { dx += dw; dw = -dw; flipX = true; } - if (dh < 0) { dy += dh; dh = -dh; flipY = true; } - - //basic out of bound checks - if (dw == 0 || dh == 0) return; - if (sw == 0) sw = 1; - if (sh == 0) sh = 1; - if (dx + dw <= swr->portX) return; - if (dy + dh <= swr->portY) return; - if (dx >= swr->maxX) return; - if (dy >= swr->maxY) return; - - int odw = dw, odh = dh; - int osw = sw, osh = sh; - - int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; - - //out of bounds adjustment checks - int diffxl = 0, diffyl = 0, diffxu = 0, diffyu = 0; - if (dx < minx) { diffxl = minx - dx; dx = minx; dw -= diffxl; } - if (dy < miny) { diffyl = miny - dy; dy = miny; dh -= diffyl; } - if (dx + dw > maxx) { diffxu = dx + dw - maxx; dw -= diffxu; } - if (dy + dh > maxy) { diffyu = dy + dh - maxy; dh -= diffyu; } - - if (diffxl != 0 || diffyl != 0 || diffxu != 0 || diffyu != 0) - { - //adjust source coordinates too - diffxl = (int)((long)diffxl * osw / odw); - diffyl = (int)((long)diffyl * osh / odh); - diffxu = (int)((long)(diffxu + 1) * osw / odw); - diffyu = (int)((long)(diffyu + 1) * osh / odh); - sx += flipX ? diffxu : diffxl; - sy += flipY ? diffyu : diffyl; - sw -= diffxl + diffxu; - sh -= diffyl + diffyu; - if (sw <= 0 || sh <= 0) return; - } - - //clip the source coords into bounds too - if (sx < 0) { sw += sx; sx = 0; } - if (sy < 0) { sh += sy; sy = 0; } - if (sx + sw >= texture->width) { sw = texture->width - sx; } - if (sy + sh >= texture->height) { sh = texture->height - sy; } - if (sw <= 0 || sh <= 0) return; - - //okay, now we can finally get on with rendering - - int ixs = 0, oxs = 1, iys = 0, oys = 1; - if (flipX) ixs = dw - 1, oxs = -1; - if (flipY) iys = dh - 1, oys = -1; - - // tweak these if stuff doesn't look right - typedef int32_t fixedp_t; - const int fp_prec = 14; - - fixedp_t ystep = (sh == dh) ? (1 << fp_prec) : ((fixedp_t) osh << fp_prec) / odh; - fixedp_t xstep = (sw == dw) ? (1 << fp_prec) : ((fixedp_t) osw << fp_prec) / odw; - fixedp_t oxs2 = oxs * xstep; - fixedp_t oys2 = oys * ystep; - fixedp_t ixs2 = ixs * xstep; - fixedp_t iys2 = iys * ystep; - - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); - - if (sw == dw) - { - fixedp_t ys2 = (fixedp_t) iys2; - for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) - { - uintpixel_t* dstline; - const uintpixel_t* srcline; - dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; - if (dh == sh) - srcline = &texture->buffer[(sy + ys) * texture->width + sx]; - else - srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; - - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) - { - uintpixel_t pixel = srcline[xs]; - if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), srcalpha, invalpha); - } - } - } - else - { - fixedp_t ys2 = iys2; - for (int y = 0, ys = iys; y < dh; y++, ys += oys, ys2 += oys2) - { - uintpixel_t* dstline; - const uintpixel_t* srcline; - dstline = &swr->fb[(dy + y) * swr->fbPitch + dx]; - if (dh == sh) - srcline = &texture->buffer[(sy + ys) * texture->width + sx]; - else - srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; - - fixedp_t xs2 = ixs2; - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) - { - uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; - if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), srcalpha, invalpha); - } - } - } -} - -static void swrDrawSprite( - Renderer* renderer, float dx, float dy, float dw, float dh, - SWTexture* texture, int sx, int sy, int sw, int sh, - uint32_t tintColor, float alpha -) -{ - SWRenderer *swr = (SWRenderer*) renderer; - - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &dw, &dh); - - swrDrawSpriteInternal( - renderer, - swrFloor(dx), - swrFloor(dy), - swrCeiling(dw), - swrCeiling(dh), - texture, - sx, sy, - sw, sh, - swrConvertPixel(tintColor), - swrIntAlpha(alpha) - ); -} - -static void swrDrawSpriteRotatedInternal( - Renderer* renderer, int dx, int dy, int dw, int dh, - SWTexture* texture, int sx, int sy, int sw, int sh, - uintpixel_t tintColor, int alpha, - float angleDeg, - float pivotX, - float pivotY -) -{ - SWRenderer* swr = (SWRenderer*) renderer; - float angleRad = -angleDeg * M_PI / 180.0f; - - bool flipX = false, flipY = false; - if (dw < 0) { dw = -dw; dx -= dw; pivotX = dw - pivotX; flipX = true; } - if (dh < 0) { dh = -dh; dy -= dh; pivotY = dh - pivotY; flipY = true; } - - float cosA = cosf(angleRad); - float sinA = sinf(angleRad); - - float cnrx[4], cnry[4]; - cnrx[0] = cnrx[3] = dx; - cnry[0] = cnry[1] = dy; - cnrx[1] = cnrx[2] = dx + dw; - cnry[2] = cnry[3] = dy + dh; - - float pxa = pivotX + dx; - float pya = pivotY + dy; - - float minXf = FLT_MAX, minYf = FLT_MAX, maxXf = -FLT_MAX, maxYf = -FLT_MAX; - for (int i = 0; i < 4; i++) - { - float cxi = cnrx[i] - pxa; - float cyi = cnry[i] - pya; - float rx = cosA * cxi - sinA * cyi + pxa; - float ry = sinA * cxi + cosA * cyi + pya; - if (minXf > rx) minXf = rx; - if (maxXf < rx) maxXf = rx; - if (minYf > ry) minYf = ry; - if (maxYf < ry) maxYf = ry; - } - - // minX, minY, maxX, maxY now represent an AABB of pixels we should loop over - int minX = swrFloor(minXf); - int minY = swrFloor(minYf); - int maxX = swrCeiling(maxXf); - int maxY = swrCeiling(maxYf); - - // basic out-of-bound checks - if (maxX < swr->portX) return; - if (maxY < swr->portY) return; - if (minX >= swr->maxX) return; - if (minY >= swr->maxY) return; - - // however, we'll need to clip it against out of bounds first - int minXc = minX, minYc = minY, maxXc = maxX, maxYc = maxY; - int minx = swr->portX, miny = swr->portY, maxx = swr->portX + swr->portW, maxy = swr->portY + swr->portH; - - if (minXc < minx) minXc = minx; - if (minYc < miny) minYc = miny; - if (maxXc >= maxx) maxXc = maxx; - if (maxYc >= maxy) maxYc = maxy; - - // some final clip checks - if (minXc >= maxXc || minYc >= maxYc) return; - - int sox = flipX ? sw - 1 : 0; - int soy = flipY ? sh - 1 : 0; - int six = flipX ? -1 : 1; - int siy = flipY ? -1 : 1; - - float sw_dw = (float) sw / dw; - float sh_dh = (float) sh / dh; - - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); - - for (int cy = minYc; cy < maxYc; cy++) - { - uintpixel_t *dstline = &swr->fb[cy * swr->fbPitch]; - for (int cx = minXc; cx < maxXc; cx++) - { - // we need to determine the texture-space coordinate of cx/cy - float ox = (float) cx + 0.5f - pxa; - float oy = (float) cy + 0.5f - pya; - - // "undo" the rotation - float lx = cosA * ox + sinA * oy; - float ly = -sinA * ox + cosA * oy; - - // turn it into a texture-local coordinate - lx += pxa - dx; - ly += pya - dy; - - if (lx < 0 || ly < 0 || lx >= (float) dw || ly >= (float) dh) continue; - - lx = lx * sw_dw; - ly = ly * sh_dh; - - int tx = (int)(sox + lx * six); - int ty = (int)(soy + ly * siy); - - if (tx < 0) tx = 0; - if (ty < 0) ty = 0; - if (tx >= sw) tx = sw - 1; - if (ty >= sh) ty = sh - 1; - - tx += sx; - ty += sy; - - uintpixel_t src = texture->buffer[ty * texture->width + tx]; - - if (opaque(src)) - alphaBlend(&dstline[cx], tint(tintColor, src), srcalpha, invalpha); - } - } -} - -static void swrDrawSpriteRotated( - Renderer* renderer, float dx, float dy, float dw, float dh, - SWTexture* texture, int sx, int sy, int sw, int sh, - uint32_t tintColor, float alpha, - float angleDeg, - float pivotX, - float pivotY -) -{ - SWRenderer* swr = (SWRenderer*) renderer; - - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &pivotX, &pivotY); - swrTransformSizeIfNeeded(swr, &dw, &dh); - - swrDrawSpriteRotatedInternal( - renderer, - swrFloor(dx), - swrFloor(dy), - swrCeiling(dw), - swrCeiling(dh), - texture, - sx, sy, - sw, sh, - swrConvertPixel(tintColor), - swrIntAlpha(alpha), - angleDeg, - pivotX, - pivotY - ); -} - static void SWRenderer_drawSprite(Renderer* renderer, int32_t tpagIndex, float x, float y, float originX, float originY, float xscale, float yscale, float angleDeg, uint32_t color, float alpha) @@ -913,29 +316,10 @@ static void SWRenderer_drawRectangle(Renderer* renderer, float x1, float y1, flo { uintpixel_t pxcolor = swrConvertPixel(color); - SWRenderer* swr = (SWRenderer*) renderer; - if (outline) - { swrDrawRectangle(renderer, x1, y1, x2, y2, pxcolor, alpha); - } else - { - swrTransformPosIfNeeded(swr, &x1, &y1); - swrTransformPosIfNeeded(swr, &x2, &y2); - - int alphaInt = swrIntAlpha(alpha); - int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); - int xd = x2i - x1i; - int yd = y2i - y1i; - if (xd < 0) { x1i = x2i; xd = -xd; } - if (yd < 0) { y1i = y2i; yd = -yd; } - if (xd <= 0 || yd <= 0) return; - - for (int y = 0; y <= yd; y++) { - swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor, pxcolor, alphaInt); - } - } + swrFillRectangle(renderer, x1, y1, x2, y2, pxcolor, alpha); } static void SWRenderer_drawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, @@ -947,30 +331,10 @@ static void SWRenderer_drawRectangleColor(Renderer* renderer, float x1, float y1 uintpixel_t pxcolor3 = swrConvertPixel(color3); uintpixel_t pxcolor4 = swrConvertPixel(color4); - SWRenderer* swr = (SWRenderer*) renderer; - if (outline) - { swrDrawRectangleColor(renderer, x1, y1, x2, y2, pxcolor1, pxcolor2, pxcolor3, pxcolor4, alpha); - } else - { - swrTransformPosIfNeeded(swr, &x1, &y1); - swrTransformPosIfNeeded(swr, &x2, &y2); - - int alphaInt = swrIntAlpha(alpha); - int x1i = swrFloor(x1), x2i = swrCeiling(x2), y1i = swrFloor(y1), y2i = swrCeiling(y2); - int xd = x2i - x1i; - int yd = y2i - y1i; - if (xd < 0) { x1i = x2i; xd = -xd; } - if (yd < 0) { y1i = y2i; yd = -yd; } - if (xd <= 0 || yd <= 0) return; - - // TODO: blending vertically - for (int y = 0; y <= yd; y++) { - swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor1, pxcolor2, alphaInt); - } - } + swrFillRectangleColor(renderer, x1, y1, x2, y2, pxcolor1, pxcolor2, pxcolor3, pxcolor4, alpha); } static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2, float y2, @@ -986,123 +350,6 @@ static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2 swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha); } -static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uint32_t color1, uint32_t color2, uint32_t color3, int alpha) -{ - // TODO: update this - (void) color2; - (void) color3; - - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); - - // Figure out the maximum Y extent of the triangle. - // (Note that we know yup is the minimum.) - int xmid, ymid, xmid2 = xup, xmax, ymax; - if (yleft < yright) { - xmax = xright, ymax = yright; - xmid = xleft, ymid = yleft; - if (yright != yup) - xmid2 = xup + (xright - xup) * (ymid - yup) / (yright - yup); - } else { - xmax = xleft, ymax = yleft; - xmid = xright, ymid = yright; - if (yleft != yup) - xmid2 = xup + (xleft - xup) * (ymid - yup) / (yleft - yup); - } - - for (int y = yup; y < ymax; y++) - { - if (y < 0) continue; - if (y >= swr->height) break; - - int x1 = xup, x2 = xup; - if (y <= ymid) - { - // Lines: between up and mid, and between up and max - if (ymid != yup) - x1 = xup + (xmid - xup) * (y - yup) / (ymid - yup); - - if (ymid != yup) - x2 = xup + (xmid2 - xup) * (y - yup) / (ymid - yup); - } - else - { - // Lines: between mid and max, and between up and max - if (ymax != yup) - x1 = xup + (xmax - xup) * (y - yup) / (ymax - yup); - - if (ymax != ymid) - x2 = xmid + (xmax - xmid) * (y - ymid) / (ymax - ymid); - } - - if (x1 >= x2) { - int tmp = x1; - x1 = x2; - x2 = tmp; - } - - if (x1 < swr->portX) x1 = swr->portX; - if (x1 >= swr->maxX) continue; - if (x2 < swr->portX) continue; - if (x2 >= swr->maxX) x2 = swr->maxX - 1; - if (x1 > x2) continue; - - uintpixel_t* line = &swr->fb[y * swr->width]; - for (int x = x1; x < x2; x++) { - alphaBlend(&line[x], color1, srcalpha, invalpha); - } - } -} - -static void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color1, uint32_t color2, uint32_t color3, float alpha) -{ - float xup, yup, xleft, yleft, xright, yright; - uint32_t colorup, colorleft, colorright; - - SWRenderer* swr = (SWRenderer*) renderer; - swrTransformPosIfNeeded(swr, &x1, &y1); - swrTransformPosIfNeeded(swr, &x2, &y2); - swrTransformPosIfNeeded(swr, &x3, &y3); - - //which vertex is higher? - xup = x1, yup = y1; colorup = color1; - xleft = x2, yleft = y2; colorleft = color2; - xright = x3, yright = y3; colorright = color3; - if (yup > y2) { - xup = x2, yup = y2, colorup = color2; - xleft = x1, yleft = y1, colorleft = color1; - //xright = x3, yright = y3; - } - if (yup > y3) { - xup = x3, yup = y3, colorup = color3; - xleft = x1, yleft = y1, colorleft = color1; - xright = x2, yright = y2, colorright = color2; - } - - if (xleft > xright) { - float tmp = xleft; - xleft = xright; - xright = tmp; - tmp = yleft; - yleft = yright; - yright = tmp; - uint32_t tmp2 = colorleft; - colorleft = colorright; - colorright = tmp2; - } - - swrDrawTriangleInternal( - swr, - swrFloor(xup), swrFloor(yup), - swrFloor(xleft), swrCeiling(yleft), - swrFloor(xright), swrCeiling(yright), - swrConvertPixel(colorup), - swrConvertPixel(colorleft), - swrConvertPixel(colorright), - swrIntAlpha(alpha) - ); -} - static void SWRenderer_drawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color1, uint32_t color2, uint32_t color3, @@ -1129,226 +376,6 @@ static void SWRenderer_drawLineColor(Renderer* renderer, float x1, float y1, flo swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color1), swrConvertPixel(color2), alpha); } -typedef struct -{ - Font* font; - TexturePageItem* fontTpag; // single TPAG for regular fonts (NULL for sprite fonts) - int fontTpagIndex; - int fontPageId; - Sprite* spriteFontSprite; // source sprite for sprite fonts (NULL for regular fonts) -} -SwrFontState; - -static bool swrResolveFontState(SWRenderer* swr, DataWin* dw, Font* font, SwrFontState* state) -{ - state->font = font; - state->fontTpag = NULL; - state->fontTpagIndex = 0; - state->spriteFontSprite = NULL; - - if (font->isSpriteFont) - { - state->spriteFontSprite = &dw->sprt.sprites[font->spriteIndex]; - } - else - { - state->fontTpagIndex = font->tpagIndex; - if (state->fontTpagIndex < 0) return false; - - state->fontTpag = &dw->tpag.items[state->fontTpagIndex]; - int16_t pageId = state->fontTpag->texturePageId; - if (0 > pageId || (uint32_t) pageId >= swr->totalTextureCount) return false; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return false; - - state->fontPageId = pageId; - } - - return true; -} - -static bool swrResolveGlyph( - SWRenderer* swr, DataWin* dw, SwrFontState* state, FontGlyph* glyph, float cursorX, float cursorY, - int* tpagIndex, int* pageId, int* sx, int* sy, int* sw, int* sh, float* dx, float* dy -) -{ - Font* font = state->font; - if (font->isSpriteFont && state->spriteFontSprite != NULL) - { - Sprite* sprite = state->spriteFontSprite; - int32_t glyphIndex = (int32_t) (glyph - font->glyphs); - if (0 > glyphIndex || glyphIndex >= (int32_t) sprite->textureCount) return false; - - int32_t tpagIdx = sprite->tpagIndices[glyphIndex]; - if (0 > tpagIdx) return false; - - TexturePageItem* glyphTpag = &dw->tpag.items[tpagIdx]; - int16_t pid = glyphTpag->texturePageId; - if (0 > pid || (uint32_t) pid >= swr->totalTextureCount) return false; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pid)) return false; - - *tpagIndex = tpagIdx; - *pageId = glyphTpag->texturePageId; - - *sx = glyphTpag->sourceX; - *sy = glyphTpag->sourceY; - *sw = glyphTpag->sourceWidth; - *sh = glyphTpag->sourceHeight; - - *dx = cursorX + glyph->offset; - *dy = cursorY + glyphTpag->targetY - sprite->originY; - } - else - { - *tpagIndex = state->fontTpagIndex; - *pageId = state->fontPageId; - - *sx = state->fontTpag->sourceX + glyph->sourceX; - *sy = state->fontTpag->sourceY + glyph->sourceY; - *sw = glyph->sourceWidth; - *sh = glyph->sourceHeight; - - *dx = cursorX + glyph->offset; - *dy = cursorY; - } - - return true; -} - -static void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation) -{ - Renderer* renderer = &swr->base; - DataWin* dwin = renderer->dataWin; - - int32_t fontIndex = renderer->drawFont; - if (0 > fontIndex || dwin->font.count <= (uint32_t) fontIndex) return; - - Font* font = &dwin->font.fonts[fontIndex]; - - SwrFontState fontState; - memset(&fontState, 0, sizeof fontState); // silence warning treated as error - - if (!swrResolveFontState(swr, dwin, font, &fontState)) return; - - // TODO: do we need to mirror the way the text scrolls too?! - float cosA = 1.0f, sinA = 0.0f, angleRad = 0.0f; - bool mustRotate = swrMustRotateSensitive(angleDeg); - if (UNLIKELY(mustRotate)) - { - angleRad = -angleDeg * M_PI / 180.0f; - cosA = cosf(angleRad); - sinA = sinf(angleRad); - } - - int textLen = (int) strlen(text); - int lineCount = TextUtils_countLines(text, textLen); - float lineStride = (0.0f > lineSeparation) ? TextUtils_lineStride(font) : (lineSeparation / (font->scaleY != 0.0f ? font->scaleY : 1.0f)); - - // Vertical alignment offset - float totalHeight = (float) lineCount * lineStride; - float valignOffset = 0; - if (renderer->drawValign == 1) valignOffset = -totalHeight / 2.0f; - else if (renderer->drawValign == 2) valignOffset = -totalHeight; - - xscale *= font->scaleX; - yscale *= font->scaleY; - - // Iterate through lines. HTML5 subtracts ascenderOffset from the per-line y offset - // (see yyFont.GR_Text_Draw), shifting glyphs up so the baseline aligns with the drawn y. - float cursorY = valignOffset - (float) font->ascenderOffset; - int32_t lineStart = 0; - - for (int32_t lineIdx = 0; lineCount > lineIdx; lineIdx++) { - // Find end of current line - int32_t lineEnd = lineStart; - while (textLen > lineEnd && !TextUtils_isNewlineChar(text[lineEnd])) { - lineEnd++; - } - int32_t lineLen = lineEnd - lineStart; - - // Horizontal alignment offset for this line - float lineWidth = TextUtils_measureLineWidth(font, text + lineStart, lineLen); - float halignOffset = 0; - if (renderer->drawHalign == 1) halignOffset = -lineWidth / 2.0f; - else if (renderer->drawHalign == 2) halignOffset = -lineWidth; - - float cursorX = halignOffset; - - // Render each glyph in the line - decode each codepoint once and carry it forward as next iteration's ch (also used for kerning) - int32_t pos = 0; - uint16_t ch = 0; - bool hasCh = false; - if (lineLen > pos) { - ch = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); - hasCh = true; - } - - while (hasCh) { - FontGlyph* glyph = TextUtils_findGlyph(font, ch); - - uint16_t nextCh = 0; - bool hasNext = lineLen > pos; - if (hasNext) nextCh = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); - - if (glyph != nullptr) { - bool drewSuccessfully = false; - if (glyph->sourceWidth != 0 && glyph->sourceHeight != 0) { - int fontTpagIndex = 0, pageId = 0; - int sx, sy, sw, sh, dw, dh; - float dx, dy; - if (swrResolveGlyph(swr, dwin, &fontState, glyph, cursorX, cursorY, - &fontTpagIndex, &pageId, &sx, &sy, &sw, &sh, &dx, &dy)) - { - dx *= xscale; dx += x; - dy *= xscale; dy += y; - dw = swrCeiling(xscale * glyph->sourceWidth); - dh = swrCeiling(yscale * glyph->sourceHeight); - - // TODO: at 640x480, for some reason, without this fixup the - // letters in the "Name the fallen human." screen don't shake - dx = roundf(dx * 2) / 2; - dy = roundf(dy * 2) / 2; - - SWTexture* texture = swr->textures[pageId]; - - if (UNLIKELY(mustRotate)) - { - dx -= x; - dy -= y; - float ndx = cosA * dx - sinA * dy; - float ndy = sinA * dx + cosA * dy; - ndx += x; - ndy += y; - swrDrawSpriteRotated(renderer, ndx, ndy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, 0.0f, 0.0f); - } - else - { - swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); - } - - drewSuccessfully = true; - } - } - - cursorX += glyph->shift; - if (drewSuccessfully && hasNext) { - cursorX += TextUtils_getKerningOffset(glyph, nextCh); - } - } - - ch = nextCh; - hasCh = hasNext; - } - - cursorY += lineStride; - // Skip past the newline, treating \r\n and \n\r as single breaks - if (textLen > lineEnd) { - lineStart = TextUtils_skipNewline(text, lineEnd, textLen); - } else { - lineStart = lineEnd; - } - } -} - static void SWRenderer_drawText(Renderer* renderer, const char* text, float x, float y, float xscale, float yscale, float angleDeg, float lineSeparation) { From 4d5460c02e9b7480a5dccaf00441ad933a23bd28 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 20:04:45 +0300 Subject: [PATCH 082/130] * Remove unused variable --- src/sw/sw_drawing.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 7ec362fbc..2d30e6909 100755 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -194,7 +194,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); - for (int i = 0; x < xe; i++) + while (x < xe) { x++; if (px < 0) @@ -244,7 +244,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); - for (int i = 0; y < ye; i++) + while (y < ye) { y++; if (py <= 0) From 578c1da96d0252bdbc1a9989e1fbc4e02c23e9f7 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 20:14:01 +0300 Subject: [PATCH 083/130] * Provide a dummy Runner_setNextFrame for AppKit builds --- src/backends/appkit.m | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/backends/appkit.m b/src/backends/appkit.m index 5c72fa6e7..7c3c31b97 100644 --- a/src/backends/appkit.m +++ b/src/backends/appkit.m @@ -707,3 +707,15 @@ void platformSleepUntil(uint64_t time) { YIELD(); } } + +#ifdef ENABLE_SW_RENDERER + +void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) +{ + // TODO: this is a placeholder + (void) framebuffer; + (void) width; + (void) height; +} + +#endif From 54a752c58c95ca580025dde319531d487c22d362 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 20:14:49 +0300 Subject: [PATCH 084/130] * Rename "Runner_setNextFrame" to "platformSetNextFramebuffer" --- src/backends/appkit.m | 2 +- src/backends/glfw2.c | 2 +- src/backends/glfw3.c | 2 +- src/backends/sdl1.c | 10 +++++----- src/backends/sdl2.c | 2 +- src/backends/sdl3.c | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/backends/appkit.m b/src/backends/appkit.m index 7c3c31b97..3aeadd81e 100644 --- a/src/backends/appkit.m +++ b/src/backends/appkit.m @@ -710,7 +710,7 @@ void platformSleepUntil(uint64_t time) { #ifdef ENABLE_SW_RENDERER -void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { // TODO: this is a placeholder (void) framebuffer; diff --git a/src/backends/glfw2.c b/src/backends/glfw2.c index 3f07ef37e..1d845caf6 100644 --- a/src/backends/glfw2.c +++ b/src/backends/glfw2.c @@ -256,7 +256,7 @@ void platformInitFunctions(Runner *runner) { static uint32_t* nextFb = NULL; static int fbWidth = 0, fbHeight = 0; -void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { nextFb = framebuffer; fbWidth = width; fbHeight = height; diff --git a/src/backends/glfw3.c b/src/backends/glfw3.c index 23af551ee..7bf8dfd1a 100644 --- a/src/backends/glfw3.c +++ b/src/backends/glfw3.c @@ -329,7 +329,7 @@ void platformInitFunctions(Runner *runner) { static uint32_t* nextFb = NULL; static int fbWidth = 0, fbHeight = 0; -void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { nextFb = framebuffer; fbWidth = width; fbHeight = height; diff --git a/src/backends/sdl1.c b/src/backends/sdl1.c index cc65997c0..69bd227d1 100644 --- a/src/backends/sdl1.c +++ b/src/backends/sdl1.c @@ -317,7 +317,7 @@ void platformInitFunctions(Runner *runner) { static SDL_Surface* nextFb = NULL; -void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { if (nextFb) { SDL_FreeSurface(nextFb); nextFb = NULL; @@ -378,10 +378,10 @@ static int32_t SDLKeyToGml(int sdlkey) { case SDLK_RCTRL: return VK_CONTROL; case SDLK_LALT: case SDLK_RALT: return VK_ALT; - case SDLK_UP: return VK_UP; - case SDLK_DOWN: return VK_DOWN; - case SDLK_LEFT: return VK_LEFT; - case SDLK_RIGHT: return VK_RIGHT; +case SDLK_KP8: case SDLK_UP: return VK_UP; +case SDLK_KP5:case SDLK_KP2: case SDLK_DOWN: return VK_DOWN; +case SDLK_KP4: case SDLK_LEFT: return VK_LEFT; +case SDLK_KP6: case SDLK_RIGHT: return VK_RIGHT; case SDLK_F1: return VK_F1; case SDLK_F2: return VK_F2; case SDLK_F3: return VK_F3; diff --git a/src/backends/sdl2.c b/src/backends/sdl2.c index 67290aaea..373dbec85 100644 --- a/src/backends/sdl2.c +++ b/src/backends/sdl2.c @@ -281,7 +281,7 @@ void platformInitFunctions(Runner *runner) { static SDL_Surface* nextFb = NULL; -void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { if (nextFb) { SDL_FreeSurface(nextFb); nextFb = NULL; diff --git a/src/backends/sdl3.c b/src/backends/sdl3.c index 7c9f6bee2..a15e75fec 100644 --- a/src/backends/sdl3.c +++ b/src/backends/sdl3.c @@ -237,7 +237,7 @@ void platformInitFunctions(Runner *runner) { static SDL_Surface* nextFb = NULL; -void Runner_setNextFrame(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { if (nextFb) { SDL_DestroySurface(nextFb); nextFb = NULL; From e530bf861e1d303f493b2527d1a9cd4376657254 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 20:26:31 +0300 Subject: [PATCH 085/130] * Oop, forgot to commit this --- src/sw/sw_renderer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 01587bd44..21a33842e 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -7,7 +7,7 @@ #include "sw_renderer_private.h" -void Runner_setNextFrame(uintpixel_t* framebuffer, int width, int height); +void platformSetNextFramebuffer(uintpixel_t* framebuffer, int width, int height); static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) { @@ -85,7 +85,7 @@ static void SWRenderer_endFrameEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; assert(!swr->drawingToSurface); - Runner_setNextFrame(swr->fb, swr->width, swr->height); + platformSetNextFramebuffer(swr->fb, swr->width, swr->height); } static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t viewY, int32_t viewW, int32_t viewH, From b0352f4611abc4b418568661045c6d485c4f9b1a Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 19 Aug 2026 23:09:26 +0300 Subject: [PATCH 086/130] * Refactor sprite_create_from_surface implementation This should make things more correct at higher resolution, at the cost of visual fidelity within the surface. --- src/sw/sw_renderer.c | 82 +++++++++++++----------------------------- src/sw/sw_texture.c | 86 ++++++++++++++++++++++++++++++++++++++++++++ src/sw/sw_texture.h | 2 ++ 3 files changed, 113 insertions(+), 57 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 21a33842e..ee462cb1c 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -925,30 +925,35 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su { SWRenderer* swr = (SWRenderer*) renderer; - swrTransformPosIntIfNeeded(swr, &x, &y); - swrTransformSizeIntIfNeeded(swr, &w, &h); - swrTransformSizeIntIfNeeded(swr, &xorig, &yorig); - - (void) removeback; (void) smooth; - SWTexture *srcTex, temp1; + int cropLeft = x; + int cropTop = y; + int cropRight = x + w; + int cropBottom = y + h; - if (surfaceID == APPLICATION_SURFACE_ID) { + SWTexture *srcTex, temp1; + if (surfaceID == APPLICATION_SURFACE_ID) + { srcTex = &temp1; temp1.width = swr->width; temp1.height = swr->height; temp1.buffer = swr->fb; - } else { - if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) { + + swrTransformPosIntIfNeeded(swr, &cropLeft, &cropTop); + swrTransformPosIntIfNeeded(swr, &cropRight, &cropBottom); + } + else + { + if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL){ fprintf(stderr, "%s: Invalid surface ID %d\n", __func__, surfaceID); - return 0; + return -1; } SWSurface* surf = swr->surfaces[surfaceID]; swrCommitShadowWritesToSurfaceIfNeeded(swr, surf); srcTex = surf->texture; } - + int32_t texturePageId = swrFindSurfaceTextureSlot(swr); int32_t tpagIndex = swrFindSurfaceTPagSlot(swr); if (texturePageId == -1 || tpagIndex == -1) { @@ -956,47 +961,10 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su return 0; } - SWTexture* tex = swrCreateTexture(NULL, w, h); - - // grab the pixels. - for (int iy = 0; iy < h; iy++) - { - uintpixel_t* dstline = &tex->buffer[iy * tex->width]; - if ((iy + y) < 0 || (iy + y) >= srcTex->height) - { - for (int ix = 0; ix < w; ix++) - dstline[ix] = 0; - - continue; - } - - uintpixel_t* srcline = &srcTex->buffer[(iy + y) * srcTex->width + x]; - - int ix = 0, sx = x; - // left edge - for (; sx < 0 && ix < w; sx++, ix++) - dstline[ix] = 0; - - // in-bounds - for (; sx < srcTex->width && ix < w; sx++, ix++) -#if PIXEL_SIZE == 8 - dstline[ix] = srcline[ix]; -#else - dstline[ix] = srcline[ix] | TRANSPARENT_MASK; -#endif - - // right edge - for (; ix < w; ix++) - dstline[ix] = 0; - } - - int32_t spriteW = w; - int32_t spriteH = h; + SWTexture* tex = swrCropSectionFromTexture(srcTex, w, h, cropLeft, cropTop, cropRight, cropBottom); + if (removeback) + swrRemoveBackgroundFromTexture(tex); - int32_t targetW = spriteW; - int32_t targetH = spriteH; - swrReverseTransformSizeIntIfNeeded(swr, &targetW, &targetH); - swr->textures[texturePageId] = tex; // TODO[MrPowerGamerBR]: This is supposed to be refactored, not to modify data.win structs directly. @@ -1004,14 +972,14 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su TexturePageItem* tpag = &dw->tpag.items[tpagIndex]; tpag->sourceX = 0; tpag->sourceY = 0; - tpag->sourceWidth = (uint16_t) spriteW; - tpag->sourceHeight = (uint16_t) spriteH; + tpag->sourceWidth = (uint16_t) w; + tpag->sourceHeight = (uint16_t) h; tpag->targetX = 0; tpag->targetY = 0; - tpag->targetWidth = (uint16_t) (spriteW / swr->scaleX); - tpag->targetHeight = (uint16_t) (spriteH / swr->scaleY); - tpag->boundingWidth = (uint16_t) spriteW; - tpag->boundingHeight = (uint16_t) spriteH; + tpag->targetWidth = (uint16_t) w; + tpag->targetHeight = (uint16_t) h; + tpag->boundingWidth = (uint16_t) w; + tpag->boundingHeight = (uint16_t) h; tpag->texturePageId = texturePageId; uint32_t spriteIndex = DataWin_allocSpriteSlot(dw, swr->originalSpriteCount); diff --git a/src/sw/sw_texture.c b/src/sw/sw_texture.c index 6d5089e4a..baac69e7f 100755 --- a/src/sw/sw_texture.c +++ b/src/sw/sw_texture.c @@ -50,3 +50,89 @@ void swrFreeTexture(SWTexture* texture) free(texture->buffer); free(texture); } + +SWTexture* swrCropSectionFromTexture(SWTexture* src, int32_t width, int32_t height, int32_t cropLeft, int32_t cropTop, int32_t cropRight, int32_t cropBottom) +{ + if (width <= 0 || height <= 0) { + fprintf(stderr, "SWR: Cannot resize texture to %dx%d.\n", width, height); + return NULL; + } + + if (cropLeft < 0) cropLeft = 0; + if (cropTop < 0) cropTop = 0; + if (cropRight >= src->width) cropRight = src->width; + if (cropBottom >= src->height) cropBottom = src->height; + + if (cropLeft >= cropRight || cropTop >= cropBottom) { + fprintf(stderr, "SWR: Invalid crop coordinates for resize.\n"); + return NULL; + } + + int cropWidth = cropRight - cropLeft; + int cropHeight = cropBottom - cropTop; + + SWTexture* dst = swrCreateTexture(NULL, width, height); + int32_t *mapRows = NULL, *mapCols = NULL; + + if (cropWidth != width) { + mapCols = safeCalloc(width, sizeof(int32_t)); + for (int32_t i = 0; i < width; i++) + mapCols[i] = i * (cropRight - cropLeft) / width + cropLeft; + } + if (cropHeight != height) { + mapRows = safeCalloc(height, sizeof(int32_t)); + for (int32_t i = 0; i < height; i++) + mapRows[i] = i * (cropBottom - cropTop) / height + cropTop; + } + + for (int32_t y = 0, y1 = cropTop; y < height; y++, y1++) + { + uintpixel_t* dstbuf = &dst->buffer[y * width]; + const uintpixel_t* srcbuf = &src->buffer[(mapRows ? mapRows[y] : y1) * src->width]; + + if (width == cropWidth) + { + for (int32_t x = 0, x1 = cropLeft; x < width; x++, x1++) { + dstbuf[x] = srcbuf[x1]; + } + } + else + { + for (int32_t x = 0; x < width; x++) { + dstbuf[x] = srcbuf[mapCols[x]]; + } + } + } + + if (mapCols) free(mapCols); + if (mapRows) free(mapRows); + + return dst; +} + +// Emulates the `removeback` flag from `sprite_create_from_surface`. +void swrRemoveBackgroundFromTexture(SWTexture* texture) +{ + // bottom left pixel + uintpixel_t background = texture->buffer[texture->width * (texture->height - 1)]; +#ifdef TRANSPARENT_MASK + background &= ~TRANSPARENT_MASK; +#endif + + size_t widthheight = texture->width * texture->height; + for (size_t i = 0; i < widthheight; i++) + { +#ifdef TRANSPARENT_MASK + if ((texture->buffer[i] & ~TRANSPARENT_MASK) == background) +#else + if (texture->buffer[i] == background) +#endif + { +#ifdef PXL_TRANSPARENT + texture->buffer[i] = PXL_TRANSPARENT; +#else + texture->buffer[i] = 0; +#endif + } + } +} diff --git a/src/sw/sw_texture.h b/src/sw/sw_texture.h index 7c335f201..ab49cf18b 100755 --- a/src/sw/sw_texture.h +++ b/src/sw/sw_texture.h @@ -5,5 +5,7 @@ SWTexture* swrCreateTextureEx(const void* srcBuffer, int width, int height, bool SWTexture* swrCreateTexture(const uint8_t* srcBuffer, int width, int height); SWTexture* swrCopyTexture(SWTexture* texture); void swrFreeTexture(SWTexture* texture); +SWTexture* swrCropSectionFromTexture(SWTexture* src, int32_t width, int32_t height, int32_t cropLeft, int32_t cropTop, int32_t cropRight, int32_t cropBottom); +void swrRemoveBackgroundFromTexture(SWTexture* texture); #endif//_SW_TEXTURE_H From 420db30735f12f40d1ae26725cb5ba417b46687f Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Tue, 8 Sep 2026 15:34:22 -0400 Subject: [PATCH 087/130] remove --- Makefile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Makefile b/Makefile index ed3f3afd6..f95f91ecb 100644 --- a/Makefile +++ b/Makefile @@ -92,9 +92,6 @@ SYSCFLAGS += $(GLFW3_CFLAGS) LIBS += $(GLFW3_LIBS) DEFINES += $(DEFINE)USE_GLFW3 ENABLE_GLAD := 1 -ifdef ENABLE_GLES -DISABLE_SW_RENDERER := 1 -endif endif ifeq ($(BACKEND),glfw2) GLFW2_CFLAGS := $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags libglfw) @@ -103,9 +100,6 @@ SYSCFLAGS += $(GLFW2_CFLAGS) LIBS += $(GLFW2_LIBS) DEFINES += $(DEFINE)USE_GLFW2 ENABLE_GLAD := 1 -ifdef ENABLE_GLES -DISABLE_SW_RENDERER := 1 -endif endif ifeq ($(BACKEND),sdl1) SDL1_CFLAGS := $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags sdl) From 19eee3e7287c6e700c5e4ef8a817b1d752b0b584 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Tue, 8 Sep 2026 15:35:46 -0400 Subject: [PATCH 088/130] fix --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index f95f91ecb..f6d7128ac 100644 --- a/Makefile +++ b/Makefile @@ -197,6 +197,7 @@ $(error must enable at least 1 renderer) endif endif endif +endif ifeq ($(AUDIO_BACKEND),miniaudio) INCLUDES += $(INC)src/audio/miniaudio $(INC)vendor/miniaudio From e6b3e454ebf3f7bcfb1c18bb11fdbec63fe8812f Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Tue, 8 Sep 2026 15:38:45 -0400 Subject: [PATCH 089/130] fix --- src/sw/sw_texture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_texture.c b/src/sw/sw_texture.c index baac69e7f..20d8221aa 100755 --- a/src/sw/sw_texture.c +++ b/src/sw/sw_texture.c @@ -75,12 +75,12 @@ SWTexture* swrCropSectionFromTexture(SWTexture* src, int32_t width, int32_t heig int32_t *mapRows = NULL, *mapCols = NULL; if (cropWidth != width) { - mapCols = safeCalloc(width, sizeof(int32_t)); + mapCols = (int32_t *)safeCalloc(width, sizeof(int32_t)); for (int32_t i = 0; i < width; i++) mapCols[i] = i * (cropRight - cropLeft) / width + cropLeft; } if (cropHeight != height) { - mapRows = safeCalloc(height, sizeof(int32_t)); + mapRows = (int32_t *)safeCalloc(height, sizeof(int32_t)); for (int32_t i = 0; i < height; i++) mapRows[i] = i * (cropBottom - cropTop) / height + cropTop; } From 1c118cbf20feef8bba728b8057a0305f81738964 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Tue, 8 Sep 2026 15:42:25 -0400 Subject: [PATCH 090/130] bleh --- src/backends/noop.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/backends/noop.c b/src/backends/noop.c index b71767fc3..171e6a991 100644 --- a/src/backends/noop.c +++ b/src/backends/noop.c @@ -96,4 +96,8 @@ void platformSleepUntil(uint64_t time) { while (nowNanos() < time) { YIELD(); } -} \ No newline at end of file +} + +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { + (void)framebuffer, (void)width, (void)height; +} From 96a259c4b6a198cfd3bb38cdeb03e2d66406b570 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Tue, 8 Sep 2026 15:43:52 -0400 Subject: [PATCH 091/130] fix --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17e83579d..a2d0030db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,6 +357,7 @@ if(PLATFORM STREQUAL "cli" OR PLATFORM STREQUAL "vita" OR PLATFORM STREQUAL "swi # GLAD add_library(glad STATIC vendor/glad/src/glad.c) target_include_directories(glad PUBLIC vendor/glad/include) + target_compile_options(glad PRIVATE -Wno-unused-but-set-variable) set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} glad) endif() endif() From 75172da788cb39ecb75ba26cdcbeacd40a622e79 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sat, 12 Sep 2026 20:42:38 -0400 Subject: [PATCH 092/130] chmod -x --- src/sw/sw_drawing.c | 0 src/sw/sw_drawing.h | 0 src/sw/sw_inlined.h | 0 src/sw/sw_pixel_calc.h | 0 src/sw/sw_renderer_private.h | 0 src/sw/sw_surface.c | 0 src/sw/sw_surface.h | 0 src/sw/sw_texture.c | 0 src/sw/sw_texture.h | 0 src/sw/sw_texture_lru.c | 0 src/sw/sw_texture_lru.h | 0 src/sw/sw_transform.h | 0 12 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/sw/sw_drawing.c mode change 100755 => 100644 src/sw/sw_drawing.h mode change 100755 => 100644 src/sw/sw_inlined.h mode change 100755 => 100644 src/sw/sw_pixel_calc.h mode change 100755 => 100644 src/sw/sw_renderer_private.h mode change 100755 => 100644 src/sw/sw_surface.c mode change 100755 => 100644 src/sw/sw_surface.h mode change 100755 => 100644 src/sw/sw_texture.c mode change 100755 => 100644 src/sw/sw_texture.h mode change 100755 => 100644 src/sw/sw_texture_lru.c mode change 100755 => 100644 src/sw/sw_texture_lru.h mode change 100755 => 100644 src/sw/sw_transform.h diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c old mode 100755 new mode 100644 diff --git a/src/sw/sw_drawing.h b/src/sw/sw_drawing.h old mode 100755 new mode 100644 diff --git a/src/sw/sw_inlined.h b/src/sw/sw_inlined.h old mode 100755 new mode 100644 diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h old mode 100755 new mode 100644 diff --git a/src/sw/sw_renderer_private.h b/src/sw/sw_renderer_private.h old mode 100755 new mode 100644 diff --git a/src/sw/sw_surface.c b/src/sw/sw_surface.c old mode 100755 new mode 100644 diff --git a/src/sw/sw_surface.h b/src/sw/sw_surface.h old mode 100755 new mode 100644 diff --git a/src/sw/sw_texture.c b/src/sw/sw_texture.c old mode 100755 new mode 100644 diff --git a/src/sw/sw_texture.h b/src/sw/sw_texture.h old mode 100755 new mode 100644 diff --git a/src/sw/sw_texture_lru.c b/src/sw/sw_texture_lru.c old mode 100755 new mode 100644 diff --git a/src/sw/sw_texture_lru.h b/src/sw/sw_texture_lru.h old mode 100755 new mode 100644 diff --git a/src/sw/sw_transform.h b/src/sw/sw_transform.h old mode 100755 new mode 100644 From f16b81648076070316bc025dcb99198512e25e31 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sun, 13 Sep 2026 00:03:07 -0400 Subject: [PATCH 093/130] new log system --- src/sw/sw_drawing.c | 6 +++--- src/sw/sw_renderer.c | 42 ++++++++++++++++++------------------ src/sw/sw_renderer_private.h | 2 +- src/sw/sw_surface.c | 2 +- src/sw/sw_texture.c | 4 ++-- src/sw/sw_texture_lru.c | 14 ++++++------ 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 2d30e6909..bf35b7bd2 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -668,7 +668,7 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor return true; // restore the original framebuffer - fprintf(stderr, "back to original framebuffer\n"); + logInfo("back to original framebuffer\n"); swr->drawingToSurface = false; swr->fb = swr->mainFb; swr->width = swr->mainWidth; @@ -699,7 +699,7 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor } if (targetSurfaceId < 0 || (size_t) targetSurfaceId >= swr->surfaceCount || swr->surfaces[targetSurfaceId] == NULL) { - fprintf(stderr, "swr: Invalid surface id %d\n", targetSurfaceId); + logError("swr: Invalid surface id %d\n", targetSurfaceId); return false; } @@ -746,7 +746,7 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor swr->maxY = swr->viewH = swr->portH = surface->height; swr->scaleX = swr->scaleY = 1.0f; - fprintf(stderr, "switching to surface %p, fb %p, %dx%d\n", surface, swr->fb, swr->width, swr->height); + logInfo("switching to surface %p, fb %p, %dx%d\n", surface, swr->fb, swr->width, swr->height); return true; } diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index ee462cb1c..f949b5955 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -41,7 +41,7 @@ static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) dataWin->tpag.items[i].texturePageId = -1; } - fprintf(stderr, "SWRenderer initialized.\n"); + logInfo("SWRenderer initialized.\n"); } static void SWRenderer_destroy(Renderer* renderer) @@ -51,7 +51,7 @@ static void SWRenderer_destroy(Renderer* renderer) // TODO: why didn't I implement this. (void) swr; - fprintf(stderr, "SWRenderer destroyed.\n"); + logInfo("SWRenderer destroyed.\n"); } static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gameH, int32_t windowW, int32_t windowH) @@ -206,18 +206,18 @@ static void SWRenderer_drawSprite(Renderer* renderer, int32_t tpagIndex, float x DataWin* dwin = renderer->dataWin; if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) { - fprintf(stderr, "%s: tpagIndex of %d is invalid\n", __func__, tpagIndex); + logError("%s: tpagIndex of %d is invalid\n", __func__, tpagIndex); return; } TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; int16_t pageId = tpag->texturePageId; if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) { - fprintf(stderr, "%s: tpagIndex of %d is invalid, as pageId of %d is invalid\n", __func__, tpagIndex, pageId); + logError("%s: tpagIndex of %d is invalid, as pageId of %d is invalid\n", __func__, tpagIndex, pageId); return; } if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) { - fprintf(stderr, "%s: could not ensure texture is loaded, tpagIndex: %d, pageId: %d\n", __func__, tpagIndex, pageId); + logError("%s: could not ensure texture is loaded, tpagIndex: %d, pageId: %d\n", __func__, tpagIndex, pageId); return; } @@ -574,7 +574,7 @@ static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) //if (mode != bm_normal && mode != bm_add && mode != bm_subtract) { - fprintf(stderr, "swr: unsupported blend mode: %d\n", mode); + logWarn("swr: unsupported blend mode: %d\n", mode); } } @@ -607,7 +607,7 @@ static void SWRenderer_gpuSetColorWriteEnable(Renderer* renderer, bool red, bool SWRenderer* swr = (SWRenderer*) renderer; if (!swr->drawingToSurface) { - fprintf(stderr, "swr: gpuSetColorWriteEnable not supported for main framebuffer"); + logWarn("swr: gpuSetColorWriteEnable not supported for main framebuffer"); return; } @@ -643,7 +643,7 @@ static void SWRenderer_gpuGetColorWriteEnable(Renderer* renderer, bool* red, boo SWRenderer* swr = (SWRenderer*) renderer; if (!swr->drawingToSurface) { - fprintf(stderr, "swr: gpuGetColorWriteEnable not supported for main framebuffer"); + logWarn("swr: gpuGetColorWriteEnable not supported for main framebuffer"); return; } @@ -680,7 +680,7 @@ static int32_t SWRenderer_createSurface(Renderer* renderer, int32_t width, int32 } if (slot < 0) { - fprintf(stderr, "swr: Could not create surface, too many exist at once.\n"); + logError("swr: Could not create surface, too many exist at once.\n"); return slot; } @@ -740,7 +740,7 @@ static void SWRenderer_drawSurface(Renderer* renderer, int32_t surfaceID, surface = &localSurface; } else { if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) { - fprintf(stderr, "swr: Invalid surface id %d for drawSurface\n", surfaceID); + logError("swr: Invalid surface id %d for drawSurface\n", surfaceID); return; } @@ -793,12 +793,12 @@ static void SWRenderer_surfaceResize(Renderer* renderer, int32_t surfaceID, int3 SWRenderer* swr = (SWRenderer*) renderer; if (surfaceID == APPLICATION_SURFACE_ID) { - fprintf(stderr, "swr: Don't support resizing the application window with this. There must be another way! (need to set to %dx%d)\n", width, height); + logError("swr: Don't support resizing the application window with this. There must be another way! (need to set to %dx%d)\n", width, height); return; } if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) { - fprintf(stderr, "swr: Cannot resize surface id %d, it's invalid\n", surfaceID); + logError("swr: Cannot resize surface id %d, it's invalid\n", surfaceID); return; } @@ -811,12 +811,12 @@ static void SWRenderer_surfaceFree(Renderer* renderer, int32_t surfaceID) SWRenderer* swr = (SWRenderer*) renderer; if (surfaceID == APPLICATION_SURFACE_ID) { - fprintf(stderr, "swr: Don't support SWRenderer_surfaceCopy the application window with this. There must be another way!\n"); + logError("swr: Don't support SWRenderer_surfaceCopy the application window with this. There must be another way!\n"); return; } if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL) { - fprintf(stderr, "swr: Cannot resize surface id %d, it's invalid\n", surfaceID); + logError("swr: Cannot resize surface id %d, it's invalid\n", surfaceID); return; } @@ -843,7 +843,7 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, temp1.buffer = swr->mainFb; } else if (DestSurfaceID < 0 || (size_t) DestSurfaceID >= swr->surfaceCount || swr->surfaces[DestSurfaceID] == NULL) { - fprintf(stderr, "swr: Cannot resize surface id %d, it's invalid (dest in surfaceCopy)\n", DestSurfaceID); + logError("swr: Cannot resize surface id %d, it's invalid (dest in surfaceCopy)\n", DestSurfaceID); return; } else { @@ -857,7 +857,7 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, temp2.buffer = swr->mainFb; } else if (SrcSurfaceID < 0 || (size_t) SrcSurfaceID >= swr->surfaceCount || swr->surfaces[SrcSurfaceID] == NULL) { - fprintf(stderr, "swr: Cannot resize surface id %d, it's invalid (src in surfaceCopy)\n", SrcSurfaceID); + logError("swr: Cannot resize surface id %d, it's invalid (src in surfaceCopy)\n", SrcSurfaceID); return; } else { @@ -946,7 +946,7 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su else { if (surfaceID < 0 || (size_t) surfaceID >= swr->surfaceCount || swr->surfaces[surfaceID] == NULL){ - fprintf(stderr, "%s: Invalid surface ID %d\n", __func__, surfaceID); + logError("%s: Invalid surface ID %d\n", __func__, surfaceID); return -1; } SWSurface* surf = swr->surfaces[surfaceID]; @@ -957,7 +957,7 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su int32_t texturePageId = swrFindSurfaceTextureSlot(swr); int32_t tpagIndex = swrFindSurfaceTPagSlot(swr); if (texturePageId == -1 || tpagIndex == -1) { - fprintf(stderr, "%s: Sprite overflow!!\n", __func__); + logError("%s: Sprite overflow!!\n", __func__); return 0; } @@ -995,7 +995,7 @@ static int32_t SWRenderer_createSpriteFromSurface(Renderer* renderer, int32_t su sprite->maskCount = 0; sprite->masks = nullptr; - fprintf(stderr, "%s: Allocated surface sprite with ID %d\n", __func__, spriteIndex); + logInfo("%s: Allocated surface sprite with ID %d\n", __func__, spriteIndex); return spriteIndex; } @@ -1008,7 +1008,7 @@ static void SWRenderer_deleteSprite(Renderer* renderer, int32_t spriteIndex) // Refuse to delete original data.win sprites if (swr->originalSpriteCount > (uint32_t) spriteIndex) { - fprintf(stderr, "%s: Cannot delete sprite with index %d, it's invalid.\n", __func__, spriteIndex); + logError("%s: Cannot delete sprite with index %d, it's invalid.\n", __func__, spriteIndex); return; } @@ -1037,7 +1037,7 @@ static void SWRenderer_deleteSprite(Renderer* renderer, int32_t spriteIndex) memset(sprite, 0, sizeof(Sprite)); sprite->name = keepName; - fprintf(stderr, "SWR: Deleted sprite %d\n", spriteIndex); + logInfo("SWR: Deleted sprite %d\n", spriteIndex); } static void SWRenderer_drawTiledPart(Renderer* renderer, int32_t tpagIndex, diff --git a/src/sw/sw_renderer_private.h b/src/sw/sw_renderer_private.h index 6b9b61738..d1f078160 100644 --- a/src/sw/sw_renderer_private.h +++ b/src/sw/sw_renderer_private.h @@ -6,7 +6,7 @@ #include "pixel_convert.h" // Unimplemented Functions -#define UNIMP() do { fprintf(stderr, "NYI %s\n", __func__); } while (0) +#define UNIMP() do { logWarn("NYI %s\n", __func__); } while (0) //#define UNIMP() do { } while (0) #define UNIMP2() do { } while (0) diff --git a/src/sw/sw_surface.c b/src/sw/sw_surface.c index 50c060925..30f4e627a 100644 --- a/src/sw/sw_surface.c +++ b/src/sw/sw_surface.c @@ -76,7 +76,7 @@ void swrCommitShadowWritesToSurfaceIfNeeded(SWRenderer* swr, SWSurface* surface) if (swr->writeMask & WRITE_MASK_ALPHA) l |= 0x8000; #else //although it DOES ues rgb332, needs special handling for ALPHA - fprintf(stderr, "swr: Unimplemented color masking for 8-bit mode TODO\n"); + logError("swr: Unimplemented color masking for 8-bit mode TODO\n"); swrFreeTexture(surface->texture); surface->texture = surface->shadowTexture; surface->shadowTexture = NULL; diff --git a/src/sw/sw_texture.c b/src/sw/sw_texture.c index 20d8221aa..98ecf089f 100644 --- a/src/sw/sw_texture.c +++ b/src/sw/sw_texture.c @@ -54,7 +54,7 @@ void swrFreeTexture(SWTexture* texture) SWTexture* swrCropSectionFromTexture(SWTexture* src, int32_t width, int32_t height, int32_t cropLeft, int32_t cropTop, int32_t cropRight, int32_t cropBottom) { if (width <= 0 || height <= 0) { - fprintf(stderr, "SWR: Cannot resize texture to %dx%d.\n", width, height); + logError("SWR: Cannot resize texture to %dx%d.\n", width, height); return NULL; } @@ -64,7 +64,7 @@ SWTexture* swrCropSectionFromTexture(SWTexture* src, int32_t width, int32_t heig if (cropBottom >= src->height) cropBottom = src->height; if (cropLeft >= cropRight || cropTop >= cropBottom) { - fprintf(stderr, "SWR: Invalid crop coordinates for resize.\n"); + logError("SWR: Invalid crop coordinates for resize.\n"); return NULL; } diff --git a/src/sw/sw_texture_lru.c b/src/sw/sw_texture_lru.c index 71b8d049d..5a6c69ac3 100644 --- a/src/sw/sw_texture_lru.c +++ b/src/sw/sw_texture_lru.c @@ -68,31 +68,31 @@ bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) break; } - fprintf(stderr, "swr: Failed to decode TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); + logWarn("swr: Failed to decode TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); } else { - fprintf(stderr, "swr: Failed to load TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); + logWarn("swr: Failed to load TXTR page %u. This is likely because we're out of memory, so evicting a texture.\n", pageId); } int tail = swrTailTextureIndexLRU(swr, true); if (tail == -1) { - fprintf(stderr, "swr: Looks like we can't fit this texture in memory at all. Bummer.\n"); + logError("swr: Looks like we can't fit this texture in memory at all. Bummer.\n"); break; } swrEvictTextureFromCache(swr, tail); - fprintf(stderr, "swr: Evicted texture %d, trying again.\n", tail); + logInfo("swr: Evicted texture %d, trying again.\n", tail); } while (!pixels); if (pixels == nullptr) { - fprintf(stderr, "swr: Failed to decode TXTR page %u.\n", pageId); + logError("swr: Failed to decode TXTR page %u.\n", pageId); return false; } swr->textures[pageId] = swrCreateTexture(pixels, w, h); free(pixels); - fprintf(stderr, "SWR: Loaded TXTR page %u (%dx%d)\n", pageId, w, h); + logInfo("SWR: Loaded TXTR page %u (%dx%d)\n", pageId, w, h); // add it to the LRU do @@ -103,7 +103,7 @@ bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) int tail = swrTailTextureIndexLRU(swr, true); if (tail == -1) { - fprintf(stderr, "swr: Come on now.\n"); + logError("swr: Come on now.\n"); assert(tail != -1); return false; } From d5450a5ccb916f5233cc3e2dbd67f52bdbb6ee85 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sun, 13 Sep 2026 14:08:15 -0400 Subject: [PATCH 094/130] drop pages --- src/sw/sw_texture_lru.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sw/sw_texture_lru.c b/src/sw/sw_texture_lru.c index 5a6c69ac3..a77437162 100644 --- a/src/sw/sw_texture_lru.c +++ b/src/sw/sw_texture_lru.c @@ -64,7 +64,8 @@ bool swrEnsureTextureIsLoaded(SWRenderer* swr, uint32_t pageId) if (!txtr->mapped) { free(txtr->blobData); txtr->blobData = NULL; - } + } else + dropMappedRange(txtr->blobData, 0, txtr->blobSize); break; } From 3a67994e16fea6017a95be516efa5102c3ccd33c Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 18:17:08 +0300 Subject: [PATCH 095/130] * Add support for gradients in swrDrawTriangleInternal. * Add a way to make alpha blending, three-way blending, and tinting faster but less accurate. --- src/sw/sw_drawing.c | 84 ++++++++++++++++++++++++++++++++++-- src/sw/sw_pixel_calc.h | 98 ++++++++++++++++++++++++++++++------------ src/sw/sw_renderer.c | 5 +++ src/utils.h | 2 +- 4 files changed, 157 insertions(+), 32 deletions(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index bf35b7bd2..9b57a67a4 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -509,7 +509,7 @@ static void swrDrawSpriteRotatedInternal( } } -static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uint32_t color1, uint32_t color2, uint32_t color3, int alpha) +static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, int alpha) { // TODO: update this (void) color2; @@ -533,6 +533,65 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft xmid2 = xup + (xleft - xup) * (ymid - yup) / (yleft - yup); } + if (color1 == color2 && color2 == color3) + { + // fast path: the triangle is all the same color + for (int y = yup; y < ymax; y++) + { + if (y < 0) continue; + if (y >= swr->height) break; + + int x1 = xup, x2 = xup; + if (y <= ymid) + { + // Lines: between up and mid, and between up and max + if (ymid != yup) + x1 = xup + (xmid - xup) * (y - yup) / (ymid - yup); + + if (ymid != yup) + x2 = xup + (xmid2 - xup) * (y - yup) / (ymid - yup); + } + else + { + // Lines: between mid and max, and between up and max + if (ymax != yup) + x1 = xup + (xmax - xup) * (y - yup) / (ymax - yup); + + if (ymax != ymid) + x2 = xmid + (xmax - xmid) * (y - ymid) / (ymax - ymid); + } + + if (x1 >= x2) { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + + if (x1 < swr->portX) x1 = swr->portX; + if (x1 >= swr->maxX) continue; + if (x2 < swr->portX) continue; + if (x2 >= swr->maxX) x2 = swr->maxX - 1; + if (x1 > x2) continue; + + uintpixel_t* line = &swr->fb[y * swr->width]; + for (int x = x1; x < x2; x++) { + alphaBlend(&line[x], color1, srcalpha, invalpha); + } + } + + return; + } + + // slow path: the triangle is all three different colors + + int area = (xleft - xup) * (yright - yup) - (yleft - yup) * (xright - xup); + if (area == 0) { + logDebug("SWR: Area is 0, returning early"); + return; + } + + // tried making this 32-bit and it didn't work. maybe another day. + int64_t areaReciprocal = ((int64_t) 65535 << 16) / area; for (int y = yup; y < ymax; y++) { if (y < 0) continue; @@ -571,8 +630,27 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft if (x1 > x2) continue; uintpixel_t* line = &swr->fb[y * swr->width]; - for (int x = x1; x < x2; x++) { - alphaBlend(&line[x], color1, srcalpha, invalpha); + + int e_up = (xleft - x1) * (yright - y) - (yleft - y) * (xright - x1); + int e_left = (xright - x1) * (yup - y) - (yright - y) * (xup - x1); + int d_up = (yleft - yright); + int d_left = (yright - yup); + + for (int x = x1; x < x2; x++) + { + int w1 = (int)((e_up * areaReciprocal) >> 16); + int w2 = (int)((e_left * areaReciprocal) >> 16); + + if (w1 < 0) w1 = 0; + if (w1 > 65535) w1 = 65535; + if (w2 < 0) w2 = 0; + if (w2 > 65535 - w1) w2 = 65535 - w1; + + uintpixel_t blended = swrThreeWayBlend(color1, color2, color3, w1, w2, 65535 - w1 - w2); + alphaBlend(&line[x], blended, srcalpha, invalpha); + + e_up += d_up; + e_left += d_left; } } } diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index 9d55130c9..2d47b88f9 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -4,6 +4,20 @@ #include "defines.h" #include "pixel_convert.h" +// Define if you want dithered alpha blending and triangle color blending. +// It might be a good bit faster than doing blending the proper way. +//#define SW_DITHERED_BLENDING + +// Define if you want tinting to be implemented inaccurately +//#define SW_INACCURATE_TINTING + +// Random number generator to be used for 8-bpp blending operations. +FORCE_INLINE int fastRandomIsh() +{ + static uint32_t rng = 1; + return rng = (uint64_t)rng * 48271 % 0x7fffffff; +} + // Check if a pixel is opaque. // // Later, this should be changed to perform full alpha-blending @@ -20,11 +34,22 @@ FORCE_INLINE bool opaque(uintpixel_t color) // Multiplies a color value (`color`) by another color value (`tintColor`). FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) { -#if PIXEL_SIZE == 32 - Pixel32ARGB x, y; - +#if PIXEL_SIZE == 8 + if (tintColor == 0xFF || tintColor == PXL_TRANSPARENT) + return color; +#elif PIXEL_SIZE == 16 + if ((tintColor & 0x7FFF) == 0x7FFF) + return color; +#else if ((tintColor & 0xFFFFFF) == 0xFFFFFF) return color; +#endif + +#if PIXEL_SIZE == 8 || defined SW_INACCURATE_TINTING + // fast but probably doesn't really work all that well + return color & tintColor; +#elif PIXEL_SIZE == 32 + Pixel32ARGB x, y; x.l = color; y.l = tintColor; @@ -34,9 +59,6 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) x.p.r = (int)x.p.r * y.p.r / 255; return x.l; #elif PIXEL_SIZE == 16 - if ((tintColor & 0x7FFF) == 0x7FFF) - return color; - int tcb = tintColor & 0x1F; int tcg = (tintColor >> 5) & 0x1F; int tcr = (tintColor >> 10) & 0x1F; @@ -50,12 +72,6 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) cg = (cg * tcg) / 32; cr = (cr * tcr) / 32; return ca | cb | (cg << 5) | (cr << 10); -#elif PIXEL_SIZE == 8 - // fast but hacky - if (tintColor == 0xFF || tintColor == PXL_TRANSPARENT) - return color; - - return color & tintColor; #endif } @@ -82,7 +98,14 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int srcalp return; #endif -#if PIXEL_SIZE == 32 +#if PIXEL_SIZE == 8 || defined SW_DITHERED_BLENDING + if (srcalpha < 240) { + if ((fastRandomIsh() & 0xFF) >= srcalpha) + return; + } + + *dcolor = scolor; +#elif PIXEL_SIZE == 32 Pixel32ARGB dc, sc; dc.l = *dcolor; sc.l = scolor; @@ -120,29 +143,16 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int srcalp dcg = (dcg * dstalpha + scg * srcalpha) >> 8; dcb = (dcb * dstalpha + scb * srcalpha) >> 8; - //clamp to 0 + // clamp to 0 dcr &= ((-dcr) >> 31); dcg &= ((-dcg) >> 31); dcb &= ((-dcb) >> 31); - //clamp to 255 + // clamp to 255 dcr |= ((signed char)(dcr >> 1) >> 7); dcg |= ((signed char)(dcg >> 1) >> 7); dcb |= ((signed char)(dcb >> 1) >> 7); *dcolor = 0x8000 | dcb | (dcg << 5) | (dcr << 10); -#else - if (srcalpha < 240) { - static int alphaApproximationThingy = 0; - alphaApproximationThingy += 1339; - if (alphaApproximationThingy > 601000) - alphaApproximationThingy = 0; - - //gotta love that RNG - if ((alphaApproximationThingy & 0xFF) >= srcalpha) - return; - } - - *dcolor = scolor; #endif } @@ -180,4 +190,36 @@ FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) } } +// Blends a pixel between three colors. +// frac means 0-65535 where 65535 means one. And frac1 + frac2 + frac3 MUST be equal to 65535. +FORCE_INLINE uintpixel_t swrThreeWayBlend(uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uint16_t frac1, uint16_t frac2, uint16_t frac3) +{ +#if PIXEL_SIZE == 8 || defined SW_DITHERED_BLENDING + int rng = fastRandomIsh() & 0xFFFF; + if (rng < frac1) return color1; else rng -= frac1; + if (rng < frac2) return color2; + (void) frac3; + return color3; +#elif PIXEL_SIZE == 32 + Pixel32ARGB x1, x2, x3, out; + x1.l = color1; + x2.l = color2; + x3.l = color3; + out.p.r = (x1.p.r * frac1 + x2.p.r * frac2 + x3.p.r * frac3) >> 16; + out.p.g = (x1.p.g * frac1 + x2.p.g * frac2 + x3.p.g * frac3) >> 16; + out.p.b = (x1.p.b * frac1 + x2.p.b * frac2 + x3.p.b * frac3) >> 16; + out.p.a = x1.p.a; + return out.l; +#elif PIXEL_SIZE == 16 + int c1r = color1 & 0x1F, c1g = (color2 >> 5) & 0x1F, c1b = (color3 >> 10) & 0x1F; + int c2r = color2 & 0x1F, c2g = (color2 >> 5) & 0x1F, c2b = (color3 >> 10) & 0x1F; + int c3r = color3 & 0x1F, c3g = (color2 >> 5) & 0x1F, c3b = (color3 >> 10) & 0x1F; + int ca = color1 & 0x8000; + int cr = (c1r * frac1 + c2r * frac2 + c3r * frac3) >> 16; + int cg = (c1g * frac1 + c2g * frac2 + c3g * frac3) >> 16; + int cb = (c1b * frac1 + c2b * frac2 + c3b * frac3) >> 16; + return ca | cb | (cg << 5) | (cr << 10); +#endif +} + #endif//_SW_PIXEL_CALC_H diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index f949b5955..063ddaac1 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -85,6 +85,11 @@ static void SWRenderer_endFrameEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; assert(!swr->drawingToSurface); + + + // DEBUG + swrDrawTriangle(renderer, 300, 20, 20, 300, 580, 400, 0xFF0000, 0x00FF00, 0x0000FF, 1.0f); + platformSetNextFramebuffer(swr->fb, swr->width, swr->height); } diff --git a/src/utils.h b/src/utils.h index 67ec2863b..f697c5b2c 100644 --- a/src/utils.h +++ b/src/utils.h @@ -254,7 +254,7 @@ static inline void dropMappedRange(uint8_t *base, size_t off, size_t len) { static int checked = 0; if (!checked) { HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll"); - if (hKernel32) pDiscardVirtualMemory = (DiscardVirtualMemory_t)GetProcAddress(hKernel32, "DiscardVirtualMemory"); + if (hKernel32) pDiscardVirtualMemory = (DiscardVirtualMemory_t) (void*) GetProcAddress(hKernel32, "DiscardVirtualMemory"); checked = 1; } if (pDiscardVirtualMemory != nullptr) pDiscardVirtualMemory((PVOID)(base + off), (size_t)len); From 65f85b912464925388d0b95c7bd920021f095f06 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 18:17:47 +0300 Subject: [PATCH 096/130] * Don't forget to remove that triangle --- src/sw/sw_renderer.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 063ddaac1..7cd819613 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -86,10 +86,6 @@ static void SWRenderer_endFrameEnd(Renderer* renderer) SWRenderer* swr = (SWRenderer*) renderer; assert(!swr->drawingToSurface); - - // DEBUG - swrDrawTriangle(renderer, 300, 20, 20, 300, 580, 400, 0xFF0000, 0x00FF00, 0x0000FF, 1.0f); - platformSetNextFramebuffer(swr->fb, swr->width, swr->height); } From c05162117bd035e52baf72cbd35efa733c3b7181 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 18:49:40 +0300 Subject: [PATCH 097/130] * Add a slightly worse but faster RNG --- src/sw/sw_pixel_calc.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index 2d47b88f9..b8036ecba 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -14,8 +14,11 @@ // Random number generator to be used for 8-bpp blending operations. FORCE_INLINE int fastRandomIsh() { - static uint32_t rng = 1; - return rng = (uint64_t)rng * 48271 % 0x7fffffff; + static int rng = 0; + rng += 1339; + if (rng > 601000) + rng = 0; + return rng; } // Check if a pixel is opaque. From c5a1d1b9fc2c326d01e9ddd82ab5788748c0c072 Mon Sep 17 00:00:00 2001 From: Emma Date: Mon, 14 Sep 2026 17:53:28 +0100 Subject: [PATCH 098/130] Expand common function for vertex draw and primitive begin texture (#537) * vertex draw common * primitive begin texture common * fix primitive begin func * fix declarations --- GameMaker-HTML5 | 1 + src/gl/gl_renderer.c | 14 ++------------ src/gl_common/gl_common.c | 28 +++++++++++++++------------- src/gl_common/gl_common.h | 14 +++++++------- src/gl_legacy/gl_legacy_renderer.c | 14 ++------------ 5 files changed, 27 insertions(+), 44 deletions(-) create mode 160000 GameMaker-HTML5 diff --git a/GameMaker-HTML5 b/GameMaker-HTML5 new file mode 160000 index 000000000..8a7ea0790 --- /dev/null +++ b/GameMaker-HTML5 @@ -0,0 +1 @@ +Subproject commit 8a7ea0790cc920cb1b760ca4d474ab9e4cad0b1a diff --git a/src/gl/gl_renderer.c b/src/gl/gl_renderer.c index 53e285186..a9799477d 100644 --- a/src/gl/gl_renderer.c +++ b/src/gl/gl_renderer.c @@ -401,15 +401,9 @@ static void glPrimitiveBeginTexture( int32_t texture ) { GLRenderer* gl = (GLRenderer*)renderer; - glPrimitiveBegin(renderer, primitiveType); - GLuint texId = 0; glResolvePrimitiveTexture(gl, texture, &texId); - - GLCommon_primitiveBeginTexture( - &gl->currentPrimitive, primitiveType, - gl->whiteTexture, texId - ); + GLCommon_primitiveBeginTexture(gl, primitiveType, texId); } static void glPrimitiveEnd(Renderer* renderer) { @@ -489,16 +483,12 @@ static void glDrawVertex(Renderer* renderer, float x, float y, float z, uint32_t gl->currentPrimitive.vertexCount = 0; } - GlVertex* vert = &gl->vertexData[gl->currentPrimitive.vertexCount]; - GLCommon_drawVertex( - vert, + gl, x, y, z, color, alpha, u, v ); - - gl->currentPrimitive.vertexCount++; } // ===[ Vtable Implementations ]=== diff --git a/src/gl_common/gl_common.c b/src/gl_common/gl_common.c index ac714df6d..eb42b5f79 100644 --- a/src/gl_common/gl_common.c +++ b/src/gl_common/gl_common.c @@ -232,24 +232,21 @@ void GlPrimitive_reset(GlPrimitive* primitive) { primitive->hasTexture = false; } -void GLCommon_primitiveBegin(GlPrimitive* primitive, int32_t type, int32_t textureId) { +static void _primitiveBeginEx(GlPrimitive* primitive, int32_t type, GLuint textureId, GLuint fallbackTexture) { primitive->type = type; primitive->vertexCount = 0; - primitive->textureId = textureId; primitive->hasTexture = (textureId != 0); + primitive->textureId = primitive->hasTexture + ? textureId + : fallbackTexture; } -void GLCommon_primitiveBeginTexture( - GlPrimitive* primitive, int32_t primitiveType, - GLuint whiteTexture, GLuint resolvedTexture -) { - primitive->type = primitiveType; - primitive->vertexCount = 0; +void GLCommon_primitiveBegin(GlPrimitive* primitive, int32_t type, int32_t textureId) { + _primitiveBeginEx(primitive, type, textureId, 0); +} - primitive->hasTexture = resolvedTexture != 0; - primitive->textureId = primitive->hasTexture - ? resolvedTexture - : whiteTexture; +void GLCommon_primitiveBeginTexture(GLRenderer* gl, int32_t primitiveType, GLuint resolvedTexture) { + _primitiveBeginEx(&gl->currentPrimitive, primitiveType, resolvedTexture, gl->whiteTexture); } bool GLCommon_primitivePrepare( @@ -277,11 +274,14 @@ bool GLCommon_primitivePrepare( } void GLCommon_drawVertex( - GlVertex* vertex, + GLRenderer* gl, float x, float y, float z, uint32_t color, float alpha, float u, float v ) { + int32_t vertexCount = gl->currentPrimitive.vertexCount; + GlVertex* vertex = &gl->vertexData[vertexCount]; + vertex->x = x; vertex->y = y; vertex->z = z; @@ -293,6 +293,8 @@ void GLCommon_drawVertex( vertex->g = (uint8_t)BGR_G(color); vertex->b = (uint8_t)BGR_B(color); vertex->a = floatToUnormByte(alpha); + + gl->currentPrimitive.vertexCount++; } // ===[ Debug UI font (drawTextUI) ]=== diff --git a/src/gl_common/gl_common.h b/src/gl_common/gl_common.h index db9488995..f29954621 100644 --- a/src/gl_common/gl_common.h +++ b/src/gl_common/gl_common.h @@ -7,6 +7,9 @@ #include "data_win.h" #include "debug_font/debug_font.h" +struct GLRenderer; +typedef struct GLRenderer GLRenderer; + #if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || defined(__SWITCH__) #include #elif PLATFORM_PS3 @@ -104,16 +107,13 @@ typedef struct { void GlPrimitive_reset(GlPrimitive* primitive); void GLCommon_primitiveBegin(GlPrimitive* primitive, int32_t type, int32_t textureId); -void GLCommon_primitiveBeginTexture( - GlPrimitive* primitive, int32_t primitiveType, - GLuint whiteTexture, GLuint resolvedTexture -); +void GLCommon_primitiveBeginTexture(GLRenderer* gl, int32_t primitiveType, GLuint resolvedTexture); bool GLCommon_primitivePrepare( GlPrimitive* primitive, GLuint whiteTexture, GLenum* mode, GLuint* textureId ); void GLCommon_drawVertex( - GlVertex* vertex, + GLRenderer* gl, float x, float y, float z, uint32_t color, float alpha, float u, float v @@ -157,7 +157,7 @@ enum GlMode { GL_MODE_MODERN = 1 }; -typedef struct { +struct GLRenderer { Renderer base; // Must be first field for struct embedding enum GlMode glMode; @@ -204,6 +204,6 @@ typedef struct { bool alphaTestEnable; float alphaTestRef; -} GLRenderer; +}; #endif /* _BS_GL_COMMON_H_ */ diff --git a/src/gl_legacy/gl_legacy_renderer.c b/src/gl_legacy/gl_legacy_renderer.c index e5bcd274b..ab04ff6c1 100644 --- a/src/gl_legacy/gl_legacy_renderer.c +++ b/src/gl_legacy/gl_legacy_renderer.c @@ -469,15 +469,9 @@ static void glPrimitiveBeginTexture( int32_t texture ) { GLRenderer* gl = (GLRenderer*)renderer; - glPrimitiveBegin(renderer, primitiveType); - GLuint texId = 0; glResolvePrimitiveTexture(gl, texture, &texId); - - GLCommon_primitiveBeginTexture( - &gl->currentPrimitive, primitiveType, - gl->whiteTexture, texId - ); + GLCommon_primitiveBeginTexture(gl, primitiveType, texId); } static void glPrimitiveEnd(Renderer* renderer) { @@ -514,16 +508,12 @@ static void glDrawVertex(Renderer* renderer, float x, float y, float z, uint32_t GLRenderer* gl = (GLRenderer*) renderer; legacyPrimitiveEnsureCapacity(gl, gl->currentPrimitive.vertexCount + 1); - GlVertex* vert = &gl->vertexData[gl->currentPrimitive.vertexCount]; - GLCommon_drawVertex( - vert, + gl, x, y, z, color, alpha, u, v ); - - gl->currentPrimitive.vertexCount++; } static void glDrawVertexBuffer(MAYBE_UNUSED Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture, int32_t offset, int32_t number) { From 56b85526982dd078f830fdd862daedf0c505f3ec Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 21:56:48 +0300 Subject: [PATCH 099/130] Add primitive drawing + various blend mode fixes --- src/sw/pixel_convert.h | 13 +- src/sw/sw_drawing.c | 43 +++--- src/sw/sw_drawing.h | 1 + src/sw/sw_pixel_calc.h | 12 ++ src/sw/sw_renderer.c | 273 +++++++++++++++++++++++++++++++---- src/sw/sw_renderer_private.h | 13 ++ 6 files changed, 306 insertions(+), 49 deletions(-) diff --git a/src/sw/pixel_convert.h b/src/sw/pixel_convert.h index fa0195aec..43adad81b 100644 --- a/src/sw/pixel_convert.h +++ b/src/sw/pixel_convert.h @@ -15,7 +15,7 @@ typedef uint8_t uintpixel_t; #error "Unknown pixel size!" #endif -// The native format coming out of GameMaker Studio's assets. +// The native format coming out of GameMaker Studio. typedef union { struct { @@ -82,6 +82,17 @@ uintpixel_t swrConvertPixelBase(uint32_t gmPixel) #endif } +FORCE_INLINE float swrGetAlpha(uintpixel_t pixel) +{ +#if PIXEL_SIZE == 8 + return pixel == PXL_TRANSPARENT ? 0.0 : 1.0f; +#elif PIXEL_SIZE == 16 + return (pixel & 0x8000) ? 1.0f : 0.0f; +#else + return ((pixel & 0xFF000000) >> 24) / 255.0f; +#endif +} + #if PIXEL_SIZE == 32 #define TRANSPARENT_MASK 0xFF000000 #elif PIXEL_SIZE == 16 diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 9b57a67a4..b691e7046 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -18,7 +18,7 @@ SwrFontState; // ==== Internal functions ==== -FORCE_INLINE void swrPlotPixel(Renderer* renderer, int x, int y, uintpixel_t color, int srcalpha, int dstalpha) +FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int srcalpha, int dstalpha) { SWRenderer* swr = (SWRenderer*) renderer; @@ -192,7 +192,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M } #endif - swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, srcalpha, invalpha); while (x < xe) { @@ -218,7 +218,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M color = resultPixel.l; #endif - swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, srcalpha, invalpha); } } else @@ -242,7 +242,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M } #endif - swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, srcalpha, invalpha); while (y < ye) { @@ -268,7 +268,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M color = resultPixel.l; #endif - swrPlotPixel(renderer, x, y, color, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, srcalpha, invalpha); } } } @@ -511,9 +511,7 @@ static void swrDrawSpriteRotatedInternal( static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, int alpha) { - // TODO: update this - (void) color2; - (void) color3; + logDebug("swrDrawTriangleInternal: Coords: (%d,%d) (%d,%d) (%d,%d)\n", xup, yup, xleft, yleft, xright, yright); int srcalpha = swrCalcSrcAlpha(swr, alpha); int invalpha = swrCalcDstAlpha(swr, alpha); @@ -538,7 +536,6 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft // fast path: the triangle is all the same color for (int y = yup; y < ymax; y++) { - if (y < 0) continue; if (y >= swr->height) break; int x1 = xup, x2 = xup; @@ -573,9 +570,11 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft if (x2 >= swr->maxX) x2 = swr->maxX - 1; if (x1 > x2) continue; - uintpixel_t* line = &swr->fb[y * swr->width]; - for (int x = x1; x < x2; x++) { - alphaBlend(&line[x], color1, srcalpha, invalpha); + if (y >= 0) { + uintpixel_t* line = &swr->fb[y * swr->width]; + for (int x = x1; x < x2; x++) { + alphaBlend(&line[x], color1, srcalpha, invalpha); + } } } @@ -586,7 +585,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft int area = (xleft - xup) * (yright - yup) - (yleft - yup) * (xright - xup); if (area == 0) { - logDebug("SWR: Area is 0, returning early"); + logDebug("SWR: Area is 0, returning early. Coords: (%d,%d) (%d,%d) (%d,%d)\n", xup, yup, xleft, yleft, xright, yright); return; } @@ -594,7 +593,6 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft int64_t areaReciprocal = ((int64_t) 65535 << 16) / area; for (int y = yup; y < ymax; y++) { - if (y < 0) continue; if (y >= swr->height) break; int x1 = xup, x2 = xup; @@ -629,6 +627,8 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft if (x2 >= swr->maxX) x2 = swr->maxX - 1; if (x1 > x2) continue; + if (y < 0) continue; + uintpixel_t* line = &swr->fb[y * swr->width]; int e_up = (xleft - x1) * (yright - y) - (yleft - y) * (xright - x1); @@ -746,13 +746,12 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor return true; // restore the original framebuffer - logInfo("back to original framebuffer\n"); + logDebug("swr: back to original framebuffer (%d)\n", targetSurfaceId); swr->drawingToSurface = false; swr->fb = swr->mainFb; swr->width = swr->mainWidth; swr->height = swr->mainHeight; swr->fbPitch = swr->mainPitch; - swr->blendMode = bm_normal; swr->currentSurfaceIndex = -1; swr->writeMask = WRITE_MASK_ALL; @@ -789,7 +788,6 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor swr->mainWidth = swr->width; swr->mainHeight = swr->height; swr->mainPitch = swr->fbPitch; - swr->blendMode = bm_normal; // and the old transform swr->lastViewX = swr->viewX; @@ -814,7 +812,6 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor swr->height = surface->height; swr->fbPitch = surface->width; swr->drawingToSurface = true; - swr->blendMode = bm_normal; swr->currentSurfaceIndex = targetSurfaceId; swr->writeMask = WRITE_MASK_ALL; @@ -824,11 +821,19 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor swr->maxY = swr->viewH = swr->portH = surface->height; swr->scaleX = swr->scaleY = 1.0f; - logInfo("switching to surface %p, fb %p, %dx%d\n", surface, swr->fb, swr->width, swr->height); + logDebug("swr: switching to surface %d -> %p, fb %p, %dx%d\n", targetSurfaceId, surface, swr->fb, swr->width, swr->height); return true; } +void swrPlotPixel(Renderer* renderer, float x, float y, uint32_t color, float alpha) +{ + SWRenderer *swr = (SWRenderer*) renderer; + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + swrPlotPixel_(renderer, (float) x, (float) y, color, srcalpha, invalpha); +} + void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha) { SWRenderer *swr = (SWRenderer*) renderer; diff --git a/src/sw/sw_drawing.h b/src/sw/sw_drawing.h index 5dee54f5c..50fcc30ca 100644 --- a/src/sw/sw_drawing.h +++ b/src/sw/sw_drawing.h @@ -2,6 +2,7 @@ #define _SW_DRAWING_H bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView); +void swrPlotPixel(Renderer* renderer, float x, float y, uintpixel_t color, float alpha); void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha); void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, uintpixel_t color2, float alpha); void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha); diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index b8036ecba..d251d0c98 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -11,6 +11,10 @@ // Define if you want tinting to be implemented inaccurately //#define SW_INACCURATE_TINTING +// Define if you want to disable support for additional blend modes and only support bm_normal. +// Should be set if you enable SW_DITHERED_BLENDING. +//#define SW_NO_BLEND_MODE_SUPPORT + // Random number generator to be used for 8-bpp blending operations. FORCE_INLINE int fastRandomIsh() { @@ -168,6 +172,9 @@ FORCE_INLINE int swrIntAlpha(float alphaf) // Calculates the source alpha for a pixel based on the current blend mode. FORCE_INLINE int swrCalcSrcAlpha(SWRenderer* swr, int alpha) { +#ifdef SW_NO_BLEND_MODE_SUPPORT + return alpha; +#else switch (swr->blendMode) { default: @@ -177,11 +184,15 @@ FORCE_INLINE int swrCalcSrcAlpha(SWRenderer* swr, int alpha) case bm_subtract: return -alpha; } +#endif } // Calculates the destination alpha for a pixel based on the current blend mode. FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) { +#ifdef SW_NO_BLEND_MODE_SUPPORT + return 256 - alpha; +#else switch (swr->blendMode) { default: @@ -191,6 +202,7 @@ FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) case bm_subtract: return 256; } +#endif } // Blends a pixel between three colors. diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 7cd819613..bbf8438ea 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -7,8 +7,14 @@ #include "sw_renderer_private.h" +#define MAX_TRIS 4096 +#define VERTICES_PER_TRIANGLE 3 +#define VERTICES_PER_QUAD 4 + void platformSetNextFramebuffer(uintpixel_t* framebuffer, int width, int height); +static void SWRenderer_gpuSetColorWriteEnable(Renderer* renderer, bool red, bool green, bool blue, bool alpha); + static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) { SWRenderer* swr = (SWRenderer*) renderer; @@ -41,6 +47,13 @@ static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) dataWin->tpag.items[i].texturePageId = -1; } + // Allocate vertex data for primitive support + swr->maxVertexCount = MAX_TRIS * VERTICES_PER_QUAD; + swr->vertexData = (SWVertex*) safeCalloc(swr->maxVertexCount, sizeof(SWVertex)); + swr->vertexCount = 0; + swr->primitiveType = 0; + swr->primitiveBegun = false; + logInfo("SWRenderer initialized.\n"); } @@ -71,6 +84,8 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam swr->width = windowW; swr->height = windowH; } + + logDebug("SWRenderer_beginFrame\n"); } // This used to be just one, "endFrame". Not sure what the difference is. @@ -86,7 +101,11 @@ static void SWRenderer_endFrameEnd(Renderer* renderer) SWRenderer* swr = (SWRenderer*) renderer; assert(!swr->drawingToSurface); + logDebug("SWRenderer_endFrameEnd\n"); + platformSetNextFramebuffer(swr->fb, swr->width, swr->height); + + swr->primitiveOverflow = false; } static void SWRenderer_beginView(Renderer* renderer, int32_t viewX, int32_t viewY, int32_t viewW, int32_t viewH, @@ -527,12 +546,6 @@ static void SWRenderer_drawSurfaceTiled(Renderer* renderer, int32_t surfaceID, f } } -static void SWRenderer_flush(Renderer* renderer) -{ - (void)renderer; - UNIMP(); -} - static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alpha) { SWRenderer* swr = (SWRenderer*) renderer; @@ -565,30 +578,13 @@ static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alp } } -static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) -{ - //UNIMP(); - //(void)renderer; (void)mode; - - SWRenderer* swr = (SWRenderer*) renderer; - swr->blendMode = mode; - - //if (mode != bm_normal && mode != bm_add && mode != bm_subtract) - { - logWarn("swr: unsupported blend mode: %d\n", mode); - } -} - -static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, int32_t dfactor, int32_t sfactor_alpha, int32_t dfactor_alpha) -{ - UNIMP(); - (void)renderer; (void)sfactor; (void)dfactor; (void)sfactor_alpha; (void)dfactor_alpha; -} - static void SWRenderer_gpuSetBlendEnable(Renderer* renderer, bool enable) { UNIMP(); - (void)renderer; (void)enable; + + SWRenderer* swr = (SWRenderer*) renderer; + if (!enable) + swr->blendMode = bm_normal; } static void SWRenderer_gpuSetAlphaTestEnable(Renderer* renderer, bool enable) @@ -603,15 +599,40 @@ static void SWRenderer_gpuSetAlphaTestRef(Renderer* renderer, uint8_t ref) (void)renderer; (void)ref; } +static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) +{ + // these are the only ones we support right now + if (mode != bm_normal && mode != bm_add && mode != bm_subtract) { + logWarn("swr: unsupported blend mode %d\n", mode); + mode = bm_normal; + } + +#ifdef SW_NO_BLEND_MODE_SUPPORT + mode = bm_normal; +#endif + + SWRenderer* swr = (SWRenderer*) renderer; + swr->blendMode = mode; + + logDebug("swr: switching to blend mode %d\n", mode); + + if (swr->usingAlphaBlendState) + { + swr->usingAlphaBlendState = false; + SWRenderer_gpuSetColorWriteEnable(renderer, true, true, true, true); + } +} + static void SWRenderer_gpuSetColorWriteEnable(Renderer* renderer, bool red, bool green, bool blue, bool alpha) { SWRenderer* swr = (SWRenderer*) renderer; if (!swr->drawingToSurface) { - logWarn("swr: gpuSetColorWriteEnable not supported for main framebuffer"); + logWarn("swr: gpuSetColorWriteEnable not supported for main framebuffer\n"); return; } + logWarn("SWRenderer_gpuSetColorWriteEnable(%d, %d, %d, %d)\n", red, green, blue, alpha); SWSurface* currSurf = swr->surfaces[swr->currentSurfaceIndex]; swrCommitShadowWritesToSurfaceIfNeeded(swr, currSurf); @@ -644,7 +665,7 @@ static void SWRenderer_gpuGetColorWriteEnable(Renderer* renderer, bool* red, boo SWRenderer* swr = (SWRenderer*) renderer; if (!swr->drawingToSurface) { - logWarn("swr: gpuGetColorWriteEnable not supported for main framebuffer"); + logWarn("swr: gpuGetColorWriteEnable not supported for main framebuffer\n"); return; } @@ -654,6 +675,50 @@ static void SWRenderer_gpuGetColorWriteEnable(Renderer* renderer, bool* red, boo *alpha = (swr->writeMask & WRITE_MASK_ALPHA) != 0; } +static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, int32_t dfactor, int32_t sfactor_alpha, int32_t dfactor_alpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + if (sfactor == bm_src_alpha && dfactor == bm_one) { + swr->blendMode = bm_add; + } + else if (sfactor == bm_src_alpha && (dfactor == bm_dest_alpha || dfactor == bm_inv_src_alpha)) { + swr->blendMode = bm_normal; + } + /* else */ { + swr->blendMode = bm_normal; + logWarn("swr: unsupported ext blend mode combo: sfactor=%d dfactor=%d\n", sfactor, dfactor); + } + + // alpha handling now + bool unhandled_alpha = false; + if (swr->drawingToSurface && (sfactor_alpha == bm_dest_alpha && dfactor_alpha == bm_zero)) { + swr->usingAlphaBlendState = true; + SWRenderer_gpuSetColorWriteEnable(renderer, true, true, true, false); + } + else { + swr->usingAlphaBlendState = false; + SWRenderer_gpuSetColorWriteEnable(renderer, true, true, true, true); + unhandled_alpha = true; + } + + /* if (unhandled_alpha) */ { + logWarn("swr: unsupported ext blend mode combo: sfactoralpha=%d dfactoralpha=%d\n", sfactor_alpha, dfactor_alpha); + } +} + +static void SWRenderer_flush(Renderer* renderer) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + UNIMP(); + + if (swr->drawingToSurface) { + bool red, green, blue, alpha; + SWRenderer_gpuGetColorWriteEnable(renderer, &red, &green, &blue, &alpha); + SWRenderer_gpuSetColorWriteEnable(renderer, red, green, blue, alpha); + } +} + static bool SWRenderer_gpuGetBlendEnable(Renderer* renderer) { UNIMP(); @@ -1199,6 +1264,151 @@ static void SWRenderer_applyProjection(Renderer* renderer, const Matrix4f* world UNIMP(); } +static void SWRenderer_primitiveBegin(Renderer* renderer, int32_t primitiveType) +{ + SWRenderer* swr = (SWRenderer*) renderer; + swr->primitiveType = primitiveType; + swr->vertexCount = 0; + swr->primitiveBegun = true; + logDebug("swr: primitiveBegin(%d)\n", primitiveType); +} + +static void SWRenderer_primitiveBeginTexture(Renderer* renderer, int32_t primitiveType, int32_t texture) +{ + static bool shownWarning = false; + if (!shownWarning) { + shownWarning = true; + logError("SWR: Do not support primitiveBeginTexture. Redirect to primitiveBegin.\n"); + } + + (void) texture; + return SWRenderer_primitiveBegin(renderer, primitiveType); +} + +static void swrPrimitiveLine(Renderer* renderer, SWVertex* vtx0, SWVertex* vtx1) +{ + float alphaAvg = (swrGetAlpha(vtx0->color) + swrGetAlpha(vtx1->color)) / 2; + SWRenderer_drawLineColor(renderer, vtx0->x, vtx0->y, vtx1->x, vtx1->y, 1.0f, vtx0->color, vtx1->color, alphaAvg); +} + +static void swrPrimitiveTriangle(Renderer* renderer, SWVertex* vtx0, SWVertex* vtx1, SWVertex* vtx2) +{ + const bool bWireFrameMode = false; // for debugging + + float alphaAvg = (swrGetAlpha(vtx0->color) + swrGetAlpha(vtx1->color) + swrGetAlpha(vtx2->color)) / 3; + SWRenderer_drawTriangle(renderer, vtx0->x, vtx0->y, vtx1->x, vtx1->y, vtx2->x, vtx2->y, vtx0->color, vtx1->color, vtx2->color, alphaAvg, bWireFrameMode); +} + +static void SWRenderer_primitiveEnd(Renderer* renderer) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + logWarn("SWR: Ending with %d vertices, primitive type %d\n", swr->vertexCount, swr->primitiveType); + switch (swr->primitiveType) + { + case PRIMITIVE_POINTS: + { + for (int i = 0; i < swr->vertexCount; i++) + { + SWVertex *vtx = &swr->vertexData[i]; + swrPlotPixel(renderer, vtx->x, vtx->y, swrConvertPixel(vtx->color), swrGetAlpha(vtx->color)); + } + break; + } + case PRIMITIVE_LINES: + { + for (int i = 0; i + 1 < swr->vertexCount; i += 2) + swrPrimitiveLine(renderer, &swr->vertexData[i], &swr->vertexData[i + 1]); + + break; + } + case PRIMITIVE_LINE_STRIP: + { + for (int i = 0; i + 1 < swr->vertexCount; i++) + swrPrimitiveLine(renderer, &swr->vertexData[i], &swr->vertexData[i + 1]); + + break; + } + case PRIMITIVE_TRIANGLES: + { + for (int i = 0; i + 2 < swr->vertexCount; i += 3) + swrPrimitiveTriangle(renderer, &swr->vertexData[i], &swr->vertexData[i + 1], &swr->vertexData[i + 2]); + + break; + } + case PRIMITIVE_TRIANGLE_STRIP: + { + if (swr->vertexCount < 3) + break; + + for (int i = 0; i < swr->vertexCount; i++) + swrPrimitiveTriangle(renderer, &swr->vertexData[i], &swr->vertexData[i + 1], &swr->vertexData[i + 2]); + + break; + } + case PRIMITIVE_TRIANGLE_FAN: + { + for (int i = 1; i + 1 < swr->vertexCount; i += 2) + swrPrimitiveTriangle(renderer, &swr->vertexData[0], &swr->vertexData[i], &swr->vertexData[i + 1]); + + break; + } + default: + { + logError("SWR: Unimplemented primitive type %d", swr->primitiveType); + break; + } + } + + swr->primitiveBegun = false; + swr->vertexCount = 0; +} + +static void SWRenderer_drawVertex(Renderer* renderer, float x, float y, float z, uint32_t color, float alphaMod, float u, float v) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + if (swr->vertexCount >= swr->maxVertexCount) + { + // TODO: we could just expand this? + if (!swr->primitiveOverflow) + logError("SWR: Vertex overflow. Vertices will be ignored until the next primitiveEnd."); + + swr->primitiveOverflow = true; + } + + SWVertex* pVertex = &swr->vertexData[swr->vertexCount++]; + + Pixel32ABGR pixel; + pixel.l = color; + pixel.p.a = (uint8_t)(pixel.p.a * alphaMod); + + pVertex->color = swrConvertPixel(pixel.l); + pVertex->x = x; + pVertex->y = y; + + logDebug("swr: drawVertex(%f, %f)\n", x, y); + + // Texture mapping and the third dimension are not supported. + (void) z; + (void) u; + (void) v; +} + +static void SWRenderer_drawVertexBuffer(Renderer* renderer, VertexBuffer* buffer, int32_t primitive, int32_t texture, int32_t offset, int32_t count) +{ + // TODO + + (void) renderer; + (void) buffer; + (void) primitive; + (void) texture; + (void) offset; + (void) count; + + UNIMP(); +} + Renderer* SWRenderer_create(void) { SWRenderer* swr = (SWRenderer*) safeCalloc(1, sizeof(SWRenderer)); @@ -1266,6 +1476,11 @@ Renderer* SWRenderer_create(void) swrVtable.shaderIsCompiled = SWRenderer_shaderIsCompiled; swrVtable.shadersSupported = SWRenderer_shadersSupported; swrVtable.applyProjection = SWRenderer_applyProjection; + swrVtable.primitiveBegin = SWRenderer_primitiveBegin; + swrVtable.primitiveBeginTexture = SWRenderer_primitiveBeginTexture; + swrVtable.primitiveEnd = SWRenderer_primitiveEnd; + swrVtable.drawVertex = SWRenderer_drawVertex; + swrVtable.drawVertexBuffer = SWRenderer_drawVertexBuffer; swrVtable.drawTile = NULL; diff --git a/src/sw/sw_renderer_private.h b/src/sw/sw_renderer_private.h index d1f078160..bb7d40640 100644 --- a/src/sw/sw_renderer_private.h +++ b/src/sw/sw_renderer_private.h @@ -44,6 +44,11 @@ SWSurface; #define WRITE_MASK_BLUE (4) #define WRITE_MASK_ALPHA (8) +typedef struct { + float x, y; + uintpixel_t color; +} SWVertex; + typedef struct { Renderer base; @@ -86,10 +91,18 @@ typedef struct float defaultScaleX, defaultScaleY; int blendMode; + bool usingAlphaBlendState; // only used for surfaces. The application surface doesn't support these at the moment. int currentSurfaceIndex; int writeMask; + + SWVertex* vertexData; + int vertexCount; + int maxVertexCount; + int primitiveType; + bool primitiveBegun; + bool primitiveOverflow; } SWRenderer; From dd31c7647547be88ccbccad41d3fde161595ddd5 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 22:50:56 +0300 Subject: [PATCH 100/130] * Move configuration to a separate file. * Restructure alphaBlend in preparation for more proper bm_subtract support. --- src/sw/defines.h | 10 +--- src/sw/sw_config.h | 39 ++++++++++++++++ src/sw/sw_drawing.c | 44 ++++++++++------- src/sw/sw_pixel_calc.h | 104 ++++++++++++++++++++++------------------- 4 files changed, 121 insertions(+), 76 deletions(-) create mode 100644 src/sw/sw_config.h diff --git a/src/sw/defines.h b/src/sw/defines.h index 8c3072aef..989f3d61e 100644 --- a/src/sw/defines.h +++ b/src/sw/defines.h @@ -2,15 +2,7 @@ #define _SW_DEFINES_H #include "common.h" - -// CONFIG: Change the size of a pixel. -// -// 32-bit: 0xAARRGGBB -// 16-bit: 0b0RRRRRGGGGGBBBBB -// 8-bit: 0bBBGGGRRR -#define PIXEL_SIZE 32 -//#define PIXEL_SIZE 16 -//#define PIXEL_SIZE 8 +#include "sw_config.h" #if defined(__GNUC__) && (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)) #define LIKELY(cond) __builtin_expect(!!(cond), 1) diff --git a/src/sw/sw_config.h b/src/sw/sw_config.h new file mode 100644 index 000000000..9b76308a8 --- /dev/null +++ b/src/sw/sw_config.h @@ -0,0 +1,39 @@ +#ifndef __SW_CONFIG_H +#define __SW_CONFIG_H + +// -======- USER CONFIG START -======- + +// Change the bit-depth of the final image. Valid values are 8, 16, or 32. +#define PIXEL_SIZE 32 +//#define PIXEL_SIZE 16 +//#define PIXEL_SIZE 8 + +// Pixel formats for each mode: +// 32-bit: 0xAARRGGBB +// 16-bit: 0b0RRRRRGGGGGBBBBB +// 8-bit: 0bBBGGGRRR + +// Define if you want dithered alpha blending and triangle color blending. +// It might be a good bit faster than doing blending the proper way. +//#define SW_DITHERED_BLENDING + +// Define if you want tinting to be implemented inaccurately +//#define SW_INACCURATE_TINTING + +// Define if you want to disable support for additional blend modes and only support bm_normal. +// Should be set if you enable SW_DITHERED_BLENDING. +//#define SW_NO_BLEND_MODE_SUPPORT + +// -======- USER CONFIG END -======- + +// Disable blend mode support forcefully if we're on 8-bpp color +// or if we requested dithered blending +#if PIXEL_SIZE == 8 || defined SW_DITHERED_BLENDING + +#ifndef SW_NO_BLEND_MODE_SUPPORT +#define SW_NO_BLEND_MODE_SUPPORT +#endif // !SW_NO_BLEND_MODE_SUPPORT + +#endif // SW_DITHERED_BLENDING + +#endif // __SW_CONFIG_H diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index b691e7046..6daeb327d 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -18,14 +18,14 @@ SwrFontState; // ==== Internal functions ==== -FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int srcalpha, int dstalpha) +FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int blendmode, int srcalpha, int dstalpha) { SWRenderer* swr = (SWRenderer*) renderer; if (x < swr->portX || y < swr->portY) return; if (x >= swr->maxX || y >= swr->maxY) return; - alphaBlend(&swr->fb[y * swr->fbPitch + x], color, srcalpha, dstalpha); + alphaBlend(&swr->fb[y * swr->fbPitch + x], color, blendmode, srcalpha, dstalpha); } static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) @@ -40,6 +40,7 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe int srcalpha = swrCalcSrcAlpha(swr, alpha); int invalpha = swrCalcDstAlpha(swr, alpha); + int blendmode = swr->blendMode; #if PIXEL_SIZE == 32 if (color == color2) @@ -47,7 +48,7 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe { uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; for (int i = 0; i < dw; i++) - alphaBlend(&line[i], color, srcalpha, invalpha); + alphaBlend(&line[i], color, blendmode, srcalpha, invalpha); } #if PIXEL_SIZE == 32 else @@ -76,7 +77,7 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe rinit += rstep; ginit += gstep; binit += bstep; - alphaBlend(&line[i], resultPixel.l, srcalpha, invalpha); + alphaBlend(&line[i], resultPixel.l, blendmode, srcalpha, invalpha); } } #endif @@ -94,6 +95,7 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe int srcalpha = swrCalcSrcAlpha(swr, alpha); int invalpha = swrCalcDstAlpha(swr, alpha); + int blendmode = swr->blendMode; #if PIXEL_SIZE == 32 if (color == color2) @@ -102,7 +104,7 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe for (int i = 0; i < dh; i++) { uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - alphaBlend(&line[0], color, srcalpha, invalpha); + alphaBlend(&line[0], color, blendmode, srcalpha, invalpha); } } #if PIXEL_SIZE == 32 @@ -132,7 +134,7 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe rinit += rstep; ginit += gstep; binit += bstep; - alphaBlend(&line[0], resultPixel.l, srcalpha, invalpha); + alphaBlend(&line[0], resultPixel.l, blendmode, srcalpha, invalpha); } } #endif @@ -151,11 +153,13 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M return; } + SWRenderer* swr = (SWRenderer*) renderer; int dx = x2 - x1, dy = y2 - y1; int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; - int srcalpha = swrCalcSrcAlpha((SWRenderer*) renderer, alpha); - int invalpha = swrCalcDstAlpha((SWRenderer*) renderer, alpha); + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int invalpha = swrCalcDstAlpha(swr, alpha); + int blendmode = swr->blendMode; uintpixel_t color = color1; #if PIXEL_SIZE == 32 @@ -192,7 +196,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M } #endif - swrPlotPixel_(renderer, x, y, color, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); while (x < xe) { @@ -218,7 +222,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M color = resultPixel.l; #endif - swrPlotPixel_(renderer, x, y, color, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); } } else @@ -242,7 +246,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M } #endif - swrPlotPixel_(renderer, x, y, color, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); while (y < ye) { @@ -268,7 +272,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, M color = resultPixel.l; #endif - swrPlotPixel_(renderer, x, y, color, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); } } } @@ -346,6 +350,7 @@ static void swrDrawSpriteInternal( int srcalpha = swrCalcSrcAlpha(swr, alpha); int invalpha = swrCalcDstAlpha(swr, alpha); + int blendmode = swr->blendMode; if (sw == dw) { @@ -364,7 +369,7 @@ static void swrDrawSpriteInternal( { uintpixel_t pixel = srcline[xs]; if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), srcalpha, invalpha); + alphaBlend(&dstline[x], tint(tintColor, pixel), blendmode, srcalpha, invalpha); } } } @@ -386,7 +391,7 @@ static void swrDrawSpriteInternal( { uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; if (opaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), srcalpha, invalpha); + alphaBlend(&dstline[x], tint(tintColor, pixel), blendmode, srcalpha, invalpha); } } } @@ -467,6 +472,7 @@ static void swrDrawSpriteRotatedInternal( int srcalpha = swrCalcSrcAlpha(swr, alpha); int invalpha = swrCalcDstAlpha(swr, alpha); + int blendmode = swr->blendMode; for (int cy = minYc; cy < maxYc; cy++) { @@ -504,7 +510,7 @@ static void swrDrawSpriteRotatedInternal( uintpixel_t src = texture->buffer[ty * texture->width + tx]; if (opaque(src)) - alphaBlend(&dstline[cx], tint(tintColor, src), srcalpha, invalpha); + alphaBlend(&dstline[cx], tint(tintColor, src), blendmode, srcalpha, invalpha); } } } @@ -515,6 +521,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft int srcalpha = swrCalcSrcAlpha(swr, alpha); int invalpha = swrCalcDstAlpha(swr, alpha); + int blendmode = swr->blendMode; // Figure out the maximum Y extent of the triangle. // (Note that we know yup is the minimum.) @@ -573,7 +580,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft if (y >= 0) { uintpixel_t* line = &swr->fb[y * swr->width]; for (int x = x1; x < x2; x++) { - alphaBlend(&line[x], color1, srcalpha, invalpha); + alphaBlend(&line[x], color1, blendmode, srcalpha, invalpha); } } } @@ -647,7 +654,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft if (w2 > 65535 - w1) w2 = 65535 - w1; uintpixel_t blended = swrThreeWayBlend(color1, color2, color3, w1, w2, 65535 - w1 - w2); - alphaBlend(&line[x], blended, srcalpha, invalpha); + alphaBlend(&line[x], blended, blendmode, srcalpha, invalpha); e_up += d_up; e_left += d_left; @@ -831,7 +838,8 @@ void swrPlotPixel(Renderer* renderer, float x, float y, uint32_t color, float al SWRenderer *swr = (SWRenderer*) renderer; int srcalpha = swrCalcSrcAlpha(swr, alpha); int invalpha = swrCalcDstAlpha(swr, alpha); - swrPlotPixel_(renderer, (float) x, (float) y, color, srcalpha, invalpha); + int blendmode = swr->blendMode; + swrPlotPixel_(renderer, (float) x, (float) y, color, blendmode, srcalpha, invalpha); } void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha) diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index d251d0c98..e73165301 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -4,17 +4,6 @@ #include "defines.h" #include "pixel_convert.h" -// Define if you want dithered alpha blending and triangle color blending. -// It might be a good bit faster than doing blending the proper way. -//#define SW_DITHERED_BLENDING - -// Define if you want tinting to be implemented inaccurately -//#define SW_INACCURATE_TINTING - -// Define if you want to disable support for additional blend modes and only support bm_normal. -// Should be set if you enable SW_DITHERED_BLENDING. -//#define SW_NO_BLEND_MODE_SUPPORT - // Random number generator to be used for 8-bpp blending operations. FORCE_INLINE int fastRandomIsh() { @@ -91,18 +80,25 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) // // NOTE: Obviously I could use SIMD here, but old computers didn't have SIMD, and the code // runs fast enough on modern computers to not need to do SIMD. -FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int srcalpha, int dstalpha) +FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srcalpha, int dstalpha) { #if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 - // it's so significant here we might as well fill in the whole color - if (LIKELY(dstalpha < 3 && srcalpha > 253)) { - *dcolor = scolor; - return; + +#ifndef SW_NO_BLEND_MODE_SUPPORT + if (LIKELY(blendmode == bm_normal)) +#endif + { + // it's so significant here we might as well fill in the whole color + if (LIKELY(dstalpha < 2)) { + *dcolor = scolor; + return; + } + + // it's so insignificant here nobody will notice if we just don't... + if (UNLIKELY(dstalpha > 253)) + return; } - - // it's so insignificant here nobody will notice if we just don't... - if (UNLIKELY(srcalpha == 0)) - return; + #endif #if PIXEL_SIZE == 8 || defined SW_DITHERED_BLENDING @@ -112,30 +108,22 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int srcalp } *dcolor = scolor; -#elif PIXEL_SIZE == 32 + return; +#endif + + /* Extract pixel channels */ +#if PIXEL_SIZE == 32 Pixel32ARGB dc, sc; dc.l = *dcolor; sc.l = scolor; - int dcr = (dc.p.r * dstalpha + sc.p.r * srcalpha) >> 8; - int dcg = (dc.p.g * dstalpha + sc.p.g * srcalpha) >> 8; - int dcb = (dc.p.b * dstalpha + sc.p.b * srcalpha) >> 8; - - //clamp to 0 - dcr &= ((-dcr) >> 31); - dcg &= ((-dcg) >> 31); - dcb &= ((-dcb) >> 31); - //clamp to 255 - dcr |= ((signed char)(dcr >> 1) >> 7); - dcg |= ((signed char)(dcg >> 1) >> 7); - dcb |= ((signed char)(dcb >> 1) >> 7); - - dc.p.r = dcr; - dc.p.g = dcg; - dc.p.b = dcb; - dc.p.a = 0xFF; - - *dcolor = dc.l; + int scr = sc.p.r; + int scg = sc.p.g; + int scb = sc.p.b; + int dcr = dc.p.r; + int dcg = dc.p.g; + int dcb = dc.p.b; + int dca = 0xFF; #elif PIXEL_SIZE == 16 int scb = scolor & 0x1F; int scg = (scolor >> 5) & 0x1F; @@ -145,21 +133,39 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int srcalp int dcb = _dcolor & 0x1F; int dcg = (_dcolor >> 5) & 0x1F; int dcr = (_dcolor >> 10) & 0x1F; + int dca = 0xFF; +#endif + /* Perform the actual blending ops on them */ dcr = (dcr * dstalpha + scr * srcalpha) >> 8; dcg = (dcg * dstalpha + scg * srcalpha) >> 8; dcb = (dcb * dstalpha + scb * srcalpha) >> 8; - // clamp to 0 - dcr &= ((-dcr) >> 31); - dcg &= ((-dcg) >> 31); - dcb &= ((-dcb) >> 31); - // clamp to 255 - dcr |= ((signed char)(dcr >> 1) >> 7); - dcg |= ((signed char)(dcg >> 1) >> 7); - dcb |= ((signed char)(dcb >> 1) >> 7); +#ifndef SW_NO_BLEND_MODE_SUPPORT + /* Clamp them if needed */ + if (UNLIKELY(blendmode != bm_normal)) + { + //clamp to 0 + dcr &= ((-dcr) >> 31); + dcg &= ((-dcg) >> 31); + dcb &= ((-dcb) >> 31); + //clamp to 255 + dcr |= ((signed char)(dcr >> 1) >> 7); + dcg |= ((signed char)(dcg >> 1) >> 7); + dcb |= ((signed char)(dcb >> 1) >> 7); + } +#endif + + /* Then re-assemble the pixel. */ +#if PIXEL_SIZE == 32 + dc.p.r = dcr; + dc.p.g = dcg; + dc.p.b = dcb; + dc.p.a = dca; - *dcolor = 0x8000 | dcb | (dcg << 5) | (dcr << 10); + *dcolor = dc.l; +#elif PIXEL_SIZE == 16 + *dcolor = (dca ? 0x8000 : 0) | dcb | (dcg << 5) | (dcr << 10); #endif } From 7a18ec8306c83e4c32d54b63638749cd5ac31430 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 22:54:13 +0300 Subject: [PATCH 101/130] * Add support for bm_subtract properly. --- src/sw/sw_pixel_calc.h | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index e73165301..1d6882b27 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -120,6 +120,7 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendm int scr = sc.p.r; int scg = sc.p.g; int scb = sc.p.b; + int sca = sc.p.a; int dcr = dc.p.r; int dcg = dc.p.g; int dcb = dc.p.b; @@ -128,6 +129,7 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendm int scb = scolor & 0x1F; int scg = (scolor >> 5) & 0x1F; int scr = (scolor >> 10) & 0x1F; + int sca = (scolor & 0x8000) ? 255 : 0; uintpixel_t _dcolor = *dcolor; int dcb = _dcolor & 0x1F; @@ -136,10 +138,20 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendm int dca = 0xFF; #endif - /* Perform the actual blending ops on them */ - dcr = (dcr * dstalpha + scr * srcalpha) >> 8; - dcg = (dcg * dstalpha + scg * srcalpha) >> 8; - dcb = (dcb * dstalpha + scb * srcalpha) >> 8; + if (UNLIKELY(blendmode == bm_subtract)) + { + dcr = (dcr * (255 - scr)) >> 8; + dcg = (dcg * (255 - scg)) >> 8; + dcb = (dcb * (255 - scb)) >> 8; + dca = (dca * (255 - sca)) >> 8; + } + else + { + /* Perform the actual blending ops on them */ + dcr = (dcr * dstalpha + scr * srcalpha) >> 8; + dcg = (dcg * dstalpha + scg * srcalpha) >> 8; + dcb = (dcb * dstalpha + scb * srcalpha) >> 8; + } #ifndef SW_NO_BLEND_MODE_SUPPORT /* Clamp them if needed */ From f55afea72a152f4b994e584b12cc40a3701706db Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 23:37:47 +0300 Subject: [PATCH 102/130] * Improve pixel calculations in dithered mode + some cleanups. --- src/sw/sw_config.h | 25 ++++++++---- src/sw/sw_pixel_calc.h | 89 +++++++++++++++++++++++++++++------------- src/sw/sw_renderer.c | 10 ++--- 3 files changed, 83 insertions(+), 41 deletions(-) diff --git a/src/sw/sw_config.h b/src/sw/sw_config.h index 9b76308a8..43b7e48e1 100644 --- a/src/sw/sw_config.h +++ b/src/sw/sw_config.h @@ -20,19 +20,30 @@ // Define if you want tinting to be implemented inaccurately //#define SW_INACCURATE_TINTING -// Define if you want to disable support for additional blend modes and only support bm_normal. -// Should be set if you enable SW_DITHERED_BLENDING. -//#define SW_NO_BLEND_MODE_SUPPORT +// Define if you want to decrease the quality of additional blend modes and +// only fully support bm_normal. Automatically set if you enable SW_DITHERED_BLENDING. +//#define SW_BAD_BLEND_MODE_SUPPORT + +// Completely exclude bm_subtract support // -======- USER CONFIG END -======- +// Force enable dithered blending if in 8bpp mode. +#if PIXEL_SIZE == 8 + +#ifndef SW_DITHERED_BLENDING +#define SW_DITHERED_BLENDING +#endif // SW_DITHERED_BLENDING + +#endif // PIXEL_SIZE == 8 + // Disable blend mode support forcefully if we're on 8-bpp color // or if we requested dithered blending -#if PIXEL_SIZE == 8 || defined SW_DITHERED_BLENDING +#ifdef SW_DITHERED_BLENDING -#ifndef SW_NO_BLEND_MODE_SUPPORT -#define SW_NO_BLEND_MODE_SUPPORT -#endif // !SW_NO_BLEND_MODE_SUPPORT +#ifndef SW_BAD_BLEND_MODE_SUPPORT +#define SW_BAD_BLEND_MODE_SUPPORT +#endif // !SW_BAD_BLEND_MODE_SUPPORT #endif // SW_DITHERED_BLENDING diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index 1d6882b27..7c5ed14aa 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -80,36 +80,75 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) // // NOTE: Obviously I could use SIMD here, but old computers didn't have SIMD, and the code // runs fast enough on modern computers to not need to do SIMD. -FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srcalpha, int dstalpha) +// +// Another note: Wow, it' become a huge mess of ifdef's... +FORCE_INLINE +void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srcalpha, int dstalpha) { +#ifdef SW_DITHERED_BLENDING + /* Dithered blending does not use the dstalpha member */ + (void) dstalpha; +#endif + + /* If we didn't disable subtract support and are in dithered blending mode */ +#if defined SW_DITHERED_BLENDING +#ifndef SW_NO_SUBTRACT_SUPPORT + if (UNLIKELY(blendmode == bm_subtract)) + { + #if PIXEL_SIZE == 8 + srcalpha = scolor & 0x7; + srcalpha = (srcalpha << 5) | (srcalpha << 2) | (srcalpha >> 1); + #elif PIXEL_SIZE == 16 + srcalpha = scolor & 0x1F; + srcalpha = (srcalpha << 3) | (srcalpha >> 2); + #elif PIXEL_SIZE == 32 + srcalpha = scolor & 0xFF; + #endif + + if (UNLIKELY(srcalpha < 5)) + return; + if (UNLIKELY(srcalpha > 250)) + *dcolor = 0; + + if ((fastRandomIsh() & 0xFF) < srcalpha) + *dcolor = 0; + + return; + } +#endif // !SW_NO_SUBTRACT_SUPPORT +#endif // defined SW_DITHERED_BLENDING + #if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 -#ifndef SW_NO_BLEND_MODE_SUPPORT + /* Check extremely common cases in 32- and 16-bit modes */ +#ifndef SW_BAD_BLEND_MODE_SUPPORT if (LIKELY(blendmode == bm_normal)) #endif { // it's so significant here we might as well fill in the whole color - if (LIKELY(dstalpha < 2)) { + if (LIKELY(srcalpha > 253)) { *dcolor = scolor; return; } // it's so insignificant here nobody will notice if we just don't... - if (UNLIKELY(dstalpha > 253)) + if (UNLIKELY(srcalpha < 4)) return; } -#endif +#endif // PIXEL_SIZE == 32 || PIXEL_SIZE == 16 -#if PIXEL_SIZE == 8 || defined SW_DITHERED_BLENDING - if (srcalpha < 240) { + /* With dithered blending, randomly place the color, or don't */ +#ifdef SW_DITHERED_BLENDING + if (srcalpha < 240) + { if ((fastRandomIsh() & 0xFF) >= srcalpha) return; } *dcolor = scolor; - return; -#endif + +#else /* Extract pixel channels */ #if PIXEL_SIZE == 32 @@ -136,7 +175,7 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendm int dcg = (_dcolor >> 5) & 0x1F; int dcr = (_dcolor >> 10) & 0x1F; int dca = 0xFF; -#endif +#endif // PIXEL_SIZE if (UNLIKELY(blendmode == bm_subtract)) { @@ -153,7 +192,7 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendm dcb = (dcb * dstalpha + scb * srcalpha) >> 8; } -#ifndef SW_NO_BLEND_MODE_SUPPORT +#ifndef SW_BAD_BLEND_MODE_SUPPORT /* Clamp them if needed */ if (UNLIKELY(blendmode != bm_normal)) { @@ -166,7 +205,7 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendm dcg |= ((signed char)(dcg >> 1) >> 7); dcb |= ((signed char)(dcb >> 1) >> 7); } -#endif +#endif // SW_BAD_BLEND_MODE_SUPPORT /* Then re-assemble the pixel. */ #if PIXEL_SIZE == 32 @@ -179,6 +218,8 @@ FORCE_INLINE void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendm #elif PIXEL_SIZE == 16 *dcolor = (dca ? 0x8000 : 0) | dcb | (dcg << 5) | (dcr << 10); #endif + +#endif // SW_DITHERED_BLENDING } // Calculates an internal "alpha" value from GML-provided "alpha" values. @@ -190,26 +231,20 @@ FORCE_INLINE int swrIntAlpha(float alphaf) // Calculates the source alpha for a pixel based on the current blend mode. FORCE_INLINE int swrCalcSrcAlpha(SWRenderer* swr, int alpha) { -#ifdef SW_NO_BLEND_MODE_SUPPORT + // Here you would depend on swr->blendMode, but all + // of them return the same value right now. + (void) swr; return alpha; -#else - switch (swr->blendMode) - { - default: - return alpha; - case bm_add: - return alpha; - case bm_subtract: - return -alpha; - } -#endif } // Calculates the destination alpha for a pixel based on the current blend mode. FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) { -#ifdef SW_NO_BLEND_MODE_SUPPORT - return 256 - alpha; +#ifdef SW_DITHERED_BLENDING + /* Dithered blending does not use the dstalpha member */ + (void) swr; + (void) alpha; + return 0; #else switch (swr->blendMode) { @@ -227,7 +262,7 @@ FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) // frac means 0-65535 where 65535 means one. And frac1 + frac2 + frac3 MUST be equal to 65535. FORCE_INLINE uintpixel_t swrThreeWayBlend(uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uint16_t frac1, uint16_t frac2, uint16_t frac3) { -#if PIXEL_SIZE == 8 || defined SW_DITHERED_BLENDING +#if defined SW_DITHERED_BLENDING int rng = fastRandomIsh() & 0xFFFF; if (rng < frac1) return color1; else rng -= frac1; if (rng < frac2) return color2; diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index bbf8438ea..2bf78d5df 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -607,10 +607,6 @@ static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) mode = bm_normal; } -#ifdef SW_NO_BLEND_MODE_SUPPORT - mode = bm_normal; -#endif - SWRenderer* swr = (SWRenderer*) renderer; swr->blendMode = mode; @@ -684,7 +680,7 @@ static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, i else if (sfactor == bm_src_alpha && (dfactor == bm_dest_alpha || dfactor == bm_inv_src_alpha)) { swr->blendMode = bm_normal; } - /* else */ { + else { swr->blendMode = bm_normal; logWarn("swr: unsupported ext blend mode combo: sfactor=%d dfactor=%d\n", sfactor, dfactor); } @@ -701,7 +697,7 @@ static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, i unhandled_alpha = true; } - /* if (unhandled_alpha) */ { + if (unhandled_alpha) { logWarn("swr: unsupported ext blend mode combo: sfactoralpha=%d dfactoralpha=%d\n", sfactor_alpha, dfactor_alpha); } } @@ -1282,7 +1278,7 @@ static void SWRenderer_primitiveBeginTexture(Renderer* renderer, int32_t primiti } (void) texture; - return SWRenderer_primitiveBegin(renderer, primitiveType); + SWRenderer_primitiveBegin(renderer, primitiveType); } static void swrPrimitiveLine(Renderer* renderer, SWVertex* vtx0, SWVertex* vtx1) From 270d776d9f8bb34f52ead82eee115ad687d0e51f Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 23:39:59 +0300 Subject: [PATCH 103/130] * Rename some files in SW renderer --- src/sw/{defines.h => sw_defines.h} | 0 src/sw/sw_inlined.h | 2 +- src/sw/sw_pixel_calc.h | 4 ++-- src/sw/{pixel_convert.h => sw_pixel_convert.h} | 8 ++++---- src/sw/sw_renderer_private.h | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) rename src/sw/{defines.h => sw_defines.h} (100%) rename src/sw/{pixel_convert.h => sw_pixel_convert.h} (95%) diff --git a/src/sw/defines.h b/src/sw/sw_defines.h similarity index 100% rename from src/sw/defines.h rename to src/sw/sw_defines.h diff --git a/src/sw/sw_inlined.h b/src/sw/sw_inlined.h index 9bc8dd7ce..e37407a57 100644 --- a/src/sw/sw_inlined.h +++ b/src/sw/sw_inlined.h @@ -1,7 +1,7 @@ #ifndef _SW_INLINED_H #define _SW_INLINED_H -#include "defines.h" +#include "sw_defines.h" FORCE_INLINE int swrMin(int a, int b) { return a < b ? a : b; } FORCE_INLINE int swrMax(int a, int b) { return a > b ? a : b; } diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index 7c5ed14aa..cc2329ab2 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -1,8 +1,8 @@ #ifndef _SW_PIXEL_CALC_H #define _SW_PIXEL_CALC_H -#include "defines.h" -#include "pixel_convert.h" +#include "sw_defines.h" +#include "sw_pixel_convert.h" // Random number generator to be used for 8-bpp blending operations. FORCE_INLINE int fastRandomIsh() diff --git a/src/sw/pixel_convert.h b/src/sw/sw_pixel_convert.h similarity index 95% rename from src/sw/pixel_convert.h rename to src/sw/sw_pixel_convert.h index 43adad81b..82983a890 100644 --- a/src/sw/pixel_convert.h +++ b/src/sw/sw_pixel_convert.h @@ -1,7 +1,7 @@ -#ifndef _PIXEL_CONVERT_H -#define _PIXEL_CONVERT_H +#ifndef _SW__PIXEL_CONVERT_H +#define _SW__PIXEL_CONVERT_H -#include "defines.h" +#include "sw_defines.h" #include "binary_utils.h" #if PIXEL_SIZE == 32 @@ -111,4 +111,4 @@ FORCE_INLINE float swrGetAlpha(uintpixel_t pixel) #endif -#endif//_PIXEL_CONVERT_H +#endif//_SW__PIXEL_CONVERT_H diff --git a/src/sw/sw_renderer_private.h b/src/sw/sw_renderer_private.h index bb7d40640..4613dde25 100644 --- a/src/sw/sw_renderer_private.h +++ b/src/sw/sw_renderer_private.h @@ -2,8 +2,8 @@ #define _SW_RENDERER_PRIVATE_H #include "sw_renderer.h" -#include "defines.h" -#include "pixel_convert.h" +#include "sw_defines.h" +#include "sw_pixel_convert.h" // Unimplemented Functions #define UNIMP() do { logWarn("NYI %s\n", __func__); } while (0) From e382f12e08a4d82506e6a279b0ba18df13ba7769 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 23:44:56 +0300 Subject: [PATCH 104/130] * Move some more configuration properties to sw_config.h. --- src/sw/sw_config.h | 11 ++++++++++- src/sw/sw_renderer_private.h | 6 ------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sw/sw_config.h b/src/sw/sw_config.h index 43b7e48e1..2a602b7ad 100644 --- a/src/sw/sw_config.h +++ b/src/sw/sw_config.h @@ -24,7 +24,16 @@ // only fully support bm_normal. Automatically set if you enable SW_DITHERED_BLENDING. //#define SW_BAD_BLEND_MODE_SUPPORT -// Completely exclude bm_subtract support +// Completely exclude bm_subtract support. +//#define SW_NO_SUBTRACT_SUPPORT + +// Amount of textures that can be loaded in at once. If too many are loaded, +// start unloading. Note that this isn't particularly effective towards memory +// optimization, and we should be looking into something else. +#define TEXTURE_LRU_LENGTH 64 + +// Amount of surfaces that can be created at the same time. +#define SURFACE_MAX_COUNT 64 // -======- USER CONFIG END -======- diff --git a/src/sw/sw_renderer_private.h b/src/sw/sw_renderer_private.h index 4613dde25..256147128 100644 --- a/src/sw/sw_renderer_private.h +++ b/src/sw/sw_renderer_private.h @@ -15,12 +15,6 @@ #define M_PI 3.1415926535897932384626 #endif -// Configurable Properties -#define TEXTURE_LRU_LENGTH 64 -#define SURFACE_MAX_COUNT 64 -// (NOTE: See PIXEL_SIZE in defines.h) - - // Struct Definitions typedef struct { From 1ee582691c2daef447a8f90adb12ee0e249a185c Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Mon, 14 Sep 2026 23:52:40 +0300 Subject: [PATCH 105/130] * Add a parameter to platformSetNextFramebuffer + 16-bit compile fixes --- src/backends/appkit.m | 3 ++- src/backends/glfw2.c | 2 +- src/backends/glfw3.c | 5 ++++- src/backends/noop.c | 4 ++-- src/backends/sdl1.c | 32 ++++++++++++++++++++------------ src/backends/sdl2.c | 32 ++++++++++++++++++++------------ src/backends/sdl3.c | 6 +++++- src/sw/sw_drawing.c | 2 +- src/sw/sw_renderer.c | 4 ++-- src/sw/sw_surface.c | 12 ++++++------ 10 files changed, 63 insertions(+), 39 deletions(-) diff --git a/src/backends/appkit.m b/src/backends/appkit.m index 3aeadd81e..467b62baf 100644 --- a/src/backends/appkit.m +++ b/src/backends/appkit.m @@ -710,12 +710,13 @@ void platformSleepUntil(uint64_t time) { #ifdef ENABLE_SW_RENDERER -void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height, int bpp) { // TODO: this is a placeholder (void) framebuffer; (void) width; (void) height; + (void) bpp; } #endif diff --git a/src/backends/glfw2.c b/src/backends/glfw2.c index 1d845caf6..544b24124 100644 --- a/src/backends/glfw2.c +++ b/src/backends/glfw2.c @@ -256,7 +256,7 @@ void platformInitFunctions(Runner *runner) { static uint32_t* nextFb = NULL; static int fbWidth = 0, fbHeight = 0; -void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height, int bpp) { nextFb = framebuffer; fbWidth = width; fbHeight = height; diff --git a/src/backends/glfw3.c b/src/backends/glfw3.c index 7bf8dfd1a..e9718f92e 100644 --- a/src/backends/glfw3.c +++ b/src/backends/glfw3.c @@ -329,10 +329,13 @@ void platformInitFunctions(Runner *runner) { static uint32_t* nextFb = NULL; static int fbWidth = 0, fbHeight = 0; -void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height, int bpp) { nextFb = framebuffer; fbWidth = width; fbHeight = height; + if (bpp != 32) { + logWarn("platformSetNextFramebuffer: BPP of %d is not supported.\n", bpp); + } } #endif diff --git a/src/backends/noop.c b/src/backends/noop.c index 171e6a991..88c0702c5 100644 --- a/src/backends/noop.c +++ b/src/backends/noop.c @@ -98,6 +98,6 @@ void platformSleepUntil(uint64_t time) { } } -void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { - (void)framebuffer, (void)width, (void)height; +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height, int bpp) { + (void)framebuffer, (void)width, (void)height, (void)bpp; } diff --git a/src/backends/sdl1.c b/src/backends/sdl1.c index 69bd227d1..c11a41e7f 100644 --- a/src/backends/sdl1.c +++ b/src/backends/sdl1.c @@ -317,23 +317,31 @@ void platformInitFunctions(Runner *runner) { static SDL_Surface* nextFb = NULL; -void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height, int bpp) { if (nextFb) { SDL_FreeSurface(nextFb); nextFb = NULL; } - nextFb = SDL_CreateRGBSurfaceFrom( - framebuffer, - width, - height, - 32, - width * 4, - 0x00ff0000, // Rmask - 0x0000ff00, // Gmask - 0x000000ff, // Bmask - 0x00000000 // Amask - ); + int rmask, gmask, bmask; + if (bpp == 32) { + rmask = 0x00ff0000; + gmask = 0x0000ff00; + bmask = 0x000000ff; + } + else if (bpp == 16) { + rmask = 0x7C00; + gmask = 0x03E0; + bmask = 0x001F; + } + else { + // 8bpp + rmask = 0x07; + gmask = 0x38; + bmask = 0xC0; + } + + nextFb = SDL_CreateRGBSurfaceFrom(framebuffer, width, height, bpp, width * bpp / 8, rmask, gmask, bmask, 0); } #endif diff --git a/src/backends/sdl2.c b/src/backends/sdl2.c index d41c9c3cf..395bf15b8 100644 --- a/src/backends/sdl2.c +++ b/src/backends/sdl2.c @@ -291,23 +291,31 @@ void platformInitFunctions(Runner *runner) { static SDL_Surface* nextFb = NULL; -void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height, int bpp) { if (nextFb) { SDL_FreeSurface(nextFb); nextFb = NULL; } + + int rmask, gmask, bmask, amask; + if (bpp == 32) { + rmask = 0x00ff0000; + gmask = 0x0000ff00; + bmask = 0x000000ff; + } + else if (bpp == 16) { + rmask = 0x7C00; + gmask = 0x03E0; + bmask = 0x001F; + } + else { + // 8bpp + rmask = 0x07; + gmask = 0x38; + bmask = 0xC0; + } - nextFb = SDL_CreateRGBSurfaceFrom( - framebuffer, - width, - height, - 32, - width * 4, - 0x00ff0000, // Rmask - 0x0000ff00, // Gmask - 0x000000ff, // Bmask - 0x00000000 // Amask - ); + nextFb = SDL_CreateRGBSurfaceFrom(framebuffer, width, height, bpp, width * bpp / 8, rmask, gmask, bmask, 0); } #endif diff --git a/src/backends/sdl3.c b/src/backends/sdl3.c index a15e75fec..023e6e3a2 100644 --- a/src/backends/sdl3.c +++ b/src/backends/sdl3.c @@ -237,12 +237,16 @@ void platformInitFunctions(Runner *runner) { static SDL_Surface* nextFb = NULL; -void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height) { +void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height, int bpp) { if (nextFb) { SDL_DestroySurface(nextFb); nextFb = NULL; } + if (bpp != 32) { + logWarn("platformSetNextFramebuffer: BPP of %d is not supported.\n", bpp); + } + nextFb = SDL_CreateSurfaceFrom( width, height, diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 6daeb327d..6aa9a1e8b 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -833,7 +833,7 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor return true; } -void swrPlotPixel(Renderer* renderer, float x, float y, uint32_t color, float alpha) +void swrPlotPixel(Renderer* renderer, float x, float y, uintpixel_t color, float alpha) { SWRenderer *swr = (SWRenderer*) renderer; int srcalpha = swrCalcSrcAlpha(swr, alpha); diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 2bf78d5df..de1485d0b 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -11,7 +11,7 @@ #define VERTICES_PER_TRIANGLE 3 #define VERTICES_PER_QUAD 4 -void platformSetNextFramebuffer(uintpixel_t* framebuffer, int width, int height); +void platformSetNextFramebuffer(uintpixel_t* framebuffer, int width, int height, int bpp); static void SWRenderer_gpuSetColorWriteEnable(Renderer* renderer, bool red, bool green, bool blue, bool alpha); @@ -103,7 +103,7 @@ static void SWRenderer_endFrameEnd(Renderer* renderer) logDebug("SWRenderer_endFrameEnd\n"); - platformSetNextFramebuffer(swr->fb, swr->width, swr->height); + platformSetNextFramebuffer(swr->fb, swr->width, swr->height, PIXEL_SIZE); swr->primitiveOverflow = false; } diff --git a/src/sw/sw_surface.c b/src/sw/sw_surface.c index 30f4e627a..02a2f1145 100644 --- a/src/sw/sw_surface.c +++ b/src/sw/sw_surface.c @@ -70,13 +70,13 @@ void swrCommitShadowWritesToSurfaceIfNeeded(SWRenderer* swr, SWSurface* surface) if (swr->writeMask & WRITE_MASK_ALPHA) x.p.a = 255; mask = x.l; #elif PIXEL_SIZE == 16 - if (swr->writeMask & WRITE_MASK_RED) l |= 0x7C00; - if (swr->writeMask & WRITE_MASK_GREEN) l |= 0x03E0; - if (swr->writeMask & WRITE_MASK_BLUE) l |= 0x001F; - if (swr->writeMask & WRITE_MASK_ALPHA) l |= 0x8000; + if (swr->writeMask & WRITE_MASK_RED) mask |= 0x7C00; + if (swr->writeMask & WRITE_MASK_GREEN) mask |= 0x03E0; + if (swr->writeMask & WRITE_MASK_BLUE) mask |= 0x001F; + if (swr->writeMask & WRITE_MASK_ALPHA) mask |= 0x8000; #else - //although it DOES ues rgb332, needs special handling for ALPHA - logError("swr: Unimplemented color masking for 8-bit mode TODO\n"); + // TODO: although it DOES use rgb332, needs special handling for ALPHA + logError("swr: Unimplemented color masking for 8-bit mode\n"); swrFreeTexture(surface->texture); surface->texture = surface->shadowTexture; surface->shadowTexture = NULL; From 4085062bbc06225fd4c6794669ea710368c18d71 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 15 Sep 2026 00:06:23 +0300 Subject: [PATCH 106/130] * More 16-bit color fixes. --- src/sw/sw_pixel_calc.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index cc2329ab2..baa75ed66 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -200,10 +200,16 @@ void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srca dcr &= ((-dcr) >> 31); dcg &= ((-dcg) >> 31); dcb &= ((-dcb) >> 31); + #if PIXEL_SIZE == 16 + if (dcr > 31) dcr = 31; + if (dcg > 31) dcg = 31; + if (dcb > 31) dcb = 31; + #else //clamp to 255 dcr |= ((signed char)(dcr >> 1) >> 7); dcg |= ((signed char)(dcg >> 1) >> 7); dcb |= ((signed char)(dcb >> 1) >> 7); + #endif } #endif // SW_BAD_BLEND_MODE_SUPPORT @@ -279,9 +285,9 @@ FORCE_INLINE uintpixel_t swrThreeWayBlend(uintpixel_t color1, uintpixel_t color2 out.p.a = x1.p.a; return out.l; #elif PIXEL_SIZE == 16 - int c1r = color1 & 0x1F, c1g = (color2 >> 5) & 0x1F, c1b = (color3 >> 10) & 0x1F; - int c2r = color2 & 0x1F, c2g = (color2 >> 5) & 0x1F, c2b = (color3 >> 10) & 0x1F; - int c3r = color3 & 0x1F, c3g = (color2 >> 5) & 0x1F, c3b = (color3 >> 10) & 0x1F; + int c1b = color1 & 0x1F, c1g = (color1 >> 5) & 0x1F, c1r = (color1 >> 10) & 0x1F; + int c2b = color2 & 0x1F, c2g = (color2 >> 5) & 0x1F, c2r = (color2 >> 10) & 0x1F; + int c3b = color3 & 0x1F, c3g = (color3 >> 5) & 0x1F, c3r = (color3 >> 10) & 0x1F; int ca = color1 & 0x8000; int cr = (c1r * frac1 + c2r * frac2 + c3r * frac3) >> 16; int cg = (c1g * frac1 + c2g * frac2 + c3g * frac3) >> 16; From e69d9919a9ed22a9bd90416f2ad21e125e63fb19 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 15 Sep 2026 00:40:10 +0300 Subject: [PATCH 107/130] * More adjustments to 8bpp rendering --- src/sw/sw_config.h | 16 +++++----------- src/sw/sw_pixel_calc.h | 17 ++++++++++++++++- src/sw/sw_renderer.c | 3 +++ src/sw/sw_surface.c | 22 ++++++++++++++++------ 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/sw/sw_config.h b/src/sw/sw_config.h index 2a602b7ad..9e68e0692 100644 --- a/src/sw/sw_config.h +++ b/src/sw/sw_config.h @@ -42,18 +42,12 @@ #ifndef SW_DITHERED_BLENDING #define SW_DITHERED_BLENDING -#endif // SW_DITHERED_BLENDING +#endif -#endif // PIXEL_SIZE == 8 - -// Disable blend mode support forcefully if we're on 8-bpp color -// or if we requested dithered blending -#ifdef SW_DITHERED_BLENDING +#ifndef SW_NO_SUBTRACT_SUPPORT +#define SW_NO_SUBTRACT_SUPPORT +#endif -#ifndef SW_BAD_BLEND_MODE_SUPPORT -#define SW_BAD_BLEND_MODE_SUPPORT -#endif // !SW_BAD_BLEND_MODE_SUPPORT - -#endif // SW_DITHERED_BLENDING +#endif // PIXEL_SIZE == 8 #endif // __SW_CONFIG_H diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index baa75ed66..434f8dade 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -92,7 +92,14 @@ void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srca /* If we didn't disable subtract support and are in dithered blending mode */ #if defined SW_DITHERED_BLENDING -#ifndef SW_NO_SUBTRACT_SUPPORT +#ifdef SW_NO_SUBTRACT_SUPPORT + +#ifndef SW_BAD_BLEND_MODE_SUPPORT + if (UNLIKELY(blendmode == bm_subtract)) + return; +#endif // SW_NO_BLEND_MODE_SUPPORT + +#else // SW_NO_SUBTRACT_SUPPORT if (UNLIKELY(blendmode == bm_subtract)) { #if PIXEL_SIZE == 8 @@ -146,6 +153,14 @@ void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srca return; } +#ifndef SW_BAD_BLEND_MODE_SUPPORT + if (UNLIKELY(blendmode == bm_add)) + { + *dcolor |= scolor; + return; + } +#endif + *dcolor = scolor; #else diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index de1485d0b..b83c88e6b 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -568,6 +568,9 @@ static void SWRenderer_clearScreen(Renderer* renderer, uint32_t color, float alp color |= (alpha > 0.5f); #endif } +#else + if (alpha < 0.5f) + color = PXL_TRANSPARENT; #endif for (int y = 0; y < swr->height; y++) { diff --git a/src/sw/sw_surface.c b/src/sw/sw_surface.c index 02a2f1145..66b4d5310 100644 --- a/src/sw/sw_surface.c +++ b/src/sw/sw_surface.c @@ -75,18 +75,28 @@ void swrCommitShadowWritesToSurfaceIfNeeded(SWRenderer* swr, SWSurface* surface) if (swr->writeMask & WRITE_MASK_BLUE) mask |= 0x001F; if (swr->writeMask & WRITE_MASK_ALPHA) mask |= 0x8000; #else - // TODO: although it DOES use rgb332, needs special handling for ALPHA - logError("swr: Unimplemented color masking for 8-bit mode\n"); - swrFreeTexture(surface->texture); - surface->texture = surface->shadowTexture; - surface->shadowTexture = NULL; - return; + bool copyAlpha = false; + if (swr->writeMask & WRITE_MASK_RED) mask |= 0x07; + if (swr->writeMask & WRITE_MASK_GREEN) mask |= 0x38; + if (swr->writeMask & WRITE_MASK_BLUE) mask |= 0xC0; + if (swr->writeMask & WRITE_MASK_ALPHA) copyAlpha = true; #endif uintpixel_t invmask = ~mask; size_t max = surface->texture->width * surface->texture->height; for (size_t i = 0; i < max; i++) { + #if PIXEL_SIZE == 8 + // If the new pixel is transparent + if (surface->shadowTexture->buffer[i] == PXL_TRANSPARENT) { + surface->texture->buffer[i] = copyAlpha ? PXL_TRANSPARENT : PXL_TRANSPARENT + 1; + continue; + } + // If the old pixel was transparent and we aren't copying alpha + if (surface->texture->buffer[i] == PXL_TRANSPARENT && !copyAlpha) { + continue; + } + #endif surface->shadowTexture->buffer[i] = surface->texture->buffer[i] = (surface->texture->buffer[i] & invmask) | (surface->shadowTexture->buffer[i] & mask); } From 30865d33008c5da31ffd095ea9b3c94331349439 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 15 Sep 2026 00:48:20 +0300 Subject: [PATCH 108/130] * Move text drawing into its own function. --- src/sw/sw_drawing.c | 223 ------------------------------------------ src/sw/sw_text.c | 231 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+), 223 deletions(-) create mode 100644 src/sw/sw_text.c diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 6aa9a1e8b..65cfd2a67 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -1,21 +1,8 @@ #include #include #include -#include "text_utils.h" #include "sw_renderer_private.h" -// ==== Internal structures ==== - -typedef struct -{ - Font* font; - TexturePageItem* fontTpag; // single TPAG for regular fonts (NULL for sprite fonts) - int fontTpagIndex; - int fontPageId; - Sprite* spriteFontSprite; // source sprite for sprite fonts (NULL for regular fonts) -} -SwrFontState; - // ==== Internal functions ==== FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int blendmode, int srcalpha, int dstalpha) @@ -662,81 +649,6 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft } } -static bool swrResolveFontState(SWRenderer* swr, DataWin* dw, Font* font, SwrFontState* state) -{ - state->font = font; - state->fontTpag = NULL; - state->fontTpagIndex = 0; - state->spriteFontSprite = NULL; - - if (font->isSpriteFont) - { - state->spriteFontSprite = &dw->sprt.sprites[font->spriteIndex]; - } - else - { - state->fontTpagIndex = font->tpagIndex; - if (state->fontTpagIndex < 0) return false; - - state->fontTpag = &dw->tpag.items[state->fontTpagIndex]; - int16_t pageId = state->fontTpag->texturePageId; - if (0 > pageId || (uint32_t) pageId >= swr->totalTextureCount) return false; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return false; - - state->fontPageId = pageId; - } - - return true; -} - -static bool swrResolveGlyph( - SWRenderer* swr, DataWin* dw, SwrFontState* state, FontGlyph* glyph, float cursorX, float cursorY, - int* tpagIndex, int* pageId, int* sx, int* sy, int* sw, int* sh, float* dx, float* dy -) -{ - Font* font = state->font; - if (font->isSpriteFont && state->spriteFontSprite != NULL) - { - Sprite* sprite = state->spriteFontSprite; - int32_t glyphIndex = (int32_t) (glyph - font->glyphs); - if (0 > glyphIndex || glyphIndex >= (int32_t) sprite->textureCount) return false; - - int32_t tpagIdx = sprite->tpagIndices[glyphIndex]; - if (0 > tpagIdx) return false; - - TexturePageItem* glyphTpag = &dw->tpag.items[tpagIdx]; - int16_t pid = glyphTpag->texturePageId; - if (0 > pid || (uint32_t) pid >= swr->totalTextureCount) return false; - if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pid)) return false; - - *tpagIndex = tpagIdx; - *pageId = glyphTpag->texturePageId; - - *sx = glyphTpag->sourceX; - *sy = glyphTpag->sourceY; - *sw = glyphTpag->sourceWidth; - *sh = glyphTpag->sourceHeight; - - *dx = cursorX + glyph->offset; - *dy = cursorY + glyphTpag->targetY - sprite->originY; - } - else - { - *tpagIndex = state->fontTpagIndex; - *pageId = state->fontPageId; - - *sx = state->fontTpag->sourceX + glyph->sourceX; - *sy = state->fontTpag->sourceY + glyph->sourceY; - *sw = glyph->sourceWidth; - *sh = glyph->sourceHeight; - - *dx = cursorX + glyph->offset; - *dy = cursorY; - } - - return true; -} - // ==== Exposed interface ==== bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView) @@ -1038,138 +950,3 @@ void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, swrIntAlpha(alpha) ); } - -void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation) -{ - Renderer* renderer = &swr->base; - DataWin* dwin = renderer->dataWin; - - int32_t fontIndex = renderer->drawFont; - if (0 > fontIndex || dwin->font.count <= (uint32_t) fontIndex) return; - - Font* font = &dwin->font.fonts[fontIndex]; - - SwrFontState fontState; - memset(&fontState, 0, sizeof fontState); // silence warning treated as error - - if (!swrResolveFontState(swr, dwin, font, &fontState)) return; - - // TODO: do we need to mirror the way the text scrolls too?! - float cosA = 1.0f, sinA = 0.0f, angleRad = 0.0f; - bool mustRotate = swrMustRotateSensitive(angleDeg); - if (UNLIKELY(mustRotate)) - { - angleRad = -angleDeg * M_PI / 180.0f; - cosA = cosf(angleRad); - sinA = sinf(angleRad); - } - - int textLen = (int) strlen(text); - int lineCount = TextUtils_countLines(text, textLen); - float lineStride = (0.0f > lineSeparation) ? TextUtils_lineStride(font) : (lineSeparation / (font->scaleY != 0.0f ? font->scaleY : 1.0f)); - - // Vertical alignment offset - float totalHeight = (float) lineCount * lineStride; - float valignOffset = 0; - if (renderer->drawValign == 1) valignOffset = -totalHeight / 2.0f; - else if (renderer->drawValign == 2) valignOffset = -totalHeight; - - xscale *= font->scaleX; - yscale *= font->scaleY; - - // Iterate through lines. HTML5 subtracts ascenderOffset from the per-line y offset - // (see yyFont.GR_Text_Draw), shifting glyphs up so the baseline aligns with the drawn y. - float cursorY = valignOffset - (float) font->ascenderOffset; - int32_t lineStart = 0; - - for (int32_t lineIdx = 0; lineCount > lineIdx; lineIdx++) { - // Find end of current line - int32_t lineEnd = lineStart; - while (textLen > lineEnd && !TextUtils_isNewlineChar(text[lineEnd])) { - lineEnd++; - } - int32_t lineLen = lineEnd - lineStart; - - // Horizontal alignment offset for this line - float lineWidth = TextUtils_measureLineWidth(font, text + lineStart, lineLen); - float halignOffset = 0; - if (renderer->drawHalign == 1) halignOffset = -lineWidth / 2.0f; - else if (renderer->drawHalign == 2) halignOffset = -lineWidth; - - float cursorX = halignOffset; - - // Render each glyph in the line - decode each codepoint once and carry it forward as next iteration's ch (also used for kerning) - int32_t pos = 0; - uint16_t ch = 0; - bool hasCh = false; - if (lineLen > pos) { - ch = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); - hasCh = true; - } - - while (hasCh) { - FontGlyph* glyph = TextUtils_findGlyph(font, ch); - - uint16_t nextCh = 0; - bool hasNext = lineLen > pos; - if (hasNext) nextCh = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); - - if (glyph != nullptr) { - bool drewSuccessfully = false; - if (glyph->sourceWidth != 0 && glyph->sourceHeight != 0) { - int fontTpagIndex = 0, pageId = 0; - int sx, sy, sw, sh, dw, dh; - float dx, dy; - if (swrResolveGlyph(swr, dwin, &fontState, glyph, cursorX, cursorY, - &fontTpagIndex, &pageId, &sx, &sy, &sw, &sh, &dx, &dy)) - { - dx *= xscale; dx += x; - dy *= xscale; dy += y; - dw = swrCeiling(xscale * glyph->sourceWidth); - dh = swrCeiling(yscale * glyph->sourceHeight); - - // TODO: at 640x480, for some reason, without this fixup the - // letters in the "Name the fallen human." screen don't shake - dx = roundf(dx * 2) / 2; - dy = roundf(dy * 2) / 2; - - SWTexture* texture = swr->textures[pageId]; - - if (UNLIKELY(mustRotate)) - { - dx -= x; - dy -= y; - float ndx = cosA * dx - sinA * dy; - float ndy = sinA * dx + cosA * dy; - ndx += x; - ndy += y; - swrDrawSpriteRotated(renderer, ndx, ndy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, 0.0f, 0.0f); - } - else - { - swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); - } - - drewSuccessfully = true; - } - } - - cursorX += glyph->shift; - if (drewSuccessfully && hasNext) { - cursorX += TextUtils_getKerningOffset(glyph, nextCh); - } - } - - ch = nextCh; - hasCh = hasNext; - } - - cursorY += lineStride; - // Skip past the newline, treating \r\n and \n\r as single breaks - if (textLen > lineEnd) { - lineStart = TextUtils_skipNewline(text, lineEnd, textLen); - } else { - lineStart = lineEnd; - } - } -} diff --git a/src/sw/sw_text.c b/src/sw/sw_text.c new file mode 100644 index 000000000..ab30eae19 --- /dev/null +++ b/src/sw/sw_text.c @@ -0,0 +1,231 @@ +#include +#include +#include +#include "text_utils.h" +#include "sw_renderer_private.h" + +// ==== Internal structures ==== + +typedef struct +{ + Font* font; + TexturePageItem* fontTpag; // single TPAG for regular fonts (NULL for sprite fonts) + int fontTpagIndex; + int fontPageId; + Sprite* spriteFontSprite; // source sprite for sprite fonts (NULL for regular fonts) +} +SwrFontState; + +// ==== Internal functions ==== + +static bool swrResolveFontState(SWRenderer* swr, DataWin* dw, Font* font, SwrFontState* state) +{ + state->font = font; + state->fontTpag = NULL; + state->fontTpagIndex = 0; + state->spriteFontSprite = NULL; + + if (font->isSpriteFont) + { + state->spriteFontSprite = &dw->sprt.sprites[font->spriteIndex]; + } + else + { + state->fontTpagIndex = font->tpagIndex; + if (state->fontTpagIndex < 0) return false; + + state->fontTpag = &dw->tpag.items[state->fontTpagIndex]; + int16_t pageId = state->fontTpag->texturePageId; + if (0 > pageId || (uint32_t) pageId >= swr->totalTextureCount) return false; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) return false; + + state->fontPageId = pageId; + } + + return true; +} + +static bool swrResolveGlyph( + SWRenderer* swr, DataWin* dw, SwrFontState* state, FontGlyph* glyph, float cursorX, float cursorY, + int* tpagIndex, int* pageId, int* sx, int* sy, int* sw, int* sh, float* dx, float* dy +) +{ + Font* font = state->font; + if (font->isSpriteFont && state->spriteFontSprite != NULL) + { + Sprite* sprite = state->spriteFontSprite; + int32_t glyphIndex = (int32_t) (glyph - font->glyphs); + if (0 > glyphIndex || glyphIndex >= (int32_t) sprite->textureCount) return false; + + int32_t tpagIdx = sprite->tpagIndices[glyphIndex]; + if (0 > tpagIdx) return false; + + TexturePageItem* glyphTpag = &dw->tpag.items[tpagIdx]; + int16_t pid = glyphTpag->texturePageId; + if (0 > pid || (uint32_t) pid >= swr->totalTextureCount) return false; + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pid)) return false; + + *tpagIndex = tpagIdx; + *pageId = glyphTpag->texturePageId; + + *sx = glyphTpag->sourceX; + *sy = glyphTpag->sourceY; + *sw = glyphTpag->sourceWidth; + *sh = glyphTpag->sourceHeight; + + *dx = cursorX + glyph->offset; + *dy = cursorY + glyphTpag->targetY - sprite->originY; + } + else + { + *tpagIndex = state->fontTpagIndex; + *pageId = state->fontPageId; + + *sx = state->fontTpag->sourceX + glyph->sourceX; + *sy = state->fontTpag->sourceY + glyph->sourceY; + *sw = glyph->sourceWidth; + *sh = glyph->sourceHeight; + + *dx = cursorX + glyph->offset; + *dy = cursorY; + } + + return true; +} + +// ==== Exposed interface ==== + +void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation) +{ + Renderer* renderer = &swr->base; + DataWin* dwin = renderer->dataWin; + + int32_t fontIndex = renderer->drawFont; + if (0 > fontIndex || dwin->font.count <= (uint32_t) fontIndex) return; + + Font* font = &dwin->font.fonts[fontIndex]; + + SwrFontState fontState; + memset(&fontState, 0, sizeof fontState); // silence warning treated as error + + if (!swrResolveFontState(swr, dwin, font, &fontState)) return; + + // TODO: do we need to mirror the way the text scrolls too?! + float cosA = 1.0f, sinA = 0.0f, angleRad = 0.0f; + bool mustRotate = swrMustRotateSensitive(angleDeg); + if (UNLIKELY(mustRotate)) + { + angleRad = -angleDeg * M_PI / 180.0f; + cosA = cosf(angleRad); + sinA = sinf(angleRad); + } + + int textLen = (int) strlen(text); + int lineCount = TextUtils_countLines(text, textLen); + float lineStride = (0.0f > lineSeparation) ? TextUtils_lineStride(font) : (lineSeparation / (font->scaleY != 0.0f ? font->scaleY : 1.0f)); + + // Vertical alignment offset + float totalHeight = (float) lineCount * lineStride; + float valignOffset = 0; + if (renderer->drawValign == 1) valignOffset = -totalHeight / 2.0f; + else if (renderer->drawValign == 2) valignOffset = -totalHeight; + + xscale *= font->scaleX; + yscale *= font->scaleY; + + // Iterate through lines. HTML5 subtracts ascenderOffset from the per-line y offset + // (see yyFont.GR_Text_Draw), shifting glyphs up so the baseline aligns with the drawn y. + float cursorY = valignOffset - (float) font->ascenderOffset; + int32_t lineStart = 0; + + for (int32_t lineIdx = 0; lineCount > lineIdx; lineIdx++) { + // Find end of current line + int32_t lineEnd = lineStart; + while (textLen > lineEnd && !TextUtils_isNewlineChar(text[lineEnd])) { + lineEnd++; + } + int32_t lineLen = lineEnd - lineStart; + + // Horizontal alignment offset for this line + float lineWidth = TextUtils_measureLineWidth(font, text + lineStart, lineLen); + float halignOffset = 0; + if (renderer->drawHalign == 1) halignOffset = -lineWidth / 2.0f; + else if (renderer->drawHalign == 2) halignOffset = -lineWidth; + + float cursorX = halignOffset; + + // Render each glyph in the line - decode each codepoint once and carry it forward as next iteration's ch (also used for kerning) + int32_t pos = 0; + uint16_t ch = 0; + bool hasCh = false; + if (lineLen > pos) { + ch = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); + hasCh = true; + } + + while (hasCh) { + FontGlyph* glyph = TextUtils_findGlyph(font, ch); + + uint16_t nextCh = 0; + bool hasNext = lineLen > pos; + if (hasNext) nextCh = TextUtils_decodeUtf8(text + lineStart, lineLen, &pos); + + if (glyph != nullptr) { + bool drewSuccessfully = false; + if (glyph->sourceWidth != 0 && glyph->sourceHeight != 0) { + int fontTpagIndex = 0, pageId = 0; + int sx, sy, sw, sh, dw, dh; + float dx, dy; + if (swrResolveGlyph(swr, dwin, &fontState, glyph, cursorX, cursorY, + &fontTpagIndex, &pageId, &sx, &sy, &sw, &sh, &dx, &dy)) + { + dx *= xscale; dx += x; + dy *= xscale; dy += y; + dw = swrCeiling(xscale * glyph->sourceWidth); + dh = swrCeiling(yscale * glyph->sourceHeight); + + // TODO: at 640x480, for some reason, without this fixup the + // letters in the "Name the fallen human." screen don't shake + dx = roundf(dx * 2) / 2; + dy = roundf(dy * 2) / 2; + + SWTexture* texture = swr->textures[pageId]; + + if (UNLIKELY(mustRotate)) + { + dx -= x; + dy -= y; + float ndx = cosA * dx - sinA * dy; + float ndy = sinA * dx + cosA * dy; + ndx += x; + ndy += y; + swrDrawSpriteRotated(renderer, ndx, ndy, dw, dh, texture, sx, sy, sw, sh, color, alpha, angleDeg, 0.0f, 0.0f); + } + else + { + swrDrawSprite(renderer, dx, dy, dw, dh, texture, sx, sy, sw, sh, color, alpha); + } + + drewSuccessfully = true; + } + } + + cursorX += glyph->shift; + if (drewSuccessfully && hasNext) { + cursorX += TextUtils_getKerningOffset(glyph, nextCh); + } + } + + ch = nextCh; + hasCh = hasNext; + } + + cursorY += lineStride; + // Skip past the newline, treating \r\n and \n\r as single breaks + if (textLen > lineEnd) { + lineStart = TextUtils_skipNewline(text, lineEnd, textLen); + } else { + lineStart = lineEnd; + } + } +} From faafd73da54000ae1ca2bd3d42d1102bcd56ef83 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 15 Sep 2026 01:20:41 +0300 Subject: [PATCH 109/130] * Add debug font. It isn't the same as the rest of them because uhhhhh what stopped me from just using it exactly? Just that it sucks? --- src/sw/sw_config.h | 3 + src/sw/sw_debug_font.c | 139 +++++++++++++++++++++++++++++++++++ src/sw/sw_drawing.h | 1 - src/sw/sw_renderer.c | 19 +++++ src/sw/sw_renderer_private.h | 1 + src/sw/sw_text.c | 81 ++++++++++++++++++++ src/sw/sw_text.h | 7 ++ 7 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 src/sw/sw_debug_font.c create mode 100644 src/sw/sw_text.h diff --git a/src/sw/sw_config.h b/src/sw/sw_config.h index 9e68e0692..6ec87f774 100644 --- a/src/sw/sw_config.h +++ b/src/sw/sw_config.h @@ -35,6 +35,9 @@ // Amount of surfaces that can be created at the same time. #define SURFACE_MAX_COUNT 64 +// Whether to enable the debug font. +#define SW_ENABLE_DEBUG_FONT + // -======- USER CONFIG END -======- // Force enable dithered blending if in 8bpp mode. diff --git a/src/sw/sw_debug_font.c b/src/sw/sw_debug_font.c new file mode 100644 index 000000000..e3769a30a --- /dev/null +++ b/src/sw/sw_debug_font.c @@ -0,0 +1,139 @@ +#include +#include "sw_config.h" + +#ifdef SW_ENABLE_DEBUG_FONT + +// Borrowed from http://www.fial.com/~scott/tamsyn-font/ + +const uint8_t swrDebugFont1bpp[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, + 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x3e, 0x60, 0x60, 0x3c, 0x06, 0x06, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6d, 0x6e, 0x3c, 0x08, 0x1e, 0x3b, 0x5b, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x6c, 0x38, 0x39, 0x6d, 0x66, 0x66, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x1c, 0x7f, 0x1c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x0c, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x6e, 0x6e, 0x66, 0x76, 0x76, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x1c, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0e, 0x1e, 0x36, 0x66, 0x7f, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x60, 0x60, 0x7c, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x30, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x0c, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x46, 0x06, 0x06, 0x0c, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x63, 0x63, 0x6f, 0x6b, 0x6b, 0x6b, 0x6f, 0x6f, 0x60, 0x60, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x60, 0x60, 0x60, 0x66, 0x66, 0x36, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x73, 0x7b, 0x6f, 0x67, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0c, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x60, 0x60, 0x70, 0x3c, 0x0e, 0x06, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x60, 0x60, 0x60, 0x60, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x00, 0x00, + 0x00, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x0c, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x78, 0x00, 0x00, + 0x00, 0x60, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x70, 0x3c, 0x0e, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x6b, 0x6b, 0x7f, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf0, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x33, 0x7b, 0x6f, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#endif // SW_ENABLE_DEBUG_FONT diff --git a/src/sw/sw_drawing.h b/src/sw/sw_drawing.h index 50fcc30ca..333c7d645 100644 --- a/src/sw/sw_drawing.h +++ b/src/sw/sw_drawing.h @@ -13,6 +13,5 @@ void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, flo void swrDrawSprite(Renderer* renderer, float dx, float dy, float dw, float dh, SWTexture* texture, int sx, int sy, int sw, int sh, uint32_t tintColor, float alpha); void swrDrawSpriteRotated(Renderer* renderer, float dx, float dy, float dw, float dh, SWTexture* texture, int sx, int sy, int sw, int sh, uint32_t tintColor, float alpha, float angleDeg, float pivotX, float pivotY); void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color1, uint32_t color2, uint32_t color3, float alpha); -void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation); #endif//_SW_DRAWING_H diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index b83c88e6b..75d5a0f4b 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -418,6 +418,24 @@ static void SWRenderer_drawTextColor(Renderer* renderer, const char* text, float swrDrawText(swr, text, x, y, xscale, yscale, angleDeg, c1, renderer->drawAlpha, lineSeparation); } +static void SWRenderer_drawTextUI(Renderer* renderer, const char* text, float x, float y, + float xscale, float yscale, float angleDeg, + int32_t c1, int32_t c2, int32_t c3, int32_t c4, float alpha, + float lineSeparation) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + (void) xscale; + (void) yscale; + (void) angleDeg; + (void) c2; + (void) c3; + (void) c4; + (void) lineSeparation; + + swrDrawDebugText(swr, text, (int) x, (int) y, c1, alpha); +} + static void SWRenderer_drawSpriteTiled(Renderer* renderer, int32_t tpagIndex, float originX, float originY, float x, float y, float xscale, float yscale, bool tileX, bool tileY, @@ -1480,6 +1498,7 @@ Renderer* SWRenderer_create(void) swrVtable.primitiveEnd = SWRenderer_primitiveEnd; swrVtable.drawVertex = SWRenderer_drawVertex; swrVtable.drawVertexBuffer = SWRenderer_drawVertexBuffer; + swrVtable.drawTextUI = SWRenderer_drawTextUI; swrVtable.drawTile = NULL; diff --git a/src/sw/sw_renderer_private.h b/src/sw/sw_renderer_private.h index 256147128..5c0993808 100644 --- a/src/sw/sw_renderer_private.h +++ b/src/sw/sw_renderer_private.h @@ -109,5 +109,6 @@ SWRenderer; #include "sw_surface.h" #include "sw_drawing.h" #include "sw_texture_lru.h" +#include "sw_text.h" #endif//_SW_RENDERER_PRIVATE_H diff --git a/src/sw/sw_text.c b/src/sw/sw_text.c index ab30eae19..a451bf32e 100644 --- a/src/sw/sw_text.c +++ b/src/sw/sw_text.c @@ -4,6 +4,9 @@ #include "text_utils.h" #include "sw_renderer_private.h" +#define SWR_DEBUG_FONT_LINE_HEIGHT 16 +#define SWR_DEBUG_FONT_CHAR_WIDTH 8 + // ==== Internal structures ==== typedef struct @@ -18,6 +21,16 @@ SwrFontState; // ==== Internal functions ==== +FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int blendmode, int srcalpha, int dstalpha) +{ + SWRenderer* swr = (SWRenderer*) renderer; + + if (x < swr->portX || y < swr->portY) return; + if (x >= swr->maxX || y >= swr->maxY) return; + + alphaBlend(&swr->fb[y * swr->fbPitch + x], color, blendmode, srcalpha, dstalpha); +} + static bool swrResolveFontState(SWRenderer* swr, DataWin* dw, Font* font, SwrFontState* state) { state->font = font; @@ -93,6 +106,37 @@ static bool swrResolveGlyph( return true; } +#ifdef SW_ENABLE_DEBUG_FONT + +extern const uint8_t swrDebugFont1bpp[]; + +static void swrDrawDebugFontChar(SWRenderer* swr, char chr, int ax, int ay, uint32_t color, float alphaf) +{ + if (chr == ' ') + return; + if (chr < '!' || chr > '~') + chr = '?'; + + int alpha = swrIntAlpha(alphaf); + int srcalpha = swrCalcSrcAlpha(swr, alpha); + int dstalpha = swrCalcDstAlpha(swr, alpha); + int blendmode = swr->blendMode; + + uintpixel_t actualColor = swrConvertPixel(color); + for (int y = 0; y < SWR_DEBUG_FONT_LINE_HEIGHT; y++) + { + for (int x = 0; x < SWR_DEBUG_FONT_CHAR_WIDTH; x++) + { + uint8_t row = swrDebugFont1bpp[chr * SWR_DEBUG_FONT_LINE_HEIGHT + y]; + + if (row & (1 << (SWR_DEBUG_FONT_CHAR_WIDTH - 1 - x))) + swrPlotPixel_(&swr->base, ax + x, ay + y, actualColor, blendmode, srcalpha, dstalpha); + } + } +} + +#endif + // ==== Exposed interface ==== void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation) @@ -229,3 +273,40 @@ void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xsca } } } + +#ifdef SW_ENABLE_DEBUG_FONT + +void swrDrawDebugText(SWRenderer* swr, const char* text, int x, int y, uint32_t color, float alpha) +{ + int px = 0; + int py = 0; + + while (*text) + { + char chr = *text; + text++; + + if (chr == '\n') { + px = 0; + py += SWR_DEBUG_FONT_LINE_HEIGHT; + continue; + } + + swrDrawDebugFontChar(swr, chr, x + px, y + py, color, alpha); + px += SWR_DEBUG_FONT_CHAR_WIDTH; + } +} + +#else + +void swrDrawDebugText(SWRenderer* swr, const char* text, int x, int y, uint32_t color, float alpha) +{ + (void) swr; + (void) text; + (void) x; + (void) y; + (void) color; + (void) alpha; +} + +#endif diff --git a/src/sw/sw_text.h b/src/sw/sw_text.h new file mode 100644 index 000000000..2dd7e1a5d --- /dev/null +++ b/src/sw/sw_text.h @@ -0,0 +1,7 @@ +#ifndef _SW_TEXT_H +#define _SW_TEXT_H + +void swrDrawText(SWRenderer* swr, const char* text, float x, float y, float xscale, float yscale, float angleDeg, int32_t color, float alpha, float lineSeparation); +void swrDrawDebugText(SWRenderer* swr, const char* text, int x, int y, uint32_t color, float alpha); + +#endif \ No newline at end of file From 4beb0c71551ebeafd671e2f27e4656b9780583c7 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Tue, 15 Sep 2026 01:47:59 +0300 Subject: [PATCH 110/130] * Reset portX and portY on endView. Because offsetX and offsetY are calculated on beginView, so whatever that is is just stale data. --- src/sw/sw_renderer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 75d5a0f4b..042bfca36 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -182,8 +182,8 @@ static void SWRenderer_endView(Renderer* renderer) swr->viewX = 0; swr->viewY = 0; - swr->portX = swr->offsetX; - swr->portY = swr->offsetY; + swr->portX = 0; + swr->portY = 0; swr->portW = swr->viewW = swr->width; swr->portH = swr->viewH = swr->height; swr->maxX = swr->portX + swr->portW; @@ -432,7 +432,7 @@ static void SWRenderer_drawTextUI(Renderer* renderer, const char* text, float x, (void) c3; (void) c4; (void) lineSeparation; - + swrDrawDebugText(swr, text, (int) x, (int) y, c1, alpha); } From c2f497a70724952cdf804de597ccf3c5e94c23a4 Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Tue, 15 Sep 2026 15:27:25 -0400 Subject: [PATCH 111/130] remove cancerous growth --- GameMaker-HTML5 | 1 - 1 file changed, 1 deletion(-) delete mode 160000 GameMaker-HTML5 diff --git a/GameMaker-HTML5 b/GameMaker-HTML5 deleted file mode 160000 index 8a7ea0790..000000000 --- a/GameMaker-HTML5 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8a7ea0790cc920cb1b760ca4d474ab9e4cad0b1a From f292054207dcb11d3acf785d4079cec0ad725fc1 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 15:40:51 +0300 Subject: [PATCH 112/130] Remove unused variable from sdl2.c, add xorshift32 as an option for dithered blending --- src/backends/sdl2.c | 2 +- src/sw/sw_config.h | 4 ++++ src/sw/sw_pixel_calc.h | 18 +++++++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/backends/sdl2.c b/src/backends/sdl2.c index 395bf15b8..4588769c4 100644 --- a/src/backends/sdl2.c +++ b/src/backends/sdl2.c @@ -297,7 +297,7 @@ void platformSetNextFramebuffer(uint32_t* framebuffer, int width, int height, in nextFb = NULL; } - int rmask, gmask, bmask, amask; + int rmask, gmask, bmask; if (bpp == 32) { rmask = 0x00ff0000; gmask = 0x0000ff00; diff --git a/src/sw/sw_config.h b/src/sw/sw_config.h index 6ec87f774..5979cc6a5 100644 --- a/src/sw/sw_config.h +++ b/src/sw/sw_config.h @@ -17,6 +17,10 @@ // It might be a good bit faster than doing blending the proper way. //#define SW_DITHERED_BLENDING +// For dithered blending mode, if we want to use the slightly slower, +// but more random-looking RNG +//#define SW_USE_XORSHIFT32_FOR_DITHERING + // Define if you want tinting to be implemented inaccurately //#define SW_INACCURATE_TINTING diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index 434f8dade..51449eae1 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -7,11 +7,19 @@ // Random number generator to be used for 8-bpp blending operations. FORCE_INLINE int fastRandomIsh() { - static int rng = 0; - rng += 1339; - if (rng > 601000) - rng = 0; - return rng; + static uint32_t rngseed = 1337; +#ifdef SW_USE_XORSHIFT32_FOR_DITHERING + uint32_t x = rngseed; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return rngseed = x; +#else + rngseed += 1339; + if (rngseed > 601000) + rngseed = 0; + return rngseed; +#endif } // Check if a pixel is opaque. From 0a2c048529610d202b200360e7fb8ce25ac23d46 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 15:46:15 +0300 Subject: [PATCH 113/130] Move sw_debug_font to a header. --- src/sw/{sw_debug_font.c => sw_debug_font.h} | 5 +---- src/sw/sw_text.c | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) rename src/sw/{sw_debug_font.c => sw_debug_font.h} (99%) diff --git a/src/sw/sw_debug_font.c b/src/sw/sw_debug_font.h similarity index 99% rename from src/sw/sw_debug_font.c rename to src/sw/sw_debug_font.h index e3769a30a..044899ec9 100644 --- a/src/sw/sw_debug_font.c +++ b/src/sw/sw_debug_font.h @@ -1,11 +1,8 @@ -#include -#include "sw_config.h" - #ifdef SW_ENABLE_DEBUG_FONT // Borrowed from http://www.fial.com/~scott/tamsyn-font/ -const uint8_t swrDebugFont1bpp[] = +extern const uint8_t swrDebugFont1bpp[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, diff --git a/src/sw/sw_text.c b/src/sw/sw_text.c index a451bf32e..1c43fafeb 100644 --- a/src/sw/sw_text.c +++ b/src/sw/sw_text.c @@ -108,7 +108,7 @@ static bool swrResolveGlyph( #ifdef SW_ENABLE_DEBUG_FONT -extern const uint8_t swrDebugFont1bpp[]; +#include "sw_debug_font.h" static void swrDrawDebugFontChar(SWRenderer* swr, char chr, int ax, int ay, uint32_t color, float alphaf) { From 97025f0abbe79e1b5cbf1b46f0d2c3a64d736b61 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 15:46:41 +0300 Subject: [PATCH 114/130] This was meant to say 'static' not 'extern' --- src/sw/sw_debug_font.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sw/sw_debug_font.h b/src/sw/sw_debug_font.h index 044899ec9..07ca8cf95 100644 --- a/src/sw/sw_debug_font.h +++ b/src/sw/sw_debug_font.h @@ -2,7 +2,7 @@ // Borrowed from http://www.fial.com/~scott/tamsyn-font/ -extern const uint8_t swrDebugFont1bpp[] = +static const uint8_t swrDebugFont1bpp[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, From cf2e5f8ee8b1248da37ed9c1bdcc86361a7fd7cc Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 15:49:26 +0300 Subject: [PATCH 115/130] Not sure why CI wasnt bothered by this before, but it is now --- src/sw/sw_drawing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 65cfd2a67..bcc0192c6 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -374,7 +374,7 @@ static void swrDrawSpriteInternal( srcline = &texture->buffer[(sy + (int)(ys2 >> fp_prec)) * texture->width + sx]; fixedp_t xs2 = ixs2; - for (int x = 0, xs = ixs; x < dw; x++, xs += oxs, xs2 += oxs2) + for (int x = 0; x < dw; x++, xs2 += oxs2) { uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; if (opaque(pixel)) From 1a928d934332a1f87ae1cef527c452425213d093 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 17:40:58 +0300 Subject: [PATCH 116/130] Add support for line thickness + clean up tabs/spaces differences --- src/sw/sw_config.h | 3 + src/sw/sw_debug_font.h | 254 +++++++++++++++++++------------------- src/sw/sw_drawing.c | 189 ++++++++++++++++------------ src/sw/sw_drawing.h | 10 +- src/sw/sw_inlined.h | 5 + src/sw/sw_pixel_calc.h | 24 ++-- src/sw/sw_pixel_convert.h | 58 ++++----- src/sw/sw_renderer.c | 46 +++++-- 8 files changed, 335 insertions(+), 254 deletions(-) diff --git a/src/sw/sw_config.h b/src/sw/sw_config.h index 5979cc6a5..aceb72ff1 100644 --- a/src/sw/sw_config.h +++ b/src/sw/sw_config.h @@ -42,6 +42,9 @@ // Whether to enable the debug font. #define SW_ENABLE_DEBUG_FONT +// In debug mode, print a debug line every time a frame starts/ends. +#define SW_DEBUG_FRAME_DRAW_BOUNDS + // -======- USER CONFIG END -======- // Force enable dithered blending if in 8bpp mode. diff --git a/src/sw/sw_debug_font.h b/src/sw/sw_debug_font.h index 07ca8cf95..3d8488800 100644 --- a/src/sw/sw_debug_font.h +++ b/src/sw/sw_debug_font.h @@ -4,133 +4,133 @@ static const uint8_t swrDebugFont1bpp[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, - 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x18, 0x3e, 0x60, 0x60, 0x3c, 0x06, 0x06, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x38, 0x6d, 0x6e, 0x3c, 0x08, 0x1e, 0x3b, 0x5b, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x38, 0x6c, 0x6c, 0x6c, 0x38, 0x39, 0x6d, 0x66, 0x66, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x36, 0x1c, 0x7f, 0x1c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x0c, 0x0c, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0x6e, 0x6e, 0x66, 0x76, 0x76, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x1c, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x0e, 0x1e, 0x36, 0x66, 0x7f, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x60, 0x60, 0x7c, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x30, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x0c, 0x0c, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3c, 0x46, 0x06, 0x06, 0x0c, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3e, 0x63, 0x63, 0x6f, 0x6b, 0x6b, 0x6b, 0x6f, 0x6f, 0x60, 0x60, 0x3f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1e, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1e, 0x30, 0x60, 0x60, 0x60, 0x66, 0x66, 0x36, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x63, 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x63, 0x73, 0x7b, 0x6f, 0x67, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0c, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3e, 0x60, 0x60, 0x70, 0x3c, 0x0e, 0x06, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, - 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x60, 0x60, 0x60, 0x60, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x06, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x00, 0x00, - 0x00, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x18, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x0c, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x78, 0x00, 0x00, - 0x00, 0x60, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x70, 0x3c, 0x0e, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x6b, 0x6b, 0x7f, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf0, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, - 0x70, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, - 0x00, 0x33, 0x7b, 0x6f, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, + 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x3e, 0x60, 0x60, 0x3c, 0x06, 0x06, 0x7c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6d, 0x6e, 0x3c, 0x08, 0x1e, 0x3b, 0x5b, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x6c, 0x38, 0x39, 0x6d, 0x66, 0x66, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x1c, 0x7f, 0x1c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x0c, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x6e, 0x6e, 0x66, 0x76, 0x76, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x1c, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0e, 0x1e, 0x36, 0x66, 0x7f, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x60, 0x60, 0x7c, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x30, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x0c, 0x0c, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x46, 0x06, 0x06, 0x0c, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x63, 0x63, 0x6f, 0x6b, 0x6b, 0x6b, 0x6f, 0x6f, 0x60, 0x60, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x60, 0x60, 0x60, 0x60, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x60, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x60, 0x60, 0x60, 0x66, 0x66, 0x36, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x66, 0x6c, 0x78, 0x70, 0x78, 0x6c, 0x66, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x73, 0x7b, 0x6f, 0x67, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x0c, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x60, 0x60, 0x70, 0x3c, 0x0e, 0x06, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6b, 0x7f, 0x77, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x60, 0x60, 0x60, 0x60, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x7e, 0x60, 0x60, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x00, 0x00, + 0x00, 0x60, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x0c, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x78, 0x00, 0x00, + 0x00, 0x60, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x70, 0x3c, 0x0e, 0x06, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x7e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x6b, 0x6b, 0x7f, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x3c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf0, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x70, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, + 0x00, 0x33, 0x7b, 0x6f, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; #endif // SW_ENABLE_DEBUG_FONT diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index bcc0192c6..debe2d2bb 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -5,6 +5,8 @@ // ==== Internal functions ==== +static void swrDrawTriangleTransformed(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, int alpha); + FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int blendmode, int srcalpha, int dstalpha) { SWRenderer* swr = (SWRenderer*) renderer; @@ -127,16 +129,55 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe #endif } -static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, MAYBE_UNUSED int width, uintpixel_t color1, uintpixel_t color2, int alpha) +static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, int width, uintpixel_t color1, uintpixel_t color2, int alpha, int alignment) { if (x1 == x2) { - swrDrawVLineInt(renderer, x1, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); + if (alignment == SWR_LINE_ALIGN_CENTER) + x1 -= width / 2; + else if (alignment == SWR_LINE_ALIGN_M1) + x1 -= width; + + logDebug("swrDrawLineInt V: width: %d\n", width); + for (int i = 0; i < width; i++) { + swrDrawVLineInt(renderer, x1 + i, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); + } return; } if (y1 == y2) { - swrDrawHLineInt(renderer, swrMin(x1, x2), y1, swrAbs(x1 - x2), color1, color2, alpha); + if (alignment == SWR_LINE_ALIGN_CENTER) + y1 -= width / 2; + else if (alignment == SWR_LINE_ALIGN_M1) + y1 -= width; + + logDebug("swrDrawLineInt H: width: %d\n", width); + for (int i = 0; i < width; i++) { + swrDrawHLineInt(renderer, swrMin(x1, x2), y1 + i, swrAbs(x1 - x2), color1, color2, alpha); + } + return; + } + + if (UNLIKELY(width > 1)) + { + // HACK: Just draw two triangles instead. + int xd = swrAbs(x1 - x2), yd = swrAbs(y1 - y2); + int line_length_sqr = xd * yd + yd * yd; + if (line_length_sqr <= 0) + return; + + float line_length = sqrtf(line_length_sqr); + float xds = (float)xd * width / line_length; + float yds = (float)yd * width / line_length; + + float x1l = x1 - yds, y1l = y1 + xds; + float x1r = x1 + yds, y1r = y1 - xds; + float x2l = x2 - yds, y2l = y2 + xds; + float x2r = x2 + yds, y2r = y2 - xds; + + logDebug("swrDrawLineInt N/A: width: %d, xds: %f, yds: %f\n", width, xds, yds); + swrDrawTriangleTransformed(renderer, x1l, y1l, x1r, y1r, x2l, y2l, color1, color1, color2, alpha); + swrDrawTriangleTransformed(renderer, x2r, y2r, x1r, y1r, x2l, y2l, color2, color1, color2, alpha); return; } @@ -649,6 +690,52 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft } } +static void swrDrawTriangleTransformed(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, int alpha) +{ + float xup, yup, xleft, yleft, xright, yright; + uint32_t colorup, colorleft, colorright; + + SWRenderer* swr = (SWRenderer*) renderer; + + //which vertex is higher? + xup = x1, yup = y1; colorup = color1; + xleft = x2, yleft = y2; colorleft = color2; + xright = x3, yright = y3; colorright = color3; + if (yup > y2) { + xup = x2, yup = y2, colorup = color2; + xleft = x1, yleft = y1, colorleft = color1; + //xright = x3, yright = y3; + } + if (yup > y3) { + xup = x3, yup = y3, colorup = color3; + xleft = x1, yleft = y1, colorleft = color1; + xright = x2, yright = y2, colorright = color2; + } + + if (xleft > xright) { + float tmp = xleft; + xleft = xright; + xright = tmp; + tmp = yleft; + yleft = yright; + yright = tmp; + uint32_t tmp2 = colorleft; + colorleft = colorright; + colorright = tmp2; + } + + swrDrawTriangleInternal( + swr, + swrFloor(xup), swrFloor(yup), + swrFloor(xleft), swrCeiling(yleft), + swrFloor(xright), swrCeiling(yright), + colorup, + colorleft, + colorright, + alpha + ); +} + // ==== Exposed interface ==== bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView) @@ -754,53 +841,34 @@ void swrPlotPixel(Renderer* renderer, float x, float y, uintpixel_t color, float swrPlotPixel_(renderer, (float) x, (float) y, color, blendmode, srcalpha, invalpha); } -void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha) -{ - SWRenderer *swr = (SWRenderer*) renderer; - float thickness = 1; - - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &dw, &thickness); - - // TODO: use thickness - swrDrawHLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dw), color, color2, swrIntAlpha(alpha)); -} - -void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, uintpixel_t color2, float alpha) -{ - SWRenderer *swr = (SWRenderer*) renderer; - float thickness = 1; - - swrTransformPosIfNeeded(swr, &dx, &dy); - swrTransformSizeIfNeeded(swr, &thickness, &dh); - - // TODO: use thickness - swrDrawVLineInt(renderer, swrFloor(dx), swrFloor(dy), swrCeiling(dh), color, color2, swrIntAlpha(alpha)); -} - -void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha) +void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha, int alignment) { SWRenderer* swr = (SWRenderer*) renderer; swrTransformPosIfNeeded(swr, &x1, &y1); swrTransformPosIfNeeded(swr, &x2, &y2); + logDebug("swrDrawLine BEFORE width: %f\n", width); swrTransformSizeIfNeeded(swr, &width, NULL); - swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrCeiling(x2), swrCeiling(y2), swrCeiling(width), color, color2, swrIntAlpha(alpha)); + int iwidth = swrRound(width); + logDebug("swrDrawLine AFTER width: %f intwidth: %d\n", width, iwidth); + swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrFloor(x2), swrFloor(y2), iwidth, color, color2, swrIntAlpha(alpha), alignment); } void swrDrawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha) { - swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color, color, alpha); - swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color, color, alpha); - swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color, color, alpha); - swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color, color, alpha); + int x1i = swrRound(x1), x2i = swrRound(x2), y1i = swrRound(y1), y2i = swrRound(y2); + swrDrawLine(renderer, x1i, y1i, x2i, y1i, 1.0f, color, color, alpha, SWR_LINE_ALIGN_P1); + swrDrawLine(renderer, x1i, y2i, x2i, y2i, 1.0f, color, color, alpha, SWR_LINE_ALIGN_M1); + swrDrawLine(renderer, x1i, y1i, x1i, y2i, 1.0f, color, color, alpha, SWR_LINE_ALIGN_P1); + swrDrawLine(renderer, x2i, y1i, x2i, y2i, 1.0f, color, color, alpha, SWR_LINE_ALIGN_M1); } void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uintpixel_t color4, float alpha) { - swrDrawHLine(renderer, x1, y1, (x2 - x1) + 1, color1, color2, alpha); - swrDrawHLine(renderer, x1, y2, (x2 - x1) + 1, color3, color4, alpha); - swrDrawVLine(renderer, x1, y1, (y2 - y1) + 1, color1, color3, alpha); - swrDrawVLine(renderer, x2, y1, (y2 - y1) + 1, color2, color4, alpha); + int x1i = swrRound(x1), x2i = swrRound(x2), y1i = swrRound(y1), y2i = swrRound(y2); + swrDrawLine(renderer, x1i, y1i, x2i, y1i, 1.0f, color1, color2, alpha, SWR_LINE_ALIGN_P1); + swrDrawLine(renderer, x1i, y2i, x2i, y2i, 1.0f, color3, color4, alpha, SWR_LINE_ALIGN_M1); + swrDrawLine(renderer, x1i, y1i, x1i, y2i, 1.0f, color1, color3, alpha, SWR_LINE_ALIGN_P1); + swrDrawLine(renderer, x2i, y1i, x2i, y2i, 1.0f, color2, color4, alpha, SWR_LINE_ALIGN_M1); } void swrFillRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t pxcolor, float alpha) @@ -904,49 +972,18 @@ void swrDrawSpriteRotated( void swrDrawTriangle(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uint32_t color1, uint32_t color2, uint32_t color3, float alpha) { - float xup, yup, xleft, yleft, xright, yright; - uint32_t colorup, colorleft, colorright; - SWRenderer* swr = (SWRenderer*) renderer; swrTransformPosIfNeeded(swr, &x1, &y1); swrTransformPosIfNeeded(swr, &x2, &y2); swrTransformPosIfNeeded(swr, &x3, &y3); - - //which vertex is higher? - xup = x1, yup = y1; colorup = color1; - xleft = x2, yleft = y2; colorleft = color2; - xright = x3, yright = y3; colorright = color3; - if (yup > y2) { - xup = x2, yup = y2, colorup = color2; - xleft = x1, yleft = y1, colorleft = color1; - //xright = x3, yright = y3; - } - if (yup > y3) { - xup = x3, yup = y3, colorup = color3; - xleft = x1, yleft = y1, colorleft = color1; - xright = x2, yright = y2, colorright = color2; - } - - if (xleft > xright) { - float tmp = xleft; - xleft = xright; - xright = tmp; - tmp = yleft; - yleft = yright; - yright = tmp; - uint32_t tmp2 = colorleft; - colorleft = colorright; - colorright = tmp2; - } - - swrDrawTriangleInternal( - swr, - swrFloor(xup), swrFloor(yup), - swrFloor(xleft), swrCeiling(yleft), - swrFloor(xright), swrCeiling(yright), - swrConvertPixel(colorup), - swrConvertPixel(colorleft), - swrConvertPixel(colorright), + swrDrawTriangleTransformed( + renderer, + x1, y1, + x2, y2, + x3, y3, + swrConvertPixel(color1), + swrConvertPixel(color2), + swrConvertPixel(color3), swrIntAlpha(alpha) ); } diff --git a/src/sw/sw_drawing.h b/src/sw/sw_drawing.h index 333c7d645..211bce8e2 100644 --- a/src/sw/sw_drawing.h +++ b/src/sw/sw_drawing.h @@ -1,11 +1,15 @@ #ifndef _SW_DRAWING_H #define _SW_DRAWING_H +enum { + SWR_LINE_ALIGN_CENTER, + SWR_LINE_ALIGN_M1, + SWR_LINE_ALIGN_P1, +}; + bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restoreOldView); void swrPlotPixel(Renderer* renderer, float x, float y, uintpixel_t color, float alpha); -void swrDrawHLine(Renderer* renderer, float dx, float dy, float dw, uintpixel_t color, uintpixel_t color2, float alpha); -void swrDrawVLine(Renderer* renderer, float dx, float dy, float dh, uintpixel_t color, uintpixel_t color2, float alpha); -void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha); +void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha, int alignment); void swrDrawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha); void swrDrawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uintpixel_t color4, float alpha); void swrFillRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t color, float alpha); diff --git a/src/sw/sw_inlined.h b/src/sw/sw_inlined.h index e37407a57..8dddbf706 100644 --- a/src/sw/sw_inlined.h +++ b/src/sw/sw_inlined.h @@ -25,6 +25,11 @@ FORCE_INLINE int swrCeiling(float x) return i + (x > (float) i); } +FORCE_INLINE int swrRound(float x) +{ + return swrFloor(x + 0.5f); +} + // Checks if the "rotate" version of the sprite drawing routine should be used. FORCE_INLINE bool swrMustRotate(float angleDeg) { diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index 51449eae1..42a0a7e73 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -9,16 +9,16 @@ FORCE_INLINE int fastRandomIsh() { static uint32_t rngseed = 1337; #ifdef SW_USE_XORSHIFT32_FOR_DITHERING - uint32_t x = rngseed; - x ^= x << 13; - x ^= x >> 17; - x ^= x << 5; - return rngseed = x; + uint32_t x = rngseed; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return rngseed = x; #else - rngseed += 1339; - if (rngseed > 601000) - rngseed = 0; - return rngseed; + rngseed += 1339; + if (rngseed > 601000) + rngseed = 0; + return rngseed; #endif } @@ -293,10 +293,10 @@ FORCE_INLINE uintpixel_t swrThreeWayBlend(uintpixel_t color1, uintpixel_t color2 { #if defined SW_DITHERED_BLENDING int rng = fastRandomIsh() & 0xFFFF; - if (rng < frac1) return color1; else rng -= frac1; - if (rng < frac2) return color2; + if (rng < frac1) return color1; else rng -= frac1; + if (rng < frac2) return color2; (void) frac3; - return color3; + return color3; #elif PIXEL_SIZE == 32 Pixel32ARGB x1, x2, x3, out; x1.l = color1; diff --git a/src/sw/sw_pixel_convert.h b/src/sw/sw_pixel_convert.h index 82983a890..69c7966b2 100644 --- a/src/sw/sw_pixel_convert.h +++ b/src/sw/sw_pixel_convert.h @@ -18,67 +18,67 @@ typedef uint8_t uintpixel_t; // The native format coming out of GameMaker Studio. typedef union { - struct { - uint8_t r, g, b, a; - } p; - uint32_t l; + struct { + uint8_t r, g, b, a; + } p; + uint32_t l; } Pixel32ABGR; // This is the format the renderer knows how to work with. typedef union { - struct { + struct { #ifdef IS_BIG_ENDIAN - uint8_t a, r, g, b; + uint8_t a, r, g, b; #else - uint8_t b, g, r, a; + uint8_t b, g, r, a; #endif - } p; - uint32_t l; + } p; + uint32_t l; } Pixel32ARGB; FORCE_INLINE UNUSED uint16_t abgr8888_to_rgb1555(uint32_t xl) { - Pixel32ABGR x; - x.l = xl; - return (x.p.b >> 3) | ((x.p.g >> 3) << 5) | ((x.p.r >> 3) << 10) | ((x.p.a >> 7) << 15); + Pixel32ABGR x; + x.l = xl; + return (x.p.b >> 3) | ((x.p.g >> 3) << 5) | ((x.p.r >> 3) << 10) | ((x.p.a >> 7) << 15); } FORCE_INLINE UNUSED uint8_t abgr8888_to_rgb332(uint32_t xl) { - Pixel32ABGR x; - x.l = xl; - + Pixel32ABGR x; + x.l = xl; + #if PIXEL_SIZE == 8 - //check if transparent - if (x.p.a < 128) - return PXL_TRANSPARENT; + //check if transparent + if (x.p.a < 128) + return PXL_TRANSPARENT; #endif - - uint8_t pxl = (x.p.r >> 5) | ((x.p.g >> 5) << 3) | ((x.p.b >> 6) << 6); - + + uint8_t pxl = (x.p.r >> 5) | ((x.p.g >> 5) << 3) | ((x.p.b >> 6) << 6); + #if PIXEL_SIZE == 8 - //hacky fixup - if (pxl == PXL_TRANSPARENT) - pxl++; + //hacky fixup + if (pxl == PXL_TRANSPARENT) + pxl++; #endif - - return pxl; + + return pxl; } FORCE_INLINE UNUSED uintpixel_t swrConvertPixelBase(uint32_t gmPixel) { #if PIXEL_SIZE == 32 - return (gmPixel & 0xFF00FF00) | ((gmPixel & 0xFF) << 16) | ((gmPixel >> 16) & 0xFF); + return (gmPixel & 0xFF00FF00) | ((gmPixel & 0xFF) << 16) | ((gmPixel >> 16) & 0xFF); #elif PIXEL_SIZE == 16 - return abgr8888_to_rgb1555(gmPixel); + return abgr8888_to_rgb1555(gmPixel); #elif PIXEL_SIZE == 8 - return abgr8888_to_rgb332(gmPixel); + return abgr8888_to_rgb332(gmPixel); #endif } diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 042bfca36..9a8ddcb9e 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -85,7 +85,9 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam swr->height = windowH; } - logDebug("SWRenderer_beginFrame\n"); +#ifdef SW_DEBUG_FRAME_DRAW_BOUNDS + logDebug("swr: begin drawing frame\n"); +#endif } // This used to be just one, "endFrame". Not sure what the difference is. @@ -96,15 +98,21 @@ static void SWRenderer_endFrameInit(Renderer* renderer) //this is kinda useless to do twice isn't it? } +static void swrDebug(Renderer* renderer); + static void SWRenderer_endFrameEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; assert(!swr->drawingToSurface); - logDebug("SWRenderer_endFrameEnd\n"); +#ifdef SW_DEBUG_FRAME_DRAW_BOUNDS + logDebug("swr: end drawing frame\n"); +#endif platformSetNextFramebuffer(swr->fb, swr->width, swr->height, PIXEL_SIZE); + swrDebug(renderer); + swr->primitiveOverflow = false; } @@ -367,7 +375,8 @@ static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2 #ifdef TRANSPARENT_MASK colorCvt |= TRANSPARENT_MASK; #endif - swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha); + logDebug("SWRenderer_drawLine width: %f\n", width); + swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha, SWR_LINE_ALIGN_CENTER); } static void SWRenderer_drawTriangle(Renderer* renderer, @@ -380,9 +389,9 @@ static void SWRenderer_drawTriangle(Renderer* renderer, uintpixel_t color1cvt = swrConvertPixel(color1); uintpixel_t color2cvt = swrConvertPixel(color2); uintpixel_t color3cvt = swrConvertPixel(color3); - swrDrawLine(renderer, x1, y1, x2, y2, 1, color1cvt, color2cvt, renderer->drawAlpha); - swrDrawLine(renderer, x1, y1, x3, y3, 1, color1cvt, color3cvt, renderer->drawAlpha); - swrDrawLine(renderer, x2, y2, x3, y3, 1, color3cvt, color3cvt, renderer->drawAlpha); + swrDrawLine(renderer, x1, y1, x2, y2, 1, color1cvt, color2cvt, alpha, SWR_LINE_ALIGN_CENTER); + swrDrawLine(renderer, x1, y1, x3, y3, 1, color1cvt, color3cvt, alpha, SWR_LINE_ALIGN_CENTER); + swrDrawLine(renderer, x2, y2, x3, y3, 1, color3cvt, color3cvt, alpha, SWR_LINE_ALIGN_CENTER); } else { @@ -393,7 +402,8 @@ static void SWRenderer_drawTriangle(Renderer* renderer, static void SWRenderer_drawLineColor(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uint32_t color1, uint32_t color2, float alpha) { - swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color1), swrConvertPixel(color2), alpha); + logDebug("SWRenderer_drawLineColor width: %f\n", width); + swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color1), swrConvertPixel(color2), alpha, SWR_LINE_ALIGN_CENTER); } static void SWRenderer_drawText(Renderer* renderer, const char* text, float x, float y, @@ -1426,6 +1436,28 @@ static void SWRenderer_drawVertexBuffer(Renderer* renderer, VertexBuffer* buffer UNIMP(); } +static void SWRenderer_drawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, + uint32_t color, float alpha, bool outline); + +static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2, float y2, + float width, uint32_t color, float alpha); + +static void SWRenderer_drawTriangle(Renderer* renderer, + float x1, float y1, float x2, float y2, float x3, float y3, + uint32_t color1, uint32_t color2, uint32_t color3, + float alpha, bool outline); + +static void swrDebug(Renderer* renderer) +{ + SWRenderer_drawRectangle(renderer, 50, 50, 200, 200, 0xFF00FF, 1.0f, true); + SWRenderer_drawLine(renderer, 250, 50, 300, 200, 1.0f, 0x00FF00, 1.0f); + SWRenderer_drawLine(renderer, 300, 50, 350, 200, 2.0f, 0x00FF00, 1.0f); + SWRenderer_drawLine(renderer, 350, 50, 400, 200, 5.0f, 0x00FF00, 1.0f); + SWRenderer_drawLine(renderer, 400, 50, 450, 200, 10.0f, 0x00FF00, 1.0f); + SWRenderer_drawTriangle(renderer, 450, 50, 500, 50, 450, 200, 0xFF0000, 0xFF0000, 0xFF0000, 1.0f, false); + SWRenderer_drawTriangle(renderer, 500, 50, 550, 50, 500, 200, 0x0000FF, 0x0000FF, 0x0000FF, 1.0f, true); +} + Renderer* SWRenderer_create(void) { SWRenderer* swr = (SWRenderer*) safeCalloc(1, sizeof(SWRenderer)); From 098fbbaa2c047ff135be1ab68ad4c334ac2b4104 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 17:41:24 +0300 Subject: [PATCH 117/130] Don't forget to remove the debug code --- src/sw/sw_renderer.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 9a8ddcb9e..4d4d4fb7f 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -98,8 +98,6 @@ static void SWRenderer_endFrameInit(Renderer* renderer) //this is kinda useless to do twice isn't it? } -static void swrDebug(Renderer* renderer); - static void SWRenderer_endFrameEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; @@ -110,9 +108,7 @@ static void SWRenderer_endFrameEnd(Renderer* renderer) #endif platformSetNextFramebuffer(swr->fb, swr->width, swr->height, PIXEL_SIZE); - - swrDebug(renderer); - + swr->primitiveOverflow = false; } @@ -1436,28 +1432,6 @@ static void SWRenderer_drawVertexBuffer(Renderer* renderer, VertexBuffer* buffer UNIMP(); } -static void SWRenderer_drawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, - uint32_t color, float alpha, bool outline); - -static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2, float y2, - float width, uint32_t color, float alpha); - -static void SWRenderer_drawTriangle(Renderer* renderer, - float x1, float y1, float x2, float y2, float x3, float y3, - uint32_t color1, uint32_t color2, uint32_t color3, - float alpha, bool outline); - -static void swrDebug(Renderer* renderer) -{ - SWRenderer_drawRectangle(renderer, 50, 50, 200, 200, 0xFF00FF, 1.0f, true); - SWRenderer_drawLine(renderer, 250, 50, 300, 200, 1.0f, 0x00FF00, 1.0f); - SWRenderer_drawLine(renderer, 300, 50, 350, 200, 2.0f, 0x00FF00, 1.0f); - SWRenderer_drawLine(renderer, 350, 50, 400, 200, 5.0f, 0x00FF00, 1.0f); - SWRenderer_drawLine(renderer, 400, 50, 450, 200, 10.0f, 0x00FF00, 1.0f); - SWRenderer_drawTriangle(renderer, 450, 50, 500, 50, 450, 200, 0xFF0000, 0xFF0000, 0xFF0000, 1.0f, false); - SWRenderer_drawTriangle(renderer, 500, 50, 550, 50, 500, 200, 0x0000FF, 0x0000FF, 0x0000FF, 1.0f, true); -} - Renderer* SWRenderer_create(void) { SWRenderer* swr = (SWRenderer*) safeCalloc(1, sizeof(SWRenderer)); From a605c6278ca992a432b89b6a02abed6cc2e4aa7d Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 18:14:43 +0300 Subject: [PATCH 118/130] Fix issues with line thickness at non-right angles --- src/sw/sw_drawing.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index debe2d2bb..2529b62ef 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -161,23 +161,27 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i if (UNLIKELY(width > 1)) { // HACK: Just draw two triangles instead. - int xd = swrAbs(x1 - x2), yd = swrAbs(y1 - y2); - int line_length_sqr = xd * yd + yd * yd; + int xd = x2 - x1, yd = y2 - y1; + int line_length_sqr = xd * xd + yd * yd; if (line_length_sqr <= 0) return; float line_length = sqrtf(line_length_sqr); - float xds = (float)xd * width / line_length; - float yds = (float)yd * width / line_length; + float xds = (float)-yd * (width * 0.5f) / line_length; + float yds = (float)+xd * (width * 0.5f) / line_length; - float x1l = x1 - yds, y1l = y1 + xds; - float x1r = x1 + yds, y1r = y1 - xds; - float x2l = x2 - yds, y2l = y2 + xds; - float x2r = x2 + yds, y2r = y2 - xds; + float x1l = x1 - xds, y1l = y1 - yds; + float x1r = x1 + xds, y1r = y1 + yds; + float x2l = x2 - xds, y2l = y2 - yds; + float x2r = x2 + xds, y2r = y2 + yds; logDebug("swrDrawLineInt N/A: width: %d, xds: %f, yds: %f\n", width, xds, yds); swrDrawTriangleTransformed(renderer, x1l, y1l, x1r, y1r, x2l, y2l, color1, color1, color2, alpha); swrDrawTriangleTransformed(renderer, x2r, y2r, x1r, y1r, x2l, y2l, color2, color1, color2, alpha); + swrPlotPixel_(renderer, x1l, y1l, 0xFF00FF, bm_normal, 256, 0); + swrPlotPixel_(renderer, x2l, y2l, 0xFF00FF, bm_normal, 256, 0); + swrPlotPixel_(renderer, x1r, y1r, 0xFF00FF, bm_normal, 256, 0); + swrPlotPixel_(renderer, x2r, y2r, 0xFF00FF, bm_normal, 256, 0); return; } From 6228dd702e5eb058314f1d769095813f32c312e7 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 18:16:54 +0300 Subject: [PATCH 119/130] Clean up debug logs --- src/sw/sw_config.h | 2 +- src/sw/sw_drawing.c | 11 ++--------- src/sw/sw_renderer.c | 10 ++++------ 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/sw/sw_config.h b/src/sw/sw_config.h index aceb72ff1..91031eff9 100644 --- a/src/sw/sw_config.h +++ b/src/sw/sw_config.h @@ -43,7 +43,7 @@ #define SW_ENABLE_DEBUG_FONT // In debug mode, print a debug line every time a frame starts/ends. -#define SW_DEBUG_FRAME_DRAW_BOUNDS +//#define SW_DEBUG_FRAME_DRAW_BOUNDS // -======- USER CONFIG END -======- diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 2529b62ef..6c3445bd7 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -138,7 +138,6 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i else if (alignment == SWR_LINE_ALIGN_M1) x1 -= width; - logDebug("swrDrawLineInt V: width: %d\n", width); for (int i = 0; i < width; i++) { swrDrawVLineInt(renderer, x1 + i, swrMin(y1, y2), swrAbs(y1 - y2), color1, color2, alpha); } @@ -151,7 +150,6 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i else if (alignment == SWR_LINE_ALIGN_M1) y1 -= width; - logDebug("swrDrawLineInt H: width: %d\n", width); for (int i = 0; i < width; i++) { swrDrawHLineInt(renderer, swrMin(x1, x2), y1 + i, swrAbs(x1 - x2), color1, color2, alpha); } @@ -175,7 +173,6 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i float x2l = x2 - xds, y2l = y2 - yds; float x2r = x2 + xds, y2r = y2 + yds; - logDebug("swrDrawLineInt N/A: width: %d, xds: %f, yds: %f\n", width, xds, yds); swrDrawTriangleTransformed(renderer, x1l, y1l, x1r, y1r, x2l, y2l, color1, color1, color2, alpha); swrDrawTriangleTransformed(renderer, x2r, y2r, x1r, y1r, x2l, y2l, color2, color1, color2, alpha); swrPlotPixel_(renderer, x1l, y1l, 0xFF00FF, bm_normal, 256, 0); @@ -549,8 +546,6 @@ static void swrDrawSpriteRotatedInternal( static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, int alpha) { - logDebug("swrDrawTriangleInternal: Coords: (%d,%d) (%d,%d) (%d,%d)\n", xup, yup, xleft, yleft, xright, yright); - int srcalpha = swrCalcSrcAlpha(swr, alpha); int invalpha = swrCalcDstAlpha(swr, alpha); int blendmode = swr->blendMode; @@ -624,7 +619,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft int area = (xleft - xup) * (yright - yup) - (yleft - yup) * (xright - xup); if (area == 0) { - logDebug("SWR: Area is 0, returning early. Coords: (%d,%d) (%d,%d) (%d,%d)\n", xup, yup, xleft, yleft, xright, yright); + //logDebug("SWR: Area is 0, returning early. Coords: (%d,%d) (%d,%d) (%d,%d)\n", xup, yup, xleft, yleft, xright, yright); return; } @@ -756,7 +751,7 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor return true; // restore the original framebuffer - logDebug("swr: back to original framebuffer (%d)\n", targetSurfaceId); + //logDebug("swr: back to original framebuffer (%d)\n", targetSurfaceId); swr->drawingToSurface = false; swr->fb = swr->mainFb; swr->width = swr->mainWidth; @@ -850,10 +845,8 @@ void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, flo SWRenderer* swr = (SWRenderer*) renderer; swrTransformPosIfNeeded(swr, &x1, &y1); swrTransformPosIfNeeded(swr, &x2, &y2); - logDebug("swrDrawLine BEFORE width: %f\n", width); swrTransformSizeIfNeeded(swr, &width, NULL); int iwidth = swrRound(width); - logDebug("swrDrawLine AFTER width: %f intwidth: %d\n", width, iwidth); swrDrawLineInt(renderer, swrFloor(x1), swrFloor(y1), swrFloor(x2), swrFloor(y2), iwidth, color, color2, swrIntAlpha(alpha), alignment); } diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 4d4d4fb7f..a068abda9 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -371,7 +371,6 @@ static void SWRenderer_drawLine(Renderer* renderer, float x1, float y1, float x2 #ifdef TRANSPARENT_MASK colorCvt |= TRANSPARENT_MASK; #endif - logDebug("SWRenderer_drawLine width: %f\n", width); swrDrawLine(renderer, x1, y1, x2, y2, width, colorCvt, colorCvt, alpha, SWR_LINE_ALIGN_CENTER); } @@ -398,7 +397,6 @@ static void SWRenderer_drawTriangle(Renderer* renderer, static void SWRenderer_drawLineColor(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uint32_t color1, uint32_t color2, float alpha) { - logDebug("SWRenderer_drawLineColor width: %f\n", width); swrDrawLine(renderer, x1, y1, x2, y2, width, swrConvertPixel(color1), swrConvertPixel(color2), alpha, SWR_LINE_ALIGN_CENTER); } @@ -637,7 +635,7 @@ static void SWRenderer_gpuSetBlendMode(Renderer* renderer, int32_t mode) SWRenderer* swr = (SWRenderer*) renderer; swr->blendMode = mode; - logDebug("swr: switching to blend mode %d\n", mode); + //logDebug("swr: switching to blend mode %d\n", mode); if (swr->usingAlphaBlendState) { @@ -1293,7 +1291,7 @@ static void SWRenderer_primitiveBegin(Renderer* renderer, int32_t primitiveType) swr->primitiveType = primitiveType; swr->vertexCount = 0; swr->primitiveBegun = true; - logDebug("swr: primitiveBegin(%d)\n", primitiveType); + //logDebug("swr: primitiveBegin(%d)\n", primitiveType); } static void SWRenderer_primitiveBeginTexture(Renderer* renderer, int32_t primitiveType, int32_t texture) @@ -1326,7 +1324,7 @@ static void SWRenderer_primitiveEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; - logWarn("SWR: Ending with %d vertices, primitive type %d\n", swr->vertexCount, swr->primitiveType); + //logWarn("SWR: Ending with %d vertices, primitive type %d\n", swr->vertexCount, swr->primitiveType); switch (swr->primitiveType) { case PRIMITIVE_POINTS: @@ -1410,7 +1408,7 @@ static void SWRenderer_drawVertex(Renderer* renderer, float x, float y, float z, pVertex->x = x; pVertex->y = y; - logDebug("swr: drawVertex(%f, %f)\n", x, y); + //logDebug("swr: drawVertex(%f, %f)\n", x, y); // Texture mapping and the third dimension are not supported. (void) z; From 275bcb9102c553b5af7b47e682e3be647fffd9b6 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 18:26:22 +0300 Subject: [PATCH 120/130] FillRectangleColor now supports vertical gradient blending too --- src/sw/sw_drawing.c | 21 +++++++++++++++++++-- src/sw/sw_pixel_calc.h | 28 ++++++++++++++++++++++++++++ src/sw/sw_renderer.c | 5 +++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 6c3445bd7..94462d89d 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -889,6 +889,11 @@ void swrFillRectangle(Renderer* renderer, float x1, float y1, float x2, float y2 void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, uintpixel_t pxcolor1, uintpixel_t pxcolor2, uintpixel_t pxcolor3, uintpixel_t pxcolor4, float alpha) { + if (pxcolor1 == pxcolor2 && pxcolor2 == pxcolor3 && pxcolor3 == pxcolor4) { + swrFillRectangle(renderer, x1, y1, x2, y2, pxcolor1, alpha); + return; + } + SWRenderer* swr = (SWRenderer*) renderer; swrTransformPosIfNeeded(swr, &x1, &y1); swrTransformPosIfNeeded(swr, &x2, &y2); @@ -905,8 +910,20 @@ void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, flo (void) pxcolor3; (void) pxcolor4; - for (int y = 0; y <= yd; y++) { - swrDrawHLineInt(renderer, x1i, y1i + y, xd, pxcolor1, pxcolor2, alphaInt); + uint64_t inc = (65536ULL << 16) / yd; + uint64_t weightfp = 0; + + for (int y = 0; y <= yd; y++, weightfp += inc) + { + uint16_t weight = (uint16_t)(weightfp >> 16); + if (weight > 65535) weight = 65535; + uint16_t weightOther = 65535 - weight; + + uintpixel_t intcolor1, intcolor2; + intcolor1 = swrTwoWayBlend(pxcolor1, pxcolor4, weightOther, weight); + intcolor2 = swrTwoWayBlend(pxcolor2, pxcolor3, weightOther, weight); + + swrDrawHLineInt(renderer, x1i, y1i + y, xd, intcolor1, intcolor2, alphaInt); } } diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index 42a0a7e73..ce7a7a35b 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -287,6 +287,34 @@ FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) #endif } +// Blends a pixel between two colors. +// frac means 0-65535 where 65535 means one. And frac1 + frac2 MUST be equal to 65535. +FORCE_INLINE uintpixel_t swrTwoWayBlend(uintpixel_t color1, uintpixel_t color2, uint16_t frac1, uint16_t frac2) +{ +#if defined SW_DITHERED_BLENDING + int rng = fastRandomIsh() & 0xFFFF; + if (rng < frac1) return color1; + return color2; +#elif PIXEL_SIZE == 32 + Pixel32ARGB x1, x2, out; + x1.l = color1; + x2.l = color2; + out.p.r = (x1.p.r * frac1 + x2.p.r * frac2) >> 16; + out.p.g = (x1.p.g * frac1 + x2.p.g * frac2) >> 16; + out.p.b = (x1.p.b * frac1 + x2.p.b * frac2) >> 16; + out.p.a = x1.p.a; + return out.l; +#elif PIXEL_SIZE == 16 + int c1b = color1 & 0x1F, c1g = (color1 >> 5) & 0x1F, c1r = (color1 >> 10) & 0x1F; + int c2b = color2 & 0x1F, c2g = (color2 >> 5) & 0x1F, c2r = (color2 >> 10) & 0x1F; + int ca = color1 & 0x8000; + int cr = (c1r * frac1 + c2r * frac2) >> 16; + int cg = (c1g * frac1 + c2g * frac2) >> 16; + int cb = (c1b * frac1 + c2b * frac2) >> 16; + return ca | cb | (cg << 5) | (cr << 10); +#endif +} + // Blends a pixel between three colors. // frac means 0-65535 where 65535 means one. And frac1 + frac2 + frac3 MUST be equal to 65535. FORCE_INLINE uintpixel_t swrThreeWayBlend(uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uint16_t frac1, uint16_t frac2, uint16_t frac3) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index a068abda9..533bdb747 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -98,6 +98,11 @@ static void SWRenderer_endFrameInit(Renderer* renderer) //this is kinda useless to do twice isn't it? } + +static void SWRenderer_drawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, + uint32_t color1, uint32_t color2, uint32_t color3, uint32_t color4, + float alpha, bool outline); + static void SWRenderer_endFrameEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; From bbe876acdc34f3cfa11d4103c6af1b81232b9856 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 18:35:14 +0300 Subject: [PATCH 121/130] Now using swrTwoWayBlend for swrDrawHLineInt and swrDrawVLineInt too. This allows for dithering in 16 and 8 bit mode too. --- src/sw/sw_drawing.c | 85 ++++++++++++------------------------------ src/sw/sw_pixel_calc.h | 1 + 2 files changed, 24 insertions(+), 62 deletions(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 94462d89d..68ecb90e4 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -31,45 +31,26 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe int invalpha = swrCalcDstAlpha(swr, alpha); int blendmode = swr->blendMode; -#if PIXEL_SIZE == 32 if (color == color2) -#endif { uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; for (int i = 0; i < dw; i++) alphaBlend(&line[i], color, blendmode, srcalpha, invalpha); } -#if PIXEL_SIZE == 32 else { - Pixel32ARGB clr1, clr2; - clr1.l = color; - clr2.l = color2; - - uint32_t rinit = clr1.p.r << 20; - uint32_t ginit = clr1.p.g << 20; - uint32_t binit = clr1.p.b << 20; - int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; - int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; - int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; - rstep /= dw; - gstep /= dw; - bstep /= dw; - uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; - for (int i = 0; i < dw; i++) + uint32_t inc = (65536U << 14) / dw; + uint32_t weightfp = 0; + for (int i = 0; i < dw; i++, weightfp += inc) { - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - alphaBlend(&line[i], resultPixel.l, blendmode, srcalpha, invalpha); + uint32_t weight2 = (weightfp >> 14); + if (weight2 > 65535) weight2 = 65535; + uint32_t weight1 = 65535 - weight2; + uintpixel_t result = swrTwoWayBlend(color, color2, (uint16_t) weight1, (uint16_t) weight2); + alphaBlend(&line[i], result, blendmode, srcalpha, invalpha); } } -#endif } static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) @@ -86,9 +67,7 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe int invalpha = swrCalcDstAlpha(swr, alpha); int blendmode = swr->blendMode; -#if PIXEL_SIZE == 32 if (color == color2) -#endif { for (int i = 0; i < dh; i++) { @@ -96,37 +75,20 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe alphaBlend(&line[0], color, blendmode, srcalpha, invalpha); } } -#if PIXEL_SIZE == 32 else { - Pixel32ARGB clr1, clr2; - clr1.l = color; - clr2.l = color2; - - uint32_t rinit = clr1.p.r << 20; - uint32_t ginit = clr1.p.g << 20; - uint32_t binit = clr1.p.b << 20; - int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; - int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; - int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; - rstep /= dh; - gstep /= dh; - bstep /= dh; - - for (int i = 0; i < dh; i++) + uint32_t inc = (65536U << 14) / dh; + uint32_t weightfp = 0; + for (int i = 0; i < dh; i++, weightfp += inc) { + uint32_t weight2 = (weightfp >> 14); + if (weight2 > 65535) weight2 = 65535; + uint32_t weight1 = 65535 - weight2; + uintpixel_t result = swrTwoWayBlend(color, color2, (uint16_t) weight1, (uint16_t) weight2); uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - alphaBlend(&line[0], resultPixel.l, blendmode, srcalpha, invalpha); + alphaBlend(&line[0], result, blendmode, srcalpha, invalpha); } } -#endif } static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, int width, uintpixel_t color1, uintpixel_t color2, int alpha, int alignment) @@ -910,18 +872,17 @@ void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, flo (void) pxcolor3; (void) pxcolor4; - uint64_t inc = (65536ULL << 16) / yd; - uint64_t weightfp = 0; - + uint32_t inc = (65536U << 14) / yd; + uint32_t weightfp = 0; for (int y = 0; y <= yd; y++, weightfp += inc) { - uint16_t weight = (uint16_t)(weightfp >> 16); - if (weight > 65535) weight = 65535; - uint16_t weightOther = 65535 - weight; + uint32_t weight2 = (uint32_t)(weightfp >> 14); + if (weight2 > 65535) weight2 = 65535; + uint32_t weight1 = 65535 - weight2; uintpixel_t intcolor1, intcolor2; - intcolor1 = swrTwoWayBlend(pxcolor1, pxcolor4, weightOther, weight); - intcolor2 = swrTwoWayBlend(pxcolor2, pxcolor3, weightOther, weight); + intcolor1 = swrTwoWayBlend(pxcolor1, pxcolor4, (uint16_t) weight1, (uint16_t) weight2); + intcolor2 = swrTwoWayBlend(pxcolor2, pxcolor3, (uint16_t) weight1, (uint16_t) weight2); swrDrawHLineInt(renderer, x1i, y1i + y, xd, intcolor1, intcolor2, alphaInt); } diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index ce7a7a35b..0abffb74e 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -294,6 +294,7 @@ FORCE_INLINE uintpixel_t swrTwoWayBlend(uintpixel_t color1, uintpixel_t color2, #if defined SW_DITHERED_BLENDING int rng = fastRandomIsh() & 0xFFFF; if (rng < frac1) return color1; + (void) frac2; return color2; #elif PIXEL_SIZE == 32 Pixel32ARGB x1, x2, out; From 3238ae45231af8eda643c714f5044f23de51089a Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 18:51:30 +0300 Subject: [PATCH 122/130] FillRectangleColor works properly in dithered blending mode (has a special case and stuff) --- src/sw/sw_drawing.c | 113 ++++++++++++++++++++++------------------- src/sw/sw_pixel_calc.h | 45 ++++++++++++++-- src/sw/sw_renderer.c | 5 -- 3 files changed, 101 insertions(+), 62 deletions(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index 68ecb90e4..a2c0f51df 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -153,18 +153,8 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i int blendmode = swr->blendMode; uintpixel_t color = color1; -#if PIXEL_SIZE == 32 - Pixel32ARGB clr1, clr2; - clr1.l = color1; - clr2.l = color2; - - uint32_t rinit = clr1.p.r << 20; - uint32_t ginit = clr1.p.g << 20; - uint32_t binit = clr1.p.b << 20; - int32_t rstep = ((int)clr2.p.r - clr1.p.r) << 20; - int32_t gstep = ((int)clr2.p.g - clr1.p.g) << 20; - int32_t bstep = ((int)clr2.p.b - clr1.p.b) << 20; -#endif + uint32_t inc = (65536U << 14); + uint32_t weightfp = 0; if (dy1 <= dx1) { @@ -177,15 +167,11 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i x = x2, y = y2, xe = x1; } -#if PIXEL_SIZE == 32 if (dx1 > 0) { - rstep /= dx1; - gstep /= dx1; - bstep /= dx1; + inc /= dx1; } else { - rstep = gstep = bstep = 0; + inc = 0; } -#endif swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); @@ -201,18 +187,13 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) y++; else y--; px += 2 * (dy1 - dx1); } - -#if PIXEL_SIZE == 32 - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - color = resultPixel.l; -#endif - + + uint32_t weight2 = (weightfp >> 14); + if (weight2 > 65535) weight2 = 65535; + uint32_t weight1 = 65535 - weight2; + weightfp += inc; + color = swrTwoWayBlend(color1, color2, (uint16_t) weight1, (uint16_t) weight2); + swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); } } @@ -227,15 +208,11 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i x = x2, y = y2, ye = y1; } -#if PIXEL_SIZE == 32 if (dy1 > 0) { - rstep /= dy1; - gstep /= dy1; - bstep /= dy1; + inc /= dy1; } else { - rstep = gstep = bstep = 0; + inc = 0; } -#endif swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); @@ -251,18 +228,13 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i if ((dx < 0 && dy < 0) || (dx > 0 && dy > 0)) x++; else x--; py += 2 * (dx1 - dy1); } - -#if PIXEL_SIZE == 32 - Pixel32ARGB resultPixel; - resultPixel.p.r = rinit >> 20; - resultPixel.p.g = ginit >> 20; - resultPixel.p.b = binit >> 20; - rinit += rstep; - ginit += gstep; - binit += bstep; - color = resultPixel.l; -#endif - + + uint32_t weight2 = (weightfp >> 14); + if (weight2 > 65535) weight2 = 65535; + uint32_t weight1 = 65535 - weight2; + weightfp += inc; + color = swrTwoWayBlend(color1, color2, (uint16_t) weight1, (uint16_t) weight2); + swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); } } @@ -868,10 +840,7 @@ void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, flo if (yd < 0) { y1i = y2i; yd = -yd; } if (xd <= 0 || yd <= 0) return; - // TODO: blending vertically - (void) pxcolor3; - (void) pxcolor4; - +#ifndef SW_DITHERED_BLENDING uint32_t inc = (65536U << 14) / yd; uint32_t weightfp = 0; for (int y = 0; y <= yd; y++, weightfp += inc) @@ -886,6 +855,46 @@ void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, flo swrDrawHLineInt(renderer, x1i, y1i + y, xd, intcolor1, intcolor2, alphaInt); } +#else + // Dithered blending CANNOT use the above code because of its inherent randomness. + // It chooses either one color or the other based on the alpha / probability. + // As such, we need a slightly more complex four-way operation. + int srcalpha = swrCalcSrcAlpha(swr, alphaInt); + int invalpha = swrCalcDstAlpha(swr, alphaInt); + int blendmode = swr->blendMode; + + uint32_t incx = (65536U << 14) / xd; + uint32_t incy = (65536U << 14) / yd; + uint32_t weightfpy = 0; + for (int y = 0, ay = y1i; y <= yd; y++, ay++, weightfpy += incy) + { + uint32_t weightD = (uint32_t)(weightfpy >> 14); + if (weightD > 65535) weightD = 65535; + uint32_t weightU = 65535 - weightD; + + uintpixel_t *line = &swr->fb[ay * swr->fbPitch + x1i]; + uint32_t weightfpx = 0; + for (int x = 0; x <= xd; x++, weightfpx += incx) + { + if (UNLIKELY(srcalpha < 250)) { + if ((swrFastRng() & 0xFF) >= srcalpha) + continue; + } + + uint32_t weightR = (uint32_t)(weightfpx >> 14); + if (weightR > 65535) weightR = 65535; + uint32_t weightL = 65535 - weightR; + + uint16_t weight1 = (uint16_t)((weightL * weightU) >> 16); + uint16_t weight2 = (uint16_t)((weightR * weightU) >> 16); + uint16_t weight3 = (uint16_t)((weightR * weightD) >> 16); + uint16_t weight4 = (uint16_t)((weightL * weightD) >> 16); + + uintpixel_t result = swrFourWayBlend(pxcolor1, pxcolor2, pxcolor3, pxcolor4, weight1, weight2, weight3, weight4); + alphaBlend(&line[x], result, blendmode, 256, 0); + } + } +#endif } void swrDrawSprite( diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index 0abffb74e..e549999fa 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -5,7 +5,7 @@ #include "sw_pixel_convert.h" // Random number generator to be used for 8-bpp blending operations. -FORCE_INLINE int fastRandomIsh() +FORCE_INLINE int swrFastRng() { static uint32_t rngseed = 1337; #ifdef SW_USE_XORSHIFT32_FOR_DITHERING @@ -125,7 +125,7 @@ void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srca if (UNLIKELY(srcalpha > 250)) *dcolor = 0; - if ((fastRandomIsh() & 0xFF) < srcalpha) + if ((swrFastRng() & 0xFF) < srcalpha) *dcolor = 0; return; @@ -157,7 +157,7 @@ void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srca #ifdef SW_DITHERED_BLENDING if (srcalpha < 240) { - if ((fastRandomIsh() & 0xFF) >= srcalpha) + if ((swrFastRng() & 0xFF) >= srcalpha) return; } @@ -292,7 +292,7 @@ FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) FORCE_INLINE uintpixel_t swrTwoWayBlend(uintpixel_t color1, uintpixel_t color2, uint16_t frac1, uint16_t frac2) { #if defined SW_DITHERED_BLENDING - int rng = fastRandomIsh() & 0xFFFF; + int rng = swrFastRng() & 0xFFFF; if (rng < frac1) return color1; (void) frac2; return color2; @@ -321,7 +321,7 @@ FORCE_INLINE uintpixel_t swrTwoWayBlend(uintpixel_t color1, uintpixel_t color2, FORCE_INLINE uintpixel_t swrThreeWayBlend(uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uint16_t frac1, uint16_t frac2, uint16_t frac3) { #if defined SW_DITHERED_BLENDING - int rng = fastRandomIsh() & 0xFFFF; + int rng = swrFastRng() & 0xFFFF; if (rng < frac1) return color1; else rng -= frac1; if (rng < frac2) return color2; (void) frac3; @@ -348,4 +348,39 @@ FORCE_INLINE uintpixel_t swrThreeWayBlend(uintpixel_t color1, uintpixel_t color2 #endif } +// Blends a pixel between four colors. +// frac means 0-65535 where 65535 means one. And frac1 + frac2 + frac3 + frac4 MUST be equal to 65535. +FORCE_INLINE uintpixel_t swrFourWayBlend(uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, uintpixel_t color4, uint16_t frac1, uint16_t frac2, uint16_t frac3, uint16_t frac4) +{ +#if defined SW_DITHERED_BLENDING + int rng = swrFastRng() & 0xFFFF; + if (rng < frac1) return color1; else rng -= frac1; + if (rng < frac2) return color2; + if (rng < frac3) return color3; + (void) frac4; + return color4; +#elif PIXEL_SIZE == 32 + Pixel32ARGB x1, x2, x3, x4, out; + x1.l = color1; + x2.l = color2; + x3.l = color3; + x4.l = color4; + out.p.r = (x1.p.r * frac1 + x2.p.r * frac2 + x3.p.r * frac3 + x4.p.r * frac4) >> 16; + out.p.g = (x1.p.g * frac1 + x2.p.g * frac2 + x3.p.g * frac3 + x4.p.g * frac4) >> 16; + out.p.b = (x1.p.b * frac1 + x2.p.b * frac2 + x3.p.b * frac3 + x4.p.b * frac4) >> 16; + out.p.a = x1.p.a; + return out.l; +#elif PIXEL_SIZE == 16 + int c1b = color1 & 0x1F, c1g = (color1 >> 5) & 0x1F, c1r = (color1 >> 10) & 0x1F; + int c2b = color2 & 0x1F, c2g = (color2 >> 5) & 0x1F, c2r = (color2 >> 10) & 0x1F; + int c3b = color3 & 0x1F, c3g = (color3 >> 5) & 0x1F, c3r = (color3 >> 10) & 0x1F; + int c4b = color4 & 0x1F, c4g = (color4 >> 5) & 0x1F, c4b = (color4 >> 10) & 0x1F; + int ca = color1 & 0x8000; + int cr = (c1r * frac1 + c2r * frac2 + c3r * frac3 + c4r * frac4) >> 16; + int cg = (c1g * frac1 + c2g * frac2 + c3g * frac3 + c4g * frac4) >> 16; + int cb = (c1b * frac1 + c2b * frac2 + c3b * frac3 + c4b * frac4) >> 16; + return ca | cb | (cg << 5) | (cr << 10); +#endif +} + #endif//_SW_PIXEL_CALC_H diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 533bdb747..a068abda9 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -98,11 +98,6 @@ static void SWRenderer_endFrameInit(Renderer* renderer) //this is kinda useless to do twice isn't it? } - -static void SWRenderer_drawRectangleColor(Renderer* renderer, float x1, float y1, float x2, float y2, - uint32_t color1, uint32_t color2, uint32_t color3, uint32_t color4, - float alpha, bool outline); - static void SWRenderer_endFrameEnd(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; From 7b20315bc5821d7dd1a10399a9cbb147c3e7981d Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 19:25:23 +0300 Subject: [PATCH 123/130] Add support for semi-transparent pixels --- src/sw/sw_config.h | 14 +++++++ src/sw/sw_drawing.c | 87 ++++++++++++++++++++++++++++++++---------- src/sw/sw_pixel_calc.h | 40 +++++++++++++++---- src/sw/sw_text.c | 4 +- 4 files changed, 116 insertions(+), 29 deletions(-) diff --git a/src/sw/sw_config.h b/src/sw/sw_config.h index 91031eff9..811946f8c 100644 --- a/src/sw/sw_config.h +++ b/src/sw/sw_config.h @@ -13,6 +13,10 @@ // 16-bit: 0b0RRRRRGGGGGBBBBB // 8-bit: 0bBBGGGRRR +// Define if you want to disable support for semi-transparent sprites. +// It should be a little bit faster in scenes with heavy use of semi-transparent pixels. +//#define SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT + // Define if you want dithered alpha blending and triangle color blending. // It might be a good bit faster than doing blending the proper way. //#define SW_DITHERED_BLENDING @@ -60,4 +64,14 @@ #endif // PIXEL_SIZE == 8 +#if PIXEL_SIZE != 32 + +// forcefully define this. 16-bit and 8-bit color modes CANNOT work with +// semi-transparent textures. +#ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT +#define SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT +#endif + +#endif // PIXEL_SIZE != 32 + #endif // __SW_CONFIG_H diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index a2c0f51df..a9c647af7 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -27,8 +27,8 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe if (dx + dw >= swr->maxX) dw = swr->maxX - dx; if (dw <= 0) return; - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); + int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); + int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; if (color == color2) @@ -63,8 +63,8 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe if (dy + dh >= swr->maxY) dh = swr->maxY - dy; if (dh <= 0) return; - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); + int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); + int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; if (color == color2) @@ -148,8 +148,8 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i int dx = x2 - x1, dy = y2 - y1; int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); + int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); + int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; uintpixel_t color = color1; @@ -240,6 +240,39 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i } } +#ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT + +FORCE_INLINE void swrCalculateAlphaBlending( + uintpixel_t* px, + uintpixel_t src, + uintpixel_t tintColor, + int alpha, + int blendmode +) +{ + // perform alpha blending for real now. a good bit slower + uint8_t alphau; + uintpixel_t color; + int aalpha, asrcalpha, ainvalpha; + + alphau = swrGetAlphaU8(src); + + // first pass tint because GM does not do premultiplied alpha + color = tint(swrAlphaToColor(alphau), src); + + // second pass tint for coloring + color = tint(tintColor, color); + + // recalculate alpha parameters individually + aalpha = (alpha * alphau) >> 8; + asrcalpha = swrCalcSrcAlpha(blendmode, aalpha); + ainvalpha = swrCalcDstAlpha(blendmode, aalpha); + + alphaBlend(px, color, blendmode, asrcalpha, ainvalpha); +} + +#endif + static void swrDrawSpriteInternal( Renderer* renderer, int dx, int dy, int dw, int dh, SWTexture* texture, int sx, int sy, int sw, int sh, @@ -311,8 +344,8 @@ static void swrDrawSpriteInternal( fixedp_t ixs2 = ixs * xstep; fixedp_t iys2 = iys * ystep; - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); + int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); + int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; if (sw == dw) @@ -331,8 +364,12 @@ static void swrDrawSpriteInternal( for (int x = 0, xs = ixs; x < dw; x++, xs += oxs) { uintpixel_t pixel = srcline[xs]; - if (opaque(pixel)) + if (swrIsOpaque(pixel)) alphaBlend(&dstline[x], tint(tintColor, pixel), blendmode, srcalpha, invalpha); +#ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT + else if (!swrIsFullyTransparent(pixel)) + swrCalculateAlphaBlending(&dstline[x], pixel, tintColor, alpha, blendmode); +#endif } } } @@ -353,8 +390,12 @@ static void swrDrawSpriteInternal( for (int x = 0; x < dw; x++, xs2 += oxs2) { uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; - if (opaque(pixel)) + if (swrIsOpaque(pixel)) alphaBlend(&dstline[x], tint(tintColor, pixel), blendmode, srcalpha, invalpha); +#ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT + else if (!swrIsFullyTransparent(pixel)) + swrCalculateAlphaBlending(&dstline[x], pixel, tintColor, alpha, blendmode); +#endif } } } @@ -433,9 +474,11 @@ static void swrDrawSpriteRotatedInternal( float sw_dw = (float) sw / dw; float sh_dh = (float) sh / dh; - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); + int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); + int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; + + logDebug("PixelBuffer: %p (%dx%d)", texture->buffer); for (int cy = minYc; cy < maxYc; cy++) { @@ -471,17 +514,21 @@ static void swrDrawSpriteRotatedInternal( ty += sy; uintpixel_t src = texture->buffer[ty * texture->width + tx]; - - if (opaque(src)) + + if (swrIsOpaque(src)) alphaBlend(&dstline[cx], tint(tintColor, src), blendmode, srcalpha, invalpha); +#ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT + else if (!swrIsFullyTransparent(src)) + swrCalculateAlphaBlending(&dstline[cx], src, tintColor, alpha, blendmode); +#endif } } } static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, int alpha) { - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); + int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); + int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; // Figure out the maximum Y extent of the triangle. @@ -768,8 +815,8 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor void swrPlotPixel(Renderer* renderer, float x, float y, uintpixel_t color, float alpha) { SWRenderer *swr = (SWRenderer*) renderer; - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int invalpha = swrCalcDstAlpha(swr, alpha); + int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); + int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; swrPlotPixel_(renderer, (float) x, (float) y, color, blendmode, srcalpha, invalpha); } @@ -859,8 +906,8 @@ void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, flo // Dithered blending CANNOT use the above code because of its inherent randomness. // It chooses either one color or the other based on the alpha / probability. // As such, we need a slightly more complex four-way operation. - int srcalpha = swrCalcSrcAlpha(swr, alphaInt); - int invalpha = swrCalcDstAlpha(swr, alphaInt); + int srcalpha = swrCalcSrcAlpha(swr->blendMode, alphaInt); + int invalpha = swrCalcDstAlpha(swr->blendMode, alphaInt); int blendmode = swr->blendMode; uint32_t incx = (65536U << 14) / xd; diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index e549999fa..94b8f920e 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -26,15 +26,41 @@ FORCE_INLINE int swrFastRng() // // Later, this should be changed to perform full alpha-blending // (at least in 32-bit pixel mode) -FORCE_INLINE bool opaque(uintpixel_t color) +FORCE_INLINE bool swrIsOpaque(uintpixel_t color) { #if PIXEL_SIZE == 8 return (color != PXL_TRANSPARENT); +#else +#ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT + return (color & TRANSPARENT_MASK) == TRANSPARENT_MASK; #else return (color & TRANSPARENT_MASK) != 0; #endif +#endif +} + +#ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT + +// NOTE: These only work properly for 32-bit color. +// (But you shouldn't have meddled with the #error I put in in sw_config.h...) +FORCE_INLINE bool swrIsFullyTransparent(uintpixel_t color) +{ + return (color & TRANSPARENT_MASK) == 0; +} + +FORCE_INLINE uint8_t swrGetAlphaU8(uintpixel_t color) +{ + return color >> 24; } +// Turns the specified color (including alpha channel) into +FORCE_INLINE uintpixel_t swrAlphaToColor(uint8_t alpha) +{ + return alpha | (alpha << 8) | (alpha << 16); +} + +#endif + // Multiplies a color value (`color`) by another color value (`tintColor`). FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) { @@ -258,24 +284,24 @@ FORCE_INLINE int swrIntAlpha(float alphaf) } // Calculates the source alpha for a pixel based on the current blend mode. -FORCE_INLINE int swrCalcSrcAlpha(SWRenderer* swr, int alpha) +FORCE_INLINE int swrCalcSrcAlpha(int blendMode, int alpha) { - // Here you would depend on swr->blendMode, but all + // Here you would depend on blendMode, but all // of them return the same value right now. - (void) swr; + (void) blendMode; return alpha; } // Calculates the destination alpha for a pixel based on the current blend mode. -FORCE_INLINE int swrCalcDstAlpha(SWRenderer* swr, int alpha) +FORCE_INLINE int swrCalcDstAlpha(int blendMode, int alpha) { #ifdef SW_DITHERED_BLENDING /* Dithered blending does not use the dstalpha member */ - (void) swr; + (void) blendMode; (void) alpha; return 0; #else - switch (swr->blendMode) + switch (blendMode) { default: return 256 - alpha; diff --git a/src/sw/sw_text.c b/src/sw/sw_text.c index 1c43fafeb..09b72dff4 100644 --- a/src/sw/sw_text.c +++ b/src/sw/sw_text.c @@ -118,8 +118,8 @@ static void swrDrawDebugFontChar(SWRenderer* swr, char chr, int ax, int ay, uint chr = '?'; int alpha = swrIntAlpha(alphaf); - int srcalpha = swrCalcSrcAlpha(swr, alpha); - int dstalpha = swrCalcDstAlpha(swr, alpha); + int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); + int dstalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; uintpixel_t actualColor = swrConvertPixel(color); From 38a950bc9ce0aef7010d0b5b977c8a118eb31a62 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 19:38:47 +0300 Subject: [PATCH 124/130] Completely remove swrCalcDstAlpha and swrCalcSrcAlpha These are completely unnecessary (srcCalcSrcAlpha always returned alpha) And I don't see myself implementing any other blend modes anytime soon. --- src/sw/sw_drawing.c | 65 +++++++++++++----------------------------- src/sw/sw_pixel_calc.h | 50 +++++++------------------------- src/sw/sw_text.c | 8 ++---- 3 files changed, 33 insertions(+), 90 deletions(-) diff --git a/src/sw/sw_drawing.c b/src/sw/sw_drawing.c index a9c647af7..425dbfaea 100644 --- a/src/sw/sw_drawing.c +++ b/src/sw/sw_drawing.c @@ -7,14 +7,14 @@ static void swrDrawTriangleTransformed(Renderer* renderer, float x1, float y1, float x2, float y2, float x3, float y3, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, int alpha); -FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int blendmode, int srcalpha, int dstalpha) +FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int blendmode, int alpha) { SWRenderer* swr = (SWRenderer*) renderer; if (x < swr->portX || y < swr->portY) return; if (x >= swr->maxX || y >= swr->maxY) return; - alphaBlend(&swr->fb[y * swr->fbPitch + x], color, blendmode, srcalpha, dstalpha); + alphaBlend(&swr->fb[y * swr->fbPitch + x], color, blendmode, alpha); } static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixel_t color, UNUSED uintpixel_t color2, int alpha) @@ -27,15 +27,13 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe if (dx + dw >= swr->maxX) dw = swr->maxX - dx; if (dw <= 0) return; - int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); - int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; if (color == color2) { uintpixel_t *line = &swr->fb[dy * swr->fbPitch + dx]; for (int i = 0; i < dw; i++) - alphaBlend(&line[i], color, blendmode, srcalpha, invalpha); + alphaBlend(&line[i], color, blendmode, alpha); } else { @@ -48,7 +46,7 @@ static void swrDrawHLineInt(Renderer* renderer, int dx, int dy, int dw, uintpixe if (weight2 > 65535) weight2 = 65535; uint32_t weight1 = 65535 - weight2; uintpixel_t result = swrTwoWayBlend(color, color2, (uint16_t) weight1, (uint16_t) weight2); - alphaBlend(&line[i], result, blendmode, srcalpha, invalpha); + alphaBlend(&line[i], result, blendmode, alpha); } } } @@ -63,8 +61,6 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe if (dy + dh >= swr->maxY) dh = swr->maxY - dy; if (dh <= 0) return; - int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); - int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; if (color == color2) @@ -72,7 +68,7 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe for (int i = 0; i < dh; i++) { uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - alphaBlend(&line[0], color, blendmode, srcalpha, invalpha); + alphaBlend(&line[0], color, blendmode, alpha); } } else @@ -86,7 +82,7 @@ static void swrDrawVLineInt(Renderer* renderer, int dx, int dy, int dh, uintpixe uint32_t weight1 = 65535 - weight2; uintpixel_t result = swrTwoWayBlend(color, color2, (uint16_t) weight1, (uint16_t) weight2); uintpixel_t *line = &swr->fb[(dy + i) * swr->fbPitch + dx]; - alphaBlend(&line[0], result, blendmode, srcalpha, invalpha); + alphaBlend(&line[0], result, blendmode, alpha); } } } @@ -137,10 +133,6 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i swrDrawTriangleTransformed(renderer, x1l, y1l, x1r, y1r, x2l, y2l, color1, color1, color2, alpha); swrDrawTriangleTransformed(renderer, x2r, y2r, x1r, y1r, x2l, y2l, color2, color1, color2, alpha); - swrPlotPixel_(renderer, x1l, y1l, 0xFF00FF, bm_normal, 256, 0); - swrPlotPixel_(renderer, x2l, y2l, 0xFF00FF, bm_normal, 256, 0); - swrPlotPixel_(renderer, x1r, y1r, 0xFF00FF, bm_normal, 256, 0); - swrPlotPixel_(renderer, x2r, y2r, 0xFF00FF, bm_normal, 256, 0); return; } @@ -148,8 +140,6 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i int dx = x2 - x1, dy = y2 - y1; int dx1 = swrAbs(dx), dy1 = swrAbs(dy), xe, ye, x, y; int px = 2 * dy1 - dx1, py = 2 * dx1 - dy1; - int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); - int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; uintpixel_t color = color1; @@ -173,7 +163,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i inc = 0; } - swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, blendmode, alpha); while (x < xe) { @@ -194,7 +184,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i weightfp += inc; color = swrTwoWayBlend(color1, color2, (uint16_t) weight1, (uint16_t) weight2); - swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, blendmode, alpha); } } else @@ -214,7 +204,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i inc = 0; } - swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, blendmode, alpha); while (y < ye) { @@ -235,7 +225,7 @@ static void swrDrawLineInt(Renderer* renderer, int x1, int y1, int x2, int y2, i weightfp += inc; color = swrTwoWayBlend(color1, color2, (uint16_t) weight1, (uint16_t) weight2); - swrPlotPixel_(renderer, x, y, color, blendmode, srcalpha, invalpha); + swrPlotPixel_(renderer, x, y, color, blendmode, alpha); } } } @@ -253,7 +243,7 @@ FORCE_INLINE void swrCalculateAlphaBlending( // perform alpha blending for real now. a good bit slower uint8_t alphau; uintpixel_t color; - int aalpha, asrcalpha, ainvalpha; + int aalpha; alphau = swrGetAlphaU8(src); @@ -263,12 +253,9 @@ FORCE_INLINE void swrCalculateAlphaBlending( // second pass tint for coloring color = tint(tintColor, color); - // recalculate alpha parameters individually + // recalculate alpha aalpha = (alpha * alphau) >> 8; - asrcalpha = swrCalcSrcAlpha(blendmode, aalpha); - ainvalpha = swrCalcDstAlpha(blendmode, aalpha); - - alphaBlend(px, color, blendmode, asrcalpha, ainvalpha); + alphaBlend(px, color, blendmode, aalpha); } #endif @@ -344,8 +331,6 @@ static void swrDrawSpriteInternal( fixedp_t ixs2 = ixs * xstep; fixedp_t iys2 = iys * ystep; - int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); - int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; if (sw == dw) @@ -365,7 +350,7 @@ static void swrDrawSpriteInternal( { uintpixel_t pixel = srcline[xs]; if (swrIsOpaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), blendmode, srcalpha, invalpha); + alphaBlend(&dstline[x], tint(tintColor, pixel), blendmode, alpha); #ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT else if (!swrIsFullyTransparent(pixel)) swrCalculateAlphaBlending(&dstline[x], pixel, tintColor, alpha, blendmode); @@ -391,7 +376,7 @@ static void swrDrawSpriteInternal( { uintpixel_t pixel = srcline[(int)(xs2 >> fp_prec)]; if (swrIsOpaque(pixel)) - alphaBlend(&dstline[x], tint(tintColor, pixel), blendmode, srcalpha, invalpha); + alphaBlend(&dstline[x], tint(tintColor, pixel), blendmode, alpha); #ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT else if (!swrIsFullyTransparent(pixel)) swrCalculateAlphaBlending(&dstline[x], pixel, tintColor, alpha, blendmode); @@ -474,12 +459,8 @@ static void swrDrawSpriteRotatedInternal( float sw_dw = (float) sw / dw; float sh_dh = (float) sh / dh; - int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); - int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; - - logDebug("PixelBuffer: %p (%dx%d)", texture->buffer); - + for (int cy = minYc; cy < maxYc; cy++) { uintpixel_t *dstline = &swr->fb[cy * swr->fbPitch]; @@ -516,7 +497,7 @@ static void swrDrawSpriteRotatedInternal( uintpixel_t src = texture->buffer[ty * texture->width + tx]; if (swrIsOpaque(src)) - alphaBlend(&dstline[cx], tint(tintColor, src), blendmode, srcalpha, invalpha); + alphaBlend(&dstline[cx], tint(tintColor, src), blendmode, alpha); #ifndef SW_NO_SEMI_TRANSPARENT_TEXTURE_SUPPORT else if (!swrIsFullyTransparent(src)) swrCalculateAlphaBlending(&dstline[cx], src, tintColor, alpha, blendmode); @@ -527,8 +508,6 @@ static void swrDrawSpriteRotatedInternal( static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft, int yleft, int xright, int yright, uintpixel_t color1, uintpixel_t color2, uintpixel_t color3, int alpha) { - int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); - int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; // Figure out the maximum Y extent of the triangle. @@ -588,7 +567,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft if (y >= 0) { uintpixel_t* line = &swr->fb[y * swr->width]; for (int x = x1; x < x2; x++) { - alphaBlend(&line[x], color1, blendmode, srcalpha, invalpha); + alphaBlend(&line[x], color1, blendmode, alpha); } } } @@ -662,7 +641,7 @@ static void swrDrawTriangleInternal(SWRenderer* swr, int xup, int yup, int xleft if (w2 > 65535 - w1) w2 = 65535 - w1; uintpixel_t blended = swrThreeWayBlend(color1, color2, color3, w1, w2, 65535 - w1 - w2); - alphaBlend(&line[x], blended, blendmode, srcalpha, invalpha); + alphaBlend(&line[x], blended, blendmode, alpha); e_up += d_up; e_left += d_left; @@ -815,10 +794,8 @@ bool swrSwitchToSurface(Renderer* renderer, int32_t targetSurfaceId, bool restor void swrPlotPixel(Renderer* renderer, float x, float y, uintpixel_t color, float alpha) { SWRenderer *swr = (SWRenderer*) renderer; - int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); - int invalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; - swrPlotPixel_(renderer, (float) x, (float) y, color, blendmode, srcalpha, invalpha); + swrPlotPixel_(renderer, (float) x, (float) y, color, blendmode, alpha); } void swrDrawLine(Renderer* renderer, float x1, float y1, float x2, float y2, float width, uintpixel_t color, uintpixel_t color2, float alpha, int alignment) @@ -906,8 +883,6 @@ void swrFillRectangleColor(Renderer* renderer, float x1, float y1, float x2, flo // Dithered blending CANNOT use the above code because of its inherent randomness. // It chooses either one color or the other based on the alpha / probability. // As such, we need a slightly more complex four-way operation. - int srcalpha = swrCalcSrcAlpha(swr->blendMode, alphaInt); - int invalpha = swrCalcDstAlpha(swr->blendMode, alphaInt); int blendmode = swr->blendMode; uint32_t incx = (65536U << 14) / xd; diff --git a/src/sw/sw_pixel_calc.h b/src/sw/sw_pixel_calc.h index 94b8f920e..f971ff892 100644 --- a/src/sw/sw_pixel_calc.h +++ b/src/sw/sw_pixel_calc.h @@ -117,13 +117,8 @@ FORCE_INLINE uintpixel_t tint(uintpixel_t tintColor, uintpixel_t color) // // Another note: Wow, it' become a huge mess of ifdef's... FORCE_INLINE -void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srcalpha, int dstalpha) +void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srcalpha) { -#ifdef SW_DITHERED_BLENDING - /* Dithered blending does not use the dstalpha member */ - (void) dstalpha; -#endif - /* If we didn't disable subtract support and are in dithered blending mode */ #if defined SW_DITHERED_BLENDING #ifdef SW_NO_SUBTRACT_SUPPORT @@ -159,6 +154,9 @@ void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srca #endif // !SW_NO_SUBTRACT_SUPPORT #endif // defined SW_DITHERED_BLENDING + int dstalpha = 256; + +// TODO: figure out why we aren't doing this for 8-bit? #if PIXEL_SIZE == 32 || PIXEL_SIZE == 16 /* Check extremely common cases in 32- and 16-bit modes */ @@ -175,6 +173,8 @@ void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srca // it's so insignificant here nobody will notice if we just don't... if (UNLIKELY(srcalpha < 4)) return; + + dstalpha -= srcalpha; } #endif // PIXEL_SIZE == 32 || PIXEL_SIZE == 16 @@ -197,7 +197,7 @@ void alphaBlend(uintpixel_t* dcolor, uintpixel_t scolor, int blendmode, int srca *dcolor = scolor; -#else +#else // SW_DITHERED_BLENDING /* Extract pixel channels */ #if PIXEL_SIZE == 32 @@ -283,36 +283,6 @@ FORCE_INLINE int swrIntAlpha(float alphaf) return (int)(alphaf * 256); } -// Calculates the source alpha for a pixel based on the current blend mode. -FORCE_INLINE int swrCalcSrcAlpha(int blendMode, int alpha) -{ - // Here you would depend on blendMode, but all - // of them return the same value right now. - (void) blendMode; - return alpha; -} - -// Calculates the destination alpha for a pixel based on the current blend mode. -FORCE_INLINE int swrCalcDstAlpha(int blendMode, int alpha) -{ -#ifdef SW_DITHERED_BLENDING - /* Dithered blending does not use the dstalpha member */ - (void) blendMode; - (void) alpha; - return 0; -#else - switch (blendMode) - { - default: - return 256 - alpha; - case bm_add: - return 256; - case bm_subtract: - return 256; - } -#endif -} - // Blends a pixel between two colors. // frac means 0-65535 where 65535 means one. And frac1 + frac2 MUST be equal to 65535. FORCE_INLINE uintpixel_t swrTwoWayBlend(uintpixel_t color1, uintpixel_t color2, uint16_t frac1, uint16_t frac2) @@ -329,7 +299,7 @@ FORCE_INLINE uintpixel_t swrTwoWayBlend(uintpixel_t color1, uintpixel_t color2, out.p.r = (x1.p.r * frac1 + x2.p.r * frac2) >> 16; out.p.g = (x1.p.g * frac1 + x2.p.g * frac2) >> 16; out.p.b = (x1.p.b * frac1 + x2.p.b * frac2) >> 16; - out.p.a = x1.p.a; + out.p.a = (x1.p.a * frac1 + x2.p.a * frac2) >> 16; return out.l; #elif PIXEL_SIZE == 16 int c1b = color1 & 0x1F, c1g = (color1 >> 5) & 0x1F, c1r = (color1 >> 10) & 0x1F; @@ -360,7 +330,7 @@ FORCE_INLINE uintpixel_t swrThreeWayBlend(uintpixel_t color1, uintpixel_t color2 out.p.r = (x1.p.r * frac1 + x2.p.r * frac2 + x3.p.r * frac3) >> 16; out.p.g = (x1.p.g * frac1 + x2.p.g * frac2 + x3.p.g * frac3) >> 16; out.p.b = (x1.p.b * frac1 + x2.p.b * frac2 + x3.p.b * frac3) >> 16; - out.p.a = x1.p.a; + out.p.a = (x1.p.a * frac1 + x2.p.a * frac2 + x3.p.a * frac3) >> 16; return out.l; #elif PIXEL_SIZE == 16 int c1b = color1 & 0x1F, c1g = (color1 >> 5) & 0x1F, c1r = (color1 >> 10) & 0x1F; @@ -394,7 +364,7 @@ FORCE_INLINE uintpixel_t swrFourWayBlend(uintpixel_t color1, uintpixel_t color2, out.p.r = (x1.p.r * frac1 + x2.p.r * frac2 + x3.p.r * frac3 + x4.p.r * frac4) >> 16; out.p.g = (x1.p.g * frac1 + x2.p.g * frac2 + x3.p.g * frac3 + x4.p.g * frac4) >> 16; out.p.b = (x1.p.b * frac1 + x2.p.b * frac2 + x3.p.b * frac3 + x4.p.b * frac4) >> 16; - out.p.a = x1.p.a; + out.p.a = (x1.p.a * frac1 + x2.p.a * frac2 + x3.p.a * frac3 + x4.p.a * frac4) >> 16; return out.l; #elif PIXEL_SIZE == 16 int c1b = color1 & 0x1F, c1g = (color1 >> 5) & 0x1F, c1r = (color1 >> 10) & 0x1F; diff --git a/src/sw/sw_text.c b/src/sw/sw_text.c index 09b72dff4..373877343 100644 --- a/src/sw/sw_text.c +++ b/src/sw/sw_text.c @@ -21,14 +21,14 @@ SwrFontState; // ==== Internal functions ==== -FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int blendmode, int srcalpha, int dstalpha) +FORCE_INLINE void swrPlotPixel_(Renderer* renderer, int x, int y, uintpixel_t color, int blendmode, int alpha) { SWRenderer* swr = (SWRenderer*) renderer; if (x < swr->portX || y < swr->portY) return; if (x >= swr->maxX || y >= swr->maxY) return; - alphaBlend(&swr->fb[y * swr->fbPitch + x], color, blendmode, srcalpha, dstalpha); + alphaBlend(&swr->fb[y * swr->fbPitch + x], color, blendmode, alpha); } static bool swrResolveFontState(SWRenderer* swr, DataWin* dw, Font* font, SwrFontState* state) @@ -118,8 +118,6 @@ static void swrDrawDebugFontChar(SWRenderer* swr, char chr, int ax, int ay, uint chr = '?'; int alpha = swrIntAlpha(alphaf); - int srcalpha = swrCalcSrcAlpha(swr->blendMode, alpha); - int dstalpha = swrCalcDstAlpha(swr->blendMode, alpha); int blendmode = swr->blendMode; uintpixel_t actualColor = swrConvertPixel(color); @@ -130,7 +128,7 @@ static void swrDrawDebugFontChar(SWRenderer* swr, char chr, int ax, int ay, uint uint8_t row = swrDebugFont1bpp[chr * SWR_DEBUG_FONT_LINE_HEIGHT + y]; if (row & (1 << (SWR_DEBUG_FONT_CHAR_WIDTH - 1 - x))) - swrPlotPixel_(&swr->base, ax + x, ay + y, actualColor, blendmode, srcalpha, dstalpha); + swrPlotPixel_(&swr->base, ax + x, ay + y, actualColor, blendmode, alpha); } } } From 490678935617610d0109ed15e8e44578b937b1cd Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 21:51:42 +0300 Subject: [PATCH 125/130] Don't forget to fill in mainFb, mainWidth, and mainHeight, on beginning of frame Otherwise code might use a stale pointer --- src/sw/sw_renderer.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index a068abda9..55250e982 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -83,6 +83,10 @@ static void SWRenderer_beginFrame(Renderer* renderer, int32_t gameW, int32_t gam swr->fbPitch = windowW; swr->width = windowW; swr->height = windowH; + + swr->mainFb = swr->fb; + swr->mainWidth = swr->width; + swr->mainHeight = swr->height; } #ifdef SW_DEBUG_FRAME_DRAW_BOUNDS From d28ed976df0da441c687bd4b9aef524092f0763b Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 22:06:45 +0300 Subject: [PATCH 126/130] Improve surfaceCopy from screen buffer, and implement drawSpritePos as a placeholder In Deltarune Ch3, drawSpritePos seems to be consistently used to draw Tenna. Luckily, for him specifically, we don't need to do too much. Later, somebody would do actual UV mapped rendering. I won't do it here. --- src/sw/sw_renderer.c | 51 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 55250e982..b2bf4e238 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -333,10 +333,42 @@ static void SWRenderer_drawSpritePos(Renderer* renderer, int32_t tpagIndex, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float alpha) { - (void)renderer; (void)tpagIndex; - (void)x1; (void)y1; (void)x2; (void)y2; - (void)x3; (void)y3; (void)x4; (void)y4; (void)alpha; + // TODO: Implement this properly. (I won't in this PR) + // + // You basically have to implement full texture UV mapping which I won't be + // bothering with. Somebody else can get on it. + + SWRenderer* swr = (SWRenderer*) renderer; + DataWin* dwin = renderer->dataWin; + + if (tpagIndex < 0 || (uint32_t) tpagIndex >= dwin->tpag.count) { + logError("%s: tpagIndex of %d is invalid\n", __func__, tpagIndex); + return; + } + + TexturePageItem* tpag = &dwin->tpag.items[tpagIndex]; + int16_t pageId = tpag->texturePageId; + if (0 > pageId || swr->totalTextureCount <= (uint32_t) pageId) { + logError("%s: tpagIndex of %d is invalid, as pageId of %d is invalid\n", __func__, tpagIndex, pageId); + return; + } + if (!swrEnsureTextureIsLoaded(swr, (uint32_t) pageId)) { + logError("%s: could not ensure texture is loaded, tpagIndex: %d, pageId: %d\n", __func__, tpagIndex, pageId); + return; + } + + int tw = tpag->targetWidth; + int th = tpag->targetHeight; + + float ascalex = (x2 - x1) / tw; + float ascaley = (y4 - y1) / th; + + (void) x3; (void) y3; + (void) y2; (void) x4; + UNIMP(); + + SWRenderer_drawSprite(renderer, tpagIndex, x1, y1, 0.0f, 0.0f, ascalex, ascaley, 0.0f, renderer->drawColor, alpha); } static void SWRenderer_drawRectangle(Renderer* renderer, float x1, float y1, float x2, float y2, @@ -926,6 +958,7 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, SWTexture temp1, temp2; SWTexture *dstSurf, *srcSurf; + bool freeSrcSurf = false; if (DestSurfaceID == APPLICATION_SURFACE_ID) { dstSurf = &temp1; @@ -942,10 +975,17 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, } if (SrcSurfaceID == APPLICATION_SURFACE_ID) { - srcSurf = &temp2; + // TODO: resizing might be expensive temp2.width = swr->mainWidth; temp2.height = swr->mainHeight; temp2.buffer = swr->mainFb; + if (swr->mainWidth == swr->portW && swr->mainHeight == swr->portH) { + srcSurf = &temp2; + } + else { + srcSurf = swrCropSectionFromTexture(&temp2, swr->gameW, swr->gameH, swr->portX, swr->portY, swr->maxX, swr->maxY); + freeSrcSurf = true; + } } else if (SrcSurfaceID < 0 || (size_t) SrcSurfaceID >= swr->surfaceCount || swr->surfaces[SrcSurfaceID] == NULL) { logError("swr: Cannot resize surface id %d, it's invalid (src in surfaceCopy)\n", SrcSurfaceID); @@ -993,7 +1033,8 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, } } - UNIMP(); + if (freeSrcSurf) + swrFreeTexture(srcSurf); } static bool SWRenderer_surfaceGetPixels(Renderer* renderer, int32_t surfaceID, uint8_t* outRGBA) From a08b4e4837b90bbdd4a4edd8c3ba24940e869361 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 16 Sep 2026 23:05:54 +0300 Subject: [PATCH 127/130] Implement surface_copy non-part properly --- src/sw/sw_renderer.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index b2bf4e238..5f80a1a83 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -952,8 +952,6 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, int32_t SrcSurfaceID, int32_t SrcX, int32_t SrcY, int32_t SrcW, int32_t SrcH, bool part) { - (void) part; // TODO: figure out the meaning of this parameter - SWRenderer* swr = (SWRenderer*) renderer; SWTexture temp1, temp2; @@ -995,6 +993,14 @@ static void SWRenderer_surfaceCopy(Renderer* renderer, swrCommitShadowWritesToSurfaceIfNeeded(swr, swr->surfaces[SrcSurfaceID]); srcSurf = swr->surfaces[SrcSurfaceID]->texture; } + + if (!part) + { + // Not "part" means copy the whole source texture. + SrcX = SrcY = 0; + SrcW = srcSurf->width; + SrcH = srcSurf->height; + } if (SrcX + SrcW < 0) return; if (SrcY + SrcH < 0) return; From bd174d6add648c9e3cf545ccd11a72a64d0ab681 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 17 Sep 2026 08:08:51 +0100 Subject: [PATCH 128/130] SWRenderer_destroy --- src/sw/sw_renderer.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index 5f80a1a83..ecfa11406 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -60,10 +60,34 @@ static void SWRenderer_init(Renderer* renderer, DataWin* dataWin) static void SWRenderer_destroy(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; + + swr->primitiveBegun = false; + swr->primitiveOverflow = false; + swr->primitiveType = -1; + swr->vertexCount = 0; + + for (size_t i = 0; i < swr->surfaceCount; i++) + { + swrFreeSurface(swr->surfaces[i]); + } + free(swr->surfaces); + swr->surfaceCount = 0; + { + for (size_t i = 0; i < swr->totalTextureCount; i++) + { + swrFreeTexture(swr->textures[i]); + } + } + free(swr->textures); + swr->textureCount = 0; + swr->totalTextureCount = 0; + free(swr->textureIndexLRU); + free(swr->vertexData); - // TODO: why didn't I implement this. - (void) swr; + free(swr->mainFb); + swr->fb = swr->mainFb = NULL; + free(swr); logInfo("SWRenderer destroyed.\n"); } From a348dd956a48de7e2dce0a22789f2fd4df320e5c Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sat, 26 Sep 2026 20:53:41 -0400 Subject: [PATCH 129/130] texfilter stub --- src/sw/sw_renderer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index ecfa11406..e6e7b9445 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -787,6 +787,12 @@ static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, i } } +static void SWRenderer_gpuSetTexFilter(Renderer* renderer, bool enable) +{ + (void)enable; + UNIMP(); +} + static void SWRenderer_flush(Renderer* renderer) { SWRenderer* swr = (SWRenderer*) renderer; @@ -1542,6 +1548,7 @@ Renderer* SWRenderer_create(void) swrVtable.gpuGetColorWriteEnable = SWRenderer_gpuGetColorWriteEnable; swrVtable.gpuGetBlendEnable = SWRenderer_gpuGetBlendEnable; swrVtable.gpuGetBlendMode = SWRenderer_gpuGetBlendMode; + swrVtable.gpuSetTexFilter = SWRenderer_gpuSetTexFilter; swrVtable.gpuSetFog = SWRenderer_gpuSetFog; swrVtable.drawSpriteTiled = SWRenderer_drawSpriteTiled; swrVtable.drawSurfaceTiled = SWRenderer_drawSurfaceTiled; From d994c323ff1158ea2222e455cdfd1f2fadcf1dff Mon Sep 17 00:00:00 2001 From: Un1q32 Date: Sun, 27 Sep 2026 00:53:30 -0400 Subject: [PATCH 130/130] fix warnings --- src/sw/sw_renderer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sw/sw_renderer.c b/src/sw/sw_renderer.c index e6e7b9445..7a81935ed 100644 --- a/src/sw/sw_renderer.c +++ b/src/sw/sw_renderer.c @@ -789,7 +789,8 @@ static void SWRenderer_gpuSetBlendModeExt(Renderer* renderer, int32_t sfactor, i static void SWRenderer_gpuSetTexFilter(Renderer* renderer, bool enable) { - (void)enable; + (void) renderer; + (void) enable; UNIMP(); }