Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/modules/font/GenericShaper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ void GenericShaper::computeGlyphPositions(const ColoredCodepoints &codepoints, R
float advance = getGlyphAdvance(g, &glyphindex);

if (positions)
positions->push_back({ Vector2(curpos.x, curpos.y), glyphindex });
positions->push_back({ Vector2(curpos.x, curpos.y), Vector2(advance, getCombinedHeight()), glyphindex });

// Advance the x position for the next glyph.
curpos.x += advance;
Expand Down
1 change: 1 addition & 0 deletions src/modules/font/TextShaper.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class TextShaper : public Object
struct GlyphPosition
{
Vector2 position;
Vector2 advance;
GlyphIndex glyphIndex;
};

Expand Down
4 changes: 3 additions & 1 deletion src/modules/font/freetype/HarfbuzzShaper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,14 @@ void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints,

if (positions)
{
GlyphPosition p = { curpos, gindex };
GlyphPosition p = { curpos, Vector2(0.0f, 0.0f), gindex };

// Harfbuzz position coordinate systems are based on the given font.
// Freetype uses 26.6 fixed point coordinates, so harfbuzz does too.
p.position.x += (glyphpos.x_offset >> 6) / dpiScales[bufferrange.index];
p.position.y += (glyphpos.y_offset >> 6) / dpiScales[bufferrange.index];
p.advance.x = (glyphpos.x_advance >> 6) / dpiScales[bufferrange.index];
p.advance.y = (glyphpos.y_advance >> 6) / dpiScales[bufferrange.index];

positions->push_back(p);
}
Expand Down
97 changes: 70 additions & 27 deletions src/modules/graphics/Font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,38 @@ std::vector<Font::DrawCommand> Font::generateVertices(const love::font::ColoredC
return commands;
}

float Font::getAlign(const love::font::ColoredCodepoints &text, const Range range, float width, float wrap, AlignMode align, float *extraspacing) {
float offset = 0;
switch (align)
{
case ALIGN_RIGHT:
offset = floorf(wrap - width);
break;
case ALIGN_CENTER:
offset = floorf((wrap - width) / 2.0f);
break;
case ALIGN_JUSTIFY:
{
auto start = text.cps.begin() + range.getOffset();
auto end = start + range.getSize();
float numspaces = std::count(start, end, ' ');

if (text.cps[range.last] == ' ')
--numspaces;

if (width < wrap && numspaces >= 1)
*extraspacing = (wrap - width) / numspaces;
else
*extraspacing = 0.0f;
break;
}
case ALIGN_LEFT:
default:
break;
}
return offset;
}

std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const love::font::ColoredCodepoints &text, const Colorf &constantcolor, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, love::font::TextShaper::TextInfo *info)
{
wrap = std::max(wrap, 0.0f);
Expand Down Expand Up @@ -494,33 +526,7 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const love::font:

maxwidth = std::max(width, maxwidth);

switch (align)
{
case ALIGN_RIGHT:
offset.x = floorf(wrap - width);
break;
case ALIGN_CENTER:
offset.x = floorf((wrap - width) / 2.0f);
break;
case ALIGN_JUSTIFY:
{
auto start = text.cps.begin() + range.getOffset();
auto end = start + range.getSize();
float numspaces = std::count(start, end, ' ');

if (text.cps[range.last] == ' ')
--numspaces;

if (width < wrap && numspaces >= 1)
extraspacing = (wrap - width) / numspaces;
else
extraspacing = 0.0f;
break;
}
case ALIGN_LEFT:
default:
break;
}
offset.x = getAlign(text, range, width, wrap, align, &extraspacing);

std::vector<DrawCommand> newcommands = generateVertices(text, range, constantcolor, vertices, extraspacing, offset);

Expand Down Expand Up @@ -613,6 +619,43 @@ int Font::getWidth(const std::string &str)
return shaper->getWidth(str);
}

Vector2 Font::getGlyphPosition(int index, const std::string &str, float wraplimit, AlignMode align, float *width)
{
love::font::ColoredCodepoints codepoints;
love::font::getCodepointsFromString(str, codepoints.cps);

std::vector<Range> ranges;
std::vector<float> widths;
shaper->getWrap(codepoints, wraplimit, ranges, &widths);

float y = 0;
for (int i = 0; i < (int)ranges.size(); i++)
{
const auto &range = ranges[i];
if (!range.isValid())
{
y += shaper->getCombinedHeight();
continue;
}
if (index >= range.first && index <= range.last)
{
Vector2 offset(0.0f, floorf(y));
float extraspacing;
offset.x = getAlign(codepoints, range, widths[i], wraplimit, align, &extraspacing);

std::vector<love::font::TextShaper::GlyphPosition> glyphpositions;
shaper->computeGlyphPositions(codepoints, range, offset, extraspacing, &glyphpositions, nullptr, nullptr);

Vector2 pos = glyphpositions[index - range.first].position;
pos.y -= getBaseline();
*width = glyphpositions[index - range.first].advance.x;
return pos;
}
y += shaper->getCombinedHeight();
}
throw love::Exception("String index is out of range");
}

