/** * Copyright (c) 2025 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "rendertext.h" #include "display/render.h" #include "assert/assert.h" #include "display/spritebatch/spritebatch.h" #include "util/memory.h" #include "util/math.h" texture_t RENDER_TEXT_TEXTURE; static mesh_t RENDER_TEXT_QUAD_MESH; void renderTextInit(void) { const int32_t cols = FONT_COLUMN_COUNT; const int32_t rows = (FONT_TILE_COUNT + cols - 1) / cols; const int32_t inputFontWidth = cols * FONT_TILE_WIDTH; const int32_t inputFontHeight = rows * FONT_TILE_HEIGHT; int32_t outputFontWidth = inputFontWidth; int32_t outputFontHeight = inputFontHeight; // Round up to nearest power of 2 #if PSP outputFontWidth = mathNextPowTwo(inputFontWidth); outputFontHeight = mathNextPowTwo(inputFontHeight); #endif uint8_t *pixels = (uint8_t *)memoryAllocate( outputFontWidth * outputFontHeight * sizeof(uint8_t) ); // Buffer the 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 int32_t pixel = (tileY + y) * outputFontWidth + (tileX + x); const int32_t pixelOffset = pixel; uint8_t value = tile[y * FONT_TILE_WIDTH + x]; pixels[pixel] = value ? 0xFF : 0x00; // Alpha channel } } } textureInit( &RENDER_TEXT_TEXTURE, outputFontWidth, outputFontHeight, GL_ALPHA, pixels ); memoryFree(pixels); } void renderTextDrawChar( const float_t x, const float_t y, const char_t c, const uint8_t r, const uint8_t g, const uint8_t b ) { int32_t tileIndex = (int32_t)(c) - FONT_CHAR_START; assertTrue( tileIndex >= 0 && tileIndex < FONT_TILE_COUNT, "Character is out of bounds for font tiles" ); const float_t w = (float)RENDER_TEXT_TEXTURE.width; const float_t h = (float)RENDER_TEXT_TEXTURE.height; const int32_t tileX = (tileIndex % FONT_COLUMN_COUNT); const int32_t tileY = (tileIndex / FONT_COLUMN_COUNT); spriteBatchPush( &RENDER_TEXT_TEXTURE, x, y, x + FONT_TILE_WIDTH, y + FONT_TILE_HEIGHT, r, g, b, 0xFF, (tileX * FONT_TILE_WIDTH) / w, (tileY * FONT_TILE_HEIGHT) / h, ((tileX + 1) * FONT_TILE_WIDTH) / w, ((tileY + 1) * FONT_TILE_HEIGHT) / h ); } void renderTextDraw( const float_t x, const float_t y, const char_t *text, const uint8_t r, const uint8_t g, const uint8_t b ) { 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, r, g, b); posX += FONT_TILE_WIDTH; } } void renderTextMeasure( const char_t *text, int32_t *outWidth, int32_t *outHeight ) { assertNotNull(text, "Text cannot be NULL"); assertNotNull(outWidth, "Output width pointer cannot be NULL"); assertNotNull(outHeight, "Output height pointer cannot be NULL"); int32_t width = 0; int32_t height = FONT_TILE_HEIGHT; int32_t lineWidth = 0; char_t c; int32_t i = 0; while((c = text[i++]) != '\0') { if(c == '\n') { if(lineWidth > width) { width = lineWidth; } lineWidth = 0; height += FONT_TILE_HEIGHT; continue; } lineWidth += FONT_TILE_WIDTH; } if(lineWidth > width) { width = lineWidth; } *outWidth = width; *outHeight = height; } void renderTextDispose(void) { textureDispose(&RENDER_TEXT_TEXTURE); }