1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00

LowLevelGraphicsContext: Replace glyph drawing functions with single drawGlyphs()

This commit is contained in:
reuk 2024-03-26 13:13:55 +00:00
parent 03b1e918fe
commit 1560f87111
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
11 changed files with 170 additions and 89 deletions

View file

@ -2,6 +2,27 @@
# Version 8.0.0
## Change
The virtual functions LowLevelGraphicsContext::drawGlyph() and drawTextLayout()
have been removed.
**Possible Issues**
Classes overriding these functions will fail to compile.
**Workaround**
Replace drawGlyph() with drawGlyphs(), which draws several glyphs at once.
Remove implementations of drawTextLayout().
**Rationale**
On Windows and macOS, drawing several glyphs at once is faster than drawing
glyphs one-at-a-time. The new API is more general, and allows for more
performant text rendering.
## Change
JUCE widgets now query the LookAndFeel to determine the TypefaceMetricsKind to

View file

@ -105,8 +105,11 @@ public:
virtual void setFont (const Font&) = 0;
virtual const Font& getFont() = 0;
virtual void drawGlyph (int glyphNumber, const AffineTransform&) = 0;
virtual bool drawTextLayout (const AttributedString&, const Rectangle<float>&) { return false; }
/** Uses the current font to draw the provided glyph numbers. */
virtual void drawGlyphs (Span<const uint16_t>,
Span<const Point<float>>,
const AffineTransform&) = 0;
};
} // namespace juce

View file

@ -511,7 +511,7 @@ void LowLevelGraphicsPostScriptRenderer::drawImage (const Image& sourceImage, co
//==============================================================================
void LowLevelGraphicsPostScriptRenderer::drawLine (const Line <float>& line)
void LowLevelGraphicsPostScriptRenderer::drawLine (const Line<float>& line)
{
Path p;
p.addLineSegment (line, 1.0f);
@ -529,12 +529,24 @@ const Font& LowLevelGraphicsPostScriptRenderer::getFont()
return stateStack.getLast()->font;
}
void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform)
void LowLevelGraphicsPostScriptRenderer::drawGlyphs (Span<const uint16_t> glyphs,
Span<const Point<float>> positions,
const AffineTransform& transform)
{
Path p;
Font& font = stateStack.getLast()->font;
font.getTypefacePtr()->getOutlineForGlyph (font.getMetricsKind(), glyphNumber, p);
fillPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()).followedBy (transform));
jassert (glyphs.size() == positions.size());
const auto& font = stateStack.getLast()->font;
for (const auto [index, glyph] : enumerate (glyphs, size_t{}))
{
Path p;
font.getTypefacePtr()->getOutlineForGlyph (font.getMetricsKind(), glyph, p);
const auto fullTransform = AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight())
.translated (positions[index])
.followedBy (transform);
fillPath (p, fullTransform);
}
}
} // namespace juce

View file

@ -89,7 +89,9 @@ public:
//==============================================================================
const Font& getFont() override;
void setFont (const Font&) override;
void drawGlyph (int glyphNumber, const AffineTransform&) override;
void drawGlyphs (Span<const uint16_t> glyphs,
Span<const Point<float>> positions,
const AffineTransform&) override;
protected:
//==============================================================================

View file

@ -279,12 +279,9 @@ void AttributedString::draw (Graphics& g, const Rectangle<float>& area) const
{
jassert (text.length() == getLength (attributes));
if (! g.getInternalContext().drawTextLayout (*this, area))
{
TextLayout layout;
layout.createLayout (*this, area.getWidth());
layout.draw (g, area);
}
TextLayout layout;
layout.createLayout (*this, area.getWidth());
layout.draw (g, area);
}
}

View file

