[Libreoffice-commits] core.git: Branch 'libreoffice-5-1' - vcl/inc vcl/Library_vcl.mk vcl/opengl vcl/win

Tomaž Vajngerl tomaz.vajngerl at collabora.co.uk
Fri Apr 8 14:47:32 UTC 2016


 vcl/Library_vcl.mk                     |    1 
 vcl/inc/opengl/AccumulatedTextures.hxx |  113 +++++++++
 vcl/inc/opengl/FixedTextureAtlas.hxx   |    1 
 vcl/inc/opengl/PackedTextureAtlas.hxx  |   46 +++
 vcl/inc/opengl/texture.hxx             |   36 ---
 vcl/inc/openglgdiimpl.hxx              |    8 
 vcl/inc/win/salgdi.h                   |    5 
 vcl/opengl/FixedTextureAtlas.cxx       |   20 +
 vcl/opengl/PackedTextureAtlas.cxx      |  164 +++++++++++++
 vcl/opengl/gdiimpl.cxx                 |  114 +++++++++
 vcl/opengl/texture.cxx                 |  113 ++++++++-
 vcl/win/source/gdi/salgdi.cxx          |    8 
 vcl/win/source/gdi/winlayout.cxx       |  394 +++++++++++----------------------
 13 files changed, 729 insertions(+), 294 deletions(-)

New commits:
commit bd1bfeff238705fa89b4912865aa7cc6c4c1857f
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
Date:   Fri Apr 8 19:17:41 2016 +0900

    tdf#94682 optimize texture drawing on Win. (squashed multi commits)
    
    Includes commits from master:
    
    opengl: deferred and optimized (text) texture drawing
    96a098c0e8a009b77a26061dac3318da71d34ee4
    
    opengl: texture atlas impl. to efficiently packs textures
    40e9ed91bd8bbfecfc3832d73a81741d0aa97d3a
    
    opengl: use packed texture atlas for glyph cache in win. backend
    80d0b2916db81a7f47bb1d368677016bbb870df6
    
    opengl: fix wrong clipping when drawing text
    094faaae6982472375420e57d6b9e34eefdbced8
    
    opengl: cleanup texture, const internal format
    f65e77c965bb47d53c994d90b7fd0bf5009b343b
    
    Change-Id: I31ecd891d1d69e94973673930b0606e1ac884aab
    Reviewed-on: https://gerrit.libreoffice.org/23914
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>
    Tested-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 5b5c969..7f0b95d 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -135,6 +135,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
 	vcl/opengl/program \
 	vcl/opengl/texture \
 	vcl/opengl/FixedTextureAtlas \
+    vcl/opengl/PackedTextureAtlas \
     vcl/source/opengl/OpenGLContext \
     vcl/source/opengl/OpenGLHelper \
     vcl/source/window/cairo_cairo \
diff --git a/vcl/inc/opengl/AccumulatedTextures.hxx b/vcl/inc/opengl/AccumulatedTextures.hxx
new file mode 100644
index 0000000..e74c065
--- /dev/null
+++ b/vcl/inc/opengl/AccumulatedTextures.hxx
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#ifndef INCLUDED_VCL_INC_OPENGL_ACCUMULATEDTEXTURES_H
+#define INCLUDED_VCL_INC_OPENGL_ACCUMULATEDTEXTURES_H
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+#include <o3tl/make_unique.hxx>
+#include "opengl/texture.hxx"
+#include <memory>
+
+struct TextureDrawParameters
+{
+    std::vector<GLfloat> maVertices;
+    std::vector<GLfloat> maTextureCoords;
+    GLint getNumberOfVertices()
+    {
+        return maVertices.size() / 2;
+    }
+};
+
+struct AccumulatedTexturesEntry
+{
+    OpenGLTexture maTexture;
+    std::unordered_map<SalColor, TextureDrawParameters> maColorTextureDrawParametersMap;
+
+    AccumulatedTexturesEntry(const OpenGLTexture& rTexture)
+        : maTexture(rTexture)
+    {}
+
+    void insert(const OpenGLTexture& rTexture, const SalColor& aColor, const SalTwoRect& r2Rect)
+    {
+        TextureDrawParameters& aDrawParameters = maColorTextureDrawParametersMap[aColor];
+        rTexture.FillCoords<GL_TRIANGLES>(aDrawParameters.maTextureCoords, r2Rect, false);
+
+        GLfloat nX1 = r2Rect.mnDestX;
+        GLfloat nY1 = r2Rect.mnDestY;
+        GLfloat nX2 = r2Rect.mnDestX + r2Rect.mnDestWidth;
+        GLfloat nY2 = r2Rect.mnDestY + r2Rect.mnDestHeight;
+
+        auto& rVertices = aDrawParameters.maVertices;
+        rVertices.push_back(nX1);
+        rVertices.push_back(nY1);
+
+        rVertices.push_back(nX2);
+        rVertices.push_back(nY1);
+
+        rVertices.push_back(nX1);
+        rVertices.push_back(nY2);
+
+        rVertices.push_back(nX1);
+        rVertices.push_back(nY2);
+
+        rVertices.push_back(nX2);
+        rVertices.push_back(nY1);
+
+        rVertices.push_back(nX2);
+        rVertices.push_back(nY2);
+    }
+};
+
+class AccumulatedTextures
+{
+private:
+    typedef std::unordered_map<GLuint, std::unique_ptr<AccumulatedTexturesEntry>> AccumulatedTexturesMap;
+
+    AccumulatedTexturesMap maEntries;
+
+public:
+    AccumulatedTextures()
+    {}
+
+    bool empty()
+    {
+        return maEntries.empty();
+    }
+
+    void clear()
+    {
+        maEntries.clear();
+    }
+
+    void insert(OpenGLTexture& rTexture, const SalColor& aColor, const SalTwoRect& r2Rect)
+    {
+        GLuint nTextureId = rTexture.Id();
+
+        if (maEntries.find(nTextureId) == maEntries.end())
+        {
+            OpenGLTexture aWholeTexture(rTexture.GetWholeTexture());
+            maEntries[nTextureId] = o3tl::make_unique<AccumulatedTexturesEntry>(aWholeTexture);
+        }
+
+        std::unique_ptr<AccumulatedTexturesEntry>& rEntry = maEntries[nTextureId];
+        rEntry->insert(rTexture, aColor, r2Rect);
+    }
+
+    AccumulatedTexturesMap& getAccumulatedTexturesMap()
+    {
+        return maEntries;
+    }
+};
+
+#endif // INCLUDED_VCL_INC_OPENGL_TEXTURE_H
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/opengl/FixedTextureAtlas.hxx b/vcl/inc/opengl/FixedTextureAtlas.hxx
index 5b22b619..8d104a7 100644
--- a/vcl/inc/opengl/FixedTextureAtlas.hxx
+++ b/vcl/inc/opengl/FixedTextureAtlas.hxx
@@ -28,6 +28,7 @@ public:
     FixedTextureAtlasManager(int nWidthFactor, int nHeightFactor, int nTextureSize);
     ~FixedTextureAtlasManager();
     OpenGLTexture InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData);
