working on msdf atlas support
continuous-integration/drone/push Build is passing Details

pull/1/head
cosmonaut 2023-12-06 15:45:42 -08:00
parent 6e0e4d0fa0
commit 7fa1c279ce
4 changed files with 3826 additions and 224 deletions

View File

@ -46,6 +46,7 @@ add_library(Wellspring
#Public header #Public header
include/Wellspring.h include/Wellspring.h
#Source #Source
lib/json.h
lib/stb_rect_pack.h lib/stb_rect_pack.h
lib/stb_truetype.h lib/stb_truetype.h
src/Wellspring.c src/Wellspring.c

View File

@ -63,7 +63,6 @@ WELLSPRINGAPI uint32_t Wellspring_LinkedVersion(void);
/* Type definitions */ /* Type definitions */
typedef struct Wellspring_Font Wellspring_Font; typedef struct Wellspring_Font Wellspring_Font;
typedef struct Wellspring_Packer Wellspring_Packer;
typedef struct Wellspring_TextBatch Wellspring_TextBatch; typedef struct Wellspring_TextBatch Wellspring_TextBatch;
typedef struct Wellspring_FontRange typedef struct Wellspring_FontRange
@ -113,44 +112,22 @@ typedef enum Wellspring_VerticalAlignment
WELLSPRINGAPI Wellspring_Font* Wellspring_CreateFont( WELLSPRINGAPI Wellspring_Font* Wellspring_CreateFont(
const uint8_t *fontBytes, const uint8_t *fontBytes,
uint32_t fontBytesLength uint32_t fontBytesLength,
); const uint8_t *atlasJsonBytes,
uint32_t atlasJsonBytesLength
WELLSPRINGAPI Wellspring_Packer* Wellspring_CreatePacker(
Wellspring_Font *font,
float fontSize,
uint32_t width,
uint32_t height,
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_FontRange *ranges,
uint32_t numRanges
);
/* Returns a pointer to an array of rasterized pixels of the packed font.
* This data must be uploaded to a texture before you render!
* The pixel data becomes outdated if you call PackFontRanges.
* Length is width * height.
*/
WELLSPRINGAPI uint8_t* Wellspring_GetPixelDataPointer(
Wellspring_Packer *packer
); );
/* Batches are not thread-safe, recommend one batch per thread. */ /* Batches are not thread-safe, recommend one batch per thread. */
WELLSPRINGAPI Wellspring_TextBatch* Wellspring_CreateTextBatch(); WELLSPRINGAPI Wellspring_TextBatch* Wellspring_CreateTextBatch(void);
/* Also restarts the batch */ /* Also restarts the batch */
WELLSPRINGAPI void Wellspring_StartTextBatch( WELLSPRINGAPI void Wellspring_StartTextBatch(
Wellspring_TextBatch *textBatch, Wellspring_TextBatch *textBatch,
Wellspring_Packer *packer Wellspring_Font *font
); );
WELLSPRINGAPI uint8_t Wellspring_TextBounds( WELLSPRINGAPI uint8_t Wellspring_TextBounds(
Wellspring_Packer* packer, Wellspring_Font *font,
float x, float x,
float y, float y,
Wellspring_HorizontalAlignment horizontalAlignment, Wellspring_HorizontalAlignment horizontalAlignment,
@ -182,7 +159,6 @@ WELLSPRINGAPI void Wellspring_GetBufferData(
); );
WELLSPRINGAPI void Wellspring_DestroyTextBatch(Wellspring_TextBatch *textBatch); WELLSPRINGAPI void Wellspring_DestroyTextBatch(Wellspring_TextBatch *textBatch);
WELLSPRINGAPI void Wellspring_DestroyPacker(Wellspring_Packer *packer);
WELLSPRINGAPI void Wellspring_DestroyFont(Wellspring_Font *font); WELLSPRINGAPI void Wellspring_DestroyFont(Wellspring_Font *font);
#ifdef __cplusplus #ifdef __cplusplus

3455
lib/json.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -95,6 +95,9 @@
#define STBRP_SORT Wellspring_sort #define STBRP_SORT Wellspring_sort
#define STBRP_ASSERT Wellspring_assert #define STBRP_ASSERT Wellspring_assert
#define SHEREDOM_JSON_H_malloc Wellspring_malloc
#define SHEREDOM_JSON_H_free Wellspring_free
typedef uint8_t stbtt_uint8; typedef uint8_t stbtt_uint8;
typedef int8_t stbtt_int8; typedef int8_t stbtt_int8;
typedef uint16_t stbtt_uint16; typedef uint16_t stbtt_uint16;
@ -112,48 +115,51 @@ typedef int32_t stbtt_int32;
#define STB_TRUETYPE_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h" #include "stb_truetype.h"
#include "json.h"
#pragma GCC diagnostic warning "-Wunused-function" #pragma GCC diagnostic warning "-Wunused-function"
#define INITIAL_QUAD_CAPACITY 128 #define INITIAL_QUAD_CAPACITY 128
/* Structs */ /* Structs */
typedef struct PackedChar
{
float atlasLeft, atlasTop, atlasRight, atlasBottom;
float planeLeft, planeTop, planeRight, planeBottom;
int xAdvance;
} PackedChar;
typedef struct CharRange
{
PackedChar *data;
uint32_t firstCodepoint;
uint32_t charCount;
} CharRange;
typedef struct Packer
{
uint32_t width;
uint32_t height;
CharRange *ranges;
uint32_t rangeCount;
} Packer;
typedef struct Font typedef struct Font
{ {
uint8_t *fontBytes; uint8_t *fontBytes;
stbtt_fontinfo fontInfo; stbtt_fontinfo fontInfo;
int32_t ascent; float ascender;
int32_t descent; float descender;
int32_t lineGap; float lineHeight;
float scale;
float geometryScale; // all json values are premultiplied by this value, we need it for kerning
Packer packer;
} Font; } Font;
typedef struct CharRange
{
stbtt_packedchar *data;
uint32_t firstCodepoint;
uint32_t charCount;
float fontSize;
} CharRange;
typedef struct Packer
{
Font *font;
float fontSize;
stbtt_pack_context *context;
uint8_t *pixels;
uint32_t width;
uint32_t height;
uint32_t strideInBytes;
uint32_t padding;
float scale; /* precomputed at init */
CharRange *ranges;
uint32_t rangeCount;
} Packer;
typedef struct Batch typedef struct Batch
{ {
Wellspring_Vertex *vertices; Wellspring_Vertex *vertices;
@ -164,9 +170,15 @@ typedef struct Batch
uint32_t indexCount; uint32_t indexCount;
uint32_t indexCapacity; uint32_t indexCapacity;
Packer *currentPacker; Font *currentFont;
} Batch; } Batch;
typedef struct Quad
{
float x0,y0,s0,t0; // top-left
float x1,y1,s1,t1; // bottom-right
} Quad;
/* UTF-8 Decoder */ /* UTF-8 Decoder */
/* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de> /* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
@ -205,6 +217,124 @@ decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
return *state; return *state;
} }
/* JSON helpers */
static uint8_t json_object_has_key(const json_object_t *object, const char* name)
{
json_object_element_t *currentElement = object->start;
const char* currentName = currentElement->name->string;
while (SDL_strcmp(currentName, name) != 0)
{
if (currentElement->next != NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Key %s not found in JSON!", name);
return 0;
}
}
return 1;
}
static json_object_element_t* json_object_get_element_by_name(const json_object_t *object, const char* name)
{
json_object_element_t *currentElement = object->start;
const char* currentName = currentElement->name->string;
while (SDL_strcmp(currentName, name) != 0)
{
if (currentElement->next != NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Key %s not found in JSON!", name);
return NULL;
}
currentElement = currentElement->next;
currentName = currentElement->name->string;
}
return currentElement;
}
static json_object_t* json_object_get_object(const json_object_t *object, const char* name)
{
json_object_element_t *element = json_object_get_element_by_name(object, name);
if (element == NULL)
{
return NULL;
}
json_object_t *obj = json_value_as_object(element->value);
if (obj == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Value with key %s was not an object!", name);
}
return obj;
}
static const char* json_object_get_string(const json_object_t *object, const char* name)
{
json_object_element_t *element = json_object_get_element_by_name(object, name);
if (element == NULL)
{
return NULL;
}
json_string_t *str = json_value_as_string(element->value);
if (str == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Value with key %s was not a string!", name);
return NULL;
}
return str->string;
}
static uint32_t json_object_get_uint(const json_object_t *object, const char* name)
{
json_object_element_t *element = json_object_get_element_by_name(object, name);
if (element == NULL)
{
return 0;
}
json_number_t *num = json_value_as_number(element->value);
if (num == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Value with key %s was not a number!", name);
return 0;
}
return (uint32_t) SDL_strtoul(num->number, NULL, 10);
}
static double json_object_get_double(const json_object_t *object, const char* name)
{
json_object_element_t *element = json_object_get_element_by_name(object, name);
if (element == NULL)
{
return 0;
}
json_number_t *num = json_value_as_number(element->value);
if (num == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Value with key %s was not a string!", name);
return 0;
}
return SDL_atof(num->number);
}
/* API */ /* API */
uint32_t Wellspring_LinkedVersion(void) uint32_t Wellspring_LinkedVersion(void)
@ -214,107 +344,131 @@ uint32_t Wellspring_LinkedVersion(void)
Wellspring_Font* Wellspring_CreateFont( Wellspring_Font* Wellspring_CreateFont(
const uint8_t* fontBytes, const uint8_t* fontBytes,
uint32_t fontBytesLength uint32_t fontBytesLength,
const uint8_t *atlasJsonBytes,
uint32_t atlasJsonBytesLength
) { ) {
Font *font = Wellspring_malloc(sizeof(Font)); Font *font = Wellspring_malloc(sizeof(Font));
font->fontBytes = Wellspring_malloc(fontBytesLength); font->fontBytes = Wellspring_malloc(fontBytesLength);
Wellspring_memcpy(font->fontBytes, fontBytes, fontBytesLength); Wellspring_memcpy(font->fontBytes, fontBytes, fontBytesLength);
stbtt_InitFont(&font->fontInfo, font->fontBytes, 0); stbtt_InitFont(&font->fontInfo, font->fontBytes, 0);
stbtt_GetFontVMetrics(&font->fontInfo, &font->ascent, &font->descent, &font->lineGap); int stbAscender, stbDescender, stbLineHeight;
stbtt_GetFontVMetrics(&font->fontInfo, &stbAscender, &stbDescender, &stbLineHeight);
font->scale = stbtt_ScaleForPixelHeight(&font->fontInfo, 1);
json_value_t *jsonRoot = json_parse(atlasJsonBytes, atlasJsonBytesLength);
json_object_t *jsonObject = jsonRoot->payload;
if (jsonObject == NULL)
{
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Atlas JSON is invalid! Bailing!");
Wellspring_free(font->fontBytes);
Wellspring_free(font);
return NULL;
}
if (SDL_strcmp(jsonObject->start->name->string, "atlas") != 0)
{
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Atlas JSON is invalid! Bailing!");
Wellspring_free(jsonRoot);
Wellspring_free(font->fontBytes);
Wellspring_free(font);
return NULL;
}
json_object_t *atlasObject = json_value_as_object(jsonObject->start->value);
json_object_t *metricsObject = json_value_as_object(jsonObject->start->next->value);
json_array_t *glyphsArray = json_value_as_array(jsonObject->start->next->next->value);
const char* atlasType = json_object_get_string(atlasObject, "type");
if (SDL_strcmp(atlasType, "msdf") != 0)
{
SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Atlas is not MSDF! Bailing!");
Wellspring_free(jsonRoot);
Wellspring_free(font->fontBytes);
Wellspring_free(font);
return NULL;
}
font->packer.width = json_object_get_uint(atlasObject, "width");
font->packer.height = json_object_get_uint(atlasObject, "height");
font->ascender = json_object_get_double(metricsObject, "ascender");
font->descender = json_object_get_double(metricsObject, "descender");
font->lineHeight = json_object_get_double(metricsObject, "lineHeight");
font->geometryScale = font->ascender / stbAscender;
/* Pack unicode ranges */
font->packer.ranges = Wellspring_malloc(sizeof(CharRange));
font->packer.rangeCount = 1;
font->packer.ranges[0].data = NULL;
font->packer.ranges[0].charCount = 0;
int32_t charRangeIndex = 0;
json_array_element_t *currentGlyphElement = glyphsArray->start;
while (currentGlyphElement != NULL)
{
json_object_t *currentGlyphObject = json_value_as_object(currentGlyphElement->value);
uint32_t codepoint = json_object_get_uint(currentGlyphObject, "unicode");
if (font->packer.ranges[charRangeIndex].charCount == 0)
{
// first codepoint on first range
font->packer.ranges[charRangeIndex].firstCodepoint = codepoint;
}
else if (codepoint != font->packer.ranges[charRangeIndex].firstCodepoint + font->packer.ranges[charRangeIndex].charCount + 1)
{
// codepoint is not continuous, start a new range
charRangeIndex += 1;
font->packer.rangeCount += 1;
font->packer.ranges = Wellspring_realloc(font->packer.ranges, sizeof(PackedChar) * (charRangeIndex + 1));
font->packer.ranges[charRangeIndex].firstCodepoint = codepoint;
}
font->packer.ranges[charRangeIndex].charCount += 1;
font->packer.ranges[charRangeIndex].data = Wellspring_realloc(font->packer.ranges[charRangeIndex].data, sizeof(PackedChar) * font->packer.ranges[charRangeIndex].charCount);
PackedChar *packedChar = &font->packer.ranges[charRangeIndex].data[font->packer.ranges[charRangeIndex].charCount - 1];
int bearing;
stbtt_GetCodepointHMetrics(&font->fontInfo, codepoint, &packedChar->xAdvance, &bearing);
if (json_object_has_key(currentGlyphObject, "atlasBounds"))
{
json_object_t *boundsObject = json_object_get_object(currentGlyphObject, "atlasBounds");
packedChar->atlasLeft = json_object_get_double(boundsObject, "left");
packedChar->atlasRight = json_object_get_double(boundsObject, "right");
packedChar->atlasTop = json_object_get_double(boundsObject, "top");
packedChar->atlasBottom = json_object_get_double(boundsObject, "bottom");
// flip texture coords because the atlas outputs glyphs in OpenGL texture space (lol)
packedChar->atlasTop = font->packer.height - packedChar->atlasTop;
packedChar->atlasBottom = font->packer.height - packedChar->atlasBottom;
json_object_t *planeObject = json_object_get_object(currentGlyphObject, "planeBounds");
packedChar->planeLeft = json_object_get_double(planeObject, "left");
packedChar->planeRight = json_object_get_double(planeObject, "right");
packedChar->planeTop = json_object_get_double(planeObject, "top");
packedChar->planeBottom = json_object_get_double(planeObject, "bottom");
}
currentGlyphElement = currentGlyphElement->next;
}
Wellspring_free(jsonRoot);
return (Wellspring_Font*) font; return (Wellspring_Font*) font;
} }
Wellspring_Packer* Wellspring_CreatePacker( Wellspring_TextBatch* Wellspring_CreateTextBatch(void)
Wellspring_Font *font,
float fontSize,
uint32_t width,
uint32_t height,
uint32_t strideInBytes,
uint32_t padding
) {
Packer *packer = Wellspring_malloc(sizeof(Packer));
packer->font = (Font*) font;
packer->fontSize = fontSize;
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;
packer->scale = stbtt_ScaleForPixelHeight(&packer->font->fontInfo, fontSize);
stbtt_PackBegin(packer->context, packer->pixels, width, height, strideInBytes, padding, NULL);
return (Wellspring_Packer*) packer;
}
uint32_t Wellspring_PackFontRanges(
Wellspring_Packer *packer,
Wellspring_FontRange *ranges,
uint32_t numRanges
) {
Packer *myPacker = (Packer*) packer;
Wellspring_FontRange *currentFontRange;
stbtt_pack_range* stbPackRanges = Wellspring_malloc(sizeof(stbtt_pack_range) * numRanges);
CharRange *currentCharRange;
uint32_t i;
for (i = 0; i < numRanges; i += 1)
{
currentFontRange = &ranges[i];
stbPackRanges[i].font_size = myPacker->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);
}
if (!stbtt_PackFontRanges(myPacker->context, myPacker->font->fontBytes, 0, stbPackRanges, numRanges))
{
/* Font packing failed, time to bail */
for (i = 0; i < numRanges; i += 1)
{
Wellspring_free(stbPackRanges[i].chardata_for_range);
}
return 0;
}
myPacker->ranges = Wellspring_realloc(myPacker->ranges, sizeof(CharRange) * (myPacker->rangeCount + numRanges));
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;
currentCharRange->fontSize = stbPackRanges[i].font_size;
}
myPacker->rangeCount += numRanges;
Wellspring_free(stbPackRanges);
return 1;
}
uint8_t* Wellspring_GetPixelDataPointer(
Wellspring_Packer *packer
) {
Packer* myPacker = (Packer*) packer;
return myPacker->pixels;
}
Wellspring_TextBatch* Wellspring_CreateTextBatch()
{ {
Batch *batch = Wellspring_malloc(sizeof(Batch)); Batch *batch = Wellspring_malloc(sizeof(Batch));
@ -331,10 +485,10 @@ Wellspring_TextBatch* Wellspring_CreateTextBatch()
void Wellspring_StartTextBatch( void Wellspring_StartTextBatch(
Wellspring_TextBatch *textBatch, Wellspring_TextBatch *textBatch,
Wellspring_Packer *packer Wellspring_Font *font
) { ) {
Batch *batch = (Batch*) textBatch; Batch *batch = (Batch*) textBatch;
batch->currentPacker = (Packer*) packer; batch->currentFont = (Font*) font;
batch->vertexCount = 0; batch->vertexCount = 0;
batch->indexCount = 0; batch->indexCount = 0;
} }
@ -350,15 +504,15 @@ static float Wellspring_INTERNAL_GetVerticalAlignOffset(
} }
else if (verticalAlignment == WELLSPRING_VERTICALALIGNMENT_TOP) else if (verticalAlignment == WELLSPRING_VERTICALALIGNMENT_TOP)
{ {
return scale * font->ascent; return scale * font->ascender;
} }
else if (verticalAlignment == WELLSPRING_VERTICALALIGNMENT_MIDDLE) else if (verticalAlignment == WELLSPRING_VERTICALALIGNMENT_MIDDLE)
{ {
return scale * (font->ascent + font->descent) / 2.0f; return scale * (font->ascender + font->descender) / 2.0f;
} }
else /* BOTTOM */ else /* BOTTOM */
{ {
return scale * font->descent; return scale * font->descender;
} }
} }
@ -384,8 +538,39 @@ static inline uint32_t IsWhitespace(uint32_t codepoint)
} }
} }
static void GetPackedQuad(PackedChar *charData, float scale, int packerWidth, int packerHeight, int charIndex, float *xPos, float *yPos, Quad *q)
{
float texelWidth = 1.0f / packerWidth, texelHeight = 1.0f / packerHeight;
PackedChar *b = charData + charIndex;
float pl, pb, pr, pt;
float il, ib, ir, it;
pl = *xPos + b->planeLeft * scale;
pb = *yPos + b->planeBottom * scale;
pr = *xPos + b->planeRight * scale;
pt = *yPos + b->planeTop * scale;
il = b->atlasLeft * texelWidth;
ib = b->atlasBottom * texelHeight;
ir = b->atlasRight * texelWidth;
it = b->atlasTop * texelHeight;
q->x0 = pl;
q->y0 = pt;
q->x1 = pr;
q->y1 = pb;
q->s0 = il;
q->t0 = it;
q->s1 = ir;
q->t1 = ib;
*xPos += b->xAdvance * scale;
}
static uint8_t Wellspring_Internal_TextBounds( static uint8_t Wellspring_Internal_TextBounds(
Packer* packer, Font* font,
float x, float x,
float y, float y,
Wellspring_HorizontalAlignment horizontalAlignment, Wellspring_HorizontalAlignment horizontalAlignment,
@ -397,11 +582,10 @@ static uint8_t Wellspring_Internal_TextBounds(
uint32_t decodeState = 0; uint32_t decodeState = 0;
uint32_t codepoint; uint32_t codepoint;
int32_t glyphIndex; int32_t glyphIndex;
int32_t previousGlyphIndex; int32_t previousGlyphIndex = -1;
int32_t rangeIndex; int32_t rangeIndex;
stbtt_packedchar* rangeData; PackedChar* rangeData;
float rangeFontSize; Quad charQuad;
stbtt_aligned_quad charQuad;
uint32_t i, j; uint32_t i, j;
float minX = x; float minX = x;
float minY = y; float minY = y;
@ -410,7 +594,7 @@ static uint8_t Wellspring_Internal_TextBounds(
float startX = x; float startX = x;
float advance = 0; float advance = 0;
y += Wellspring_INTERNAL_GetVerticalAlignOffset(packer->font, verticalAlignment, packer->scale); y += Wellspring_INTERNAL_GetVerticalAlignOffset(font, verticalAlignment, font->scale);
for (i = 0; i < strLengthInBytes; i += 1) for (i = 0; i < strLengthInBytes; i += 1)
{ {
@ -425,17 +609,10 @@ static uint8_t Wellspring_Internal_TextBounds(
continue; continue;
} }
if (IsWhitespace(codepoint))
{
int32_t ws_adv, ws_bearing;
stbtt_GetCodepointHMetrics(&packer->font->fontInfo, codepoint, &ws_adv, &ws_bearing);
x += packer->scale * ws_adv;
maxX += packer->scale * ws_adv;
continue;
}
rangeData = NULL; rangeData = NULL;
Packer *packer = &font->packer;
/* Find the packed char data */ /* Find the packed char data */
for (j = 0; j < packer->rangeCount; j += 1) for (j = 0; j < packer->rangeCount; j += 1)
{ {
@ -445,7 +622,6 @@ static uint8_t Wellspring_Internal_TextBounds(
) { ) {
rangeData = packer->ranges[j].data; rangeData = packer->ranges[j].data;
rangeIndex = codepoint - packer->ranges[j].firstCodepoint; rangeIndex = codepoint - packer->ranges[j].firstCodepoint;
rangeFontSize = packer->ranges[j].fontSize;
break; break;
} }
} }
@ -456,22 +632,31 @@ static uint8_t Wellspring_Internal_TextBounds(
return 0; return 0;
} }
glyphIndex = stbtt_FindGlyphIndex(&packer->font->fontInfo, codepoint); if (IsWhitespace(codepoint))
if (i > 0)
{ {
x += packer->scale * stbtt_GetGlyphKernAdvance(&packer->font->fontInfo, previousGlyphIndex, glyphIndex); PackedChar *packedChar = rangeData + rangeIndex;
x += font->scale * packedChar->xAdvance;
maxX += font->scale * packedChar->xAdvance;
previousGlyphIndex = -1;
continue;
} }
stbtt_GetPackedQuad( glyphIndex = stbtt_FindGlyphIndex(&font->fontInfo, codepoint);
if (previousGlyphIndex != -1)
{
x += font->geometryScale * font->scale * stbtt_GetGlyphKernAdvance(&font->fontInfo, previousGlyphIndex, glyphIndex);
}
GetPackedQuad(
rangeData, rangeData,
font->scale,
packer->width, packer->width,
packer->height, packer->height,
rangeIndex, rangeIndex,
&x, &x,
&y, &y,
&charQuad, &charQuad
0
); );
if (charQuad.x0 < minX) { minX = charQuad.x0; } if (charQuad.x0 < minX) { minX = charQuad.x0; }
@ -504,7 +689,7 @@ static uint8_t Wellspring_Internal_TextBounds(
} }
uint8_t Wellspring_TextBounds( uint8_t Wellspring_TextBounds(
Wellspring_Packer* packer, Wellspring_Font *font,
float x, float x,
float y, float y,
Wellspring_HorizontalAlignment horizontalAlignment, Wellspring_HorizontalAlignment horizontalAlignment,
@ -514,7 +699,7 @@ uint8_t Wellspring_TextBounds(
Wellspring_Rectangle* pRectangle Wellspring_Rectangle* pRectangle
) { ) {
return Wellspring_Internal_TextBounds( return Wellspring_Internal_TextBounds(
(Packer*) packer, (Font*) font,
x, x,
y, y,
horizontalAlignment, horizontalAlignment,
@ -537,26 +722,26 @@ uint8_t Wellspring_Draw(
uint32_t strLengthInBytes uint32_t strLengthInBytes
) { ) {
Batch *batch = (Batch*) textBatch; Batch *batch = (Batch*) textBatch;
Packer *myPacker = batch->currentPacker; Font *font = batch->currentFont;
Packer *myPacker = &font->packer;
uint32_t decodeState = 0; uint32_t decodeState = 0;
uint32_t codepoint; uint32_t codepoint;
int32_t glyphIndex; int32_t glyphIndex;
int32_t previousGlyphIndex; int32_t previousGlyphIndex = -1;
int32_t rangeIndex; int32_t rangeIndex;
stbtt_packedchar *rangeData; PackedChar *rangeData;
float rangeFontSize; Quad charQuad;
stbtt_aligned_quad charQuad;
uint32_t vertexBufferIndex; uint32_t vertexBufferIndex;
uint32_t indexBufferIndex; uint32_t indexBufferIndex;
Wellspring_Rectangle bounds; Wellspring_Rectangle bounds;
uint32_t i, j; uint32_t i, j;
y += Wellspring_INTERNAL_GetVerticalAlignOffset(myPacker->font, verticalAlignment, myPacker->scale); y += Wellspring_INTERNAL_GetVerticalAlignOffset(font, verticalAlignment, font->scale);
/* FIXME: If we horizontally align, we have to decode and process glyphs twice, very inefficient. */ /* FIXME: If we horizontally align, we have to decode and process glyphs twice, very inefficient. */
if (horizontalAlignment == WELLSPRING_HORIZONTALALIGNMENT_RIGHT) if (horizontalAlignment == WELLSPRING_HORIZONTALALIGNMENT_RIGHT)
{ {
if (!Wellspring_Internal_TextBounds(myPacker, x, y, horizontalAlignment, verticalAlignment, strBytes, strLengthInBytes, &bounds)) if (!Wellspring_Internal_TextBounds(font, x, y, horizontalAlignment, verticalAlignment, strBytes, strLengthInBytes, &bounds))
{ {
/* Something went wrong while calculating bounds. */ /* Something went wrong while calculating bounds. */
return 0; return 0;
@ -566,7 +751,7 @@ uint8_t Wellspring_Draw(
} }
else if (horizontalAlignment == WELLSPRING_HORIZONTALALIGNMENT_CENTER) else if (horizontalAlignment == WELLSPRING_HORIZONTALALIGNMENT_CENTER)
{ {
if (!Wellspring_Internal_TextBounds(myPacker, x, y, horizontalAlignment, verticalAlignment, strBytes, strLengthInBytes, &bounds)) if (!Wellspring_Internal_TextBounds(font, x, y, horizontalAlignment, verticalAlignment, strBytes, strLengthInBytes, &bounds))
{ {
/* Something went wrong while calculating bounds. */ /* Something went wrong while calculating bounds. */
return 0; return 0;
@ -588,14 +773,6 @@ uint8_t Wellspring_Draw(
continue; continue;
} }
if (IsWhitespace(codepoint))
{
int32_t ws_adv, ws_bearing;
stbtt_GetCodepointHMetrics(&myPacker->font->fontInfo, codepoint, &ws_adv, &ws_bearing);
x += myPacker->scale * ws_adv;
continue;
}
rangeData = NULL; rangeData = NULL;
/* Find the packed char data */ /* Find the packed char data */
@ -607,7 +784,6 @@ uint8_t Wellspring_Draw(
) { ) {
rangeData = myPacker->ranges[j].data; rangeData = myPacker->ranges[j].data;
rangeIndex = codepoint - myPacker->ranges[j].firstCodepoint; rangeIndex = codepoint - myPacker->ranges[j].firstCodepoint;
rangeFontSize = myPacker->ranges[j].fontSize;
break; break;
} }
} }
@ -618,22 +794,30 @@ uint8_t Wellspring_Draw(
return 0; return 0;
} }
glyphIndex = stbtt_FindGlyphIndex(&myPacker->font->fontInfo, codepoint); if (IsWhitespace(codepoint))
if (i > 0)
{ {
x += myPacker->scale * stbtt_GetGlyphKernAdvance(&myPacker->font->fontInfo, previousGlyphIndex, glyphIndex); PackedChar *packedChar = rangeData + rangeIndex;
x += font->scale * packedChar->xAdvance;
previousGlyphIndex = -1;
continue;
} }
stbtt_GetPackedQuad( glyphIndex = stbtt_FindGlyphIndex(&font->fontInfo, codepoint);
if (previousGlyphIndex != -1)
{
x += font->geometryScale * font->scale * stbtt_GetGlyphKernAdvance(&font->fontInfo, previousGlyphIndex, glyphIndex);
}
GetPackedQuad(
rangeData, rangeData,
font->scale,
myPacker->width, myPacker->width,
myPacker->height, myPacker->height,
rangeIndex, rangeIndex,
&x, &x,
&y, &y,
&charQuad, &charQuad
0
); );
if (batch->vertexCount >= batch->vertexCapacity) if (batch->vertexCount >= batch->vertexCapacity)
@ -648,8 +832,6 @@ uint8_t Wellspring_Draw(
batch->indices = Wellspring_realloc(batch->indices, sizeof(uint32_t) * batch->indexCapacity); batch->indices = Wellspring_realloc(batch->indices, sizeof(uint32_t) * batch->indexCapacity);
} }
/* TODO: kerning and alignment */
vertexBufferIndex = batch->vertexCount; vertexBufferIndex = batch->vertexCount;
indexBufferIndex = batch->indexCount; indexBufferIndex = batch->indexCount;
@ -733,27 +915,15 @@ void Wellspring_DestroyTextBatch(Wellspring_TextBatch *textBatch)
Wellspring_free(batch); Wellspring_free(batch);
} }
void Wellspring_DestroyPacker(Wellspring_Packer *packer)
{
Packer* myPacker = (Packer*) packer;
uint32_t i;
stbtt_PackEnd(myPacker->context);
for (i = 0; i < myPacker->rangeCount; i += 1)
{
Wellspring_free(myPacker->ranges[i].data);
}
Wellspring_free(myPacker->ranges);
Wellspring_free(myPacker->context);
Wellspring_free(myPacker->pixels);
}
void Wellspring_DestroyFont(Wellspring_Font* font) void Wellspring_DestroyFont(Wellspring_Font* font)
{ {
Font *myFont = (Font*) font; Font *myFont = (Font*) font;
for (int i = 0; i < myFont->packer.rangeCount; i += 1)
{
Wellspring_free(myFont->packer.ranges[i].data);
}
Wellspring_free(myFont->packer.ranges);
Wellspring_free(myFont->fontBytes); Wellspring_free(myFont->fontBytes);
Wellspring_free(myFont); Wellspring_free(myFont);
} }