From 6cbf8d1f8a8c6f1356fb5b8090ddcc34e2ea1a39 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Tue, 12 Apr 2022 12:08:37 -0700 Subject: [PATCH] rework API for batching --- include/wellspring.h | 110 +++++++----- lib/stb_truetype.h | 19 +- src/wellspring.c | 401 +++++++++++++++++++++++++++++++++++-------- 3 files changed, 405 insertions(+), 125 deletions(-) diff --git a/include/wellspring.h b/include/wellspring.h index f738d80..263d457 100644 --- a/include/wellspring.h +++ b/include/wellspring.h @@ -62,8 +62,8 @@ WELLSPRINGAPI uint32_t Wellspring_LinkedVersion(void); /* Type definitions */ -typedef struct Wellspring_Font Wellspring_Font; typedef struct Wellspring_Packer Wellspring_Packer; +typedef struct Wellspring_TextBatch Wellspring_TextBatch; typedef struct Wellspring_FontRange { @@ -80,89 +80,113 @@ typedef struct Wellspring_GlyphQuad float x1, y1, s1, t1; // bottom-right; } Wellspring_GlyphQuad; +typedef struct Wellspring_Color +{ + uint8_t r, g, b, a; +} Wellspring_Color; + +typedef struct Wellspring_Vertex +{ + float x, y, z; + float u, v; + uint8_t r, g, b, a; +} Wellspring_Vertex; + /* API definition */ -WELLSPRINGAPI Wellspring_Font* Wellspring_LoadFont( - const uint8_t *fontData, /* can be freed after */ - uint32_t fontDataLength -); - -WELLSPRINGAPI void Wellspring_GetFontMetrics( - Wellspring_Font *font, - int32_t *pAscent, - int32_t *pDescent, - int32_t *pLineGap -); - -WELLSPRINGAPI int32_t Wellspring_FindGlyphIndex( - Wellspring_Font *font, - int32_t codepoint -); - -WELLSPRINGAPI void Wellspring_GetGlyphMetrics( - Wellspring_Font *font, - int32_t glyphIndex, - int32_t *pAdvance, - int32_t *pLeftSideBearing -); - -WELLSPRINGAPI int32_t Wellspring_GetGlyphKernAdvance( - Wellspring_Font *font, - int32_t glyphIndexA, - int32_t glyphIndexB -); - WELLSPRINGAPI Wellspring_Packer* Wellspring_CreatePacker( - uint8_t *pixels, + uint8_t *fontBytes, + uint32_t fontBytesLength, uint32_t width, uint32_t height, - uint32_t strideInBytes, - uint32_t padding + uint32_t strideInBytes, /* 0 means the buffer is tightly packed. */ + uint32_t padding /* A sensible value here is 1 to allow bilinear filtering. */ ); WELLSPRINGAPI uint32_t Wellspring_PackFontRanges( Wellspring_Packer *packer, - Wellspring_Font *font, Wellspring_FontRange *ranges, uint32_t numRanges ); -WELLSPRINGAPI uint8_t* Wellspring_GetPixels( - Wellspring_Packer *packer +/* This data must be uploaded to a texture before you render! + * The pixel data also becomes outdated if you call PackFontRanges. + * Length is width * height. + */ +WELLSPRINGAPI void Wellspring_GetPixels( + Wellspring_Packer *packer, + uint8_t **pData ); -WELLSPRINGAPI uint32_t Wellspring_GetGlyphQuad( +/* Batches are not thread-safe, recommend one batch per thread. */ +WELLSPRINGAPI Wellspring_TextBatch* Wellspring_TextBatchCreate(); + +/* Also restarts the batch */ +WELLSPRINGAPI void Wellspring_TextBatchStart(Wellspring_TextBatch *textBatch); + +WELLSPRINGAPI uint8_t Wellspring_DrawTextBatched( + Wellspring_TextBatch *textBatch, Wellspring_Packer *packer, - int32_t glyphIndex, - Wellspring_GlyphQuad *pGlyphQuad + float x, + float y, + float depth, + Wellspring_Color *color, + const uint8_t *strBytes, + uint32_t strLengthInBytes ); +WELLSPRINGAPI void Wellspring_TextBatchGetBufferLengths( + Wellspring_TextBatch *textBatch, + uint32_t *pVertexLength, + uint32_t *pIndexLength +); + +WELLSPRINGAPI void Wellspring_TextBatchGetBuffers( + Wellspring_TextBatch *textBatch, + Wellspring_Vertex **pVertexBuffer, + uint32_t **pIndexBuffer +); + +WELLSPRINGAPI void Wellspring_TextBatchDestroy(Wellspring_TextBatch *textBatch); + WELLSPRINGAPI void Wellspring_DestroyPacker(Wellspring_Packer *packer); -WELLSPRINGAPI void Wellspring_DestroyFont(Wellspring_Font *font); /* Function defines */ #ifdef USE_SDL2 #define Wellspring_malloc SDL_malloc +#define Wellspring_realloc SDL_realloc #define Wellspring_free SDL_free #define Wellspring_memcpy SDL_memcpy +#define Wellspring_memset SDL_memset #define Wellspring_ifloor(x) ((int) SDL_floorf(x)) #define Wellspring_iceil(x) ((int) SDL_ceilf(x)) #define Wellspring_sqrt SDL_sqrt #define Wellspring_pow SDL_pow - -/* FIXME: finish these defines */ +#define Wellspring_fmod SDL_fmod +#define Wellspring_cos SDL_cos +#define Wellspring_acos SDL_acos +#define Wellspring_fabs SDL_fabs +#define Wellspring_assert SDL_assert +#define Wellspring_strlen SDL_strlen #else #define Wellspring_malloc malloc +#define Wellspring_realloc realloc #define Wellspring_free free #define Wellspring_memcpy memcpy #define Wellspring_ifloor(x) ((int) floor(x)) #define Wellspring_iceil(x) ((int) ceil(x)) #define Wellspring_sqrt sqrt #define Wellspring_pow pow +#define Wellspring_fmod fmod +#define Wellspring_cos cos +#define Wellspring_acos acos +#define Wellspring_fabs fabs +#define Wellspring_assert assert +#define Wellspring_strlen strlen #endif diff --git a/lib/stb_truetype.h b/lib/stb_truetype.h index bbf2284..497aa23 100644 --- a/lib/stb_truetype.h +++ b/lib/stb_truetype.h @@ -424,14 +424,17 @@ int main(int arg, char **argv) #ifdef STB_TRUETYPE_IMPLEMENTATION // #define your own (u)stbtt_int8/16/32 before including to override this - #ifndef stbtt_uint8 - typedef unsigned char stbtt_uint8; - typedef signed char stbtt_int8; - typedef unsigned short stbtt_uint16; - typedef signed short stbtt_int16; - typedef unsigned int stbtt_uint32; - typedef signed int stbtt_int32; - #endif + /* this is a lie - C99 cannot check the existence of or override typedefs -cosmonaut */ + /* + * #ifndef stbtt_uint8 + * typedef unsigned char stbtt_uint8; + * typedef signed char stbtt_int8; + * typedef unsigned short stbtt_uint16; + * typedef signed short stbtt_int16; + * typedef unsigned int stbtt_uint32; + * typedef signed int stbtt_int32; + * #endif + */ typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; diff --git a/src/wellspring.c b/src/wellspring.c index b77f8ac..6ca255a 100644 --- a/src/wellspring.c +++ b/src/wellspring.c @@ -29,120 +29,373 @@ #define STBTT_malloc(x,u) ((void)(u),Wellspring_malloc(x)) #define STBTT_free(x,u) ((void)(u),Wellspring_free(x)) +#define STBTT_memcpy Wellspring_memcpy +#define STBTT_memset Wellspring_memset #define STBTT_ifloor Wellspring_ifloor #define STBTT_iceil Wellspring_iceil #define STBTT_sqrt Wellspring_sqrt #define STBTT_pow Wellspring_pow +#define STBTT_fmod Wellspring_fmod +#define STBTT_cos Wellspring_cos +#define STBTT_acos Wellspring_acos +#define STBTT_fabs Wellspring_fabs +#define STBTT_assert Wellspring_assert +#define STBTT_strlen Wellspring_strlen + +typedef uint8_t stbtt_uint8; +typedef int8_t stbtt_int8; +typedef uint16_t stbtt_uint16; +typedef int16_t stbtt_int16; +typedef uint32_t stbtt_uint32; +typedef int32_t stbtt_int32; #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION #pragma GCC diagnostic ignored "-Wunused-function" #include "stb_truetype.h" -typedef struct Font +#define INITIAL_QUAD_CAPACITY 128 + +/* Structs */ + +typedef struct CharRange { - uint8_t *fontData; - stbtt_fontinfo stbFontInfo; -} Font; + stbtt_packedchar *data; + int32_t firstCodepoint; + uint32_t charCount; +} CharRange; + +typedef struct Packer +{ + uint8_t *fontBytes; + + stbtt_pack_context *context; + uint8_t *pixels; + uint32_t width; + uint32_t height; + uint32_t strideInBytes; + uint32_t padding; + + CharRange *ranges; + uint32_t rangeCount; +} Packer; + +typedef struct Batch +{ + Wellspring_Vertex *vertices; + uint32_t vertexCount; + uint32_t vertexCapacity; + + uint32_t *indices; + uint32_t indexCount; + uint32_t indexCapacity; +} Batch; + +/* UTF-8 Decoder */ + +/* Copyright (c) 2008-2009 Bjoern Hoehrmann + * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + */ + +#define UTF8_ACCEPT 0 +#define UTF8_REJECT 1 + +static const uint8_t utf8d[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 +}; + +static uint32_t inline +decode(uint32_t* state, uint32_t* codep, uint32_t byte) { + uint32_t type = utf8d[byte]; + + *codep = (*state != UTF8_ACCEPT) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state*16 + type]; + return *state; +} + +/* API */ uint32_t Wellspring_LinkedVersion(void) { return WELLSPRING_COMPILED_VERSION; } -Wellspring_Font* Wellspring_CreateFont( - const uint8_t *fontData, - uint32_t fontDataLength -) { - Font *font; - - font = Wellspring_malloc(sizeof(Font)); - Wellspring_memcpy(font->fontData, fontData, fontDataLength); - stbtt_InitFont(&font->stbFontInfo, font->fontData, 0); - - return (Wellspring_Font*) font; -} - -void Wellspring_GetFontMetrics( - Wellspring_Font *font, - int32_t *pAscent, - int32_t *pDescent, - int32_t *pLineGap -) { - Font *myFont = (Font*) font; - stbtt_GetFontVMetrics(&myFont->stbFontInfo, pAscent, pDescent, pLineGap); -} - -int32_t Wellspring_FindGlyphIndex( - Wellspring_Font *font, - int32_t codepoint -) { - Font *myFont = (Font*) font; - return stbtt_FindGlyphIndex(&myFont->stbFontInfo, codepoint); -} - -void Wellspring_GetGlyphMetrics( - Wellspring_Font *font, - int32_t glyphIndex, - int32_t *pAdvance, - int32_t *pLeftSideBearing -) { - Font *myFont = (Font*) font; - stbtt_GetGlyphHMetrics(&myFont->stbFontInfo, glyphIndex, pAdvance, pLeftSideBearing); -} - -int32_t Wellspring_GetGlyphKernAdvance( - Wellspring_Font *font, - int32_t glyphIndexA, - int32_t glyphIndexB -) { - Font *myFont = (Font*) font; - stbtt_GetGlyphKernAdvance(&myFont->stbFontInfo, glyphIndexA, glyphIndexB); -} - Wellspring_Packer* Wellspring_CreatePacker( - uint8_t *pixels, + uint8_t *fontBytes, + uint32_t fontBytesLength, uint32_t width, uint32_t height, uint32_t strideInBytes, uint32_t padding ) { + Packer *packer = Wellspring_malloc(sizeof(Packer)); + packer->fontBytes = Wellspring_malloc(fontBytesLength); + Wellspring_memcpy(packer->fontBytes, fontBytes, fontBytesLength); + + packer->context = Wellspring_malloc(sizeof(stbtt_pack_context)); + packer->pixels = Wellspring_malloc(sizeof(uint8_t) * width * height); + + packer->width = width; + packer->height = height; + packer->strideInBytes = strideInBytes; + packer->padding = padding; + + packer->ranges = NULL; + packer->rangeCount = 0; + + stbtt_PackBegin(packer->context, packer->pixels, width, height, strideInBytes, padding, NULL); + + return (Wellspring_Packer*) packer; } uint32_t Wellspring_PackFontRanges( Wellspring_Packer *packer, - Wellspring_Font *font, Wellspring_FontRange *ranges, uint32_t numRanges ) { + Packer *myPacker = (Packer*) packer; + Wellspring_FontRange *currentFontRange; + stbtt_pack_range stbPackRanges[numRanges]; + CharRange *currentCharRange; + uint32_t i; + for (i = 0; i < numRanges; i += 1) + { + currentFontRange = &ranges[i]; + stbPackRanges[i].font_size = currentFontRange->fontSize; + stbPackRanges[i].first_unicode_codepoint_in_range = currentFontRange->firstCodepoint; + stbPackRanges[i].array_of_unicode_codepoints = NULL; + stbPackRanges[i].num_chars = currentFontRange->numChars; + stbPackRanges[i].h_oversample = currentFontRange->oversampleH; + stbPackRanges[i].v_oversample = currentFontRange->oversampleV; + stbPackRanges[i].chardata_for_range = Wellspring_malloc(sizeof(stbtt_packedchar) * currentFontRange->numChars); + } + + stbtt_PackFontRanges(myPacker->context, myPacker->fontBytes, 0, stbPackRanges, numRanges); + + myPacker->rangeCount += numRanges; + myPacker->ranges = Wellspring_realloc(myPacker->ranges, sizeof(CharRange) * myPacker->rangeCount); + + for (i = 0; i < numRanges; i += 1) + { + currentCharRange = &myPacker->ranges[myPacker->rangeCount + i]; + currentCharRange->data = stbPackRanges[i].chardata_for_range; + currentCharRange->firstCodepoint = stbPackRanges[i].first_unicode_codepoint_in_range; + currentCharRange->charCount = stbPackRanges[i].num_chars; + } + + myPacker->rangeCount += numRanges; } -uint8_t* Wellspring_GetPixels( - Wellspring_Packer *packer -) { - -} - -uint32_t Wellspring_GetGlyphQuad( +void Wellspring_GetPixels( Wellspring_Packer *packer, - int32_t glyphIndex, - Wellspring_GlyphQuad *pGlyphQuad + uint8_t **pData ) { + Packer* myPacker = (Packer*) packer; + *pData = myPacker->pixels; +} +Wellspring_TextBatch* Wellspring_TextBatchCreate() +{ + Batch *batch = Wellspring_malloc(sizeof(Batch)); + + batch->vertexCapacity = INITIAL_QUAD_CAPACITY * 4; + batch->vertices = Wellspring_malloc(sizeof(Wellspring_Vertex) * batch->vertexCapacity); + batch->vertexCount = 0; + + batch->indexCapacity = INITIAL_QUAD_CAPACITY * 6; + batch->indices = Wellspring_malloc(sizeof(uint32_t) * batch->indexCapacity); + batch->indexCount = 0; + + return (Wellspring_TextBatch*) batch; +} + +void Wellspring_TextBatchStart(Wellspring_TextBatch *textBatch) +{ + Batch *batch = (Batch*) textBatch; + batch->vertexCount = 0; + batch->indexCount = 0; +} + +uint8_t Wellspring_DrawTextBatched( + Wellspring_TextBatch *textBatch, + Wellspring_Packer *packer, + float x, + float y, + float depth, + Wellspring_Color *color, + const uint8_t *str, + uint32_t strLength +) { + Batch *batch = (Batch*) textBatch; + Packer *myPacker = (Packer*) packer; + uint32_t decodeState = 0; + uint32_t codepoint; + int32_t glyphIndex; + int32_t previousGlyphIndex; + stbtt_packedchar *rangeData; + stbtt_aligned_quad charQuad; + uint32_t vertexBufferIndex; + uint32_t indexBufferIndex; + uint32_t i; + + for (i = 0; i < strLength; i += 1) + { + if (decode(&decodeState, &codepoint, str[i])) + { + if (decodeState == UTF8_REJECT) + { + /* Something went very wrong */ + return 0; + } + + continue; + } + + /* Find the packed char data */ + for (i = 0; i < myPacker->rangeCount; i += 1) + { + if ( + codepoint >= myPacker->ranges[i].firstCodepoint && + codepoint < myPacker->ranges[i].firstCodepoint + myPacker->ranges[i].charCount + ) { + rangeData = myPacker->ranges[i].data; + glyphIndex = codepoint - myPacker->ranges[i].firstCodepoint; + } + } + + stbtt_GetPackedQuad( + rangeData, + myPacker->width, + myPacker->height, + glyphIndex, + &x, + &y, + &charQuad, + 1 + ); + + if (batch->vertexCount >= batch->vertexCapacity) + { + batch->vertexCapacity *= 2; + batch->vertices = Wellspring_realloc(batch->vertices, sizeof(Wellspring_Vertex) * batch->vertexCapacity); + } + + if (batch->indexCount >= batch->indexCapacity) + { + batch->indexCapacity *= 2; + batch->indices = Wellspring_realloc(batch->indices, sizeof(uint32_t) * batch->indexCapacity); + } + + /* TODO: kerning and alignment */ + + vertexBufferIndex = batch->vertexCount; + indexBufferIndex = batch->indexCount; + + batch->vertices[vertexBufferIndex].x = charQuad.x0; + batch->vertices[vertexBufferIndex].y = charQuad.y0; + batch->vertices[vertexBufferIndex].z = depth; + batch->vertices[vertexBufferIndex].u = charQuad.s0; + batch->vertices[vertexBufferIndex].v = charQuad.t0; + batch->vertices[vertexBufferIndex].r = color->r; + batch->vertices[vertexBufferIndex].g = color->g; + batch->vertices[vertexBufferIndex].b = color->b; + batch->vertices[vertexBufferIndex].a = color->a; + + batch->vertices[vertexBufferIndex + 1].x = charQuad.x0; + batch->vertices[vertexBufferIndex + 1].y = charQuad.y1; + batch->vertices[vertexBufferIndex + 1].z = depth; + batch->vertices[vertexBufferIndex + 1].u = charQuad.s0; + batch->vertices[vertexBufferIndex + 1].v = charQuad.t1; + batch->vertices[vertexBufferIndex + 1].r = color->r; + batch->vertices[vertexBufferIndex + 1].g = color->g; + batch->vertices[vertexBufferIndex + 1].b = color->b; + batch->vertices[vertexBufferIndex + 1].a = color->a; + + batch->vertices[vertexBufferIndex + 2].x = charQuad.x1; + batch->vertices[vertexBufferIndex + 2].y = charQuad.y0; + batch->vertices[vertexBufferIndex + 2].z = depth; + batch->vertices[vertexBufferIndex + 2].u = charQuad.s1; + batch->vertices[vertexBufferIndex + 2].v = charQuad.t0; + batch->vertices[vertexBufferIndex + 2].r = color->r; + batch->vertices[vertexBufferIndex + 2].g = color->g; + batch->vertices[vertexBufferIndex + 2].b = color->b; + batch->vertices[vertexBufferIndex + 2].a = color->a; + + batch->vertices[vertexBufferIndex + 3].x = charQuad.x1; + batch->vertices[vertexBufferIndex + 3].y = charQuad.y1; + batch->vertices[vertexBufferIndex + 3].z = depth; + batch->vertices[vertexBufferIndex + 3].u = charQuad.s1; + batch->vertices[vertexBufferIndex + 3].v = charQuad.t1; + batch->vertices[vertexBufferIndex + 3].r = color->r; + batch->vertices[vertexBufferIndex + 3].g = color->g; + batch->vertices[vertexBufferIndex + 3].b = color->b; + batch->vertices[vertexBufferIndex + 3].a = color->a; + + batch->indices[indexBufferIndex] = vertexBufferIndex; + batch->indices[indexBufferIndex + 1] = vertexBufferIndex + 1; + batch->indices[indexBufferIndex + 2] = vertexBufferIndex + 2; + batch->indices[indexBufferIndex + 3] = vertexBufferIndex + 2; + batch->indices[indexBufferIndex + 4] = vertexBufferIndex + 1; + batch->indices[indexBufferIndex + 5] = vertexBufferIndex + 3; + + batch->vertexCount += 4; + batch->indexCount += 6; + } + + return 1; +} + +void Wellspring_TextBatchGetBufferLengths( + Wellspring_TextBatch *textBatch, + uint32_t *pVertexLength, + uint32_t *pIndexLength +) { + Batch *batch = (Batch*) textBatch; + *pVertexLength = batch->vertexCount; + *pIndexLength = batch->indexCount; +} + +void Wellspring_TextBatchGetBuffers( + Wellspring_TextBatch *textBatch, + Wellspring_Vertex **pVertexBuffer, + uint32_t **pIndexBuffer +) { + Batch *batch = (Batch*) textBatch; + *pVertexBuffer = batch->vertices; + *pIndexBuffer = batch->indices; +} + +void Wellspring_TextBatchDestroy(Wellspring_TextBatch *textBatch) +{ + Batch *batch = (Batch*) textBatch; + Wellspring_free(batch->vertices); + Wellspring_free(batch->indices); + Wellspring_free(batch); } void Wellspring_DestroyPacker(Wellspring_Packer *packer) { + Packer* myPacker = (Packer*) packer; -} - -void Wellspring_DestroyFont( - Wellspring_Font *font -) { - Font* myFont = (Font*) font; - - Wellspring_free(myFont->fontData); - Wellspring_free(myFont); + Wellspring_free(myPacker->fontBytes); + Wellspring_free(myPacker->context); + Wellspring_free(myPacker->pixels); }