@ -55,23 +55,21 @@ PositionedGlyph::PositionedGlyph (const Font& font_, juce_wchar character_, int
{
}
static void drawGlyphWithFont (Graphics& g, int glyph, const Font& font, AffineTransform t)
{
auto& context = g.getInternalContext();
context.setFont (font);
context.drawGlyph (glyph, t);
}
void PositionedGlyph::draw (Graphics& g) const
{
if (! isWhitespace())
drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y));
draw (g, {});
}
void PositionedGlyph::draw (Graphics& g, AffineTransform transform) const
{
if (! isWhitespace())
drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y).followedBy (transform));
if (isWhitespace())
return;
auto& context = g.getInternalContext();
context.setFont (font);
const uint16_t glyphs[] { static_cast<uint16_t> (glyph) };
const Point<float> positions[] { Point { x, y } };
context.drawGlyphs (glyphs, positions, transform);
}
void PositionedGlyph::createPath (Path& path) const
@ -698,10 +696,17 @@ void GlyphArrangement::splitLines (const String& text, Font font, int startIndex
}
//==============================================================================
void GlyphArrangement::drawGlyphUnderline (const Graphics& g, const PositionedGlyph& pg,
int i, AffineTransform transform) const
void GlyphArrangement::drawGlyphUnderline (const Graphics& g,
int i,
AffineTransform transform) const
{
auto lineThickness = (pg.font.getDescent()) * 0.3f;
const auto pg = glyphs.getReference (i);
if (! pg.font.isUnderlined())
return;
const auto lineThickness = (pg.font.getDescent()) * 0.3f;
auto nextX = pg.x + pg.w;
if (i < glyphs.size() - 1 && approximatelyEqual (glyphs.getReference (i + 1).y, pg.y))
@ -719,39 +724,42 @@ void GlyphArrangement::draw (const Graphics& g) const
void GlyphArrangement::draw (const Graphics& g, AffineTransform transform) const
{
auto& context = g.getInternalContext();
auto lastFont = context.getFont();
bool needToRestore = false;
std::vector<uint16_t> glyphNumbers;
std::vector<Point<float>> positions;
for (int i = 0; i < glyphs.size(); ++i)
glyphNumbers.reserve (static_cast<size_t> (glyphs.size()));
positions.reserve (static_cast<size_t> (glyphs.size()));
for (auto it = glyphs.begin(), end = glyphs.end(); it != end;)
{
auto& pg = glyphs.getReference (i);
if (pg.font.isUnderlined())
drawGlyphUnderline (g, pg, i, transform);
if (! pg.isWhitespace())
const auto adjacent = std::adjacent_find (it, end, [] (const auto& a, const auto& b)
{
if (lastFont != pg.font)
{
lastFont = pg.font;
return a.font != b.font;
});
if (! needToRestore)
{
needToRestore = true;
context.saveState();
}
const auto next = adjacent + (adjacent == end ? 0 : 1);
context.setFont (lastFont);
}
glyphNumbers.clear();
std::transform (it, next, std::back_inserter (glyphNumbers), [] (const PositionedGlyph& x)
{
return (uint16_t) x.glyph;
});
context.drawGlyph (pg.glyph, AffineTransform::translation (pg.x, pg.y)
.followedBy (transform));
}
positions.clear();
std::transform (it, next, std::back_inserter (positions), [] (const PositionedGlyph& x)
{
return Point { x.x, x.y };
});
auto& ctx = g.getInternalContext();
ctx.setFont (it->font);
ctx.drawGlyphs (glyphNumbers, positions, transform);
it = next;
}
if (needToRestore)
context.restoreState();
for (const auto pair : enumerate (glyphs))
drawGlyphUnderline (g, static_cast<int> (pair.index), transform);
}
void GlyphArrangement::createPath (Path& path) const

View file

@ -322,7 +322,7 @@ private:
void splitLines (const String&, Font, int start, float x, float y, float w, float h, int maxLines,
float lineWidth, Justification, float minimumHorizontalScale);
void addLinesWithLineBreaks (const String&, const Font&, float x, float y, float width, float height, Justification);
void drawGlyphUnderline (const Graphics&, const PositionedGlyph&, int, AffineTransform) const;
void drawGlyphUnderline (const Graphics&, int, AffineTransform) const;
JUCE_LEAK_DETECTOR (GlyphArrangement)
};

View file

@ -214,6 +214,9 @@ void TextLayout::draw (Graphics& g, Rectangle<float> area) const
auto clipTop = (float) clip.getY() - origin.y;
auto clipBottom = (float) clip.getBottom() - origin.y;
std::vector<uint16_t> glyphNumbers;
std::vector<Point<float>> positions;
for (auto& line : *this)
{
auto lineRangeY = line.getLineBoundsY();
@ -231,17 +234,31 @@ void TextLayout::draw (Graphics& g, Rectangle<float> area) const
context.setFont (run->font);
context.setFill (run->colour);
for (auto& glyph : run->glyphs)
context.drawGlyph (glyph.glyphCode, AffineTransform::translation (lineOrigin.x + glyph.anchor.x,
lineOrigin.y + glyph.anchor.y));
const auto& glyphs = run->glyphs;
glyphNumbers.resize ((size_t) glyphs.size());
std::transform (glyphs.begin(), glyphs.end(), glyphNumbers.begin(), [&] (const Glyph& x)
{
return (uint16_t) x.glyphCode;
});
positions.resize ((size_t) glyphs.size());
std::transform (glyphs.begin(), glyphs.end(), positions.begin(), [&] (const Glyph& x)
{
return x.anchor;
});
context.drawGlyphs (glyphNumbers, positions, AffineTransform::translation (lineOrigin));
if (run->font.isUnderlined())
{
auto runExtent = run->getRunBoundsX();
auto lineThickness = run->font.getDescent() * 0.3f;
const auto runExtent = run->getRunBoundsX();
const auto lineThickness = run->font.getDescent() * 0.3f;
context.fillRect ({ runExtent.getStart() + lineOrigin.x, lineOrigin.y + lineThickness * 2.0f,
runExtent.getLength(), lineThickness });
context.fillRect ({ runExtent.getStart() + lineOrigin.x,
lineOrigin.y + lineThickness * 2.0f,
runExtent.getLength(),
lineThickness });
}
}
}

