mirror of
https://github.com/love2d/love.git
synced 2026-08-16 16:20:42 +02:00
font rendering: reimplement tab-as-spaces support.
This commit is contained in:
@@ -167,7 +167,7 @@ int GenericShaper::computeWordWrapIndex(const ColoredCodepoints& codepoints, Ran
|
||||
float newwidth = w + getKerning(prevglyph, g) + getGlyphAdvance(g);
|
||||
|
||||
// Only wrap when there's a non-space character.
|
||||
if (newwidth > wraplimit && g != ' ')
|
||||
if (newwidth > wraplimit && !isWhitespace(g))
|
||||
{
|
||||
// Rewind to the last seen space when wrapping.
|
||||
if (lastspaceindex != -1)
|
||||
@@ -179,10 +179,10 @@ int GenericShaper::computeWordWrapIndex(const ColoredCodepoints& codepoints, Ran
|
||||
}
|
||||
|
||||
// Don't count trailing spaces in the output width.
|
||||
if (g == ' ')
|
||||
if (isWhitespace(g))
|
||||
{
|
||||
lastspaceindex = i;
|
||||
if (prevglyph != ' ')
|
||||
if (!isWhitespace(prevglyph))
|
||||
widthbeforelastspace = w;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace love
|
||||
namespace font
|
||||
{
|
||||
|
||||
void getCodepointsFromString(const std::string& text, std::vector<uint32>& codepoints)
|
||||
void getCodepointsFromString(const std::string &text, std::vector<uint32> &codepoints)
|
||||
{
|
||||
codepoints.reserve(text.size());
|
||||
|
||||
@@ -45,20 +45,20 @@ void getCodepointsFromString(const std::string& text, std::vector<uint32>& codep
|
||||
codepoints.push_back(g);
|
||||
}
|
||||
}
|
||||
catch (utf8::exception& e)
|
||||
catch (utf8::exception &e)
|
||||
{
|
||||
throw love::Exception("UTF-8 decoding error: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void getCodepointsFromString(const std::vector<ColoredString>& strs, ColoredCodepoints& codepoints)
|
||||
void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints)
|
||||
{
|
||||
if (strs.empty())
|
||||
return;
|
||||
|
||||
codepoints.cps.reserve(strs[0].str.size());
|
||||
|
||||
for (const ColoredString& cstr : strs)
|
||||
for (const ColoredString &cstr : strs)
|
||||
{
|
||||
// No need to add the color if the string is empty anyway, and the code
|
||||
// further on assumes no two colors share the same starting position.
|
||||
@@ -87,7 +87,10 @@ TextShaper::TextShaper(Rasterizer *rasterizer)
|
||||
, dpiScales{rasterizer->getDPIScale()}
|
||||
, height(floorf(rasterizer->getHeight() / rasterizer->getDPIScale() + 0.5f))
|
||||
, lineHeight(1)
|
||||
, useSpacesForTab(false)
|
||||
{
|
||||
if (!rasterizer->hasGlyph('\t'))
|
||||
useSpacesForTab = true;
|
||||
}
|
||||
|
||||
TextShaper::~TextShaper()
|
||||
@@ -141,7 +144,7 @@ bool TextShaper::hasGlyph(uint32 glyph) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextShaper::hasGlyphs(const std::string& text) const
|
||||
bool TextShaper::hasGlyphs(const std::string &text) const
|
||||
{
|
||||
if (text.size() == 0)
|
||||
return false;
|
||||
@@ -178,7 +181,7 @@ float TextShaper::getKerning(uint32 leftglyph, uint32 rightglyph)
|
||||
float k = 0.0f;
|
||||
bool found = false;
|
||||
|
||||
for (const auto& r : rasterizers)
|
||||
for (const auto &r : rasterizers)
|
||||
{
|
||||
if (r->hasGlyph(leftglyph) && r->hasGlyph(rightglyph))
|
||||
{
|
||||
@@ -195,7 +198,7 @@ float TextShaper::getKerning(uint32 leftglyph, uint32 rightglyph)
|
||||
return k;
|
||||
}
|
||||
|
||||
float TextShaper::getKerning(const std::string& leftchar, const std::string& rightchar)
|
||||
float TextShaper::getKerning(const std::string &leftchar, const std::string &rightchar)
|
||||
{
|
||||
uint32 left = 0;
|
||||
uint32 right = 0;
|
||||
@@ -215,8 +218,6 @@ float TextShaper::getKerning(const std::string& leftchar, const std::string& rig
|
||||
|
||||
int TextShaper::getGlyphAdvance(uint32 glyph, GlyphIndex *glyphindex)
|
||||
{
|
||||
// TODO: use spaces for tab
|
||||
|
||||
const auto it = glyphAdvances.find(glyph);
|
||||
if (it != glyphAdvances.end())
|
||||
{
|
||||
@@ -226,10 +227,14 @@ int TextShaper::getGlyphAdvance(uint32 glyph, GlyphIndex *glyphindex)
|
||||
}
|
||||
|
||||
int rasterizeri = 0;
|
||||
uint32 realglyph = glyph;
|
||||
|
||||
if (glyph == '\t' && isUsingSpacesForTab())
|
||||
realglyph = ' ';
|
||||
|
||||
for (size_t i = 0; i < rasterizers.size(); i++)
|
||||
{
|
||||
if (rasterizers[i]->hasGlyph(glyph))
|
||||
if (rasterizers[i]->hasGlyph(realglyph))
|
||||
{
|
||||
rasterizeri = (int) i;
|
||||
break;
|
||||
@@ -237,9 +242,12 @@ int TextShaper::getGlyphAdvance(uint32 glyph, GlyphIndex *glyphindex)
|
||||
}
|
||||
|
||||
const auto &r = rasterizers[rasterizeri];
|
||||
int advance = floorf(r->getGlyphSpacing(glyph) / r->getDPIScale() + 0.5f);
|
||||
int advance = floorf(r->getGlyphSpacing(realglyph) / r->getDPIScale() + 0.5f);
|
||||
|
||||
GlyphIndex glyphi = {r->getGlyphIndex(glyph), rasterizeri};
|
||||
if (glyph == '\t' && realglyph == ' ')
|
||||
advance *= SPACES_PER_TAB;
|
||||
|
||||
GlyphIndex glyphi = {r->getGlyphIndex(realglyph), rasterizeri};
|
||||
|
||||
glyphAdvances[glyph] = std::make_pair(advance, glyphi);
|
||||
if (glyphindex)
|
||||
@@ -260,7 +268,7 @@ int TextShaper::getWidth(const std::string &str)
|
||||
return info.width;
|
||||
}
|
||||
|
||||
static size_t findNewline(const ColoredCodepoints& codepoints, size_t start)
|
||||
static size_t findNewline(const ColoredCodepoints &codepoints, size_t start)
|
||||
{
|
||||
for (size_t i = start; i < codepoints.cps.size(); i++)
|
||||
{
|
||||
@@ -273,7 +281,7 @@ static size_t findNewline(const ColoredCodepoints& codepoints, size_t start)
|
||||
return codepoints.cps.size();
|
||||
}
|
||||
|
||||
void TextShaper::getWrap(const ColoredCodepoints& codepoints, float wraplimit, std::vector<Range>& lineranges, std::vector<int>* linewidths)
|
||||
void TextShaper::getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector<Range> &lineranges, std::vector<int> *linewidths)
|
||||
{
|
||||
size_t nextnewline = findNewline(codepoints, 0);
|
||||
|
||||
@@ -317,7 +325,7 @@ void TextShaper::getWrap(const ColoredCodepoints& codepoints, float wraplimit, s
|
||||
}
|
||||
}
|
||||
|
||||
void TextShaper::getWrap(const std::vector<ColoredString>& text, float wraplimit, std::vector<std::string>& lines, std::vector<int>* linewidths)
|
||||
void TextShaper::getWrap(const std::vector<ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
|
||||
{
|
||||
ColoredCodepoints cps;
|
||||
getCodepointsFromString(text, cps);
|
||||
@@ -327,7 +335,7 @@ void TextShaper::getWrap(const std::vector<ColoredString>& text, float wraplimit
|
||||
|
||||
std::string line;
|
||||
|
||||
for (const auto& range : codepointranges)
|
||||
for (const auto &range : codepointranges)
|
||||
{
|
||||
line.clear();
|
||||
|
||||
@@ -347,9 +355,9 @@ void TextShaper::getWrap(const std::vector<ColoredString>& text, float wraplimit
|
||||
}
|
||||
}
|
||||
|
||||
void TextShaper::setFallbacks(const std::vector<Rasterizer*>& fallbacks)
|
||||
void TextShaper::setFallbacks(const std::vector<Rasterizer*> &fallbacks)
|
||||
{
|
||||
for (Rasterizer* r : fallbacks)
|
||||
for (Rasterizer *r : fallbacks)
|
||||
{
|
||||
if (r->getDataType() != rasterizers[0]->getDataType())
|
||||
throw love::Exception("Font fallbacks must be of the same font type.");
|
||||
@@ -362,7 +370,7 @@ void TextShaper::setFallbacks(const std::vector<Rasterizer*>& fallbacks)
|
||||
rasterizers.resize(1);
|
||||
dpiScales.resize(1);
|
||||
|
||||
for (Rasterizer* r : fallbacks)
|
||||
for (Rasterizer *r : fallbacks)
|
||||
{
|
||||
rasterizers.push_back(r);
|
||||
dpiScales.push_back(r->getDPIScale());
|
||||
|
||||
@@ -81,11 +81,15 @@ public:
|
||||
int height;
|
||||
};
|
||||
|
||||
// This will be used if the Rasterizer doesn't have a tab character itself.
|
||||
static const int SPACES_PER_TAB = 4;
|
||||
|
||||
static love::Type type;
|
||||
|
||||
virtual ~TextShaper();
|
||||
|
||||
const std::vector<StrongRef<Rasterizer>> &getRasterizers() const { return rasterizers; }
|
||||
bool isUsingSpacesForTab() const { return useSpacesForTab; }
|
||||
|
||||
float getHeight() const;
|
||||
|
||||
@@ -128,6 +132,8 @@ protected:
|
||||
|
||||
TextShaper(Rasterizer *rasterizer);
|
||||
|
||||
static inline bool isWhitespace(uint32 codepoint) { return codepoint == ' ' || codepoint == '\t'; }
|
||||
|
||||
std::vector<StrongRef<Rasterizer>> rasterizers;
|
||||
std::vector<float> dpiScales;
|
||||
|
||||
@@ -136,6 +142,8 @@ private:
|
||||
int height;
|
||||
float lineHeight;
|
||||
|
||||
bool useSpacesForTab;
|
||||
|
||||
// maps glyphs to advance and glyph+rasterizer index.
|
||||
std::unordered_map<uint32, std::pair<int, GlyphIndex>> glyphAdvances;
|
||||
|
||||
|
||||
@@ -36,30 +36,70 @@ namespace freetype
|
||||
|
||||
HarfbuzzShaper::HarfbuzzShaper(TrueTypeRasterizer *rasterizer)
|
||||
: TextShaper(rasterizer)
|
||||
, spaceGlyphIndex()
|
||||
, tabSpacesAdvanceX(0)
|
||||
, tabSpacesAdvanceY(0)
|
||||
{
|
||||
hbFonts.push_back(hb_ft_font_create_referenced((FT_Face)rasterizer->getHandle()));
|
||||
hbBuffers.push_back(hb_buffer_create());
|
||||
|
||||
if (hbFonts[0] == nullptr || hbFonts[0] == hb_font_get_empty())
|
||||
throw love::Exception("");
|
||||
throw love::Exception("Could not create Harfbuzz font object.");
|
||||
|
||||
if (hbBuffers[0] == nullptr || hbBuffers[0] == hb_buffer_get_empty())
|
||||
throw love::Exception("Could not create Harfbuzz buffer object.");
|
||||
|
||||
updateSpacesForTabInfo();
|
||||
}
|
||||
|
||||
HarfbuzzShaper::~HarfbuzzShaper()
|
||||
{
|
||||
for (hb_buffer_t *buffer : hbBuffers)
|
||||
hb_buffer_destroy(buffer);
|
||||
for (hb_font_t *font : hbFonts)
|
||||
hb_font_destroy(font);
|
||||
}
|
||||
|
||||
void HarfbuzzShaper::setFallbacks(const std::vector<Rasterizer*>& fallbacks)
|
||||
void HarfbuzzShaper::setFallbacks(const std::vector<Rasterizer*> &fallbacks)
|
||||
{
|
||||
for (size_t i = 1; i < hbFonts.size(); i++)
|
||||
for (size_t i = 1; i < rasterizers.size(); i++)
|
||||
{
|
||||
hb_buffer_destroy(hbBuffers[i]);
|
||||
hb_font_destroy(hbFonts[i]);
|
||||
}
|
||||
|
||||
TextShaper::setFallbacks(fallbacks);
|
||||
|
||||
hbFonts.resize(rasterizers.size());
|
||||
hbBuffers.resize(rasterizers.size());
|
||||
|
||||
for (size_t i = 1; i < hbFonts.size(); i++)
|
||||
for (size_t i = 1; i < rasterizers.size(); i++)
|
||||
{
|
||||
hbFonts[i] = hb_ft_font_create_referenced((FT_Face)rasterizers[i]->getHandle());
|
||||
hbBuffers[i] = hb_buffer_create();
|
||||
}
|
||||
|
||||
updateSpacesForTabInfo();
|
||||
}
|
||||
|
||||
void HarfbuzzShaper::updateSpacesForTabInfo()
|
||||
{
|
||||
if (!isUsingSpacesForTab())
|
||||
return;
|
||||
|
||||
hb_codepoint_t glyphid = 0;
|
||||
for (size_t i = 0; i < hbFonts.size(); i++)
|
||||
{
|
||||
hb_font_t *hbfont = hbFonts[i];
|
||||
if (hb_font_get_glyph(hbfont, ' ', 0, &glyphid))
|
||||
{
|
||||
spaceGlyphIndex.index = glyphid;
|
||||
spaceGlyphIndex.rasterizerIndex = i;
|
||||
tabSpacesAdvanceX = hb_font_get_glyph_h_advance(hbfont, glyphid) * SPACES_PER_TAB;
|
||||
tabSpacesAdvanceY = hb_font_get_glyph_v_advance(hbfont, glyphid) * SPACES_PER_TAB;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints, Range range, Vector2 offset, float extraspacing, std::vector<GlyphPosition> *positions, std::vector<IndexedColor> *colors, TextInfo *info)
|
||||
@@ -70,18 +110,21 @@ void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints,
|
||||
offset.y += getBaseline();
|
||||
Vector2 curpos = offset;
|
||||
|
||||
hb_buffer_t *hbbuffer = hb_buffer_create();
|
||||
hb_buffer_t *hbbuffer = hbBuffers[0];
|
||||
hb_buffer_reset(hbbuffer);
|
||||
|
||||
hb_buffer_add_codepoints(hbbuffer, codepoints.cps.data(), codepoints.cps.size(), (unsigned int) range.getOffset(), (int) range.getSize());
|
||||
|
||||
// TODO: Expose APIs for direction and script?
|
||||
hb_buffer_guess_segment_properties(hbbuffer);
|
||||
|
||||
hb_direction_t direction = hb_buffer_get_direction(hbbuffer);
|
||||
|
||||
hb_shape(hbFonts[0], hbbuffer, nullptr, 0);
|
||||
|
||||
int glyphcount = (int) hb_buffer_get_length(hbbuffer);
|
||||
hb_glyph_info_t* glyphinfos = hb_buffer_get_glyph_infos(hbbuffer, nullptr);
|
||||
hb_glyph_position_t* glyphpositions = hb_buffer_get_glyph_positions(hbbuffer, nullptr);
|
||||
hb_glyph_info_t *glyphinfos = hb_buffer_get_glyph_infos(hbbuffer, nullptr);
|
||||
hb_glyph_position_t *glyphpositions = hb_buffer_get_glyph_positions(hbbuffer, nullptr);
|
||||
|
||||
if (positions)
|
||||
positions->reserve(positions->size() + glyphcount);
|
||||
@@ -93,7 +136,7 @@ void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints,
|
||||
for (int i = 0; i < glyphcount; i++)
|
||||
{
|
||||
bool adding = false;
|
||||
if (glyphinfos[i].codepoint == 0)
|
||||
if (glyphinfos[i].codepoint == 0 && (codepoints.cps[glyphinfos[i].cluster] != '\t' || !isUsingSpacesForTab()))
|
||||
{
|
||||
if (fallbackranges.empty() || fallbackranges.back().getMax() != glyphinfos[i-1].cluster)
|
||||
fallbackranges.push_back(Range(glyphinfos[i].cluster, 1));
|
||||
@@ -106,9 +149,7 @@ void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints,
|
||||
{
|
||||
for (size_t rasti = 1; rasti < rasterizers.size(); rasti++)
|
||||
{
|
||||
hb_buffer_t* hbbuffer = hb_buffer_create();
|
||||
|
||||
hb_buffer_destroy(hbbuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,11 +173,10 @@ void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints,
|
||||
int maxwidth = (int)curpos.x;
|
||||
|
||||
// TODO: fallbacks
|
||||
// TODO: use spaces for tab
|
||||
for (int i = 0; i < glyphcount; i++)
|
||||
{
|
||||
const hb_glyph_info_t& info = glyphinfos[i];
|
||||
const hb_glyph_position_t& glyphpos = glyphpositions[i];
|
||||
const hb_glyph_info_t &info = glyphinfos[i];
|
||||
hb_glyph_position_t &glyphpos = glyphpositions[i];
|
||||
|
||||
// TODO: this doesn't handle situations where the user inserted a color
|
||||
// change in the middle of some characters that get combined into a single
|
||||
@@ -166,6 +206,21 @@ void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints,
|
||||
if (clustercodepoint == '\r')
|
||||
continue;
|
||||
|
||||
// This is a glyph index at this point, despite the name.
|
||||
GlyphIndex gindex = { info.codepoint, 0 };
|
||||
|
||||
if (clustercodepoint == '\t' && isUsingSpacesForTab())
|
||||
{
|
||||
gindex = spaceGlyphIndex;
|
||||
|
||||
// This should be safe to overwrite.
|
||||
// TODO: RTL support?
|
||||
glyphpos.x_offset = 0;
|
||||
glyphpos.y_offset = 0;
|
||||
glyphpos.x_advance = HB_DIRECTION_IS_HORIZONTAL(direction) ? tabSpacesAdvanceX : 0;
|
||||
glyphpos.y_advance = HB_DIRECTION_IS_VERTICAL(direction) ? tabSpacesAdvanceY : 0;
|
||||
}
|
||||
|
||||
if (colorToAdd.hasValue && colors && positions)
|
||||
{
|
||||
IndexedColor c = {colorToAdd.value, positions->size()};
|
||||
@@ -175,8 +230,6 @@ void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints,
|
||||
|
||||
if (positions)
|
||||
{
|
||||
// Despite the name this is a glyph index at this point.
|
||||
GlyphIndex gindex = { info.codepoint, 0 };
|
||||
GlyphPosition p = { curpos, gindex };
|
||||
|
||||
// Harfbuzz position coordinate systems are based on the given font.
|
||||
@@ -195,8 +248,6 @@ void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints,
|
||||
curpos.x = floorf(curpos.x + extraspacing);
|
||||
}
|
||||
|
||||
hb_buffer_destroy(hbbuffer);
|
||||
|
||||
if (curpos.x > maxwidth)
|
||||
maxwidth = (int)curpos.x;
|
||||
|
||||
@@ -209,23 +260,26 @@ void HarfbuzzShaper::computeGlyphPositions(const ColoredCodepoints &codepoints,
|
||||
}
|
||||
}
|
||||
|
||||
int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints& codepoints, Range range, float wraplimit, float *width)
|
||||
int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints &codepoints, Range range, float wraplimit, float *width)
|
||||
{
|
||||
if (!range.isValid())
|
||||
range = Range(0, codepoints.cps.size());
|
||||
|
||||
hb_buffer_t* hbbuffer = hb_buffer_create();
|
||||
hb_buffer_t *hbbuffer = hbBuffers[0];
|
||||
hb_buffer_reset(hbbuffer);
|
||||
|
||||
hb_buffer_add_codepoints(hbbuffer, codepoints.cps.data(), codepoints.cps.size(), (unsigned int)range.getOffset(), (int)range.getSize());
|
||||
|
||||
// TODO: Expose APIs for direction and script?
|
||||
hb_buffer_guess_segment_properties(hbbuffer);
|
||||
|
||||
hb_direction_t direction = hb_buffer_get_direction(hbbuffer);
|
||||
|
||||
hb_shape(hbFonts[0], hbbuffer, nullptr, 0);
|
||||
|
||||
int glyphcount = (int)hb_buffer_get_length(hbbuffer);
|
||||
hb_glyph_info_t* glyphinfos = hb_buffer_get_glyph_infos(hbbuffer, nullptr);
|
||||
hb_glyph_position_t* glyphpositions = hb_buffer_get_glyph_positions(hbbuffer, nullptr);
|
||||
hb_glyph_info_t *glyphinfos = hb_buffer_get_glyph_infos(hbbuffer, nullptr);
|
||||
hb_glyph_position_t *glyphpositions = hb_buffer_get_glyph_positions(hbbuffer, nullptr);
|
||||
|
||||
float w = 0.0f;
|
||||
float outwidth = 0.0f;
|
||||
@@ -237,8 +291,8 @@ int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints& codepoints, Ra
|
||||
|
||||
for (int i = 0; i < glyphcount; i++)
|
||||
{
|
||||
const hb_glyph_info_t& info = glyphinfos[i];
|
||||
const hb_glyph_position_t& glyphpos = glyphpositions[i];
|
||||
const hb_glyph_info_t &info = glyphinfos[i];
|
||||
hb_glyph_position_t &glyphpos = glyphpositions[i];
|
||||
|
||||
uint32 clustercodepoint = codepoints.cps[info.cluster];
|
||||
|
||||
@@ -248,10 +302,20 @@ int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints& codepoints, Ra
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clustercodepoint == '\t' && isUsingSpacesForTab())
|
||||
{
|
||||
// This should be safe to overwrite.
|
||||
// TODO: RTL support?
|
||||
glyphpos.x_offset = 0;
|
||||
glyphpos.y_offset = 0;
|
||||
glyphpos.x_advance = HB_DIRECTION_IS_HORIZONTAL(direction) ? tabSpacesAdvanceX : 0;
|
||||
glyphpos.y_advance = HB_DIRECTION_IS_VERTICAL(direction) ? tabSpacesAdvanceY : 0;
|
||||
}
|
||||
|
||||
float newwidth = w + floorf((glyphpos.x_advance >> 6) / dpiScales[0] + 0.5f);
|
||||
|
||||
// Only wrap when there's a non-space character.
|
||||
if (newwidth > wraplimit && clustercodepoint != ' ')
|
||||
if (newwidth > wraplimit && !isWhitespace(clustercodepoint))
|
||||
{
|
||||
// Rewind to the last seen space when wrapping.
|
||||
if (lastspaceindex != -1)
|
||||
@@ -263,10 +327,10 @@ int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints& codepoints, Ra
|
||||
}
|
||||
|
||||
// Don't count trailing spaces in the output width.
|
||||
if (clustercodepoint == ' ')
|
||||
if (isWhitespace(clustercodepoint))
|
||||
{
|
||||
lastspaceindex = info.cluster;
|
||||
if (prevcodepoint != ' ')
|
||||
if (!isWhitespace(prevcodepoint))
|
||||
widthbeforelastspace = w;
|
||||
}
|
||||
else
|
||||
@@ -277,8 +341,6 @@ int HarfbuzzShaper::computeWordWrapIndex(const ColoredCodepoints& codepoints, Ra
|
||||
wrapindex = info.cluster;
|
||||
}
|
||||
|
||||
hb_buffer_destroy(hbbuffer);
|
||||
|
||||
if (width)
|
||||
*width = outwidth;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
extern "C"
|
||||
{
|
||||
typedef struct hb_font_t hb_font_t;
|
||||
typedef struct hb_buffer_t hb_buffer_t;
|
||||
}
|
||||
|
||||
namespace love
|
||||
@@ -50,7 +51,14 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void updateSpacesForTabInfo();
|
||||
|
||||
std::vector<hb_font_t *> hbFonts;
|
||||
std::vector<hb_buffer_t *> hbBuffers;
|
||||
|
||||
GlyphIndex spaceGlyphIndex;
|
||||
int tabSpacesAdvanceX;
|
||||
int tabSpacesAdvanceY;
|
||||
|
||||
}; // HarfbuzzShaper
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ Font::Font(love::font::Rasterizer *r, const SamplerState &s)
|
||||
, textureHeight(128)
|
||||
, samplerState()
|
||||
, dpiScale(r->getDPIScale())
|
||||
, useSpacesAsTab(false)
|
||||
, textureCacheID(0)
|
||||
{
|
||||
samplerState.minFilter = s.minFilter;
|
||||
@@ -92,10 +91,6 @@ Font::Font(love::font::Rasterizer *r, const SamplerState &s)
|
||||
if (pixelFormat == PIXELFORMAT_LA8_UNORM && !gfx->isPixelFormatSupported(pixelFormat, PIXELFORMATUSAGEFLAGS_SAMPLE))
|
||||
pixelFormat = PIXELFORMAT_RGBA8_UNORM;
|
||||
|
||||
uint32 tab = '\t';
|
||||
if (!r->hasGlyph(tab)) // No tab character in the Rasterizer.
|
||||
useSpacesAsTab = true;
|
||||
|
||||
loadVolatile();
|
||||
++fontCount;
|
||||
}
|
||||
@@ -232,24 +227,6 @@ void Font::unloadVolatile()
|
||||
love::font::GlyphData *Font::getRasterizerGlyphData(love::font::TextShaper::GlyphIndex glyphindex, float &dpiscale)
|
||||
{
|
||||
const auto &r = shaper->getRasterizers()[glyphindex.rasterizerIndex];
|
||||
|
||||
// Use spaces for the tab 'glyph'. FIXME
|
||||
if (/*glyph == '\t' &&*/ false && useSpacesAsTab)
|
||||
{
|
||||
love::font::GlyphData *spacegd = r->getGlyphData(32);
|
||||
PixelFormat fmt = spacegd->getFormat();
|
||||
|
||||
love::font::GlyphMetrics gm = {};
|
||||
gm.advance = spacegd->getAdvance() * SPACES_PER_TAB;
|
||||
gm.bearingX = spacegd->getBearingX();
|
||||
gm.bearingY = spacegd->getBearingY();
|
||||
|
||||
spacegd->release();
|
||||
|
||||
dpiscale = r->getDPIScale();
|
||||
return new love::font::GlyphData('\t', gm, fmt);
|
||||
}
|
||||
|
||||
dpiscale = r->getDPIScale();
|
||||
return r->getGlyphDataForIndex(glyphindex.index);
|
||||
}
|
||||
|
||||
@@ -201,8 +201,6 @@ private:
|
||||
int textureX, textureY;
|
||||
int rowHeight;
|
||||
|
||||
bool useSpacesAsTab;
|
||||
|
||||
// ID which is incremented when the texture cache is invalidated.
|
||||
uint32 textureCacheID;
|
||||
|
||||
@@ -211,9 +209,6 @@ private:
|
||||
// use, for edge antialiasing.
|
||||
static const int TEXTURE_PADDING = 2;
|
||||
|
||||
// This will be used if the Rasterizer doesn't have a tab character itself.
|
||||
static const int SPACES_PER_TAB = 4;
|
||||
|
||||
static StringMap<AlignMode, ALIGN_MAX_ENUM>::Entry alignModeEntries[];
|
||||
static StringMap<AlignMode, ALIGN_MAX_ENUM> alignModes;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user