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:
parent
03b1e918fe
commit
1560f87111
11 changed files with 170 additions and 89 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue