Dawn/archive/TrueTypeFont.cpp

225 lines
5.7 KiB
C++

// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "TrueTypeFont.hpp"
#ifndef STB_TRUETYPE_IMPLEMENTATION
#define STB_TRUETYPE_IMPLEMENTATION
#include <stb_truetype.h>
#endif
using namespace Dawn;
void TrueTypeFont::bakeQuad(truetypequad_t *quad,float_t *x,float_t *y,char c){
assertNotNull(quad);
assertNotNull(x);
assertNotNull(y);
assertTrue(c >= TRUETYPE_FIRST_CHAR);
assertTrue(c < (TRUETYPE_FIRST_CHAR+TRUETYPE_NUM_CHARS));
stbtt_GetBakedQuad(
this->characterData,
this->texture.getWidth(), this->texture.getHeight(),
((int32_t)c) - TRUETYPE_FIRST_CHAR,
x, y, quad,
TRUETYPE_FILL_MODE
);
}
float_t TrueTypeFont::getScale(float_t scale) {
assertTrue(scale > 0);
return scale / this->fontSize;
}
float_t TrueTypeFont::getSpaceSize(float_t fontSize) {
assertTrue(fontSize > 0);
return this->getScale(fontSize) * 48;
}
float_t TrueTypeFont::getInitialLineHeight(float_t fontSize) {
assertTrue(fontSize > 0);
return 42.0f * this->getScale(fontSize);
}
float_t TrueTypeFont::getLineHeight(float_t fontSize) {
assertTrue(fontSize > 0);
return 128.0f * this->getScale(fontSize);
}
void TrueTypeFont::buffer(
std::string text,
float_t fontSize,
float_t maxWidth,
Mesh *mesh,
struct FontMeasure *info
) {
assertNotNull(mesh);
assertNotNull(info);
assertTrue(fontSize > 0);
assertTrue(maxWidth == -1 || maxWidth > 0);
assertTrue(this->isReady());
auto stringLength = text.length();
if(stringLength == 0) {
info->length = 0;
info->realLength = 0;
info->lines.clear();
info->height = 0;
info->width = 0;
info->height = 0.0f;
info->lineHeight = 0.0f;
mesh->createBuffers(0, 0);
return;
}
auto quads = new truetypequad_t[stringLength];
assertNotNull(quads);
// Get the font scale
auto scale = this->getScale(fontSize);
assertTrue(scale > 0);
// Adjust the max width to match the scale, and allow "no max width".
maxWidth = maxWidth == -1 ? 9999999 : maxWidth * (1 / scale);
// Which index in the original text var is the current word from
int32_t wordStart = 0;
// Setup Scales
info->length = 0;
info->realLength = 0;
info->lines.clear();
info->lineHeight = this->getLineHeight(fontSize) * scale;
// Prepare the line counters
info->addLine(0, 0);
// Reset Dimensions
info->width = info->height = 0;
// Setup the initial loop state, and X/Y coords for the quad.
int32_t i = 0;
float_t x = 0;
float_t y = 0;
// Bake the first quad.
this->bakeQuad(quads, &x, &y, 'D');
y = -(quads->y0);
x = 0;
// y = this->getInitialLineHeight(fontSize) / scale;
float_t wordX = 0;
char c;
while(c = text[i++]) {
info->length++;
// When space, start of new word about to begin
if(c == FONT_SPACE) {
x += this->getSpaceSize(fontSize) / scale;
// Did this space cause a newline?
if(x > maxWidth) {
info->addLine(info->realLength, 0);
y += this->getLineHeight(fontSize) / scale;
x = 0;
}
wordX = x;
wordStart = info->realLength;
continue;
}
// New line.
if(c == FONT_NEWLINE) {
info->addLine(info->realLength, 0);
wordStart = info->realLength;
y += this->getLineHeight(fontSize) / scale;
x = 0;
continue;
}
// Generate the quad.
auto quad = quads + info->realLength;
this->bakeQuad(quad, &x, &y, c);
// Now measure the width of the line (take the right side of that quad)
if(quad->x1 > maxWidth) {
// We've exceeded the edge, go back to the start of the word and newline.
x = quad->x1 - wordX;
for(auto j = wordStart; j <= info->realLength; j++) {
quad = quads + j;
quad->x0 -= wordX;
quad->x1 -= wordX;
quad->y0 += this->getLineHeight(fontSize) / scale;
quad->y1 += this->getLineHeight(fontSize) / scale;
}
// Go back to the previous (still current) line and remove the chars
info->lines[info->lines.size() - 1].length -= info->realLength - wordStart;
// Next line begins with this word
y += this->getLineHeight(fontSize) / scale;
info->addLine(wordStart, info->realLength-wordStart);
wordX = 0;
}
info->lines[info->lines.size() - 1].length++;
info->realLength++;
}
// Initialize primitive
mesh->createBuffers(
QUAD_VERTICE_COUNT * info->realLength,
QUAD_INDICE_COUNT * info->realLength
);
for(auto j = 0; j < info->realLength; j++) {
auto quad = quads + j;
// Scale the Quad
if(scale != 1.0) {
quad->x0 *= scale;
quad->x1 *= scale;
quad->y0 *= scale;
quad->y1 *= scale;
}
// Update the dimensions.
info->width = mathMax<float_t>(info->width, quad->x1);
info->height = mathMax<float_t>(info->height, quad->y1);
// Buffer the quad.
QuadMesh::bufferQuadMesh(mesh,
glm::vec2(quad->x0, quad->y0), glm::vec2(quad->s0, quad->t0),
glm::vec2(quad->x1, quad->y1), glm::vec2(quad->s1, quad->t1),
j * QUAD_VERTICE_COUNT, j * QUAD_INDICE_COUNT
);
}
delete quads;
}
bool_t TrueTypeFont::isReady() {
return this->texture.isReady();
}
Texture * TrueTypeFont::getTexture() {
return &this->texture;
}
void TrueTypeFont::draw(Mesh *mesh, int32_t startchar, int32_t length) {
assertNotNull(mesh);
mesh->draw(
MESH_DRAW_MODE_TRIANGLES,
startchar * QUAD_INDICE_COUNT,
length == -1 ? length : length * QUAD_INDICE_COUNT
);
}
float_t TrueTypeFont::getDefaultFontSize() {
return (float_t)this->fontSize;
}