Compare commits

...

3 Commits

Author SHA1 Message Date
bjorn 408fcb8b37 Fix typo; 2022-06-27 21:43:23 -07:00
bjorn 1752b220bf Pass:text supports multicolor strings; 2022-06-27 21:42:51 -07:00
bjorn af2c53a762 Fix luax_optcolor; 2022-06-27 21:38:40 -07:00
4 changed files with 142 additions and 99 deletions

View File

@ -440,7 +440,7 @@ void luax_optcolor(lua_State* L, int index, float color[4]) {
color[0] = color[1] = color[2] = color[3] = 1.f;
break;
case LUA_TNUMBER: {
uint32_t x = lua_tonumber(L, -1);
uint32_t x = lua_tonumber(L, index);
color[0] = ((x >> 16) & 0xff) / 255.f;
color[1] = ((x >> 8) & 0xff) / 255.f;
color[2] = ((x >> 0) & 0xff) / 255.f;

View File

@ -6,6 +6,7 @@
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <string.h>
static int l_lovrPassGetType(lua_State* L) {
@ -555,14 +556,35 @@ static int l_lovrPassText(lua_State* L) {
Pass* pass = luax_checktype(L, 1, Pass);
Font* font = luax_totype(L, 2, Font);
int index = font ? 3 : 2;
size_t length;
const char* text = luaL_checklstring(L, index++, &length);
uint32_t count = 0;
ColoredString string;
ColoredString* strings = &string;
if (lua_istable(L, index)) {
count = luax_len(L, index) / 2;
strings = malloc(count * sizeof(*strings));
lovrAssert(strings, "Out of memory");
for (uint32_t i = 0; i < count; i++) {
lua_rawgeti(L, index, i * 2 + 1);
lua_rawgeti(L, index, i * 2 + 2);
luax_optcolor(L, -2, strings[i].color);
lovrCheck(lua_isstring(L, -1), "Expected a string to print");
strings[i].string = luaL_checklstring(L, -1, &strings[i].length);
lua_pop(L, 2);
}
index++;
} else {
strings[0].string = luaL_checklstring(L, index, &strings[0].length);
strings[0].color[0] = strings[0].color[1] = strings[0].color[2] = strings[0].color[3] = 1.f;
count = 1;
index++;
}
float transform[16];
index = luax_readmat4(L, index++, transform, 1);
float wrap = luax_optfloat(L, index++, 0.);
HorizontalAlign halign = luax_checkenum(L, index++, HorizontalAlign, "center");
VerticalAlign valign = luax_checkenum(L, index++, VerticalAlign, "middle");
lovrPassText(pass, font, text, length, transform, wrap, halign, valign);
lovrPassText(pass, font, strings, count, transform, wrap, halign, valign);
if (strings != &string) free(strings);
return 0;
}

View File

@ -1887,7 +1887,7 @@ static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) {
return glyph;
}
static void lovrFontUploadNewGlyphs(Font* font, uint32_t start, const char* str, uint32_t length, GlyphVertex* vertices) {
static void lovrFontUploadNewGlyphs(Font* font, uint32_t start, ColoredString* strings, uint32_t count, GlyphVertex* vertices) {
if (start >= font->glyphs.length) {
return;
}
@ -1950,23 +1950,26 @@ static void lovrFontUploadNewGlyphs(Font* font, uint32_t start, const char* str,
}
// Adjust current vertex uvs
size_t bytes;
uint32_t codepoint;
const char* end = str + length;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
if (glyph->box[2] - glyph->box[0] != 0.f) {
vertices[0].uv.u = glyph->uv[0];
vertices[0].uv.v = glyph->uv[1];
vertices[1].uv.u = glyph->uv[2];
vertices[1].uv.v = glyph->uv[1];
vertices[2].uv.u = glyph->uv[0];
vertices[2].uv.v = glyph->uv[3];
vertices[3].uv.u = glyph->uv[2];
vertices[3].uv.v = glyph->uv[3];
vertices += 4;
for (uint32_t i = 0; i < count; i++) {
size_t bytes;
uint32_t codepoint;
const char* str = strings[i].string;
const char* end = strings[i].string + strings[i].length;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
if (glyph->box[2] - glyph->box[0] != 0.f) {
vertices[0].uv.u = glyph->uv[0];
vertices[0].uv.v = glyph->uv[1];
vertices[1].uv.u = glyph->uv[2];
vertices[1].uv.v = glyph->uv[1];
vertices[2].uv.u = glyph->uv[0];
vertices[2].uv.v = glyph->uv[3];
vertices[3].uv.u = glyph->uv[2];
vertices[3].uv.v = glyph->uv[3];
vertices += 4;
}
str += bytes;
}
str += bytes;
}
}
@ -3400,11 +3403,16 @@ static void aline(GlyphVertex* vertices, uint32_t head, uint32_t tail, Horizonta
}
}
void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign) {
font = font ? font : lovrGraphicsGetDefaultFont();
uint32_t originalGlyphCount = font->glyphs.length;
GlyphVertex* vertices = tempAlloc(length * 4 * sizeof(GlyphVertex));
uint32_t maxGlyphCount = 0;
for (uint32_t i = 0; i < count; i++) {
maxGlyphCount += strings[i].length;
}
GlyphVertex* vertices = tempAlloc(maxGlyphCount * 4 * sizeof(GlyphVertex));
uint32_t vertexCount = 0;
uint32_t glyphCount = 0;
uint32_t lineCount = 1;
@ -3419,79 +3427,86 @@ void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, flo
float scale = 1.f / font->pixelDensity;
wrap /= scale;
size_t bytes;
uint32_t codepoint;
uint32_t previous = '\0';
const char* str = text;
const char* end = text + length;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
if (codepoint == ' ' || codepoint == '\t') {
Glyph* glyph = lovrFontGetGlyph(font, ' ');
wordStart = vertexCount;
x += codepoint == '\t' ? glyph->advance * 4.f : glyph->advance;
previous = '\0';
str += bytes;
continue;
} else if (codepoint == '\n') {
aline(vertices, lineStart, vertexCount, halign);
lineStart = vertexCount;
wordStart = vertexCount;
x = 0.f;
y -= leading;
lineCount++;
previous = '\0';
str += bytes;
continue;
}
for (uint32_t i = 0; i < count; i++) {
size_t bytes;
uint32_t codepoint;
uint32_t previous = '\0';
const char* str = strings[i].string;
const char* end = strings[i].string + strings[i].length;
uint8_t r = (uint8_t) (CLAMP(strings[i].color[0], 0.f, 1.f) * 255.f);
uint8_t g = (uint8_t) (CLAMP(strings[i].color[1], 0.f, 1.f) * 255.f);
uint8_t b = (uint8_t) (CLAMP(strings[i].color[2], 0.f, 1.f) * 255.f);
uint8_t a = (uint8_t) (CLAMP(strings[i].color[3], 0.f, 1.f) * 255.f);
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
// Keming
if (previous) x += lovrFontGetKerning(font, previous, codepoint);
previous = codepoint;
// Wrap
if (wrap > 0.f && x + glyph->box[2] > wrap && wordStart != lineStart) {
float dx = wordStart == vertexCount ? x : vertices[wordStart].position.x;
float dy = leading;
// Shift the vertices of the overflowing word down a line and back to the beginning
for (uint32_t i = wordStart; i < vertexCount; i++) {
vertices[i].position.x -= dx;
vertices[i].position.y -= dy;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
if (codepoint == ' ' || codepoint == '\t') {
Glyph* glyph = lovrFontGetGlyph(font, ' ');
wordStart = vertexCount;
x += codepoint == '\t' ? glyph->advance * 4.f : glyph->advance;
previous = '\0';
str += bytes;
continue;
} else if (codepoint == '\n') {
aline(vertices, lineStart, vertexCount, halign);
lineStart = vertexCount;
wordStart = vertexCount;
x = 0.f;
y -= leading;
lineCount++;
previous = '\0';
str += bytes;
continue;
}
aline(vertices, lineStart, wordStart, halign);
lineStart = wordStart;
lineCount++;
x -= dx;
y -= dy;
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
// Keming
if (previous) x += lovrFontGetKerning(font, previous, codepoint);
previous = codepoint;
// Wrap
if (wrap > 0.f && x + glyph->box[2] > wrap && wordStart != lineStart) {
float dx = wordStart == vertexCount ? x : vertices[wordStart].position.x;
float dy = leading;
// Shift the vertices of the overflowing word down a line and back to the beginning
for (uint32_t v = wordStart; v < vertexCount; v++) {
vertices[v].position.x -= dx;
vertices[v].position.y -= dy;
}
aline(vertices, lineStart, wordStart, halign);
lineStart = wordStart;
lineCount++;
x -= dx;
y -= dy;
}
// Ignore bearing for the first character on a line so it lines up perfectly (questionable)
if (vertexCount == lineStart) {
x -= glyph->box[0];
}
// Vertices
float* bb = glyph->box;
uint16_t* uv = glyph->uv;
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[3] }, { uv[0], uv[1] }, { r, g, b, a } };
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[3] }, { uv[2], uv[1] }, { r, g, b, a } };
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[1] }, { uv[0], uv[3] }, { r, g, b, a } };
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[1] }, { uv[2], uv[3] }, { r, g, b, a } };
glyphCount++;
// Advance
x += glyph->advance;
str += bytes;
}
// Ignore bearing for the first character on a line so it lines up perfectly (questionable)
if (vertexCount == lineStart) {
x -= glyph->box[0];
}
// Vertices
float* bb = glyph->box;
uint16_t* uv = glyph->uv;
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[3] }, { uv[0], uv[1] }, { 255, 255, 255, 255 } };
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[3] }, { uv[2], uv[1] }, { 255, 255, 255, 255 } };
vertices[vertexCount++] = (GlyphVertex) { { x + bb[0], y + bb[1] }, { uv[0], uv[3] }, { 255, 255, 255, 255 } };
vertices[vertexCount++] = (GlyphVertex) { { x + bb[2], y + bb[1] }, { uv[2], uv[3] }, { 255, 255, 255, 255 } };
glyphCount++;
// Advance
x += glyph->advance;
str += bytes;
}
// Align last line
aline(vertices, lineStart, vertexCount, halign);
// Resize atlas, rasterize new glyphs, upload them into atlas, recreate material
lovrFontUploadNewGlyphs(font, originalGlyphCount, text, length, vertices);
lovrFontUploadNewGlyphs(font, originalGlyphCount, strings, count, vertices);
mat4_scale(transform, scale, scale, scale);
float totalHeight = height + leading * (lineCount - 1);

