sw: save more vcl layout calls in SwFntObj

This builds on top of commit 436b829f5b904d76039db0818cff5dedf1ae89f1
(sw: save one vcl layout call in SwFntObj::DrawText(), 2018-08-16), but
now layouts are shared not only inside SwFntObj::DrawText(), but also
between SwFntObj::GetTextSize() and SwFntObj::DrawText().

To get there, create an SwFntObj cache member that stores already
calculated vcl layouts. SwFntObj already derives from SwCacheObj, so no
need to explicitly expire this cache member.

Total number of GenericSalLayout::LayoutText() invocations go down from
8 to 5 with this when pressing a key in Writer.

Change-Id: Ifbe84a5c00025604f9c1331b56074b3dc27864b5
Reviewed-on: https://gerrit.libreoffice.org/59879
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.co.uk>
diff --git a/sw/source/core/inc/fntcache.hxx b/sw/source/core/inc/fntcache.hxx
index 9edfd6d..9bf8024 100644
--- a/sw/source/core/inc/fntcache.hxx
+++ b/sw/source/core/inc/fntcache.hxx
@@ -20,8 +20,11 @@
#ifndef INCLUDED_SW_SOURCE_CORE_INC_FNTCACHE_HXX
#define INCLUDED_SW_SOURCE_CORE_INC_FNTCACHE_HXX

#include <map>

#include <vcl/font.hxx>
#include <vcl/vclptr.hxx>
#include <vcl/vcllayout.hxx>
#include <swtypes.hxx>
#include "swcache.hxx"
#include "TextFrameIndex.hxx"
@@ -54,6 +57,20 @@
extern SwFntObj *pLastFont;
extern sal_uInt8 *pMagicNo;

/**
 * Defines a substring on a given output device, to be used as an std::map<>
 * key.
 */
struct SwTextGlyphsKey
{
    VclPtr<OutputDevice> m_pOutputDevice;
    OUString m_aText;
    sal_Int32 m_nIndex;
    sal_Int32 m_nLength;

};
bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r);

class SwFntObj : public SwCacheObj
{
    friend class SwFntAccess;
@@ -75,6 +92,9 @@
    bool m_bSymbol : 1;
    bool m_bPaintBlank : 1;

    /// Cache of already calculated layout glyphs.
    std::map<SwTextGlyphsKey, SalLayoutGlyphs> m_aTextGlyphs;

    static long nPixWidth;
    static MapMode *pPixMap;

diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx
index 49b1224..2845d22 100644
--- a/sw/source/core/txtnode/fntcache.cxx
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -98,23 +98,23 @@
}

/**
 * Pre-calculates glyph items for the rendered subset of rInf's text, assuming
 * Pre-calculates glyph items for the rendered subset of rKey's text, assuming
 * outdev state does not change between the outdev calls.
 */
SalLayoutGlyphs* lcl_CreateLayout(SwDrawTextInfo& rInf, const OUString& rText, sal_Int32 nIdx,
                                  sal_Int32 nLen, SalLayoutGlyphs& rTextGlyphs)
SalLayoutGlyphs* lcl_CreateLayout(SwTextGlyphsKey& rKey, SalLayoutGlyphs& rTextGlyphs)
{
    // Not the string we want to pre-calculate.
    if (rText != rInf.GetText() || nIdx != rInf.GetIdx() || nLen != rInf.GetLen())
        return nullptr;

    // Use pre-calculated result.
    if (!rTextGlyphs.empty())
        return &rTextGlyphs;

    if (rKey.m_nIndex >= rKey.m_aText.getLength())
        // Same as in OutputDevice::GetTextArray().
        return nullptr;

    // Calculate glyph items.
    std::unique_ptr<SalLayout> pLayout = rInf.GetOut().ImplLayout(
        rText, nIdx, nLen, Point(0, 0), 0, nullptr, SalLayoutFlags::GlyphItemsOnly);
    std::unique_ptr<SalLayout> pLayout
        = rKey.m_pOutputDevice->ImplLayout(rKey.m_aText, rKey.m_nIndex, rKey.m_nLength, Point(0, 0), 0,
                                         nullptr, SalLayoutFlags::GlyphItemsOnly);
    if (!pLayout)
        return nullptr;

@@ -129,6 +129,27 @@
}
}

bool operator<(const SwTextGlyphsKey& l, const SwTextGlyphsKey& r)
{
    if (l.m_pOutputDevice.get() < r.m_pOutputDevice.get())
        return true;
    if (l.m_pOutputDevice.get() > r.m_pOutputDevice.get())
        return false;
    if (l.m_aText < r.m_aText)
        return true;
    if (l.m_aText > r.m_aText)
        return false;
    if (l.m_nIndex < r.m_nIndex)
        return true;
    if (l.m_nIndex > r.m_nIndex)
        return false;
    if (l.m_nLength < r.m_nLength)
        return true;
    if (l.m_nLength > r.m_nLength)
        return false;
    return false;
};

void SwFntCache::Flush( )
{
    if ( pLastFont )
@@ -810,8 +831,6 @@

    vcl::Font* pTmpFont = bUseScrFont ? m_pScrFont : m_pPrtFont;

    SalLayoutGlyphs aTextGlyphs;

    //  bDirectPrint and bUseScrFont should have these values:

    //  Outdev / RefDef  | Printer | VirtPrinter | Window
@@ -1426,8 +1445,8 @@

        // get screen array
        std::unique_ptr<long[]> pScrArray(new long[sal_Int32(rInf.GetLen())]);
        SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(rInf, rInf.GetText(), sal_Int32(rInf.GetIdx()),
                                                    sal_Int32(rInf.GetLen()), aTextGlyphs);
        SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), rInf.GetIdx(), rInf.GetLen() };
        SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]);
        rInf.GetOut().GetTextArray( rInf.GetText(), pScrArray.get(),
                        sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs);

@@ -1441,8 +1460,10 @@
                if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) )
                    m_pPrinter->SetFont( *m_pPrtFont );
            }
            aGlyphsKey = SwTextGlyphsKey{ m_pPrinter, rInf.GetText(), rInf.GetIdx(), rInf.GetLen() };
            pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]);
            m_pPrinter->GetTextArray(rInf.GetText(), pKernArray.get(),
                    sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
                    sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()), nullptr, pGlyphs);
        }
        else
        {
@@ -1778,7 +1799,8 @@
                sal_Int32 nTmpIdx = bBullet
                            ? (rInf.GetIdx() ? 1 : 0)
                            : sal_Int32(rInf.GetIdx());
                pGlyphs = lcl_CreateLayout(rInf, *pStr, nTmpIdx, nLen, aTextGlyphs);
                aGlyphsKey = SwTextGlyphsKey{ &rInf.GetOut(), *pStr, nTmpIdx, nLen };
                pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]);
                rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, pKernArray.get(),
                                             nTmpIdx , nLen, SalLayoutFlags::NONE, nullptr, pGlyphs );
                if (bBullet)
@@ -2012,9 +2034,11 @@
        }
        else
        {
            SwTextGlyphsKey aGlyphsKey{ &rInf.GetOut(), rInf.GetText(), rInf.GetIdx(), nLn };
            SalLayoutGlyphs* pGlyphs = lcl_CreateLayout(aGlyphsKey, m_aTextGlyphs[aGlyphsKey]);
            aTextSize.setWidth( rInf.GetOut().GetTextWidth( rInf.GetText(),
                                   sal_Int32(rInf.GetIdx()), sal_Int32(nLn),
                                                           rInf.GetVclCache()) );
                                                           rInf.GetVclCache(), pGlyphs) );
            rInf.SetKanaDiff( 0 );
        }