View file

@ -121,7 +121,9 @@ public:
void drawLine (const Line<float>&) override;
void setFont (const Font&) override;
const Font& getFont() override;
void drawGlyph (int glyphNumber, const AffineTransform& transform) override;
void drawGlyphs (Span<const uint16_t>,
Span<const Point<float>>,
const AffineTransform&) override;
private:
//==============================================================================

View file

@ -659,17 +659,6 @@ void CoreGraphicsContext::setFont (const Font& newFont)
{
state->font = newFont;
state->fontRef = nullptr;
const auto hbFont = state->font.getNativeDetails().font;
state->fontRef.reset (hb_coretext_font_get_ct_font (hbFont.get()));
CFRetain (state->fontRef.get());
const auto slant = hb_font_get_synthetic_slant (hbFont.get());
state->textMatrix = CGAffineTransformMake (state->font.getHorizontalScale(), 0, slant * state->font.getHorizontalScale(), 1.0f, 0, 0);
CGContextSetTextMatrix (context.get(), state->textMatrix);
state->inverseTextMatrix = CGAffineTransformInvert (state->textMatrix);
}
}
@ -678,28 +667,48 @@ const Font& CoreGraphicsContext::getFont()
return state->font;
}
void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& transform)
void CoreGraphicsContext::drawGlyphs (Span<const uint16_t> glyphs,
Span<const Point<float>> positions,
const AffineTransform& transform)
{
if (state->fontRef != nullptr && state->fillType.isColour())
jassert (glyphs.size() == positions.size());
if (state->fillType.isColour())
{
const CGGlyph glyphs[] { (CGGlyph) glyphNumber };
const auto scale = state->font.getHorizontalScale();
if (state->fontRef == nullptr)
{
const auto hbFont = state->font.getNativeDetails().font;
state->fontRef.reset (hb_coretext_font_get_ct_font (hbFont.get()));
CFRetain (state->fontRef.get());
const auto slant = hb_font_get_synthetic_slant (hbFont.get());
state->textMatrix = CGAffineTransformMake (scale, 0, slant * scale, 1.0f, 0, 0);
CGContextSetTextMatrix (context.get(), state->textMatrix);
state->inverseTextMatrix = CGAffineTransformInvert (state->textMatrix);
}
ScopedCGContextState scopedState (context.get());
flip();
applyTransform (AffineTransform::scale (1.0f, -1.0f).followedBy (transform));
const CGPoint positions[] { { 0.0f, 0.0f } };
CTFontDrawGlyphs (state->fontRef.get(), glyphs, positions, std::size (glyphs), context.get());
std::vector<CGPoint> pos (glyphs.size());
std::transform (positions.begin(), positions.end(), pos.begin(), [scale] (const auto& p) { return CGPointMake (p.x / scale, -p.y); });
CTFontDrawGlyphs (state->fontRef.get(), glyphs.data(), pos.data(), glyphs.size(), context.get());
}
else
{
Path p;
auto& f = state->font;
f.getTypefacePtr()->getOutlineForGlyph (f.getMetricsKind(), glyphNumber, p);
const auto scale = f.getHeight();
fillPath (p, AffineTransform::scale (scale * f.getHorizontalScale(), scale).followedBy (transform));
for (const auto [index, glyph] : enumerate (glyphs, size_t{}))
{
Path p;
auto& f = state->font;
f.getTypefacePtr()->getOutlineForGlyph (f.getMetricsKind(), glyph, p);
const auto scale = f.getHeight();
fillPath (p, AffineTransform::scale (scale * f.getHorizontalScale(), scale).translated (positions[index]).followedBy (transform));
}
}
}

View file

@ -2596,7 +2596,18 @@ public:
void setFont (const Font& newFont) override { stack->font = newFont; }
const Font& getFont() override { return stack->font; }
void drawGlyph (int i, const AffineTransform& t) override
void drawGlyphs (Span<const uint16_t> glyphs,
Span<const Point<float>> positions,
const AffineTransform& t) override
{
jassert (glyphs.size() == positions.size());
for (const auto [index, glyph] : enumerate (glyphs))
drawGlyph (glyph, AffineTransform::translation (positions[(size_t) index]).followedBy (t));
}
protected:
void drawGlyph (uint16_t i, const AffineTransform& t)
{
if (stack->clip == nullptr)
return;
@ -2660,8 +2671,7 @@ public:
}
}
protected:
StackBasedLowLevelGraphicsContext (SavedStateType* initialState) : stack (initialState) {}
explicit StackBasedLowLevelGraphicsContext (SavedStateType* initialState) : stack (initialState) {}
StackBasedLowLevelGraphicsContext() = default;
RenderingHelpers::SavedStateStack<SavedStateType> stack;