+    OpenGLTexture Reserve(int nWidth, int nHeight);
 
     int GetSubtextureSize()
     {
diff --git a/vcl/inc/opengl/PackedTextureAtlas.hxx b/vcl/inc/opengl/PackedTextureAtlas.hxx
new file mode 100644
index 0000000..17501f3
--- /dev/null
+++ b/vcl/inc/opengl/PackedTextureAtlas.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#ifndef INCLUDED_VCL_INC_OPENGL_PACKEDTEXTUREATLAS_HXX
+#define INCLUDED_VCL_INC_OPENGL_PACKEDTEXTUREATLAS_HXX
+
+#include "opengl/texture.hxx"
+
+struct PackedTexture;
+
+/**
+ * Pack texutres into one texutre atlas.
+ *
+ * This is based on algorithm described in [1] and is an
+ * addaptation of "texture atlas generator" from [2].
+ *
+ * [1]: http://www.blackpawn.com/texts/lightmaps/
+ * [2]: https://github.com/lukaszdk/texture-atlas-generator
+ *
+ */
+class VCL_PLUGIN_PUBLIC PackedTextureAtlasManager
+{
+    std::vector<std::unique_ptr<PackedTexture>> maPackedTextures;
+
+    int mnTextureWidth;
+    int mnTextureHeight;
+
+    void CreateNewTexture();
+
+public:
+    PackedTextureAtlasManager(int nTextureWidth, int nTextureHeight);
+    ~PackedTextureAtlasManager();
+    OpenGLTexture InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData);
+    OpenGLTexture Reserve(int nWidth, int nHeight);
+};
+
+#endif // INCLUDED_VCL_INC_OPENGL_PACKEDTEXTUREATLAS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/opengl/texture.hxx b/vcl/inc/opengl/texture.hxx
index 9388918..6afc176 100644
--- a/vcl/inc/opengl/texture.hxx
+++ b/vcl/inc/opengl/texture.hxx
@@ -51,30 +51,8 @@ public:
 
     bool InsertBuffer(int nX, int nY, int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData);
 
-    void IncreaseRefCount(int nSlotNumber)
-    {
-        mnRefCount++;
-        if (mpSlotReferences && nSlotNumber >= 0)
-        {
-            if (mpSlotReferences->at(nSlotNumber) == 0)
-                mnFreeSlots--;
-            mpSlotReferences->at(nSlotNumber)++;
-        }
-    }
-
-    void DecreaseRefCount(int nSlotNumber)
-    {
-        mnRefCount--;
-        if (mpSlotReferences && nSlotNumber >= 0)
-        {
-            mpSlotReferences->at(nSlotNumber)--;
-            if (mpSlotReferences->at(nSlotNumber) == 0)
-                mnFreeSlots++;
-        }
-
-        if (mnRefCount <= 0)
-            delete this;
-    }
+    void IncreaseRefCount(int nSlotNumber);
+    void DecreaseRefCount(int nSlotNumber);
 
     bool IsUnique()
     {
@@ -97,7 +75,7 @@ private:
 
 public:
                     OpenGLTexture();
-                    OpenGLTexture(ImplOpenGLTexture* pImpl, Rectangle aRectangle, int nSlotNumber = 0);
+                    OpenGLTexture(ImplOpenGLTexture* pImpl, Rectangle aRectangle, int nSlotNumber);
 
                     OpenGLTexture( int nWidth, int nHeight, bool bAllocate = true );
                     OpenGLTexture( int nWidth, int nHeight, int nFormat, int nType, void const * pData );
@@ -111,9 +89,10 @@ public:
     GLuint          Id() const;
     int             GetWidth() const;
     int             GetHeight() const;
+
     void            GetCoord( GLfloat* pCoord, const SalTwoRect& rPosAry, bool bInverted=false ) const;
     void            GetWholeCoord( GLfloat* pCoord ) const;
-
+    OpenGLTexture   GetWholeTexture();
     void            Bind();
     void            Unbind();
     void            Read( GLenum nFormat, GLenum nType, sal_uInt8* pData );
@@ -121,6 +100,8 @@ public:
     bool            HasStencil() const;
     GLuint          StencilId() const;
 
+    bool            CopyData(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData);
+
     void            SaveToFile(const OUString& rFileName);
 
     GLenum          GetFilter() const;
@@ -130,6 +111,9 @@ public:
     OpenGLTexture&  operator=( const OpenGLTexture& rTexture );
     bool            operator==( const OpenGLTexture& rTexture ) const;
     bool            operator!=( const OpenGLTexture& rTexture ) const;
+
+    template<GLenum type>
+    void FillCoords(std::vector<GLfloat>& aCoordVector, const SalTwoRect& rPosAry, bool bInverted) const;
 };
 
 #endif // INCLUDED_VCL_INC_OPENGL_TEXTURE_H
diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx
index ca8232a..6dfa73c 100644
--- a/vcl/inc/openglgdiimpl.hxx
+++ b/vcl/inc/openglgdiimpl.hxx
@@ -28,6 +28,7 @@
 #include "opengl/program.hxx"
 #include "opengl/texture.hxx"
 #include "regionband.hxx"
+#include "opengl/AccumulatedTextures.hxx"
 
 #include <vcl/opengl/OpenGLContext.hxx>
 
@@ -99,6 +100,8 @@ protected:
     SalColor mProgramSolidColor;
     double mProgramSolidTransparency;
 
+    std::unique_ptr<AccumulatedTextures> mpAccumulatedTextures;
+
     void ImplInitClipRegion();
     void ImplSetClipBit( const vcl::Region& rClip, GLuint nMask );
     void ImplDrawLineAA( double nX1, double nY1, double nX2, double nY2, bool edge = false );
@@ -144,6 +147,8 @@ public:
     void DrawLinearGradient( const Gradient& rGradient, const Rectangle& rRect );
     void DrawAxialGradient( const Gradient& rGradient, const Rectangle& rRect );
     void DrawRadialGradient( const Gradient& rGradient, const Rectangle& rRect );
+    void DeferredTextDraw(OpenGLTexture& rTexture, const SalColor nMaskColor, const SalTwoRect& rPosAry);
+    void FlushDeferredDrawing();
 
 public:
     // get the width of the device
@@ -164,6 +169,9 @@ public:
     // operations to do before painting
     void PreDraw(XOROption eOpt = IGNORE_XOR);
 
+    // initialize pre-draw state
+    void InitializePreDrawState(XOROption eOpt = IGNORE_XOR);
+
     // operations to do after painting
     void PostDraw();
 
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index 085f77e..30b1438 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -173,11 +173,16 @@ public:
 
     SalTwoRect getTwoRect() { return maRects; }
 
+    Size getBitmapSize() { return Size(maRects.mnSrcWidth, maRects.mnSrcHeight); }
+
     /// Reset the DC with the defined color.
     void fill(sal_uInt32 color);
 
     /// Obtain the texture; the caller must delete it after use.
     OpenGLTexture* getTexture();
+
+    /// Copy bitmap data to the texture. Texutre must be initialized and the correct size to hold the bitmap.
+    bool copyToTexture(OpenGLTexture& aTexture);
 };
 
 class WinSalGraphics : public SalGraphics
diff --git a/vcl/opengl/FixedTextureAtlas.cxx b/vcl/opengl/FixedTextureAtlas.cxx
index 80c1cfe..1ed8311 100644
--- a/vcl/opengl/FixedTextureAtlas.cxx
+++ b/vcl/opengl/FixedTextureAtlas.cxx
@@ -42,7 +42,7 @@ void FixedTextureAtlasManager::CreateNewTexture()
     mpTextures.back()->InitializeSlots(mWidthFactor * mHeightFactor);
 }
 
-OpenGLTexture FixedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
+OpenGLTexture FixedTextureAtlasManager::Reserve(int nWidth, int nHeight)
 {
     ImplOpenGLTexture* pTexture = nullptr;
 
@@ -71,14 +71,18 @@ OpenGLTexture FixedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, in
 
     Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight));
 
-    // If available, copy the image data to the texture
-    if (pData)
-    {
-        if (!pTexture->InsertBuffer(nX, nY, nWidth, nHeight, nFormat, nType, pData))
-            return OpenGLTexture();
-    }
-
     return OpenGLTexture(pTexture, aRectangle, nSlot);
 }
 
