225 lines
5.7 KiB
C++
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;
|
|
} |