From 880b76983eb168ced3e4c35606a39c68ad5d4742 Mon Sep 17 00:00:00 2001 From: Anthony Nicholls Date: Fri, 13 Dec 2024 14:41:26 +0000 Subject: [PATCH] Graphics: Use cached glyph shaping when only the position of text changes --- .../contexts/juce_GraphicsContext.cpp | 130 ++++++++++-------- 1 file changed, 75 insertions(+), 55 deletions(-) diff --git a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index 056a6f2245..2ce834b0c8 100644 --- a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -55,17 +55,6 @@ static auto operator< (const Justification& a, const Justification& b) //============================================================================== namespace { - struct ConfiguredArrangement - { - void draw (const Graphics& g) const - { - arrangement.draw (g, transform); - } - - GlyphArrangement arrangement; - AffineTransform transform; - }; - template class GlyphArrangementCache final : public DeletedAtShutdown { @@ -81,14 +70,14 @@ namespace [[nodiscard]] auto get (ArrangementArgs&& args, ConfigureArrangement&& configureArrangement) { const ScopedTryLock stl (lock); - return stl.isLocked() ? cache.get (args, std::forward (configureArrangement)) + return stl.isLocked() ? cache.get (std::forward (args), std::forward (configureArrangement)) : configureArrangement (args); } JUCE_DECLARE_SINGLETON_INLINE (GlyphArrangementCache, false) private: - LruCache cache; + LruCache cache; CriticalSection lock; }; @@ -336,36 +325,41 @@ void Graphics::drawSingleLineText (const String& text, const int startX, const i struct ArrangementArgs { - auto tie() const noexcept { return std::tie (font, text, startX, baselineY); } + auto tie() const noexcept { return std::tie (font, text); } bool operator< (const ArrangementArgs& other) const { return tie() < other.tie(); } const Font font; const String text; - const int startX, baselineY, flags; }; auto configureArrangement = [] (const ArrangementArgs& args) { - AffineTransform transform; GlyphArrangement arrangement; - arrangement.addLineOfText (args.font, args.text, (float) args.startX, (float) args.baselineY); - - if (args.flags != Justification::left) - { - auto w = arrangement.getBoundingBox (0, -1, true).getWidth(); - - if ((args.flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0) - w /= 2.0f; - - transform = AffineTransform::translation (-w, 0); - } - - return ConfiguredArrangement { std::move (arrangement), std::move (transform) }; + arrangement.addLineOfText (args.font, args.text, 0.0f, 0.0f); + return arrangement; }; - GlyphArrangementCache::getInstance()->get ({ context.getFont(), text, startX, baselineY, flags }, - std::move (configureArrangement)) - .draw (*this); + using Cache = GlyphArrangementCache; + ArrangementArgs args { context.getFont(), text }; + const auto arrangement = Cache::getInstance()->get (std::move (args), std::move (configureArrangement)); + + const auto transform = std::invoke ([&] + { + const auto t = AffineTransform::translation ((float) startX, + (float) baselineY); + + if (flags == Justification::left) + return t; + + auto w = arrangement.getBoundingBox (0, -1, true).getWidth(); + + if ((flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0) + w /= 2.0f; + + return t.followedBy (AffineTransform::translation (-w, 0)); + }); + + arrangement.draw (*this, transform); } void Graphics::drawMultiLineText (const String& text, const int startX, @@ -377,12 +371,12 @@ void Graphics::drawMultiLineText (const String& text, const int startX, struct ArrangementArgs { - auto tie() const noexcept { return std::tie (font, text, startX, baselineY, maximumLineWidth, justification, leading); } + auto tie() const noexcept { return std::tie (font, text, maximumLineWidth, justification, leading); } bool operator< (const ArrangementArgs& other) const { return tie() < other.tie(); } const Font font; const String text; - const int startX, baselineY, maximumLineWidth; + const int maximumLineWidth; const Justification justification; const float leading; }; @@ -391,14 +385,21 @@ void Graphics::drawMultiLineText (const String& text, const int startX, { GlyphArrangement arrangement; arrangement.addJustifiedText (args.font, args.text, - (float) args.startX, (float) args.baselineY, (float) args.maximumLineWidth, + 0.0f, 0.0f, (float) args.maximumLineWidth, args.justification, args.leading); - return ConfiguredArrangement { std::move (arrangement), {} }; + return arrangement; }; - GlyphArrangementCache::getInstance()->get ({ context.getFont(), text, startX, baselineY, maximumLineWidth, justification, leading }, - std::move (configureArrangement)) - .draw (*this); + ArrangementArgs args { context.getFont(), + text, + maximumLineWidth, + justification, + leading }; + + using Cache = GlyphArrangementCache; + Cache::getInstance()->get (std::move (args), std::move (configureArrangement)) + .draw (*this, AffineTransform::translation ((float) startX, + (float) baselineY)); } void Graphics::drawText (const String& text, Rectangle area, @@ -409,12 +410,13 @@ void Graphics::drawText (const String& text, Rectangle area, struct ArrangementArgs { - auto tie() const noexcept { return std::tie (font, text, area, justificationType, useEllipsesIfTooBig); } + auto tie() const noexcept { return std::tie (font, text, width, height, justificationType, useEllipsesIfTooBig); } bool operator< (const ArrangementArgs& other) const { return tie() < other.tie(); } const Font font; const String text; - const Rectangle area; + const float width; + const float height; const Justification justificationType; const bool useEllipsesIfTooBig; }; @@ -423,17 +425,25 @@ void Graphics::drawText (const String& text, Rectangle area, { GlyphArrangement arrangement; arrangement.addCurtailedLineOfText (args.font, args.text, 0.0f, 0.0f, - args.area.getWidth(), args.useEllipsesIfTooBig); + args.width, args.useEllipsesIfTooBig); arrangement.justifyGlyphs (0, arrangement.getNumGlyphs(), - args.area.getX(), args.area.getY(), args.area.getWidth(), args.area.getHeight(), + 0.0f, 0.0f, + args.width, args.height, args.justificationType); - return ConfiguredArrangement { std::move (arrangement), {} }; + return arrangement; }; - GlyphArrangementCache::getInstance()->get ({ context.getFont(), text, area, justificationType, useEllipsesIfTooBig }, - std::move (configureArrangement)) - .draw (*this); + ArrangementArgs args { context.getFont(), + text, + area.getWidth(), + area.getHeight(), + justificationType, + useEllipsesIfTooBig }; + + using Cache = GlyphArrangementCache; + Cache::getInstance()->get (std::move (args), std::move (configureArrangement)) + .draw (*this, AffineTransform::translation (area.getX(), area.getY())); } void Graphics::drawText (const String& text, Rectangle area, @@ -458,12 +468,13 @@ void Graphics::drawFittedText (const String& text, Rectangle area, struct ArrangementArgs { - auto tie() const noexcept { return std::tie (font, text, area, justification, maximumNumberOfLines, minimumHorizontalScale); } + auto tie() const noexcept { return std::tie (font, text, width, height, justification, maximumNumberOfLines, minimumHorizontalScale); } bool operator< (const ArrangementArgs& other) const noexcept { return tie() < other.tie(); } const Font font; const String text; - const Rectangle area; + const float width; + const float height; const Justification justification; const int maximumNumberOfLines; const float minimumHorizontalScale; @@ -473,17 +484,26 @@ void Graphics::drawFittedText (const String& text, Rectangle area, { GlyphArrangement arrangement; arrangement.addFittedText (args.font, args.text, - args.area.getX(), args.area.getY(), - args.area.getWidth(), args.area.getHeight(), + 0.0f, 0.0f, + args.width, args.height, args.justification, args.maximumNumberOfLines, args.minimumHorizontalScale); - return ConfiguredArrangement { std::move (arrangement), {} }; + return arrangement; }; - GlyphArrangementCache::getInstance()->get ({ context.getFont(), text, area.toFloat(), justification, maximumNumberOfLines, minimumHorizontalScale }, - std::move (configureArrangement)) - .draw (*this); + ArrangementArgs args { context.getFont(), + text, + (float) area.getWidth(), + (float) area.getHeight(), + justification, + maximumNumberOfLines, + minimumHorizontalScale }; + + using Cache = GlyphArrangementCache; + Cache::getInstance()->get (std::move (args), std::move (configureArrangement)) + .draw (*this, AffineTransform::translation ((float) area.getX(), + (float) area.getY())); } void Graphics::drawFittedText (const String& text, int x, int y, int width, int height,