mirror of https://github.com/bjornbytes/lovr.git
Pass:text supports multicolor strings;
This commit is contained in:
parent
af2c53a762
commit
1752b220bf
|
@ -6,6 +6,7 @@
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include <lua.h>
|
#include <lua.h>
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static int l_lovrPassGetType(lua_State* L) {
|
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);
|
Pass* pass = luax_checktype(L, 1, Pass);
|
||||||
Font* font = luax_totype(L, 2, Font);
|
Font* font = luax_totype(L, 2, Font);
|
||||||
int index = font ? 3 : 2;
|
int index = font ? 3 : 2;
|
||||||
size_t length;
|
uint32_t count = 0;
|
||||||
const char* text = luaL_checklstring(L, index++, &length);
|
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];
|
float transform[16];
|
||||||
index = luax_readmat4(L, index++, transform, 1);
|
index = luax_readmat4(L, index++, transform, 1);
|
||||||
float wrap = luax_optfloat(L, index++, 0.);
|
float wrap = luax_optfloat(L, index++, 0.);
|
||||||
HorizontalAlign halign = luax_checkenum(L, index++, HorizontalAlign, "center");
|
HorizontalAlign halign = luax_checkenum(L, index++, HorizontalAlign, "center");
|
||||||
VerticalAlign valign = luax_checkenum(L, index++, VerticalAlign, "middle");
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1887,7 +1887,7 @@ static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) {
|
||||||
return glyph;
|
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) {
|
if (start >= font->glyphs.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1950,23 +1950,26 @@ static void lovrFontUploadNewGlyphs(Font* font, uint32_t start, const char* str,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust current vertex uvs
|
// Adjust current vertex uvs
|
||||||
size_t bytes;
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
uint32_t codepoint;
|
size_t bytes;
|
||||||
const char* end = str + length;
|
uint32_t codepoint;
|
||||||
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
|
const char* str = strings[i].string;
|
||||||
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
|
const char* end = strings[i].string + strings[i].length;
|
||||||
if (glyph->box[2] - glyph->box[0] != 0.f) {
|
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
|
||||||
vertices[0].uv.u = glyph->uv[0];
|
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
|
||||||
vertices[0].uv.v = glyph->uv[1];
|
if (glyph->box[2] - glyph->box[0] != 0.f) {
|
||||||
vertices[1].uv.u = glyph->uv[2];
|
vertices[0].uv.u = glyph->uv[0];
|
||||||
vertices[1].uv.v = glyph->uv[1];
|
vertices[0].uv.v = glyph->uv[1];
|
||||||
vertices[2].uv.u = glyph->uv[0];
|
vertices[1].uv.u = glyph->uv[2];
|
||||||
vertices[2].uv.v = glyph->uv[3];
|
vertices[1].uv.v = glyph->uv[1];
|
||||||
vertices[3].uv.u = glyph->uv[2];
|
vertices[2].uv.u = glyph->uv[0];
|
||||||
vertices[3].uv.v = glyph->uv[3];
|
vertices[2].uv.v = glyph->uv[3];
|
||||||
vertices += 4;
|
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();
|
font = font ? font : lovrGraphicsGetDefaultFont();
|
||||||
uint32_t originalGlyphCount = font->glyphs.length;
|
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 vertexCount = 0;
|
||||||
uint32_t glyphCount = 0;
|
uint32_t glyphCount = 0;
|
||||||
uint32_t lineCount = 1;
|
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;
|
float scale = 1.f / font->pixelDensity;
|
||||||
wrap /= scale;
|
wrap /= scale;
|
||||||
|
|
||||||
size_t bytes;
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
uint32_t codepoint;
|
size_t bytes;
|
||||||
uint32_t previous = '\0';
|
uint32_t codepoint;
|
||||||
const char* str = text;
|
uint32_t previous = '\0';
|
||||||
const char* end = text + length;
|
const char* str = strings[i].string;
|
||||||
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
|
const char* end = strings[i].string + strings[i].length;
|
||||||
if (codepoint == ' ' || codepoint == '\t') {
|
uint8_t r = (uint8_t) (CLAMP(strings[i].color[0], 0.f, 1.f) * 255.f);
|
||||||
Glyph* glyph = lovrFontGetGlyph(font, ' ');
|
uint8_t g = (uint8_t) (CLAMP(strings[i].color[1], 0.f, 1.f) * 255.f);
|
||||||
wordStart = vertexCount;
|
uint8_t b = (uint8_t) (CLAMP(strings[i].color[2], 0.f, 1.f) * 255.f);
|
||||||
x += codepoint == '\t' ? glyph->advance * 4.f : glyph->advance;
|
uint8_t a = (uint8_t) (CLAMP(strings[i].color[3], 0.f, 1.f) * 255.f);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
|
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
|
||||||
|
if (codepoint == ' ' || codepoint == '\t') {
|
||||||
// Keming
|
Glyph* glyph = lovrFontGetGlyph(font, ' ');
|
||||||
if (previous) x += lovrFontGetKerning(font, previous, codepoint);
|
wordStart = vertexCount;
|
||||||
previous = codepoint;
|
x += codepoint == '\t' ? glyph->advance * 4.f : glyph->advance;
|
||||||
|
previous = '\0';
|
||||||
// Wrap
|
str += bytes;
|
||||||
if (wrap > 0.f && x + glyph->box[2] > wrap && wordStart != lineStart) {
|
continue;
|
||||||
float dx = wordStart == vertexCount ? x : vertices[wordStart].position.x;
|
} else if (codepoint == '\n') {
|
||||||
float dy = leading;
|
aline(vertices, lineStart, vertexCount, halign);
|
||||||
|
lineStart = vertexCount;
|
||||||
// Shift the vertices of the overflowing word down a line and back to the beginning
|
wordStart = vertexCount;
|
||||||
for (uint32_t i = wordStart; i < vertexCount; i++) {
|
x = 0.f;
|
||||||
vertices[i].position.x -= dx;
|
y -= leading;
|
||||||
vertices[i].position.y -= dy;
|
lineCount++;
|
||||||
|
previous = '\0';
|
||||||
|
str += bytes;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
aline(vertices, lineStart, wordStart, halign);
|
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
|
||||||
lineStart = wordStart;
|
|
||||||
lineCount++;
|
// Keming
|
||||||
x -= dx;
|
if (previous) x += lovrFontGetKerning(font, previous, codepoint);
|
||||||
y -= dy;
|
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
|
// Align last line
|
||||||
aline(vertices, lineStart, vertexCount, halign);
|
aline(vertices, lineStart, vertexCount, halign);
|
||||||
|
|
||||||
// Resize atlas, rasterize new glyphs, upload them into atlas, recreate material
|
// 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);
|
mat4_scale(transform, scale, scale, scale);
|
||||||
float totalHeight = height + leading * (lineCount - 1);
|
float totalHeight = height + leading * (lineCount - 1);
|
||||||
|
|
|
@ -328,6 +328,24 @@ typedef struct {
|
||||||
double spread;
|
double spread;
|
||||||
} FontInfo;
|
} FontInfo;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float color[4];
|
||||||
|
const char* string;
|
||||||
|
size_t length;
|
||||||
|
} MulticolorString;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ALIGN_LEFT,
|
||||||
|
ALIGN_CENTER,
|
||||||
|
ALIGN_RIGHT
|
||||||
|
} HorizontalAlign;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ALIGN_TOP,
|
||||||
|
ALIGN_MIDDLE,
|
||||||
|
ALIGN_BOTTOM
|
||||||
|
} VerticalAlign;
|
||||||
|
|
||||||
Font* lovrFontCreate(FontInfo* info);
|
Font* lovrFontCreate(FontInfo* info);
|
||||||
void lovrFontDestroy(void* ref);
|
void lovrFontDestroy(void* ref);
|
||||||
const FontInfo* lovrFontGetInfo(Font* font);
|
const FontInfo* lovrFontGetInfo(Font* font);
|
||||||
|
@ -376,12 +394,6 @@ typedef enum {
|
||||||
STYLE_LINE
|
STYLE_LINE
|
||||||
} DrawStyle;
|
} DrawStyle;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ALIGN_LEFT,
|
|
||||||
ALIGN_CENTER,
|
|
||||||
ALIGN_RIGHT
|
|
||||||
} HorizontalAlign;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
STENCIL_KEEP,
|
STENCIL_KEEP,
|
||||||
STENCIL_ZERO,
|
STENCIL_ZERO,
|
||||||
|
@ -399,12 +411,6 @@ typedef enum {
|
||||||
VERTEX_TRIANGLES
|
VERTEX_TRIANGLES
|
||||||
} VertexMode;
|
} VertexMode;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ALIGN_TOP,
|
|
||||||
ALIGN_MIDDLE,
|
|
||||||
ALIGN_BOTTOM
|
|
||||||
} VerticalAlign;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WINDING_COUNTERCLOCKWISE,
|
WINDING_COUNTERCLOCKWISE,
|
||||||
WINDING_CLOCKWISE
|
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 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 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 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 lovrPassFill(Pass* pass, Texture* texture);
|
||||||
void lovrPassMonkey(Pass* pass, float* transform);
|
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);
|
void lovrPassMesh(Pass* pass, Buffer* vertices, Buffer* indices, float* transform, uint32_t start, uint32_t count, uint32_t instances);
|
||||||
|
|
Loading…
Reference in New Issue