View File

@ -328,6 +328,24 @@ typedef struct {
double spread;
} FontInfo;
typedef struct {
float color[4];
const char* string;
size_t length;
} ColoredString;
typedef enum {
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT
} HorizontalAlign;
typedef enum {
ALIGN_TOP,
ALIGN_MIDDLE,
ALIGN_BOTTOM
} VerticalAlign;
Font* lovrFontCreate(FontInfo* info);
void lovrFontDestroy(void* ref);
const FontInfo* lovrFontGetInfo(Font* font);
@ -376,12 +394,6 @@ typedef enum {
STYLE_LINE
} DrawStyle;
typedef enum {
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT
} HorizontalAlign;
typedef enum {
STENCIL_KEEP,
STENCIL_ZERO,
@ -399,12 +411,6 @@ typedef enum {
VERTEX_TRIANGLES
} VertexMode;
typedef enum {
ALIGN_TOP,
ALIGN_MIDDLE,
ALIGN_BOTTOM
} VerticalAlign;
typedef enum {
WINDING_COUNTERCLOCKWISE,
WINDING_CLOCKWISE
@ -483,7 +489,7 @@ void lovrPassCircle(Pass* pass, float* transform, DrawStyle style, float angle1,
void lovrPassSphere(Pass* pass, float* transform, uint32_t segmentsH, uint32_t segmentsV);
void lovrPassCylinder(Pass* pass, float* transform, bool capped, float angle1, float angle2, uint32_t segments);
void lovrPassTorus(Pass* pass, float* transform, uint32_t segmentsT, uint32_t segmentsP);
void lovrPassText(Pass* pass, Font* font, const char* text, uint32_t length, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count, float* transform, float wrap, HorizontalAlign halign, VerticalAlign valign);
void lovrPassFill(Pass* pass, Texture* texture);
void lovrPassMonkey(Pass* pass, float* transform);
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances);