diff --git a/src/dusk/display/renderbase.c b/src/dusk/display/renderbase.c index 3cd48c8..9331e01 100644 --- a/src/dusk/display/renderbase.c +++ b/src/dusk/display/renderbase.c @@ -5,6 +5,4 @@ * https://opensource.org/licenses/MIT */ -#include "display/render.h" - -uint8_t RENDER_FRAME = 0; \ No newline at end of file +#include "display/render.h" \ No newline at end of file diff --git a/src/dusk/display/renderbase.h b/src/dusk/display/renderbase.h index 1e92afc..2039247 100644 --- a/src/dusk/display/renderbase.h +++ b/src/dusk/display/renderbase.h @@ -15,8 +15,6 @@ #define RENDER_HEIGHT 240 #endif -extern uint8_t RENDER_FRAME; - /** * Initializes the rendering system. */ diff --git a/src/dusksdl2/display/CMakeLists.txt b/src/dusksdl2/display/CMakeLists.txt index ca597b7..93379ae 100644 --- a/src/dusksdl2/display/CMakeLists.txt +++ b/src/dusksdl2/display/CMakeLists.txt @@ -7,6 +7,8 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE render.c + renderbackbuffer.c + rendertext.c ) # Subdirs diff --git a/src/dusksdl2/display/render.c b/src/dusksdl2/display/render.c index 40762dc..505c5d2 100644 --- a/src/dusksdl2/display/render.c +++ b/src/dusksdl2/display/render.c @@ -7,12 +7,15 @@ #include "render.h" #include "assert/assert.h" +#include "renderbackbuffer.h" +#include "rendertext.h" SDL_Window *RENDER_WINDOW; SDL_Renderer *RENDER_RENDERER; bool_t RENDER_RUNNING; errorret_t renderInit(void) { + // Init SDL if(SDL_Init(SDL_INIT_VIDEO) != 0) { errorThrow( "SDL Failed to Initialize: %s", @@ -20,31 +23,36 @@ errorret_t renderInit(void) { ); } + // Create window. RENDER_WINDOW = SDL_CreateWindow( "DuskSDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, RENDER_WINDOW_WIDTH_DEFAULT, RENDER_WINDOW_HEIGHT_DEFAULT, - SDL_WINDOW_SHOWN + SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI ); + if(!RENDER_WINDOW) { + errorThrow("SDL_CreateWindow failed: %s", SDL_GetError()); + } - #if RENDER_USE_FRAMEBUFFER - printf("Using framebuffer for rendering\n"); - #else - printf("Using window for rendering\n"); - #endif - + // Create renderer RENDER_RENDERER = SDL_CreateRenderer( RENDER_WINDOW, -1, SDL_RENDERER_ACCELERATED ); - if(!RENDER_RENDERER) { errorThrow("SDL_CreateRenderer failed: %s", SDL_GetError()); } + // Create back buffer. + renderBackBufferInit(); + + // Init other things + renderTextInit(); + + // Mark ready. RENDER_RUNNING = true; errorOk(); @@ -58,23 +66,27 @@ errorret_t renderDraw(void) { } } - SDL_SetRenderDrawColor(RENDER_RENDERER, 0, 0, 0, 255); - SDL_RenderClear(RENDER_RENDERER); - - // Draw red triangle - SDL_SetRenderDrawColor(RENDER_RENDERER, 255, 0, 0, 255); - SDL_RenderDrawLine(RENDER_RENDERER, 100, 100, 200, 100); - SDL_RenderDrawLine(RENDER_RENDERER, 200, 100, 150, 50); - SDL_RenderDrawLine(RENDER_RENDERER, 150, 50, 100, 100); + // Bind the backbuffer + renderBackBufferBind(); + // Draw everything + renderTextDraw(0.0f, 0.0f, "Hello World!"); + + // Unbind the backbuffer + renderBackBufferUnbind(); + renderBackBufferDraw(); + + // Draw things that render above the back buffer quad. + + // Present the renderer SDL_RenderPresent(RENDER_RENDERER); - RENDER_FRAME++; - errorOk(); } errorret_t renderDispose(void) { + renderBackBufferDispose(); + SDL_DestroyRenderer(RENDER_RENDERER); SDL_DestroyWindow(RENDER_WINDOW); SDL_Quit(); diff --git a/src/dusksdl2/display/renderbackbuffer.c b/src/dusksdl2/display/renderbackbuffer.c index 97ba697..4adf425 100644 --- a/src/dusksdl2/display/renderbackbuffer.c +++ b/src/dusksdl2/display/renderbackbuffer.c @@ -6,30 +6,89 @@ */ #include "renderbackbuffer.h" +#include "render.h" #if RENDER_USE_FRAMEBUFFER -SDL_Texture *RENDER_BACKBUFFER; + SDL_Texture *RENDER_BACKBUFFER; #else #endif -void renderBackBufferInit(void) { +errorret_t renderBackBufferInit(void) { #if RENDER_USE_FRAMEBUFFER RENDER_BACKBUFFER = SDL_CreateTexture( RENDER_RENDERER, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, - RENDER_WINDOW_WIDTH_DEFAULT, - RENDER_WINDOW_HEIGHT_DEFAULT + RENDER_WIDTH, + RENDER_HEIGHT ); if(!RENDER_BACKBUFFER) { - assertUnreachable("SDL_CreateTexture failed\n"); - return; + errorThrow("SDL_CreateTexture failed: %s", SDL_GetError()); } + + // Make sure we unbind the back buffer after creation + SDL_SetRenderTarget(RENDER_RENDERER, NULL); #else // No back buffer needed for window rendering #endif + errorOk(); +} + +void renderBackBufferBind(void) { + #if RENDER_USE_FRAMEBUFFER + SDL_SetRenderTarget(RENDER_RENDERER, RENDER_BACKBUFFER); + #endif + // Fill background with cornflower blue. + SDL_SetRenderDrawColor(RENDER_RENDERER, 100, 149, 237, 255); + SDL_RenderClear(RENDER_RENDERER); +} + +void renderBackBufferUnbind(void) { + #if RENDER_USE_FRAMEBUFFER + SDL_SetRenderTarget(RENDER_RENDERER, NULL); + #endif +} + +void renderBackBufferDraw(void) { + #if RENDER_USE_FRAMEBUFFER + // Clear background black + SDL_SetRenderDrawColor(RENDER_RENDERER, 0, 0, 0, 255); + SDL_RenderClear(RENDER_RENDERER); + + // Create a quad that is scaled to fit but maintain original aspect ratio + int32_t windowWidth, windowHeight; + SDL_GetWindowSize(RENDER_WINDOW, &windowWidth, &windowHeight); + + int32_t renderWidth, renderHeight, renderX, renderY; + if(RENDER_WIDTH * windowHeight > RENDER_HEIGHT * windowWidth) { + renderWidth = windowWidth; + renderHeight = (RENDER_HEIGHT * windowWidth) / RENDER_WIDTH; + renderX = 0; + renderY = (windowHeight - renderHeight) / 2; + } else { + renderWidth = (RENDER_WIDTH * windowHeight) / RENDER_HEIGHT; + renderHeight = windowHeight; + renderX = (windowWidth - renderWidth) / 2; + renderY = 0; + } + + // Draw the back buffer texture + SDL_Rect destRect = { renderX, renderY, renderWidth, renderHeight }; + SDL_RenderCopy(RENDER_RENDERER, RENDER_BACKBUFFER, NULL, &destRect); + #else + // No back buffer to draw + #endif +} + +errorret_t renderBackBufferDispose(void) { + #if RENDER_USE_FRAMEBUFFER + SDL_DestroyTexture(RENDER_BACKBUFFER); + RENDER_BACKBUFFER = NULL; + #endif + + errorOk(); } \ No newline at end of file diff --git a/src/dusksdl2/display/renderbackbuffer.h b/src/dusksdl2/display/renderbackbuffer.h index 8c3bd02..996d683 100644 --- a/src/dusksdl2/display/renderbackbuffer.h +++ b/src/dusksdl2/display/renderbackbuffer.h @@ -6,4 +6,32 @@ */ #pragma once -#include "dusksdl2.h" \ No newline at end of file +#include "display/renderbase.h" + +/** + * Initializes the render back buffer. May be either a framebuffer or a texture + * depending on the render settings. + */ +errorret_t renderBackBufferInit(void); + +/** + * Binds the render back buffer as the current render target. + */ +void renderBackBufferBind(void); + +/** + * Unbinds the render back buffer, returning to the default render target. + */ +void renderBackBufferUnbind(void); + +/** + * Draws the render back buffer to the screen, scaling it to fit the window. + */ +void renderBackBufferDraw(void); + +/** + * Disposes of the render back buffer, freeing any resources it holds. + * + * @return An error state if an error occurred, otherwise OK. + */ +errorret_t renderBackBufferDispose(void); \ No newline at end of file diff --git a/src/dusksdl2/display/rendertext.c b/src/dusksdl2/display/rendertext.c new file mode 100644 index 0000000..38ea445 --- /dev/null +++ b/src/dusksdl2/display/rendertext.c @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "rendertext.h" +#include "render.h" +#include "ui/font.h" +#include "assert/assert.h" + +SDL_Texture* RENDER_TEXT_TEXTURE = NULL; + +void renderTextInit(void) { + const int32_t cols = FONT_COLUMN_COUNT; + const int32_t rows = (FONT_TILE_COUNT + cols - 1) / cols; + const int32_t fontWidth = cols * FONT_TILE_WIDTH; + const int32_t fontHeight = rows * FONT_TILE_HEIGHT; + + // RGBA8888 surface + SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormat( + 0, + fontWidth, + fontHeight, + 32, + SDL_PIXELFORMAT_RGBA32 + ); + assertNotNull(surface, "Failed to create surface for text rendering"); + + const int32_t pitch_px = surface->pitch / 4; + uint32_t *pixels = (uint32_t *)surface->pixels; + + for(int tileIndex = 0; tileIndex < FONT_TILE_COUNT; ++tileIndex) { + const int32_t tileX = (tileIndex % FONT_COLUMN_COUNT) * FONT_TILE_WIDTH; + const int32_t tileY = (tileIndex / FONT_COLUMN_COUNT) * FONT_TILE_HEIGHT; + const uint8_t* tile = TILE_PIXEL_DATA[tileIndex]; + + for (int y = 0; y < FONT_TILE_HEIGHT; ++y) { + for (int x = 0; x < FONT_TILE_WIDTH; ++x) { + const uint8_t color = tile[y * FONT_TILE_WIDTH + x] ? 0xFF : 0; + // Convert to RGBA (8 bits per channel) + const uint32_t rgba = color ? 0xFFFFFFFF : 0x00000000; + pixels[(tileY + y) * pitch_px + (tileX + x)] = rgba; + } + } + } + + RENDER_TEXT_TEXTURE = SDL_CreateTextureFromSurface( + RENDER_RENDERER, + surface + ); + assertNotNull(RENDER_TEXT_TEXTURE, "Failed to create texture from surface"); + + SDL_FreeSurface(surface); + SDL_SetTextureBlendMode(RENDER_TEXT_TEXTURE, SDL_BLENDMODE_BLEND); +} + +void renderTextDrawChar( + const float_t x, + const float_t y, + const char_t c +) { + int32_t tileIndex = (int32_t)(c) - FONT_CHAR_START; + + assertTrue( + tileIndex >= 0 && tileIndex < FONT_TILE_COUNT, + "Character is out of bounds for font tiles" + ); + assertNotNull(RENDER_TEXT_TEXTURE, "Texture cannot be NULL"); + + const int32_t tileX = (tileIndex % FONT_COLUMN_COUNT) * FONT_TILE_WIDTH; + const int32_t tileY = (tileIndex / FONT_COLUMN_COUNT) * FONT_TILE_HEIGHT; + + SDL_Rect srcRect = { + .x = tileX, + .y = tileY, + .w = FONT_TILE_WIDTH, + .h = FONT_TILE_HEIGHT + }; + + SDL_Rect dstRect = { + .x = (int32_t)roundf(x), + .y = (int32_t)roundf(y), + .w = FONT_TILE_WIDTH, + .h = FONT_TILE_HEIGHT + }; + SDL_RenderCopy(RENDER_RENDERER, RENDER_TEXT_TEXTURE, &srcRect, &dstRect); +} + +void renderTextDraw( + const float_t x, + const float_t y, + const char_t *text +) { + assertNotNull(text, "Text cannot be NULL"); + + float_t posX = x; + float_t posY = y; + + char_t c; + int32_t i = 0; + while((c = text[i++]) != '\0') { + if(c == '\n') { + posX = x; + posY += FONT_TILE_HEIGHT; + continue; + } + + renderTextDrawChar(posX, posY, c); + posX += FONT_TILE_WIDTH; + } +} \ No newline at end of file diff --git a/src/dusksdl2/display/rendertext.h b/src/dusksdl2/display/rendertext.h new file mode 100644 index 0000000..a408b34 --- /dev/null +++ b/src/dusksdl2/display/rendertext.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusksdl2.h" + +extern SDL_Texture* RENDER_TEXT_TEXTURE; + +/** + * Initializes the text rendering system. + */ +void renderTextInit(void); + +/** + * Draws a single character at the specified position. + * + * @param x The x-coordinate to draw the character at. + * @param y The y-coordinate to draw the character at. + * @param c The character to draw. + */ +void renderTextDrawChar( + const float_t x, + const float_t y, + const char_t c +); + +/** + * Draws a string of text at the specified position. + * + * @param x The x-coordinate to draw the text at. + * @param y The y-coordinate to draw the text at. + * @param text The null-terminated string of text to draw. + */ +void renderTextDraw( + const float_t x, + const float_t y, + const char_t *text +); \ No newline at end of file