Merge branch 'love2d:12.0-development' into 12.0-development

This commit is contained in:
nikeinikei
2023-01-27 13:53:32 +01:00
committed by GitHub
48 changed files with 3333 additions and 1640 deletions
+1 -1
View File
@@ -90,7 +90,7 @@ void Deprecations::draw(Graphics *gfx)
int maxcount = 4;
int remaining = std::max(0, total - maxcount);
std::vector<Font::ColoredString> strings;
std::vector<font::ColoredString> strings;
Colorf white(1, 1, 1, 1);
// Grab the newest deprecation notices first.
+114 -501
View File
@@ -21,8 +21,6 @@
#include "Font.h"
#include "font/GlyphData.h"
#include "libraries/utf8/utf8.h"
#include "common/math.h"
#include "common/Matrix.h"
#include "Graphics.h"
@@ -42,20 +40,27 @@ static inline uint16 normToUint16(double n)
return (uint16) (n * LOVE_UINT16_MAX);
}
static inline uint64 packGlyphIndex(love::font::TextShaper::GlyphIndex glyphindex)
{
return ((uint64)glyphindex.rasterizerIndex << 32) | (uint64)glyphindex.index;
}
static inline love::font::TextShaper::GlyphIndex unpackGlyphIndex(uint64 packedindex)
{
return {(int) (packedindex & 0xFFFFFFFF), (int) (packedindex >> 32)};
}
love::Type Font::type("Font", &Object::type);
int Font::fontCount = 0;
const CommonFormat Font::vertexFormat = CommonFormat::XYf_STus_RGBAub;
Font::Font(love::font::Rasterizer *r, const SamplerState &s)
: rasterizers({r})
, height(r->getHeight())
, lineHeight(1)
: shaper(r->newTextShaper(), Acquire::NORETAIN)
, textureWidth(128)
, textureHeight(128)
, samplerState()
, dpiScale(r->getDPIScale())
, useSpacesAsTab(false)
, textureCacheID(0)
{
samplerState.minFilter = s.minFilter;
@@ -66,7 +71,7 @@ Font::Font(love::font::Rasterizer *r, const SamplerState &s)
// largest texture size if no rough match is found.
while (true)
{
if ((height * 0.8) * height * 30 <= textureWidth * textureHeight)
if ((shaper->getHeight() * 0.8) * shaper->getHeight() * 30 <= textureWidth * textureHeight)
break;
TextureSize nextsize = getNextTextureSize();
@@ -86,9 +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;
if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
useSpacesAsTab = true;
loadVolatile();
++fontCount;
}
@@ -170,7 +172,7 @@ void Font::createTexture()
// and transparent black otherwise.
std::vector<uint8> emptydata(datasize, 0);
if (rasterizers[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
if (shaper->getRasterizers()[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
{
if (pixelFormat == PIXELFORMAT_LA8_UNORM)
{
@@ -204,15 +206,15 @@ void Font::createTexture()
{
textureCacheID++;
std::vector<uint32> glyphstoadd;
std::vector<love::font::TextShaper::GlyphIndex> glyphstoadd;
for (const auto &glyphpair : glyphs)
glyphstoadd.push_back(glyphpair.first);
glyphstoadd.push_back(unpackGlyphIndex(glyphpair.first));
glyphs.clear();
for (uint32 g : glyphstoadd)
addGlyph(g);
for (auto glyphindex : glyphstoadd)
addGlyph(glyphindex);
}
}
@@ -222,42 +224,17 @@ void Font::unloadVolatile()
textures.clear();
}
love::font::GlyphData *Font::getRasterizerGlyphData(uint32 glyph, float &dpiscale)
love::font::GlyphData *Font::getRasterizerGlyphData(love::font::TextShaper::GlyphIndex glyphindex, float &dpiscale)
{
// Use spaces for the tab 'glyph'.
if (glyph == 9 && useSpacesAsTab)
{
love::font::GlyphData *spacegd = rasterizers[0]->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 = rasterizers[0]->getDPIScale();
return new love::font::GlyphData(glyph, gm, fmt);
}
for (const StrongRef<love::font::Rasterizer> &r : rasterizers)
{
if (r->hasGlyph(glyph))
{
dpiscale = r->getDPIScale();
return r->getGlyphData(glyph);
}
}
dpiscale = rasterizers[0]->getDPIScale();
return rasterizers[0]->getGlyphData(glyph);
const auto &r = shaper->getRasterizers()[glyphindex.rasterizerIndex];
dpiscale = r->getDPIScale();
return r->getGlyphDataForIndex(glyphindex.index);
}
const Font::Glyph &Font::addGlyph(uint32 glyph)
const Font::Glyph &Font::addGlyph(love::font::TextShaper::GlyphIndex glyphindex)
{
float glyphdpiscale = getDPIScale();
StrongRef<love::font::GlyphData> gd(getRasterizerGlyphData(glyph, glyphdpiscale), Acquire::NORETAIN);
StrongRef<love::font::GlyphData> gd(getRasterizerGlyphData(glyphindex, glyphdpiscale), Acquire::NORETAIN);
int w = gd->getWidth();
int h = gd->getHeight();
@@ -279,15 +256,13 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
// Makes sure the above code for checking if the glyph can fit at
// the current position in the texture is run again for this glyph.
return addGlyph(glyph);
return addGlyph(glyphindex);
}
}
Glyph g;
g.texture = 0;
g.spacing = floorf(gd->getAdvance() / glyphdpiscale + 0.5f);
g.texture = nullptr;
memset(g.vertices, 0, sizeof(GlyphVertex) * 4);
// Don't waste space for empty glyphs.
@@ -357,151 +332,77 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
rowHeight = std::max(rowHeight, h + TEXTURE_PADDING);
}
glyphs[glyph] = g;
return glyphs[glyph];
uint64 packedindex = packGlyphIndex(glyphindex);
glyphs[packedindex] = g;
return glyphs[packedindex];
}
const Font::Glyph &Font::findGlyph(uint32 glyph)
const Font::Glyph &Font::findGlyph(love::font::TextShaper::GlyphIndex glyphindex)
{
const auto it = glyphs.find(glyph);
uint64 packedindex = packGlyphIndex(glyphindex);
const auto it = glyphs.find(packedindex);
if (it != glyphs.end())
return it->second;
return addGlyph(glyph);
return addGlyph(glyphindex);
}
float Font::getKerning(uint32 leftglyph, uint32 rightglyph)
{
uint64 packedglyphs = ((uint64) leftglyph << 32) | (uint64) rightglyph;
const auto it = kerning.find(packedglyphs);
if (it != kerning.end())
return it->second;
float k = floorf(rasterizers[0]->getKerning(leftglyph, rightglyph) / dpiScale + 0.5f);
for (const auto &r : rasterizers)
{
if (r->hasGlyph(leftglyph) && r->hasGlyph(rightglyph))
{
k = floorf(r->getKerning(leftglyph, rightglyph) / r->getDPIScale() + 0.5f);
break;
}
}
kerning[packedglyphs] = k;
return k;
return shaper->getKerning(leftglyph, rightglyph);
}
float Font::getKerning(const std::string &leftchar, const std::string &rightchar)
{
uint32 left = 0;
uint32 right = 0;
try
{
left = utf8::peek_next(leftchar.begin(), leftchar.end());
right = utf8::peek_next(rightchar.begin(), rightchar.end());
}
catch (utf8::exception &e)
{
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
return getKerning(left, right);
}
void Font::getCodepointsFromString(const std::string &text, Codepoints &codepoints)
{
codepoints.reserve(text.size());
try
{
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
while (i != end)
{
uint32 g = *i++;
codepoints.push_back(g);
}
}
catch (utf8::exception &e)
{
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
}
void Font::getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints)
{
if (strs.empty())
return;
codepoints.cps.reserve(strs[0].str.size());
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.
if (cstr.str.size() == 0)
continue;
IndexedColor c = {cstr.color, (int) codepoints.cps.size()};
codepoints.colors.push_back(c);
getCodepointsFromString(cstr.str, codepoints.cps);
}
if (codepoints.colors.size() == 1)
{
IndexedColor c = codepoints.colors[0];
if (c.index == 0 && c.color == Colorf(1.0f, 1.0f, 1.0f, 1.0f))
codepoints.colors.pop_back();
}
return shaper->getKerning(leftchar, rightchar);
}
float Font::getHeight() const
{
return (float) floorf(height / dpiScale + 0.5f);
return shaper->getHeight();
}
std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &codepoints, const Colorf &constantcolor, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector2 offset, TextInfo *info)
std::vector<Font::DrawCommand> Font::generateVertices(const love::font::ColoredCodepoints &codepoints, Range range, const Colorf &constantcolor, std::vector<GlyphVertex> &vertices, float extra_spacing, Vector2 offset, love::font::TextShaper::TextInfo *info)
{
// Spacing counter and newline handling.
float dx = offset.x;
float dy = offset.y;
std::vector<love::font::TextShaper::GlyphPosition> glyphpositions;
std::vector<love::font::IndexedColor> colors;
shaper->computeGlyphPositions(codepoints, range, offset, extra_spacing, &glyphpositions, &colors, info);
float heightoffset = 0.0f;
size_t vertstartsize = vertices.size();
vertices.reserve(vertstartsize + glyphpositions.size() * 4);
if (rasterizers[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
heightoffset = getBaseline();
Colorf linearconstantcolor = gammaCorrectColor(constantcolor);
Color32 curcolor = toColor32(constantcolor);
int maxwidth = 0;
int curcolori = 0;
int ncolors = (int)colors.size();
// Keeps track of when we need to switch textures in our vertex array.
std::vector<DrawCommand> commands;
// Pre-allocate space for the maximum possible number of vertices.
size_t vertstartsize = vertices.size();
vertices.reserve(vertstartsize + codepoints.cps.size() * 4);
uint32 prevglyph = 0;
Colorf linearconstantcolor = gammaCorrectColor(constantcolor);
Color32 curcolor = toColor32(constantcolor);
int curcolori = -1;
int ncolors = (int) codepoints.colors.size();
for (int i = 0; i < (int) codepoints.cps.size(); i++)
for (int i = 0; i < (int) glyphpositions.size(); i++)
{
uint32 g = codepoints.cps[i];
const auto &info = glyphpositions[i];
if (curcolori + 1 < ncolors && codepoints.colors[curcolori + 1].index == i)
uint32 cacheid = textureCacheID;
const Glyph &glyph = findGlyph(info.glyphIndex);
// If findGlyph invalidates the texture cache, restart the loop.
if (cacheid != textureCacheID)
{
Colorf c = codepoints.colors[++curcolori].color;
i = -1; // The next iteration will increment this to 0.
commands.clear();
vertices.resize(vertstartsize);
curcolori = 0;
curcolor = toColor32(constantcolor);
continue;
}
if (curcolori < ncolors && colors[curcolori].index == i)
{
Colorf c = colors[curcolori].color;
c.r = std::min(std::max(c.r, 0.0f), 1.0f);
c.g = std::min(std::max(c.g, 0.0f), 1.0f);
@@ -513,54 +414,17 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
unGammaCorrectColor(c);
curcolor = toColor32(c);
curcolori++;
}
if (g == '\n')
{
if (dx > maxwidth)
maxwidth = (int) dx;
// Wrap newline, but do not print it.
dy += floorf(getHeight() * getLineHeight() + 0.5f);
dx = offset.x;
prevglyph = 0;
continue;
}
// Ignore carriage returns
if (g == '\r')
continue;
uint32 cacheid = textureCacheID;
const Glyph &glyph = findGlyph(g);
// If findGlyph invalidates the texture cache, re-start the loop.
if (cacheid != textureCacheID)
{
i = -1; // The next iteration will increment this to 0.
maxwidth = 0;
dx = offset.x;
dy = offset.y;
commands.clear();
vertices.resize(vertstartsize);
prevglyph = 0;
curcolori = -1;
curcolor = toColor32(constantcolor);
continue;
}
// Add kerning to the current horizontal offset.
dx += getKerning(prevglyph, g);
if (glyph.texture != nullptr)
{
// Copy the vertices and set their colors and relative positions.
for (int j = 0; j < 4; j++)
{
vertices.push_back(glyph.vertices[j]);
vertices.back().x += dx;
vertices.back().y += dy + heightoffset;
vertices.back().x += info.position.x;
vertices.back().y += info.position.y;
vertices.back().color = curcolor;
}
@@ -569,7 +433,7 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
{
// Add a new draw command if the texture has changed.
DrawCommand cmd;
cmd.startvertex = (int) vertices.size() - 4;
cmd.startvertex = (int)vertices.size() - 4;
cmd.vertexcount = 0;
cmd.texture = glyph.texture;
commands.push_back(cmd);
@@ -577,15 +441,6 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
commands.back().vertexcount += 4;
}
// Advance the x position for the next glyph.
dx += glyph.spacing;
// Account for extra spacing given to space characters.
if (g == ' ' && extra_spacing != 0.0f)
dx = floorf(dx + extra_spacing);
prevglyph = g;
}
const auto drawsort = [](const DrawCommand &a, const DrawCommand &b) -> bool
@@ -599,19 +454,10 @@ std::vector<Font::DrawCommand> Font::generateVertices(const ColoredCodepoints &c
std::sort(commands.begin(), commands.end(), drawsort);
if (dx > maxwidth)
maxwidth = (int) dx;
if (info != nullptr)
{
info->width = maxwidth - offset.x;
info->height = (int) dy + (dx > 0.0f ? floorf(getHeight() * getLineHeight() + 0.5f) : 0) - offset.y;
}
return commands;
}
std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCodepoints &text, const Colorf &constantcolor, float wrap, AlignMode align, std::vector<GlyphVertex> &vertices, TextInfo *info)
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);
@@ -620,17 +466,22 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
std::vector<DrawCommand> drawcommands;
vertices.reserve(text.cps.size() * 4);
std::vector<Range> ranges;
std::vector<int> widths;
std::vector<ColoredCodepoints> lines;
getWrap(text, wrap, lines, &widths);
shaper->getWrap(text, wrap, ranges, &widths);
float y = 0.0f;
float maxwidth = 0.0f;
for (int i = 0; i < (int) lines.size(); i++)
for (int i = 0; i < (int)ranges.size(); i++)
{
const auto &line = lines[i];
const auto& range = ranges[i];
if (!range.isValid())
{
y += getHeight() * getLineHeight();
continue;
}
float width = (float) widths[i];
love::Vector2 offset(0.0f, floorf(y));
@@ -648,7 +499,9 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
break;
case ALIGN_JUSTIFY:
{
float numspaces = (float) std::count(line.cps.begin(), line.cps.end(), ' ');
auto start = text.cps.begin() + range.getOffset();
auto end = start + range.getSize();
float numspaces = std::count(start, end, ' ');
if (width < wrap && numspaces >= 1)
extraspacing = (wrap - width) / numspaces;
else
@@ -660,7 +513,7 @@ std::vector<Font::DrawCommand> Font::generateVerticesFormatted(const ColoredCode
break;
}
std::vector<DrawCommand> newcommands = generateVertices(line, constantcolor, vertices, extraspacing, offset);
std::vector<DrawCommand> newcommands = generateVertices(text, range, constantcolor, vertices, extraspacing, offset);
if (!newcommands.empty())
{
@@ -724,21 +577,21 @@ void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector<D
}
}
void Font::print(graphics::Graphics *gfx, const std::vector<ColoredString> &text, const Matrix4 &m, const Colorf &constantcolor)
void Font::print(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, const Matrix4 &m, const Colorf &constantcolor)
{
ColoredCodepoints codepoints;
getCodepointsFromString(text, codepoints);
love::font::ColoredCodepoints codepoints;
love::font::getCodepointsFromString(text, codepoints);
std::vector<GlyphVertex> vertices;
std::vector<DrawCommand> drawcommands = generateVertices(codepoints, constantcolor, vertices);
std::vector<DrawCommand> drawcommands = generateVertices(codepoints, Range(), constantcolor, vertices);
printv(gfx, m, drawcommands, vertices);
}
void Font::printf(graphics::Graphics *gfx, const std::vector<ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantcolor)
void Font::printf(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantcolor)
{
ColoredCodepoints codepoints;
getCodepointsFromString(text, codepoints);
love::font::ColoredCodepoints codepoints;
love::font::getCodepointsFromString(text, codepoints);
std::vector<GlyphVertex> vertices;
std::vector<DrawCommand> drawcommands = generateVerticesFormatted(codepoints, constantcolor, wrap, align, vertices);
@@ -748,241 +601,32 @@ void Font::printf(graphics::Graphics *gfx, const std::vector<ColoredString> &tex
int Font::getWidth(const std::string &str)
{
if (str.size() == 0) return 0;
std::istringstream iss(str);
std::string line;
int max_width = 0;
while (getline(iss, line, '\n'))
{
int width = 0;
uint32 prevglyph = 0;
try
{
utf8::iterator<std::string::const_iterator> i(line.begin(), line.begin(), line.end());
utf8::iterator<std::string::const_iterator> end(line.end(), line.begin(), line.end());
while (i != end)
{
uint32 c = *i++;
// Ignore carriage returns
if (c == '\r')
continue;
const Glyph &g = findGlyph(c);
width += g.spacing + getKerning(prevglyph, c);
prevglyph = c;
}
}
catch (utf8::exception &e)
{
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
max_width = std::max(max_width, width);
}
return max_width;
return shaper->getWidth(str);
}
int Font::getWidth(uint32 glyph)
{
const Glyph &g = findGlyph(glyph);
return g.spacing;
return shaper->getGlyphAdvance(glyph);
}
void Font::getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector<ColoredCodepoints> &lines, std::vector<int> *linewidths)
void Font::getWrap(const love::font::ColoredCodepoints &codepoints, float wraplimit, std::vector<Range> &ranges, std::vector<int> *linewidths)
{
// Per-line info.
float width = 0.0f;
float widthbeforelastspace = 0.0f;
float widthoftrailingspace = 0.0f;
uint32 prevglyph = 0;
int lastspaceindex = -1;
// Keeping the indexed colors "in sync" is a bit tricky, since we split
// things up and we might skip some glyphs but we don't want to skip any
// color which starts at those indices.
Colorf curcolor(1.0f, 1.0f, 1.0f, 1.0f);
bool addcurcolor = false;
int curcolori = -1;
int endcolori = (int) codepoints.colors.size() - 1;
// A wrapped line of text.
ColoredCodepoints wline;
int i = 0;
while (i < (int) codepoints.cps.size())
{
uint32 c = codepoints.cps[i];
// Determine the current color before doing anything else, to make sure
// it's still applied to future glyphs even if this one is skipped.
if (curcolori < endcolori && codepoints.colors[curcolori + 1].index == i)
{
curcolor = codepoints.colors[curcolori + 1].color;
curcolori++;
addcurcolor = true;
}
// Split text at newlines.
if (c == '\n')
{
lines.push_back(wline);
// Ignore the width of any trailing spaces, for individual lines.
if (linewidths)
linewidths->push_back(width - widthoftrailingspace);
// Make sure the new line keeps any color that was set previously.
addcurcolor = true;
width = widthbeforelastspace = widthoftrailingspace = 0.0f;
prevglyph = 0; // Reset kerning information.
lastspaceindex = -1;
wline.cps.clear();
wline.colors.clear();
i++;
continue;
}
// Ignore carriage returns
if (c == '\r')
{
i++;
continue;
}
const Glyph &g = findGlyph(c);
float charwidth = g.spacing + getKerning(prevglyph, c);
float newwidth = width + charwidth;
// Wrap the line if it exceeds the wrap limit. Don't wrap yet if we're
// processing a newline character, though.
if (c != ' ' && newwidth > wraplimit)
{
// If this is the first character in the line and it exceeds the
// limit, skip it completely.
if (wline.cps.empty())
i++;
else if (lastspaceindex != -1)
{
// 'Rewind' to the last seen space, if the line has one.
// FIXME: This could be more efficient...
while (!wline.cps.empty() && wline.cps.back() != ' ')
wline.cps.pop_back();
while (!wline.colors.empty() && wline.colors.back().index >= (int) wline.cps.size())
wline.colors.pop_back();
// Also 'rewind' to the color that the last character is using.
for (int colori = curcolori; colori >= 0; colori--)
{
if (codepoints.colors[colori].index <= lastspaceindex)
{
curcolor = codepoints.colors[colori].color;
curcolori = colori;
break;
}
}
// Ignore the width of trailing spaces in wrapped lines.
width = widthbeforelastspace;
i = lastspaceindex;
i++; // Start the next line after the space.
}
lines.push_back(wline);
if (linewidths)
linewidths->push_back(width);
addcurcolor = true;
prevglyph = 0;
width = widthbeforelastspace = widthoftrailingspace = 0.0f;
wline.cps.clear();
wline.colors.clear();
lastspaceindex = -1;
continue;
}
if (prevglyph != ' ' && c == ' ')
widthbeforelastspace = width;
width = newwidth;
prevglyph = c;
if (addcurcolor)
{
wline.colors.push_back({curcolor, (int) wline.cps.size()});
addcurcolor = false;
}
wline.cps.push_back(c);
// Keep track of the last seen space, so we can "rewind" to it when
// wrapping.
if (c == ' ')
{
lastspaceindex = i;
widthoftrailingspace += charwidth;
}
else if (c != '\n')
widthoftrailingspace = 0.0f;
i++;
}
// Push the last line.
lines.push_back(wline);
// Ignore the width of any trailing spaces, for individual lines.
if (linewidths)
linewidths->push_back(width - widthoftrailingspace);
shaper->getWrap(codepoints, wraplimit, ranges, linewidths);
}
void Font::getWrap(const std::vector<ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
void Font::getWrap(const std::vector<love::font::ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *linewidths)
{
ColoredCodepoints cps;
getCodepointsFromString(text, cps);
std::vector<ColoredCodepoints> codepointlines;
getWrap(cps, wraplimit, codepointlines, linewidths);
std::string line;
for (const ColoredCodepoints &codepoints : codepointlines)
{
line.clear();
line.reserve(codepoints.cps.size());
for (uint32 codepoint : codepoints.cps)
{
char character[5] = {'\0'};
char *end = utf8::unchecked::append(codepoint, character);
line.append(character, end - character);
}
lines.push_back(line);
}
shaper->getWrap(text, wraplimit, lines, linewidths);
}
void Font::setLineHeight(float height)
{
lineHeight = height;
shaper->setLineHeight(height);
}
float Font::getLineHeight() const
{
return lineHeight;
return shaper->getLineHeight();
}
void Font::setSamplerState(const SamplerState &s)
@@ -1002,75 +646,44 @@ const SamplerState &Font::getSamplerState() const
int Font::getAscent() const
{
return floorf(rasterizers[0]->getAscent() / dpiScale + 0.5f);
return shaper->getAscent();
}
int Font::getDescent() const
{
return floorf(rasterizers[0]->getDescent() / dpiScale + 0.5f);
return shaper->getDescent();
}
float Font::getBaseline() const
{
float ascent = getAscent();
if (ascent != 0.0f)
return ascent;
else if (rasterizers[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
return floorf(getHeight() / 1.25f + 0.5f); // 1.25 is magic line height for true type fonts
else
return 0.0f;
return shaper->getBaseline();
}
bool Font::hasGlyph(uint32 glyph) const
{
for (const StrongRef<love::font::Rasterizer> &r : rasterizers)
{
if (r->hasGlyph(glyph))
return true;
}
return false;
return shaper->hasGlyph(glyph);
}
bool Font::hasGlyphs(const std::string &text) const
{
if (text.size() == 0)
return false;
try
{
utf8::iterator<std::string::const_iterator> i(text.begin(), text.begin(), text.end());
utf8::iterator<std::string::const_iterator> end(text.end(), text.begin(), text.end());
while (i != end)
{
uint32 codepoint = *i++;
if (!hasGlyph(codepoint))
return false;
}
}
catch (utf8::exception &e)
{
throw love::Exception("UTF-8 decoding error: %s", e.what());
}
return true;
return shaper->hasGlyphs(text);
}
void Font::setFallbacks(const std::vector<Font *> &fallbacks)
{
for (const Font *f : fallbacks)
{
if (f->rasterizers[0]->getDataType() != this->rasterizers[0]->getDataType())
throw love::Exception("Font fallbacks must be of the same font type.");
}
std::vector<love::font::Rasterizer*> rasterizerfallbacks;
for (const Font* f : fallbacks)
rasterizerfallbacks.push_back(f->shaper->getRasterizers()[0]);
rasterizers.resize(1);
shaper->setFallbacks(rasterizerfallbacks);
// NOTE: this won't invalidate already-rasterized glyphs.
for (const Font *f : fallbacks)
rasterizers.push_back(f->rasterizers[0]);
// Invalidate existing textures.
textureCacheID++;
glyphs.clear();
while (textures.size() > 1)
textures.pop_back();
rowHeight = textureX = textureY = TEXTURE_PADDING;
}
float Font::getDPIScale() const
+16 -54
View File
@@ -33,6 +33,7 @@
#include "common/Vector.h"
#include "font/Rasterizer.h"
#include "font/TextShaper.h"
#include "Texture.h"
#include "vertex.h"
#include "Volatile.h"
@@ -64,30 +65,6 @@ public:
ALIGN_MAX_ENUM
};
struct ColoredString
{
std::string str;
Colorf color;
};
struct IndexedColor
{
Colorf color;
int index;
};
struct ColoredCodepoints
{
std::vector<uint32> cps;
std::vector<IndexedColor> colors;
};
struct TextInfo
{
int width;
int height;
};
// Used to determine when to change textures in the generated vertex array.
struct DrawCommand
{
@@ -100,20 +77,17 @@ public:
virtual ~Font();
std::vector<DrawCommand> generateVertices(const ColoredCodepoints &codepoints, const Colorf &constantColor, std::vector<GlyphVertex> &vertices,
float extra_spacing = 0.0f, Vector2 offset = {}, TextInfo *info = nullptr);
std::vector<DrawCommand> generateVertices(const love::font::ColoredCodepoints &codepoints, Range range, const Colorf &constantColor, std::vector<GlyphVertex> &vertices,
float extra_spacing = 0.0f, Vector2 offset = {}, love::font::TextShaper::TextInfo *info = nullptr);
std::vector<DrawCommand> generateVerticesFormatted(const ColoredCodepoints &text, const Colorf &constantColor, float wrap, AlignMode align,
std::vector<GlyphVertex> &vertices, TextInfo *info = nullptr);
static void getCodepointsFromString(const std::string &str, Codepoints &codepoints);
static void getCodepointsFromString(const std::vector<ColoredString> &strs, ColoredCodepoints &codepoints);
std::vector<DrawCommand> generateVerticesFormatted(const love::font::ColoredCodepoints &text, const Colorf &constantColor, float wrap, AlignMode align,
std::vector<GlyphVertex> &vertices, love::font::TextShaper::TextInfo *info = nullptr);
/**
* Draws the specified text.
**/
void print(graphics::Graphics *gfx, const std::vector<ColoredString> &text, const Matrix4 &m, const Colorf &constantColor);
void printf(graphics::Graphics *gfx, const std::vector<ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantColor);
void print(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, const Matrix4 &m, const Colorf &constantColor);
void printf(graphics::Graphics *gfx, const std::vector<love::font::ColoredString> &text, float wrap, AlignMode align, const Matrix4 &m, const Colorf &constantColor);
/**
* Returns the height of the font.
@@ -141,8 +115,8 @@ public:
* @param max_width Optional output of the maximum width
* Returns a vector with the lines.
**/
void getWrap(const std::vector<ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *line_widths = nullptr);
void getWrap(const ColoredCodepoints &codepoints, float wraplimit, std::vector<ColoredCodepoints> &lines, std::vector<int> *line_widths = nullptr);
void getWrap(const std::vector<love::font::ColoredString> &text, float wraplimit, std::vector<std::string> &lines, std::vector<int> *line_widths = nullptr);
void getWrap(const love::font::ColoredCodepoints &codepoints, float wraplimit, std::vector<Range> &ranges, std::vector<int> *line_widths = nullptr);
/**
* Sets the line height (which should be a number to multiply the font size by,
@@ -191,7 +165,6 @@ private:
struct Glyph
{
Texture *texture;
int spacing;
GlyphVertex vertices[4];
};
@@ -204,26 +177,20 @@ private:
void createTexture();
TextureSize getNextTextureSize() const;
love::font::GlyphData *getRasterizerGlyphData(uint32 glyph, float &dpiscale);
const Glyph &addGlyph(uint32 glyph);
const Glyph &findGlyph(uint32 glyph);
love::font::GlyphData *getRasterizerGlyphData(love::font::TextShaper::GlyphIndex glyphindex, float &dpiscale);
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);
std::vector<StrongRef<love::font::Rasterizer>> rasterizers;
int height;
float lineHeight;
StrongRef<love::font::TextShaper> shaper;
int textureWidth;
int textureHeight;
std::vector<StrongRef<love::graphics::Texture>> textures;
std::vector<StrongRef<Texture>> textures;
// maps glyphs to glyph texture information
std::unordered_map<uint32, Glyph> glyphs;
// map of left/right glyph pairs to horizontal kerning.
std::unordered_map<uint64, float> kerning;
// maps packed glyph index values to glyph texture information
std::unordered_map<uint64, Glyph> glyphs;
PixelFormat pixelFormat;
@@ -234,8 +201,6 @@ private:
int textureX, textureY;
int rowHeight;
bool useSpacesAsTab;
// ID which is incremented when the texture cache is invalidated.
uint32 textureCacheID;
@@ -244,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;
+14 -8
View File
@@ -439,7 +439,7 @@ Mesh *Graphics::newMesh(const std::vector<Mesh::BufferAttribute> &attributes, Pr
return new Mesh(attributes, drawmode);
}
love::graphics::TextBatch *Graphics::newTextBatch(graphics::Font *font, const std::vector<Font::ColoredString> &text)
love::graphics::TextBatch *Graphics::newTextBatch(graphics::Font *font, const std::vector<love::font::ColoredString> &text)
{
return new TextBatch(font, text);
}
@@ -963,10 +963,16 @@ void Graphics::setRenderTargets(const RenderTargets &rts)
resetProjection();
// Invalidate temporary depth/stencil. This could be a clear, but if the
// user also clears a double-clear may be slow...
// Clear/reset the temporary depth/stencil buffers.
// TODO: make this deferred somehow to avoid double clearing if the user
// also calls love.graphics.clear after setCanvas.
if (rts.depthStencil.texture == nullptr && rts.temporaryRTFlags != 0)
discard({}, true);
{
OptionalColorD clearcolor;
OptionalInt clearstencil(0);
OptionalDouble cleardepth(1.0);
clear(clearcolor, clearstencil, cleardepth);
}
}
void Graphics::setRenderTarget()
@@ -1893,7 +1899,7 @@ void Graphics::drawShaderVertices(Buffer *indexbuffer, int indexcount, int insta
draw(cmd);
}
void Graphics::print(const std::vector<Font::ColoredString> &str, const Matrix4 &m)
void Graphics::print(const std::vector<love::font::ColoredString> &str, const Matrix4 &m)
{
checkSetDefaultFont();
@@ -1901,12 +1907,12 @@ void Graphics::print(const std::vector<Font::ColoredString> &str, const Matrix4
print(str, states.back().font.get(), m);
}
void Graphics::print(const std::vector<Font::ColoredString> &str, Font *font, const Matrix4 &m)
void Graphics::print(const std::vector<love::font::ColoredString> &str, Font *font, const Matrix4 &m)
{
font->print(this, str, m, states.back().color);
}
void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m)
void Graphics::printf(const std::vector<love::font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m)
{
checkSetDefaultFont();
@@ -1914,7 +1920,7 @@ void Graphics::printf(const std::vector<Font::ColoredString> &str, float wrap, F
printf(str, states.back().font.get(), wrap, align, m);
}
void Graphics::printf(const std::vector<Font::ColoredString> &str, Font *font, float wrap, Font::AlignMode align, const Matrix4 &m)
void Graphics::printf(const std::vector<love::font::ColoredString> &str, Font *font, float wrap, Font::AlignMode align, const Matrix4 &m)
{
font->printf(this, str, wrap, align, m, states.back().color);
}
+5 -5
View File
@@ -460,7 +460,7 @@ public:
Mesh *newMesh(const std::vector<Buffer::DataDeclaration> &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferDataUsage usage);
Mesh *newMesh(const std::vector<Mesh::BufferAttribute> &attributes, PrimitiveType drawmode);
TextBatch *newTextBatch(Font *font, const std::vector<Font::ColoredString> &text = {});
TextBatch *newTextBatch(Font *font, const std::vector<love::font::ColoredString> &text = {});
data::ByteData *readbackBuffer(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
GraphicsReadback *readbackBufferAsync(Buffer *buffer, size_t offset, size_t size, data::ByteData *dest, size_t destoffset);
@@ -702,14 +702,14 @@ public:
/**
* Draws text at the specified coordinates
**/
void print(const std::vector<Font::ColoredString> &str, const Matrix4 &m);
void print(const std::vector<Font::ColoredString> &str, Font *font, const Matrix4 &m);
void print(const std::vector<love::font::ColoredString> &str, const Matrix4 &m);
void print(const std::vector<love::font::ColoredString> &str, Font *font, const Matrix4 &m);
/**
* Draws formatted text on screen at the specified coordinates.
**/
void printf(const std::vector<Font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m);
void printf(const std::vector<Font::ColoredString> &str, Font *font, float wrap, Font::AlignMode align, const Matrix4 &m);
void printf(const std::vector<love::font::ColoredString> &str, float wrap, Font::AlignMode align, const Matrix4 &m);
void printf(const std::vector<love::font::ColoredString> &str, Font *font, float wrap, Font::AlignMode align, const Matrix4 &m);
/**
* Draws a series of points at the specified positions.
+70 -69
View File
@@ -49,26 +49,26 @@ void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, flo
// compute sleeve
bool is_looping = (coords[0] == coords[count - 1]);
Vector2 s;
Vector2 segment;
if (!is_looping) // virtual starting point at second point mirrored on first point
s = coords[1] - coords[0];
segment = coords[1] - coords[0];
else // virtual starting point at last vertex
s = coords[0] - coords[count - 2];
segment = coords[0] - coords[count - 2];
float len_s = s.getLength();
Vector2 ns = s.getNormal(halfwidth / len_s);
float segmentLength = segment.getLength();
Vector2 segmentNormal = segment.getNormal(halfwidth / segmentLength);
Vector2 q, r(coords[0]);
Vector2 pointA, pointB(coords[0]);
for (size_t i = 0; i + 1 < count; i++)
{
q = r;
r = coords[i + 1];
renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
pointA = pointB;
pointB = coords[i + 1];
renderEdge(anchors, normals, segment, segmentLength, segmentNormal, pointA, pointB, halfwidth);
}
q = r;
r = is_looping ? coords[1] : r + s;
renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
pointA = pointB;
pointB = is_looping ? coords[1] : pointB + segment;
renderEdge(anchors, normals, segment, segmentLength, segmentNormal, pointA, pointB, halfwidth);
vertex_count = normals.size();
@@ -108,8 +108,8 @@ void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, flo
}
void NoneJoinPolyline::renderEdge(std::vector<Vector2> &anchors, std::vector<Vector2> &normals,
Vector2 &s, float &len_s, Vector2 &ns,
const Vector2 &q, const Vector2 &r, float hw)
Vector2 &segment, float &segmentLength, Vector2 &segmentNormal,
const Vector2 &pointA, const Vector2 &pointB, float halfWidth)
{
// ns1------ns2
// | |
@@ -117,19 +117,19 @@ void NoneJoinPolyline::renderEdge(std::vector<Vector2> &anchors, std::vector<Vec
// | |
// (-ns1)----(-ns2)
anchors.push_back(q);
anchors.push_back(q);
normals.push_back(ns);
normals.push_back(-ns);
anchors.push_back(pointA);
anchors.push_back(pointA);
normals.push_back(segmentNormal);
normals.push_back(-segmentNormal);
s = (r - q);
len_s = s.getLength();
ns = s.getNormal(hw / len_s);
segment = (pointB - pointA);
segmentLength = segment.getLength();
segmentNormal = segment.getNormal(halfWidth / segmentLength);
anchors.push_back(q);
anchors.push_back(q);
normals.push_back(ns);
normals.push_back(-ns);
anchors.push_back(pointA);
anchors.push_back(pointA);
normals.push_back(segmentNormal);
normals.push_back(-segmentNormal);
}
@@ -171,41 +171,41 @@ void NoneJoinPolyline::renderEdge(std::vector<Vector2> &anchors, std::vector<Vec
* the intersection points can be efficiently calculated using Cramer's rule.
*/
void MiterJoinPolyline::renderEdge(std::vector<Vector2> &anchors, std::vector<Vector2> &normals,
Vector2 &s, float &len_s, Vector2 &ns,
const Vector2 &q, const Vector2 &r, float hw)
Vector2 &segment, float &segmentLength, Vector2 &segmentNormal,
const Vector2 &pointA, const Vector2 &pointB, float halfwidth)
{
Vector2 t = (r - q);
float len_t = t.getLength();
if (len_t == 0.0f)
Vector2 newSegment = (pointB - pointA);
float newSegmentLength = newSegment.getLength();
if (newSegmentLength == 0.0f)
{
// degenerate segment, skip it
return;
}
Vector2 nt = t.getNormal(hw / len_t);
Vector2 newSegmentNormal = newSegment.getNormal(halfwidth / newSegmentLength);
anchors.push_back(q);
anchors.push_back(q);
anchors.push_back(pointA);
anchors.push_back(pointA);
float det = Vector2::cross(s, t);
if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && Vector2::dot(s, t) > 0)
float det = Vector2::cross(segment, newSegment);
if (fabs(det) / (segmentLength * newSegmentLength) < LINES_PARALLEL_EPS && Vector2::dot(segment, newSegment) > 0)
{
// lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
normals.push_back(ns);
normals.push_back(-ns);
normals.push_back(segmentNormal);
normals.push_back(-segmentNormal);
}
else
{
// cramers rule
float lambda = Vector2::cross((nt - ns), t) / det;
Vector2 d = ns + s * lambda;
float lambda = Vector2::cross((newSegmentNormal - segmentNormal), newSegment) / det;
Vector2 d = segmentNormal + segment * lambda;
normals.push_back(d);
normals.push_back(-d);
}
s = t;
ns = nt;
len_s = len_t;
segment = newSegment;
segmentNormal = newSegmentNormal;
segmentLength = newSegmentLength;
}
/** Calculate line boundary points.
@@ -226,52 +226,53 @@ void MiterJoinPolyline::renderEdge(std::vector<Vector2> &anchors, std::vector<Ve
* uh1 = q + ns * w/2, uh2 = q + nt * w/2
*/
void BevelJoinPolyline::renderEdge(std::vector<Vector2> &anchors, std::vector<Vector2> &normals,
Vector2 &s, float &len_s, Vector2 &ns,
const Vector2 &q, const Vector2 &r, float hw)
Vector2 &segment, float &segmentLength, Vector2 &segmentNormal,
const Vector2 &pointA, const Vector2 &pointB, float halfWidth)
{
Vector2 t = (r - q);
float len_t = t.getLength();
Vector2 newSegment = (pointB - pointA);
float newSegmentLength = newSegment.getLength();
float det = Vector2::cross(s, t);
if (fabs(det) / (len_s * len_t) < LINES_PARALLEL_EPS && Vector2::dot(s, t) > 0)
float det = Vector2::cross(segment, newSegment);
if (fabs(det) / (segmentLength * newSegmentLength) < LINES_PARALLEL_EPS && Vector2::dot(segment, newSegment) > 0)
{
// lines parallel, compute as u1 = q + ns * w/2, u2 = q - ns * w/2
Vector2 n = t.getNormal(hw / len_t);
anchors.push_back(q);
anchors.push_back(q);
normals.push_back(n);
normals.push_back(-n);
s = t;
len_s = len_t;
Vector2 newSegmentNormal = newSegment.getNormal(halfWidth / newSegmentLength);
anchors.push_back(pointA);
anchors.push_back(pointA);
normals.push_back(newSegmentNormal);
normals.push_back(-newSegmentNormal);
segment = newSegment;
segmentLength = newSegmentLength;
segmentNormal = newSegmentNormal;
return; // early out
}
// cramers rule
Vector2 nt = t.getNormal(hw / len_t);
float lambda = Vector2::cross((nt - ns), t) / det;
Vector2 d = ns + s * lambda;
Vector2 newSegmentNormal = newSegment.getNormal(halfWidth / newSegmentLength);
float lambda = Vector2::cross((newSegmentNormal - segmentNormal), newSegment) / det;
Vector2 d = segmentNormal + segment * lambda;
anchors.push_back(q);
anchors.push_back(q);
anchors.push_back(q);
anchors.push_back(q);
anchors.push_back(pointA);
anchors.push_back(pointA);
anchors.push_back(pointA);
anchors.push_back(pointA);
if (det > 0) // 'left' turn -> intersection on the top
{
normals.push_back(d);
normals.push_back(-ns);
normals.push_back(-segmentNormal);
normals.push_back(d);
normals.push_back(-nt);
normals.push_back(-newSegmentNormal);
}
else
{
normals.push_back(ns);
normals.push_back(segmentNormal);
normals.push_back(-d);
normals.push_back(nt);
normals.push_back(newSegmentNormal);
normals.push_back(-d);
}
s = t;
len_s = len_t;
ns = nt;
segment = newSegment;
segmentLength = newSegmentLength;
segmentNormal = newSegmentNormal;
}
void Polyline::calc_overdraw_vertex_count(bool is_looping)
+10 -10
View File
@@ -77,18 +77,18 @@ protected:
/** Calculate line boundary points.
*
* @param[out] anchors Anchor points defining the core line.
* @param[out] normals Normals defining the edge of the sleeve.
* @param[in,out] s Direction of segment pq (updated to the segment qr).
* @param[in,out] len_s Length of segment pq (updated to the segment qr).
* @param[in,out] ns Normal on the segment pq (updated to the segment qr).
* @param[in] q Current point on the line.
* @param[in] r Next point on the line.
* @param[in] hw Half line width (see Polyline.render()).
* @param[out] anchors Anchor points defining the core line.
* @param[out] normals Normals defining the edge of the sleeve.
* @param[in,out] segment Direction of segment pq (updated to the segment qr).
* @param[in,out] segmentLength Length of segment pq (updated to the segment qr).
* @param[in,out] segmentNormal Normal on the segment pq (updated to the segment qr).
* @param[in] pointA Current point on the line (q).
* @param[in] pointB Next point on the line (r).
* @param[in] halfWidth Half line width (see Polyline.render()).
*/
virtual void renderEdge(std::vector<Vector2> &anchors, std::vector<Vector2> &normals,
Vector2 &s, float &len_s, Vector2 &ns,
const Vector2 &q, const Vector2 &r, float hw) = 0;
Vector2 &segment, float &segmentLength, Vector2 &segmentNormal,
const Vector2 &pointA, const Vector2 &pointB, float halfWidth) = 0;
Vector2 *vertices;
Vector2 *overdraw;
+11 -11
View File
@@ -30,7 +30,7 @@ namespace graphics
love::Type TextBatch::type("TextBatch", &Drawable::type);
TextBatch::TextBatch(Font *font, const std::vector<Font::ColoredString> &text)
TextBatch::TextBatch(Font *font, const std::vector<love::font::ColoredString> &text)
: font(font)
, vertexAttributes(Font::vertexFormat, 0)
, vertexData(nullptr)
@@ -112,13 +112,13 @@ void TextBatch::addTextData(const TextData &t)
std::vector<Font::GlyphVertex> vertices;
std::vector<Font::DrawCommand> newcommands;
Font::TextInfo textinfo;
love::font::TextShaper::TextInfo textinfo;
Colorf constantcolor = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
// We only have formatted text if the align mode is valid.
if (t.align == Font::ALIGN_MAX_ENUM)
newcommands = font->generateVertices(t.codepoints, constantcolor, vertices, 0.0f, Vector2(0.0f, 0.0f), &textinfo);
newcommands = font->generateVertices(t.codepoints, Range(), constantcolor, vertices, 0.0f, Vector2(0.0f, 0.0f), &textinfo);
else
newcommands = font->generateVerticesFormatted(t.codepoints, constantcolor, t.wrap, t.align, vertices, &textinfo);
@@ -172,31 +172,31 @@ void TextBatch::addTextData(const TextData &t)
regenerateVertices();
}
void TextBatch::set(const std::vector<Font::ColoredString> &text)
void TextBatch::set(const std::vector<love::font::ColoredString> &text)
{
return set(text, -1.0f, Font::ALIGN_MAX_ENUM);
}
void TextBatch::set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align)
void TextBatch::set(const std::vector<love::font::ColoredString> &text, float wrap, Font::AlignMode align)
{
if (text.empty() || (text.size() == 1 && text[0].str.empty()))
return clear();
Font::ColoredCodepoints codepoints;
Font::getCodepointsFromString(text, codepoints);
love::font::ColoredCodepoints codepoints;
love::font::getCodepointsFromString(text, codepoints);
addTextData({codepoints, wrap, align, {}, false, false, Matrix4()});
}
int TextBatch::add(const std::vector<Font::ColoredString> &text, const Matrix4 &m)
int TextBatch::add(const std::vector<love::font::ColoredString> &text, const Matrix4 &m)
{
return addf(text, -1.0f, Font::ALIGN_MAX_ENUM, m);
}
int TextBatch::addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m)
int TextBatch::addf(const std::vector<love::font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m)
{
Font::ColoredCodepoints codepoints;
Font::getCodepointsFromString(text, codepoints);
love::font::ColoredCodepoints codepoints;
love::font::getCodepointsFromString(text, codepoints);
addTextData({codepoints, wrap, align, {}, true, true, m});
+7 -7
View File
@@ -40,14 +40,14 @@ public:
static love::Type type;
TextBatch(Font *font, const std::vector<Font::ColoredString> &text = {});
TextBatch(Font *font, const std::vector<love::font::ColoredString> &text = {});
virtual ~TextBatch();
void set(const std::vector<Font::ColoredString> &text);
void set(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align);
void set(const std::vector<love::font::ColoredString> &text);
void set(const std::vector<love::font::ColoredString> &text, float wrap, Font::AlignMode align);
int add(const std::vector<Font::ColoredString> &text, const Matrix4 &m);
int addf(const std::vector<Font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m);
int add(const std::vector<love::font::ColoredString> &text, const Matrix4 &m);
int addf(const std::vector<love::font::ColoredString> &text, float wrap, Font::AlignMode align, const Matrix4 &m);
void clear();
@@ -71,10 +71,10 @@ private:
struct TextData
{
Font::ColoredCodepoints codepoints;
love::font::ColoredCodepoints codepoints;
float wrap;
Font::AlignMode align;
Font::TextInfo textInfo;
love::font::TextShaper::TextInfo textInfo;
bool useMatrix;
bool appendVertices;
Matrix4 matrix;
+1 -1
View File
@@ -207,7 +207,7 @@ private:
void initCapabilities() override;
void getAPIStats(int &shaderswitches) const override;
void endPass();
void endPass(bool presenting);
id<MTLDepthStencilState> getCachedDepthStencilState(const DepthState &depth, const StencilState &stencil);
void applyRenderState(id<MTLRenderCommandEncoder> renderEncoder, const VertexAttributes &attributes);
+7 -7
View File
@@ -983,7 +983,7 @@ bool Graphics::applyShaderUniforms(id<MTLComputeCommandEncoder> encoder, love::g
{
Shader *s = (Shader *)shader;
#ifdef LOVE_MACOS
#if defined(LOVE_MACOS) || TARGET_OS_SIMULATOR || TARGET_OS_MACCATALYST
size_t alignment = 256;
#else
size_t alignment = 16;
@@ -1053,7 +1053,7 @@ void Graphics::applyShaderUniforms(id<MTLRenderCommandEncoder> renderEncoder, lo
{
Shader *s = (Shader *)shader;
#ifdef LOVE_MACOS
#if defined(LOVE_MACOS) || TARGET_OS_SIMULATOR || TARGET_OS_MACCATALYST
size_t alignment = 256;
#else
size_t alignment = 16;
@@ -1358,7 +1358,7 @@ bool Graphics::dispatch(int x, int y, int z)
void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int /*pixelw*/, int /*pixelh*/, bool /*hasSRGBtexture*/)
{ @autoreleasepool {
endPass();
endPass(false);
bool isbackbuffer = rts.getFirstTarget().texture == nullptr;
@@ -1410,7 +1410,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int /*pixelw*/
dirtyRenderState = STATEBIT_ALL;
}}
void Graphics::endPass()
void Graphics::endPass(bool presenting)
{
// Make sure the encoder gets set up, if nothing else has done it yet.
useRenderEncoder();
@@ -1421,9 +1421,9 @@ void Graphics::endPass()
love::graphics::Texture *depthstencil = rts.depthStencil.texture.get();
// Discard the depth/stencil buffer if we're using an internal cached one,
// or if this is the backbuffer.
// or if we're presenting the backbuffer to the display.
if ((depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
|| !rts.getFirstTarget().texture.get())
|| (presenting && !rts.getFirstTarget().texture.get()))
{
attachmentStoreActions.depth = MTLStoreActionDontCare;
attachmentStoreActions.stencil = MTLStoreActionDontCare;
@@ -1562,7 +1562,7 @@ void Graphics::present(void *screenshotCallbackData)
// endPass calls useRenderEncoder, which makes sure activeDrawable is set
// when possible.
endPass();
endPass(true);
id<MTLBuffer> screenshotbuffer = nil;
+10 -7
View File
@@ -758,7 +758,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int pixelw, in
OpenGL::TempDebugGroup debuggroup("setRenderTargets");
endPass();
endPass(false);
bool iswindow = rts.getFirstTarget().texture == nullptr;
Winding vertexwinding = state.winding;
@@ -794,16 +794,18 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int pixelw, in
}
}
void Graphics::endPass()
void Graphics::endPass(bool presenting)
{
auto &rts = states.back().renderTargets;
love::graphics::Texture *depthstencil = rts.depthStencil.texture.get();
// Discard the depth/stencil buffer if we're using an internal cached one.
if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
// Discard the depth/stencil buffer if we're using an internal cached one,
// or if we're presenting the backbuffer to the display.
if ((depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
|| (presenting && !rts.getFirstTarget().texture.get()))
{
discard({}, true);
else if (!rts.getFirstTarget().texture.get())
discard({}, true); // Backbuffer
}
// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
// don't have to worry about resolving to slices.
@@ -1224,7 +1226,8 @@ void Graphics::present(void *screenshotCallbackData)
deprecations.draw(this);
flushBatchedDraws();
endPass();
endPass(true);
int w = getPixelWidth();
int h = getPixelHeight();
+1 -1
View File
@@ -149,7 +149,7 @@ private:
void initCapabilities() override;
void getAPIStats(int &shaderswitches) const override;
void endPass();
void endPass(bool presenting);
GLuint bindCachedFBO(const RenderTargets &targets);
void discard(OpenGL::FramebufferTarget target, const std::vector<bool> &colorbuffers, bool depthstencil);
+1 -1
View File
@@ -155,7 +155,7 @@ bool OpenGL::initContext()
if (getVendor() == VENDOR_AMD)
{
bugs.clearRequiresDriverTextureStateUpdate = true;
if (!gl.isCoreProfile())
if (!gl.isCoreProfile() && !GLAD_ES_VERSION_2_0)
bugs.generateMipmapsRequiresTexture2DEnable = true;
}
#endif
+3 -3
View File
@@ -30,9 +30,9 @@ namespace love
namespace graphics
{
void luax_checkcoloredstring(lua_State *L, int idx, std::vector<Font::ColoredString> &strings)
void luax_checkcoloredstring(lua_State *L, int idx, std::vector<love::font::ColoredString> &strings)
{
Font::ColoredString coloredstr;
love::font::ColoredString coloredstr;
coloredstr.color = Colorf(1.0f, 1.0f, 1.0f, 1.0f);
if (lua_istable(L, idx))
@@ -103,7 +103,7 @@ int w_Font_getWrap(lua_State *L)
{
Font *t = luax_checkfont(L, 1);
std::vector<Font::ColoredString> text;
std::vector<love::font::ColoredString> text;
luax_checkcoloredstring(L, 2, text);
float wrap = (float) luaL_checknumber(L, 3);
+1 -1
View File
@@ -30,7 +30,7 @@ namespace graphics
{
Font *luax_checkfont(lua_State *L, int idx);
void luax_checkcoloredstring(lua_State *L, int idx, std::vector<Font::ColoredString> &strings);
void luax_checkcoloredstring(lua_State *L, int idx, std::vector<love::font::ColoredString> &strings);
extern "C" int luaopen_font(lua_State *L);
} // graphics
+3 -3
View File
@@ -2112,7 +2112,7 @@ int w_newTextBatch(lua_State *L)
luax_catchexcept(L, [&](){ t = instance()->newTextBatch(font); });
else
{
std::vector<Font::ColoredString> text;
std::vector<love::font::ColoredString> text;
luax_checkcoloredstring(L, 2, text);
luax_catchexcept(L, [&](){ t = instance()->newTextBatch(font, text); });
@@ -3132,7 +3132,7 @@ int w_drawShaderVertices(lua_State *L)
int w_print(lua_State *L)
{
std::vector<Font::ColoredString> str;
std::vector<love::font::ColoredString> str;
luax_checkcoloredstring(L, 1, str);
if (luax_istype(L, 2, Font::type))
@@ -3157,7 +3157,7 @@ int w_print(lua_State *L)
int w_printf(lua_State *L)
{
std::vector<Font::ColoredString> str;
std::vector<love::font::ColoredString> str;
luax_checkcoloredstring(L, 1, str);
Font *font = nullptr;
+4 -4
View File
@@ -36,7 +36,7 @@ int w_TextBatch_set(lua_State *L)
{
TextBatch *t = luax_checktextbatch(L, 1);
std::vector<Font::ColoredString> newtext;
std::vector<love::font::ColoredString> newtext;
luax_checkcoloredstring(L, 2, newtext);
luax_catchexcept(L, [&](){ t->set(newtext); });
@@ -54,7 +54,7 @@ int w_TextBatch_setf(lua_State *L)
if (!Font::getConstant(alignstr, align))
return luax_enumerror(L, "align mode", Font::getConstants(align), alignstr);
std::vector<Font::ColoredString> newtext;
std::vector<love::font::ColoredString> newtext;
luax_checkcoloredstring(L, 2, newtext);
luax_catchexcept(L, [&](){ t->set(newtext, wraplimit, align); });
@@ -68,7 +68,7 @@ int w_TextBatch_add(lua_State *L)
int index = 0;
std::vector<Font::ColoredString> text;
std::vector<love::font::ColoredString> text;
luax_checkcoloredstring(L, 2, text);
if (luax_istype(L, 3, math::Transform::type))
@@ -102,7 +102,7 @@ int w_TextBatch_addf(lua_State *L)
int index = 0;
std::vector<Font::ColoredString> text;
std::vector<love::font::ColoredString> text;
luax_checkcoloredstring(L, 2, text);
float wrap = (float) luaL_checknumber(L, 3);