+OpenGLTexture FixedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
+{
+    OpenGLTexture aTexture = Reserve(nWidth, nHeight);
+    if (pData == nullptr)
+        return aTexture;
+
+    aTexture.CopyData(nWidth, nHeight, nFormat, nType, pData);
+
+    return aTexture;
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/PackedTextureAtlas.cxx b/vcl/opengl/PackedTextureAtlas.cxx
new file mode 100644
index 0000000..60fa1e9
--- /dev/null
+++ b/vcl/opengl/PackedTextureAtlas.cxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <sal/config.h>
+#include <vcl/opengl/OpenGLContext.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+#include "opengl/framebuffer.hxx"
+#include "opengl/texture.hxx"
+
+#include "opengl/PackedTextureAtlas.hxx"
+
+struct Node
+{
+    Rectangle mRectangle;
+    std::unique_ptr<Node> mLeftNode;
+    std::unique_ptr<Node> mRightNode;
+    bool mOccupied;
+
+    Node(Rectangle& aRectangle);
+
+    bool isLeaf();
+    Node* insert(int nWidth, int nHeight, int nPadding);
+};
+
+Node::Node(Rectangle& aRectangle)
+    : mRectangle(aRectangle)
+    , mLeftNode()
+    , mRightNode()
+    , mOccupied(false)
+{}
+
+bool Node::isLeaf()
+{
+    return mLeftNode.get()  == nullptr &&
+           mRightNode.get() == nullptr;
+}
+
+Node* Node::insert(int nWidth, int nHeight, int nPadding)
+{
+    if (!isLeaf())
+    {
+        Node* pNewNode = mLeftNode->insert(nWidth, nHeight, nPadding);
+
+        if (pNewNode != nullptr)
+            return pNewNode;
+
+        return mRightNode->insert(nWidth, nHeight, nPadding);
+    }
+    else
+    {
+        if (mOccupied)
+        {
+            return nullptr;
+        }
+
+        if (nWidth > mRectangle.GetWidth() || nHeight > mRectangle.GetHeight())
+        {   // does not fit
+            return nullptr;
+        }
+
+        if (nWidth == mRectangle.GetWidth() && nHeight == mRectangle.GetHeight())
+        {   // perfect fit
+            mOccupied = true;
+            return this;
+        }
+
+        int dw = mRectangle.GetWidth() - nWidth;
+        int dh = mRectangle.GetHeight() - nHeight;
+
+        Rectangle aLeftRect;
+        Rectangle aRightRect;
+        if (dw > dh)
+        {
+            aLeftRect = Rectangle(Point(mRectangle.Left(), mRectangle.Top()),
+                                  Size(nWidth, mRectangle.GetHeight()));
+            aRightRect = Rectangle(Point(nPadding + mRectangle.Left() + nWidth, mRectangle.Top()),
+                                   Size(mRectangle.GetWidth() - nWidth - nPadding, mRectangle.GetHeight()));
+        }
+        else
+        {
+            aLeftRect = Rectangle(Point(mRectangle.Left(), mRectangle.Top()),
+                                  Size(mRectangle.GetWidth(), nHeight));
+            aRightRect = Rectangle(Point(mRectangle.Left(), nPadding + mRectangle.Top() + nHeight),
+                                   Size(mRectangle.GetWidth(), mRectangle.GetHeight() - nHeight - nPadding));
+        }
+
+        mLeftNode.reset(new Node(aLeftRect));
+        mRightNode.reset(new Node(aRightRect));
+
+        return mLeftNode->insert(nWidth, nHeight, nPadding);
+    }
+}
+
+struct PackedTexture
+{
+    std::unique_ptr<Node> mpRootNode;
+    ImplOpenGLTexture* mpTexture;
+};
+
+PackedTextureAtlasManager::PackedTextureAtlasManager(int nTextureWidth, int nTextureHeight)
+    : mnTextureWidth(nTextureWidth)
+    , mnTextureHeight(nTextureHeight)
+{
+}
+
+PackedTextureAtlasManager::~PackedTextureAtlasManager()
+{
+    for (std::unique_ptr<PackedTexture>& pPackedTexture : maPackedTextures)
+    {
+        // Free texture early in VCL shutdown while we have a context.
+        pPackedTexture->mpTexture->Dispose();
+        pPackedTexture->mpTexture->DecreaseRefCount(0);
+    }
+}
+
+void PackedTextureAtlasManager::CreateNewTexture()
+{
+    std::unique_ptr<PackedTexture> pPackedTexture(new PackedTexture);
+    pPackedTexture->mpTexture = new ImplOpenGLTexture(mnTextureWidth, mnTextureHeight, true);
+    Rectangle aInitialRect(Point(0, 0), Size(mnTextureWidth, mnTextureHeight));
+    pPackedTexture->mpRootNode.reset(new Node(aInitialRect));
+    maPackedTextures.push_back(std::move(pPackedTexture));
+}
+
+OpenGLTexture PackedTextureAtlasManager::Reserve(int nWidth, int nHeight)
+{
+    for (std::unique_ptr<PackedTexture>& pPackedTexture : maPackedTextures)
+    {
+        Node* pNode = pPackedTexture->mpRootNode->insert(nWidth, nHeight, 1);
+        if (pNode != nullptr)
+        {
+            return OpenGLTexture(pPackedTexture->mpTexture, pNode->mRectangle, -1);
+        }
+    }
+    CreateNewTexture();
+    std::unique_ptr<PackedTexture>& pPackedTexture = maPackedTextures.back();
+    Node* pNode = pPackedTexture->mpRootNode->insert(nWidth, nHeight, 1);
+    if (pNode != nullptr)
+    {
+        return OpenGLTexture(pPackedTexture->mpTexture, pNode->mRectangle, -1);
+    }
+    return OpenGLTexture();
+}
+
+OpenGLTexture PackedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
+{
+    OpenGLTexture aTexture = Reserve(nWidth, nHeight);
+    if (aTexture && pData == nullptr)
+        return aTexture;
+
+    aTexture.CopyData(nWidth, nHeight, nFormat, nType, pData);
+
+    return aTexture;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index c9e59fd..d3d6392 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -83,6 +83,7 @@ OpenGLSalGraphicsImpl::OpenGLSalGraphicsImpl(SalGraphics& rParent, SalGeometryPr
     , mnDrawCountAtFlush(0)
     , mProgramSolidColor(SALCOLOR_NONE)
     , mProgramSolidTransparency(0.0)
+    , mpAccumulatedTextures(new AccumulatedTextures)
 {
 }
 
@@ -184,6 +185,13 @@ void OpenGLSalGraphicsImpl::DeInit()
 
 void OpenGLSalGraphicsImpl::PreDraw(XOROption eOpt)
 {
+    FlushDeferredDrawing();
+
+    InitializePreDrawState(eOpt);
+}
+
+void OpenGLSalGraphicsImpl::InitializePreDrawState(XOROption eOpt)
+{
     OpenGLZone::enter();
 
     mnDrawCount++;
@@ -263,6 +271,7 @@ void OpenGLSalGraphicsImpl::freeResources()
     {
         VCL_GL_INFO( "freeResources" );
         mpContext->makeCurrent();
+        FlushDeferredDrawing();
         mpContext->ReleaseFramebuffer( maOffscreenTex );
     }
     ReleaseContext();
@@ -352,7 +361,16 @@ const vcl::Region& OpenGLSalGraphicsImpl::getClipRegion() const
 
 bool OpenGLSalGraphicsImpl::setClipRegion( const vcl::Region& rClip )
 {
+    if (maClipRegion == rClip)
+    {
+        VCL_GL_INFO("::setClipRegion (no change) " << rClip);
+        return true;
+    }
+
+    FlushDeferredDrawing();
+
     VCL_GL_INFO( "::setClipRegion " << rClip );
+
     maClipRegion = rClip;
 
     mbUseStencil = false;
@@ -368,7 +386,16 @@ bool OpenGLSalGraphicsImpl::setClipRegion( const vcl::Region& rClip )
 // set the clip region to empty
 void OpenGLSalGraphicsImpl::ResetClipRegion()
 {
-    VCL_GL_INFO( "::ResetClipRegion" );
+    if (maClipRegion.IsEmpty())
+    {
+        VCL_GL_INFO("::ResetClipRegion (no change) ");
+        return;
+    }
+
+    FlushDeferredDrawing();
+
+    VCL_GL_INFO("::ResetClipRegion");
+
     maClipRegion.SetEmpty();
     mbUseScissor = false;
     mbUseStencil = false;
@@ -1654,6 +1681,83 @@ void OpenGLSalGraphicsImpl::DrawMask( OpenGLTexture& rMask, SalColor nMaskColor,
     mpProgram->Clean();
 }
 
+void OpenGLSalGraphicsImpl::DeferredTextDraw(OpenGLTexture& rTexture, SalColor aMaskColor, const SalTwoRect& rPosAry)
+{
+    mpAccumulatedTextures->insert(rTexture, aMaskColor, rPosAry);
+}
+
+void OpenGLSalGraphicsImpl::FlushDeferredDrawing()
+{
+    if (mpAccumulatedTextures->empty())
+        return;
+
+    InitializePreDrawState();
+
+    VCL_GL_INFO("FlushDeferredDrawing");
+
+    OpenGLZone aZone;
+
+#if 0 // Draw a background rect under text for debugging - same color shows text from the same texture
+    static sal_uInt8 r = 0xBE;
+    static sal_uInt8 g = 0xF0;
+    static sal_uInt8 b = 0xFF;
+    static std::unordered_map<GLuint, Color> aColorForTextureMap;
+
+    for (auto& rPair : mpAccumulatedTextures->getAccumulatedTexturesMap())
+    {
+        OpenGLTexture& rTexture = rPair.second->maTexture;
+        Color aUseColor;
+        if (aColorForTextureMap.find(rTexture.Id()) == aColorForTextureMap.end())
+        {
+            Color aColor(r, g, b);
+            sal_uInt16 h,s,br;
+            aColor.RGBtoHSB(h, s, br);
+            aColor = Color::HSBtoRGB((h + 40) % 360, s, br);
+            r = aColor.GetRed();
+            g = aColor.GetGreen();
+            b = aColor.GetBlue();
+            aColorForTextureMap[rTexture.Id()] = aColor;
+        }
+        aUseColor = aColorForTextureMap[rTexture.Id()];
+
+        if (!UseSolid(MAKE_SALCOLOR(aUseColor.GetRed(), aUseColor.GetGreen(), aUseColor.GetBlue())))
+            return;
+        for (auto rColorTwoRectPair: rPair.second->maColorTextureDrawParametersMap)
+        {
+            TextureDrawParameters& rParameters = rColorTwoRectPair.second;
+            ApplyProgramMatrices();
+            mpProgram->SetTextureCoord(rParameters.maTextureCoords.data());
+            mpProgram->SetVertices(rParameters.maVertices.data());
+            glDrawArrays(GL_TRIANGLES, 0, rParameters.getNumberOfVertices());
+        }
+    }
+#endif
+
+    if( !UseProgram( "textureVertexShader", "maskFragmentShader" ) )
+        return;
+
+    mpProgram->SetBlendMode(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+    for (auto& rPair : mpAccumulatedTextures->getAccumulatedTexturesMap())
+    {
+        OpenGLTexture& rTexture = rPair.second->maTexture;
+        mpProgram->SetTexture("sampler", rTexture);
+        for (auto& rColorTwoRectPair: rPair.second->maColorTextureDrawParametersMap)
+        {
+            mpProgram->SetColor("color", rColorTwoRectPair.first, 0);
+            TextureDrawParameters& rParameters = rColorTwoRectPair.second;
+            ApplyProgramMatrices();
+            mpProgram->SetTextureCoord(rParameters.maTextureCoords.data());
+            mpProgram->SetVertices(rParameters.maVertices.data());
+            glDrawArrays(GL_TRIANGLES, 0, rParameters.getNumberOfVertices());
+        }
+    }
+    mpProgram->Clean();
+    mpAccumulatedTextures->clear();
+
+    PostDraw();
+}
+
 void OpenGLSalGraphicsImpl::DrawLinearGradient( const Gradient& rGradient, const Rectangle& rRect )
 {
     OpenGLZone aZone;
@@ -2017,6 +2121,8 @@ void OpenGLSalGraphicsImpl::DoCopyBits( const SalTwoRect& rPosAry, OpenGLSalGrap
 {
     VCL_GL_INFO( "::copyBits" );
 
+    rImpl.FlushDeferredDrawing();
+
     if( !rImpl.maOffscreenTex )
     {
         VCL_GL_INFO( "::copyBits - skipping copy of un-initialized framebuffer contents of size "
@@ -2113,6 +2219,8 @@ SalBitmap* OpenGLSalGraphicsImpl::getBitmap( long nX, long nY, long nWidth, long
 
 SalColor OpenGLSalGraphicsImpl::getPixel( long nX, long nY )
 {
+    FlushDeferredDrawing();
+
     char pixel[3] = { 0, 0, 0 };
 
     PreDraw( XOROption::IMPLEMENT_XOR );
@@ -2369,6 +2477,8 @@ bool OpenGLSalGraphicsImpl::drawGradient(const tools::PolyPolygon& rPolyPoly,
 
 void OpenGLSalGraphicsImpl::flush()
 {
+    FlushDeferredDrawing();
+
     if( IsOffscreen() )
         return;
 
@@ -2383,6 +2493,8 @@ void OpenGLSalGraphicsImpl::flush()
 
 void OpenGLSalGraphicsImpl::doFlush()
 {
+    FlushDeferredDrawing();
+
     if( IsOffscreen() )
         return;
 
diff --git a/vcl/opengl/texture.cxx b/vcl/opengl/texture.cxx
index ffaaf09..61b6514 100644
--- a/vcl/opengl/texture.cxx
+++ b/vcl/opengl/texture.cxx
@@ -29,10 +29,17 @@
 #include "opengl/framebuffer.hxx"
 #include "opengl/texture.hxx"
 #include "opengl/zone.hxx"
+namespace
+{
+
+SAL_CONSTEXPR GLenum constInternalFormat = GL_RGBA8;
+
+} // end anonymous namespace
 
 // texture with allocated size
 ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate ) :
     mnRefCount( 1 ),
+    mnTexture( 0 ),
     mnWidth( nWidth ),
     mnHeight( nHeight ),
     mnFilter( GL_NEAREST ),
@@ -55,7 +62,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate )
     CHECK_GL_ERROR();
     if( bAllocate )
     {
-        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
+        glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
         CHECK_GL_ERROR();
     }
     glBindTexture( GL_TEXTURE_2D, 0 );
@@ -91,7 +98,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nX, int nY, int nWidth, int nHeight )
     CHECK_GL_ERROR();
     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
     CHECK_GL_ERROR();
-    glCopyTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, nX, nY, nWidth, nHeight, 0 );
+    glCopyTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nX, nY, nWidth, nHeight, 0 );
     CHECK_GL_ERROR();
     glBindTexture( GL_TEXTURE_2D, 0 );
     CHECK_GL_ERROR();
@@ -111,11 +118,8 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int
 {
     OpenGLVCLContextZone aContextZone;
 
-    if( !mnTexture )
-    {
-        glGenTextures( 1, &mnTexture );
-        CHECK_GL_ERROR();
-    }
+    glGenTextures( 1, &mnTexture );
+    CHECK_GL_ERROR();
     glBindTexture( GL_TEXTURE_2D, mnTexture );
     CHECK_GL_ERROR();
     glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
@@ -128,7 +132,7 @@ ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int
     CHECK_GL_ERROR();
     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
     CHECK_GL_ERROR();
-    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, mnWidth, mnHeight, 0, nFormat, nType, pData );
+    glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, mnWidth, mnHeight, 0, nFormat, nType, pData );
     CHECK_GL_ERROR();
     glBindTexture( GL_TEXTURE_2D, 0 );
     CHECK_GL_ERROR();
@@ -240,6 +244,32 @@ int ImplOpenGLTexture::FindFreeSlot()
     return -1;
 }
 
+void ImplOpenGLTexture::IncreaseRefCount(int nSlotNumber)
+{
+    mnRefCount++;
+    if (mpSlotReferences && nSlotNumber >= 0)
+    {
+        if (mpSlotReferences->at(nSlotNumber) == 0)
+            mnFreeSlots--;
+        mpSlotReferences->at(nSlotNumber)++;
+    }
+}
+
+void ImplOpenGLTexture::DecreaseRefCount(int nSlotNumber)
+{
+    mnRefCount--;
+    if (mpSlotReferences && nSlotNumber >= 0)
+    {
+        mpSlotReferences->at(nSlotNumber)--;
+        if (mpSlotReferences->at(nSlotNumber) == 0)
+            mnFreeSlots++;
+    }
+
+    if (mnRefCount <= 0)
+        delete this;
+}
+
+
 OpenGLTexture::OpenGLTexture() :
     maRect( 0, 0, 0, 0 ),
     mpImpl(nullptr),
@@ -371,6 +401,57 @@ void OpenGLTexture::GetCoord( GLfloat* pCoord, const SalTwoRect& rPosAry, bool b
     }
 }
 
+template <>
+void OpenGLTexture::FillCoords<GL_TRIANGLES>(std::vector<GLfloat>& aCoord, const SalTwoRect& rPosAry, bool bInverted) const
+{
+    VCL_GL_INFO("Add coord " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
+    VCL_GL_INFO("   With 2Rect Src  [" << rPosAry.mnSrcX << ", " << rPosAry.mnSrcY << "] wh (" << rPosAry.mnSrcWidth << ", " << rPosAry.mnSrcHeight << ")");
+    VCL_GL_INFO("   With 2Rect Dest [" << rPosAry.mnDestX << ", " << rPosAry.mnDestY << "] wh (" << rPosAry.mnDestWidth << ", " << rPosAry.mnDestHeight << ")");
+
+    GLfloat x1 = 0.0f;
+    GLfloat x2 = 0.0f;
+    GLfloat y1 = 0.0f;
+    GLfloat y2 = 0.0f;
+
+    double fTextureWidth(mpImpl->mnWidth);
+    double fTextureHeight(mpImpl->mnHeight);
+
+    if (mpImpl)
+    {
+        x1 = (maRect.Left() + rPosAry.mnSrcX) / fTextureWidth;
+        x2 = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / fTextureWidth;
+
+        if (bInverted)
+        {
+            y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / fTextureHeight;
+            y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / fTextureHeight;
+        }
+        else
+        {
+            y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / fTextureHeight;
+            y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / fTextureHeight;
+        }
+    }
+
+    aCoord.push_back(x1);
+    aCoord.push_back(y1);
+
+    aCoord.push_back(x2);
+    aCoord.push_back(y1);
+
+    aCoord.push_back(x1);
+    aCoord.push_back(y2);
+
+    aCoord.push_back(x1);
+    aCoord.push_back(y2);
+
+    aCoord.push_back(x2);
+    aCoord.push_back(y1);
+
+    aCoord.push_back(x2);
+    aCoord.push_back(y2);
+}
+
 void OpenGLTexture::GetWholeCoord( GLfloat* pCoord ) const
 {
     if( GetWidth() != mpImpl->mnWidth || GetHeight() != mpImpl->mnHeight )
@@ -389,6 +470,11 @@ void OpenGLTexture::GetWholeCoord( GLfloat* pCoord ) const
     }
 }
 
+OpenGLTexture OpenGLTexture::GetWholeTexture()
+{
+    return OpenGLTexture(mpImpl, Rectangle(Point(0, 0), Size(mpImpl->mnWidth, mpImpl->mnHeight)), -1);
+}
+
 GLenum OpenGLTexture::GetFilter() const
 {
     if( mpImpl )
@@ -396,6 +482,17 @@ GLenum OpenGLTexture::GetFilter() const
     return GL_NEAREST;
 }
 
+bool OpenGLTexture::CopyData(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8* pData)
+{
+    if (!pData || mpImpl == nullptr)
+        return false;
+
+    int nX = maRect.Left();
+    int nY = maRect.Top();
+
+    return mpImpl->InsertBuffer(nX, nY, nWidth, nHeight, nFormat, nType, pData);
+}
+
 void OpenGLTexture::SetFilter( GLenum nFilter )
 {
     if( mpImpl )
diff --git a/vcl/win/source/gdi/salgdi.cxx b/vcl/win/source/gdi/salgdi.cxx
index 42ab90e..85d54af 100644
--- a/vcl/win/source/gdi/salgdi.cxx
+++ b/vcl/win/source/gdi/salgdi.cxx
@@ -616,6 +616,14 @@ OpenGLTexture* OpenGLCompatibleDC::getTexture()
     return new OpenGLTexture(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, reinterpret_cast<sal_uInt8*>(mpData));
 }
 
+bool OpenGLCompatibleDC::copyToTexture(OpenGLTexture& aTexture)
+{
+    if (!mpImpl)
+        return false;
+
+    return aTexture.CopyData(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, reinterpret_cast<sal_uInt8*>(mpData));
+}
+
 WinSalGraphics::WinSalGraphics(WinSalGraphics::Type eType, bool bScreen, HWND hWnd, SalGeometryProvider *pProvider):
     mhLocalDC(0),
     mbPrinter(eType == WinSalGraphics::PRINTER),
diff --git a/vcl/win/source/gdi/winlayout.cxx b/vcl/win/source/gdi/winlayout.cxx
index d8073fd..49aa0c4 100644
--- a/vcl/win/source/gdi/winlayout.cxx
+++ b/vcl/win/source/gdi/winlayout.cxx
@@ -26,6 +26,8 @@
 
 #include <opengl/texture.hxx>
 #include <opengl/win/gdiimpl.hxx>
+#include "opengl/PackedTextureAtlas.hxx"
+
 #include <vcl/opengl/OpenGLHelper.hxx>
 #include <win/salgdi.h>
 #include <win/saldata.hxx>
@@ -74,13 +76,11 @@ const int GLYPH_SPACE_RATIO = 8;
 const int GLYPH_OFFSET_RATIO = GLYPH_SPACE_RATIO * 2;
 }
 
-struct OpenGLGlyphCacheChunk
+struct OpenGLGlyphDrawElement
 {
-    int mnFirstGlyph;           // Must be int to handle non-BMP code points when mbRealGlyphIndices is false
-    int mnGlyphCount;
-    std::vector<Rectangle> maLocation;
-    std::vector<int> maLeftOverhangs;
-    std::shared_ptr<OpenGLTexture> mpTexture;
+    Rectangle maLocation;
+    int maLeftOverhangs;
+    OpenGLTexture maTexture;
     int mnBaselineOffset;
     int mnHeight;
     bool mbVertical;
@@ -97,6 +97,41 @@ struct OpenGLGlyphCacheChunk
     }
 };
 
+class GlyphCache
+{
+private:
+    static PackedTextureAtlasManager sPackedTextureAtlas;
+    std::unordered_map<int, OpenGLGlyphDrawElement> maOpenGLTextureCache;
+
+public:
+    GlyphCache()
+    {}
+
+    void ReserveTextureSpace(OpenGLGlyphDrawElement& rElement, int nWidth, int nHeight)
+    {
+        rElement.maTexture = sPackedTextureAtlas.Reserve(nWidth, nHeight);
+    }
+
+    void PutDrawElementInCache(const OpenGLGlyphDrawElement& rElement, int nGlyphIndex)
+    {
+        assert(!IsGlyphCached(nGlyphIndex));
+        maOpenGLTextureCache[nGlyphIndex] = OpenGLGlyphDrawElement(rElement);
+    }
+
+    OpenGLGlyphDrawElement& GetDrawElement(int nGlyphIndex)
+    {
+        assert(IsGlyphCached(nGlyphIndex));
+        return maOpenGLTextureCache[nGlyphIndex];
+    }
+
+    bool IsGlyphCached(int nGlyphIndex)
+    {
+        return maOpenGLTextureCache.find(nGlyphIndex) != maOpenGLTextureCache.end();
+    }
+};
+
+PackedTextureAtlasManager GlyphCache::sPackedTextureAtlas(2048, 2048);
+
 // win32 specific physical font instance
 class ImplWinFontEntry : public ImplFontEntry
 {
@@ -122,7 +157,6 @@ public:
                             { return maScriptCache; }
 private:
     mutable SCRIPT_CACHE    maScriptCache;
-    std::vector<OpenGLGlyphCacheChunk> maOpenGLGlyphCache;
 
 public:
     int                     GetCachedGlyphWidth( int nCharCode ) const;
@@ -136,9 +170,15 @@ public:
     GLyphyDemo::demo_atlas_t* mpGLyphyAtlas;
     GLyphyDemo::demo_font_t*  mpGLyphyFont;
 
-    bool                    GlyphIsCached(int nGlyphIndex) const;
-    bool                    AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
-    const OpenGLGlyphCacheChunk&  GetCachedGlyphChunkFor(int nGlyphIndex) const;
+private:
+    GlyphCache maGlyphCache;
+public:
+    bool CacheGlyphToAtlas(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
+
+    GlyphCache& GetGlyphCache()
+    {
+        return maGlyphCache;
+    }
 
 private:
     IntMap                  maWidthMap;
@@ -246,146 +286,16 @@ private:
     HDC               mhDC;
 };
 
-
-#ifdef SAL_LOG_INFO
-
-namespace {
-
-char ColorFor(COLORREF aColor)
-{
-    if (aColor == RGB(0xFF, 0xFF, 0xFF))
-        return ' ';
-    else if (aColor == RGB(0x00, 0x00, 0x00))
-        return 'X';
-
-    return '0' + (10*(GetRValue(aColor) + GetGValue(aColor) + GetBValue(aColor))) / (0xFF*3);
-}
-
-void DumpGlyphBitmap(HDC hDC, const OpenGLGlyphCacheChunk& rChunk)
-{
-    HBITMAP hBitmap = static_cast<HBITMAP>(GetCurrentObject(hDC, OBJ_BITMAP));
-    if (hBitmap == NULL)
-    {
-        SAL_WARN("vcl.gdi", "GetCurrentObject failed: " << WindowsErrorString(GetLastError()));
-        return;
-    }
-
-    BITMAP aBitmap;
-    if (!GetObjectW(hBitmap, sizeof(aBitmap), &aBitmap))
-    {
-        SAL_WARN("vcl.gdi", "GetObjectW failed: " << WindowsErrorString(GetLastError()));
-        return;
-    }
-
-    SAL_INFO("vcl.gdi.opengl", "Bitmap " << hBitmap << ": " << aBitmap.bmWidth << "x" << aBitmap.bmHeight << ":");
-
-    std::ostringstream sLine("\n", std::ios_base::ate);
-    for (long y = 0; y < aBitmap.bmHeight; y++)
-    {
-        if (y == rChunk.mnBaselineOffset + rChunk.getExtraOffset())
-            sLine << "-";
-        else
-            sLine << ColorFor(GetPixel(hDC, 0, y));
-        for (long x = 1; x < std::min(75l, aBitmap.bmWidth); x++)
-            sLine << ColorFor(GetPixel(hDC, x, y));
-        if (y < aBitmap.bmHeight - 1)
-            sLine << "\n";
-    }
-    SAL_INFO("vcl.gdi.opengl", sLine.str());
-}
-
-} // anonymous namespace
-
-#endif // SAL_LOG_INFO
-
-template< typename charT, typename traits >
-inline std::basic_ostream<charT, traits> & operator <<(
-    std::basic_ostream<charT, traits> & stream, const std::vector<OpenGLGlyphCacheChunk>& rCache )
-{
-    stream << "{";
-    for (auto i = rCache.cbegin(); i != rCache.cend(); ++i)
-    {
-        stream << "[" << i->mnFirstGlyph;
-        if (i->mnGlyphCount > 1)
-            stream << ".." << (i->mnFirstGlyph + i->mnGlyphCount - 1);
-        stream << "]";
-        if (i+1 != rCache.cend())
-        {
-            stream << ",";
-            assert(i->mnFirstGlyph + i->mnGlyphCount <= (i+1)->mnFirstGlyph);
-        }
-    }
-
-    return stream << "}";
-}
-
-inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
-{
-    maWidthMap[ nCharCode ] = nCharWidth;
-}
-
-inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
-{
-    IntMap::const_iterator it = maWidthMap.find( nCharCode );
-    if( it == maWidthMap.end() )
-        return -1;
-    return it->second;
-}
-
-bool ImplWinFontEntry::GlyphIsCached(int nGlyphIndex) const
+bool ImplWinFontEntry::CacheGlyphToAtlas(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
 {
     if (nGlyphIndex == DROPPED_OUTGLYPH)
         return true;
 
-    for (size_t i = 0; i < maOpenGLGlyphCache.size(); i++)
-        if (nGlyphIndex >= maOpenGLGlyphCache[i].mnFirstGlyph &&
-            nGlyphIndex < maOpenGLGlyphCache[i].mnFirstGlyph + maOpenGLGlyphCache[i].mnGlyphCount)
-            return true;
-
-    return false;
-}
-
-bool ImplWinFontEntry::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
-{
-    const int DEFAULT_CHUNK_SIZE = 20;
-
-    if (nGlyphIndex == DROPPED_OUTGLYPH)
-        return true;
-
-    SAL_INFO("vcl.gdi.opengl", "this=" << this << " " << nGlyphIndex << " old: " << maOpenGLGlyphCache);
-
-    auto n = maOpenGLGlyphCache.begin();
-    while (n != maOpenGLGlyphCache.end() &&
-           nGlyphIndex > n->mnFirstGlyph)
-        ++n;
-    assert(n == maOpenGLGlyphCache.end() || nGlyphIndex < n->mnFirstGlyph);
-
-    int nCount = DEFAULT_CHUNK_SIZE;
-    if (n != maOpenGLGlyphCache.end() && nGlyphIndex + nCount >= n->mnFirstGlyph)
-        nCount = n->mnFirstGlyph - nGlyphIndex;
-
-    if (nCount < DEFAULT_CHUNK_SIZE)
-    {
-        if (n == maOpenGLGlyphCache.begin())
-        {
-            nGlyphIndex = std::max(0, n->mnFirstGlyph - DEFAULT_CHUNK_SIZE);
-        }
-        else
-        {
-            nGlyphIndex = std::max(n[-1].mnFirstGlyph + n[-1].mnGlyphCount,
-                                   n->mnFirstGlyph - DEFAULT_CHUNK_SIZE);
-        }
-        nCount = n->mnFirstGlyph - nGlyphIndex;
-    }
-
-    OpenGLGlyphCacheChunk aChunk;
-    aChunk.mnFirstGlyph = nGlyphIndex;
-    aChunk.mnGlyphCount = nCount;
-    aChunk.mbRealGlyphIndices = bRealGlyphIndices;
+    OpenGLGlyphDrawElement aElement;
+    aElement.mbRealGlyphIndices = bRealGlyphIndices;
 
-    std::vector<uint32_t> aCodePointsOrGlyphIndices(nCount);
-    for (int i = 0; i < nCount; i++)
-        aCodePointsOrGlyphIndices[i] = nGlyphIndex + i;
+    std::vector<uint32_t> aCodePointsOrGlyphIndices(1);
+    aCodePointsOrGlyphIndices[0] = nGlyphIndex;
 
     HDC hDC = CreateCompatibleDC(rLayout.mhDC);
     if (hDC == NULL)
@@ -424,7 +334,7 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
             return false;
         }
     }
-    std::vector<WORD> aGlyphIndices(nCount);
+    std::vector<WORD> aGlyphIndices(1);
     // Fetch the ink boxes and calculate the size of the atlas.
     if (!bRealGlyphIndices)
     {
@@ -436,69 +346,60 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
     }
     else
     {
-        for (int i = 0; i < nCount; i++)
-            aGlyphIndices[i] = aCodePointsOrGlyphIndices[i];
+        aGlyphIndices[0] = aCodePointsOrGlyphIndices[0];
     }
     Rectangle bounds(0, 0, 0, 0);
-    auto aInkBoxes = pTxt->GetGlyphInkBoxes(aGlyphIndices.data(), aGlyphIndices.data() + nCount);
+    auto aInkBoxes = pTxt->GetGlyphInkBoxes(aGlyphIndices.data(), aGlyphIndices.data() + 1);
     for (auto &box : aInkBoxes)
         bounds.Union(box + Point(bounds.Right(), 0));
 
     // bounds.Top() is the offset from the baseline at (0,0) to the top of the
     // inkbox.
-    aChunk.mnBaselineOffset = -bounds.Top();
-    aChunk.mnHeight = bounds.getHeight();
-    aChunk.mbVertical = false;
-
-    aChunk.maLeftOverhangs.resize(nCount);
-    aChunk.maLocation.resize(nCount);
+    aElement.mnBaselineOffset = -bounds.Top();
+    aElement.mnHeight = bounds.getHeight();
+    aElement.mbVertical = false;
 
     // Try hard to avoid overlap as we want to be able to use
     // individual rectangles for each glyph. The ABC widths don't
     // take anti-aliasing into consideration. Let's hope that leaving
     // "extra" space between glyphs will help.
-    std::vector<float> aGlyphAdv(nCount);   // offsets between glyphs
-    std::vector<DWRITE_GLYPH_OFFSET> aGlyphOffset(nCount, DWRITE_GLYPH_OFFSET{0.0f,0.0f});
-    std::vector<int> aEnds(nCount); // end of each glyph box
+    std::vector<float> aGlyphAdv(1);   // offsets between glyphs
+    std::vector<DWRITE_GLYPH_OFFSET> aGlyphOffset(1, DWRITE_GLYPH_OFFSET{0.0f, 0.0f});
+    std::vector<int> aEnds(1); // end of each glyph box
     float totWidth = 0;
-    for (int i = 0; i < nCount; ++i)
     {
-        int overhang = aInkBoxes[i].Left();
-        int blackWidth = aInkBoxes[i].getWidth(); // width of non-AA pixels
-        aChunk.maLeftOverhangs[i] = overhang;
+        int overhang = aInkBoxes[0].Left();
+        int blackWidth = aInkBoxes[0].getWidth(); // width of non-AA pixels
+        aElement.maLeftOverhangs = overhang;
 
-        aGlyphAdv[i] = blackWidth + aChunk.getExtraSpace();
-        aGlyphOffset[i].advanceOffset = -overhang;
+        aGlyphAdv[0] = blackWidth + aElement.getExtraSpace();
+        aGlyphOffset[0].advanceOffset = -overhang;
 
-        totWidth += aGlyphAdv[i];
-        aEnds[i] = totWidth;
+        totWidth += aGlyphAdv[0];
+        aEnds[0] = totWidth;
     }
-
     // Leave extra space also at top and bottom
-    int nBitmapWidth = totWidth,
-        nBitmapHeight = bounds.getHeight() + aChunk.getExtraSpace();
+    int nBitmapWidth = totWidth;
+    int nBitmapHeight = bounds.getHeight() + aElement.getExtraSpace();
 
-    aChunk.maLocation.resize(nCount);
     UINT nPos = 0;
-    for (int i = 0; i < nCount; i++)
+
+    // FIXME: really I don't get why 'vertical' makes any difference [!] what does it mean !?
+    if (aElement.mbVertical)
     {
-        // FIXME: really I don't get why 'vertical' makes any difference [!] what does it mean !?
-        if (aChunk.mbVertical)
-        {
-            aChunk.maLocation[i].Left() = 0;
-            aChunk.maLocation[i].Right() = nBitmapWidth;
-            aChunk.maLocation[i].Top() = nPos;
-            aChunk.maLocation[i].Bottom() = nPos + aGlyphAdv[i] + aChunk.maLeftOverhangs[i];
-        }
-        else
-        {
-            aChunk.maLocation[i].Left() = nPos;
-            aChunk.maLocation[i].Right() = aEnds[i];
-            aChunk.maLocation[i].Top() = 0;
-            aChunk.maLocation[i].Bottom() = bounds.getHeight() + aChunk.getExtraSpace();
-        }
-        nPos = aEnds[i];
+        aElement.maLocation.Left() = 0;
+        aElement.maLocation.Right() = nBitmapWidth;
+        aElement.maLocation.Top() = nPos;
+        aElement.maLocation.Bottom() = nPos + aGlyphAdv[0] + aElement.maLeftOverhangs;
+    }
+    else
+    {
+        aElement.maLocation.Left() = nPos;
+        aElement.maLocation.Right() = aEnds[0];
+        aElement.maLocation.Top() = 0;
+        aElement.maLocation.Bottom() = bounds.getHeight() + aElement.getExtraSpace();
     }
+    nPos = aEnds[0];
 
     OpenGLCompatibleDC aDC(rGraphics, 0, 0, nBitmapWidth, nBitmapHeight);
 
@@ -519,11 +420,15 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
         return false;
     }
 
-    D2D1_POINT_2F baseline = { aChunk.getExtraOffset(), aChunk.getExtraOffset() + aChunk.mnBaselineOffset };
+    D2D1_POINT_2F baseline = {
+        aElement.getExtraOffset(),
+        aElement.getExtraOffset() + aElement.mnBaselineOffset
+    };
+
     DWRITE_GLYPH_RUN glyphs = {
         pTxt->GetFontFace(),
         pTxt->GetEmHeight(),
-        nCount,
+        1,
         aGlyphIndices.data(),
         aGlyphAdv.data(),
         aGlyphOffset.data(),
@@ -533,11 +438,11 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
 
     pRT->BeginDraw();
     pRT->DrawGlyphRun(baseline, &glyphs, pBrush);
-    HRESULT hr = pRT->EndDraw();
+    HRESULT hResult = pRT->EndDraw();
 
     pBrush->Release();
 
-    switch (hr)
+    switch (hResult)
     {
     case S_OK:
         break;
@@ -554,27 +459,28 @@ bool ImplWinFontEntry::AddChunkOfGlyphs(bool bRealGlyphIndices, int nGlyphIndex,
 
     pTxt->ReleaseFont();
 
-    aChunk.mpTexture = std::unique_ptr<OpenGLTexture>(aDC.getTexture());
-
-    maOpenGLGlyphCache.insert(n, aChunk);
+    maGlyphCache.ReserveTextureSpace(aElement, nBitmapWidth, nBitmapHeight);
+    aDC.copyToTexture(aElement.maTexture);
+    maGlyphCache.PutDrawElementInCache(aElement, nGlyphIndex);
 
     SelectFont(aDC.getCompatibleHDC(), hOrigFont);
     if (hNonAntialiasedFont != NULL)
         DeleteObject(hNonAntialiasedFont);
 
-#ifdef SAL_LOG_INFO
-    SAL_INFO("vcl.gdi.opengl", "this=" << this << " now: " << maOpenGLGlyphCache);
-    DumpGlyphBitmap(aDC.getCompatibleHDC(), aChunk);
+    return true;
+}
 
-    {
-        std::ostringstream sLine;
-        for (int i = 0; i < nCount; i++)
-            sLine << aGlyphAdv[i] << ":" << aChunk.maLeftOverhangs[i] << " ";
-        SAL_INFO("vcl.gdi.opengl", "DX:offset : " << sLine.str());
-    }
-#endif
+inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
+{
+    maWidthMap[ nCharCode ] = nCharWidth;
+}
 
-    return true;
+inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
+{
+    IntMap::const_iterator it = maWidthMap.find( nCharCode );
+    if( it == maWidthMap.end() )
+        return -1;
+    return it->second;
 }
 
 SimpleWinLayout::SimpleWinLayout(HDC hDC, BYTE nCharSet, const ImplWinFontData& rWinFontData,
@@ -1273,16 +1179,6 @@ void SimpleWinLayout::Simplify( bool /*bIsBase*/ )
         mnWidth = mnBaseAdv = 0;
 }
 
-const OpenGLGlyphCacheChunk& ImplWinFontEntry::GetCachedGlyphChunkFor(int nGlyphIndex) const
-{
-    auto i = maOpenGLGlyphCache.cbegin();
-    while (i != maOpenGLGlyphCache.cend() && nGlyphIndex >= i->mnFirstGlyph + i->mnGlyphCount)
-        ++i;
-    assert(i != maOpenGLGlyphCache.cend());
-    assert(nGlyphIndex >= i->mnFirstGlyph && nGlyphIndex < i->mnFirstGlyph + i->mnGlyphCount);
-    return *i;
-}
-
 void ImplWinFontEntry::setupGLyphy(HDC hDC)
 {
     if (mbGLyphySetupCalled)
@@ -1514,10 +1410,10 @@ bool SimpleWinLayout::CacheGlyphs(SalGraphics& rGraphics) const
             nCodePoint = mpOutGlyphs[i];
         }
 
-        if (mrWinFontEntry.GlyphIsCached(nCodePoint))
+        if (mrWinFontEntry.GetGlyphCache().IsGlyphCached(nCodePoint))
             continue;
 
-        if (!mrWinFontEntry.AddChunkOfGlyphs(false, nCodePoint, *this, rGraphics))
+        if (!mrWinFontEntry.CacheGlyphToAtlas(false, nCodePoint, *this, rGraphics))
             return false;
     }
 
@@ -1539,8 +1435,6 @@ bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
     if (!pImpl)
         return false;
 
-    pImpl->PreDraw();
-
     HFONT hOrigFont = DisableFontScaling();
     Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
 
@@ -1562,17 +1456,16 @@ bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
             nCodePoint = mpOutGlyphs[i];
         }
 
-        assert(mrWinFontEntry.GlyphIsCached(nCodePoint));
+        OpenGLGlyphDrawElement& rElement(mrWinFontEntry.GetGlyphCache().GetDrawElement(nCodePoint));
+        OpenGLTexture& rTexture = rElement.maTexture;
 
-        const OpenGLGlyphCacheChunk& rChunk = mrWinFontEntry.GetCachedGlyphChunkFor(nCodePoint);
-        const int n = nCodePoint - rChunk.mnFirstGlyph;
+        SalTwoRect a2Rects(0, 0,
+                           rTexture.GetWidth(), rTexture.GetHeight(),
+                           nAdvance + aPos.X() - rElement.getExtraOffset() + rElement.maLeftOverhangs,
+                           aPos.Y() - rElement.mnBaselineOffset - rElement.getExtraOffset(),
+                           rTexture.GetWidth(), rTexture.GetHeight());
 
-        SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
-                           rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
-                           nAdvance + aPos.X() - rChunk.getExtraOffset() + rChunk.maLeftOverhangs[n],
-                           aPos.Y() - rChunk.mnBaselineOffset - rChunk.getExtraOffset(),
-                           rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
-        pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
+        pImpl->DeferredTextDraw(rTexture, salColor, a2Rects);
 
         nAdvance += mpGlyphAdvances[i];
     }
@@ -1580,8 +1473,6 @@ bool SimpleWinLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
     if( hOrigFont )
         DeleteFont(SelectFont(hDC, hOrigFont));
 
-    pImpl->PostDraw();
-
     return true;
 }
 
@@ -2788,10 +2679,12 @@ bool UniscribeLayout::CacheGlyphs(SalGraphics& rGraphics) const
     {
         for (int i = 0; i < mnGlyphCount; i++)
         {
-            if (mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]))
+            int nCodePoint = mpOutGlyphs[i];
+
+            if (mrWinFontEntry.GetGlyphCache().IsGlyphCached(nCodePoint))
                 continue;
 
-            if (!mrWinFontEntry.AddChunkOfGlyphs(true, mpOutGlyphs[i], *this, rGraphics))
+            if (!mrWinFontEntry.CacheGlyphToAtlas(true, nCodePoint, *this, rGraphics))
                 return false;
         }
     }
@@ -3002,8 +2895,6 @@ bool UniscribeLayout::DrawCachedGlyphsUsingTextures(SalGraphics& rGraphics) cons
     if (!pImpl)
         return false;
 
-    pImpl->PreDraw();
-
     // FIXME: This code snippet is mostly copied from the one in
     // UniscribeLayout::DrawTextImpl. Should be factored out.
     int nBaseClusterOffset = 0;
@@ -3051,32 +2942,33 @@ bool UniscribeLayout::DrawCachedGlyphsUsingTextures(SalGraphics& rGraphics) cons
             if (mpOutGlyphs[i] == DROPPED_OUTGLYPH)
                 continue;
 
-            assert(mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]));
-
-            const OpenGLGlyphCacheChunk& rChunk = mrWinFontEntry.GetCachedGlyphChunkFor(mpOutGlyphs[i]);
-            const int n = mpOutGlyphs[i] - rChunk.mnFirstGlyph;
+            OpenGLGlyphDrawElement& rElement = mrWinFontEntry.GetGlyphCache().GetDrawElement(mpOutGlyphs[i]);
+            OpenGLTexture& rTexture = rElement.maTexture;
 
-            if (rChunk.mbVertical)
+            if (rElement.mbVertical)
             {
-                SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
-                                   rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
-                                   aPos.X() + rChunk.maLeftOverhangs[n], nAdvance + aPos.Y(),
-                                   rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
-                pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
+                SalTwoRect a2Rects(0, 0,
+                                   rTexture.GetWidth(), rTexture.GetHeight(),
+                                   aPos.X() + rElement.maLeftOverhangs,
+                                   nAdvance + aPos.Y(),
+                                   rTexture.GetWidth(), rTexture.GetHeight());
+
+                pImpl->DeferredTextDraw(rTexture, salColor, a2Rects);
             }
             else
             {
-                SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
-                                   rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
-                                   nAdvance + aPos.X() + mpGlyphOffsets[i].du - rChunk.getExtraOffset() + rChunk.maLeftOverhangs[n],
-                                   aPos.Y() + mpGlyphOffsets[i].dv - rChunk.mnBaselineOffset - rChunk.getExtraOffset(),
-                                   rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
-                pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
+                SalTwoRect a2Rects(0, 0,
+                                   rTexture.GetWidth(), rTexture.GetHeight(),
+                                   nAdvance + aPos.X() + mpGlyphOffsets[i].du - rElement.getExtraOffset() + rElement.maLeftOverhangs,
+                                   aPos.Y() + mpGlyphOffsets[i].dv - rElement.mnBaselineOffset - rElement.getExtraOffset(),
+                                   rTexture.GetWidth(), rTexture.GetHeight());
+
+                pImpl->DeferredTextDraw(rTexture, salColor, a2Rects);
             }
+
             nAdvance += pGlyphWidths[i];
         }
     }
-    pImpl->PostDraw();
 
     return true;
 }


More information about the Libreoffice-commits mailing list