int Font::getWidth(uint32 glyph)
{
return shaper->getGlyphAdvance(glyph);
Expand Down
11 changes: 11 additions & 0 deletions src/modules/graphics/Font.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,16 @@ class Font : public Object, public Volatile
**/
int getWidth(const std::string &str);

/**
* Returns the position and width of the character at a given index.
*
* @param index The index of the codepoint
* @param str The input text
* @param wraplimit The maximum width of the text for wrapping
* @param align The text align mode
**/
Vector2 getGlyphPosition(int index, const std::string &str, float wraplimit, AlignMode align, float *width);

/**
* Returns the width of the passed glyph.
**/
Expand Down Expand Up @@ -183,6 +193,7 @@ class Font : public Object, public Volatile
const Glyph &addGlyph(love::font::TextShaper::GlyphIndex glyphindex);
const Glyph &findGlyph(love::font::TextShaper::GlyphIndex glyphindex);
void printv(Graphics *gfx, const Matrix4 &t, const std::vector<DrawCommand> &drawcommands, const std::vector<GlyphVertex> &vertices);
float getAlign(const love::font::ColoredCodepoints &text, const Range range, float width, float wrap, AlignMode align, float *extraspacing);

StrongRef<love::font::TextShaper> shaper;

Expand Down
25 changes: 25 additions & 0 deletions src/modules/graphics/wrap_Font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,30 @@ int w_Font_getWidth(lua_State *L)
return 1;
}

int w_Font_getGlyphPosition(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
int index = luaL_checkinteger(L, 2);
const char *str = luaL_checkstring(L, 3);
float wraplimit = (float) luaL_checknumber(L, 4);

Font::AlignMode align = Font::ALIGN_LEFT;
const char *astr = lua_isnoneornil(L, 5) ? nullptr : luaL_checkstring(L, 5);
if (astr != nullptr && !Font::getConstant(astr, align))
return luax_enumerror(L, "alignment", Font::getConstants(align), astr);

Vector2 pos;
float width;
float height = t->getHeight();
luax_catchexcept(L, [&]() { pos = t->getGlyphPosition(index - 1, str, wraplimit, align, &width); });

lua_pushnumber(L, pos.x);
lua_pushnumber(L, pos.y);
lua_pushnumber(L, width);
lua_pushnumber(L, height);
return 4;
}

int w_Font_getWrap(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
Expand Down Expand Up @@ -272,6 +296,7 @@ static const luaL_Reg w_Font_functions[] =
{
{ "getHeight", w_Font_getHeight },
{ "getWidth", w_Font_getWidth },
{ "getGlyphPosition", w_Font_getGlyphPosition },
{ "getWrap", w_Font_getWrap },
{ "setLineHeight", w_Font_setLineHeight },
{ "getLineHeight", w_Font_getLineHeight },
Expand Down
12 changes: 12 additions & 0 deletions testing/tests/graphics.lua
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,18 @@ love.test.graphics.Font = function(test)
local imgdata2 = love.graphics.readbackTexture(canvas)
test:compareImg(imgdata2)

-- check finding the position of glyphs
local glyphPosString = "hello, world"
for i = 1, #glyphPosString do
local x, y, w, h = font:getGlyphPosition(i, glyphPosString, 100)
test:assertEquals(font:getWidth(glyphPosString:sub(1, i - 1)), x, "compare glyph pos to length of previous")
test:assertEquals(font:getWidth(glyphPosString:sub(i, i)), w, "check width")
end
test:assertFalse(pcall(font.getGlyphPosition, font, -1, "123", 50), "negative glyph index")
test:assertFalse(pcall(font.getGlyphPosition, font, 4, "123", 50), "out of bounds glyph index")
local x, y = font:getGlyphPosition(3, "abcd", 17)
test:assertEquals(font:getHeight(), y, "glyph position with wrap")
test:assertEquals(font:getGlyphPosition(1, "a", 1000, "center"), 1000 / 2 - font:getWidth("a") / 2, "centered glyph position")
end


Expand Down
Loading