diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2016-04-08 19:17:41 +0900 |
---|---|---|
committer | Michael Meeks <michael.meeks@collabora.com> | 2016-04-08 14:47:02 +0000 |
commit | bd1bfeff238705fa89b4912865aa7cc6c4c1857f (patch) | |
tree | 09bcaa03f2873964541d59bad39e9ff182b7dc54 /vcl | |
parent | 674fc004a5c5adcce4bb2c2841298431a79c01f2 (diff) |
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@collabora.com>
Tested-by: Michael Meeks <michael.meeks@collabora.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/Library_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/inc/opengl/AccumulatedTextures.hxx | 113 | ||||
-rw-r--r-- | vcl/inc/opengl/FixedTextureAtlas.hxx | 1 | ||||
-rw-r--r-- | vcl/inc/opengl/PackedTextureAtlas.hxx | 46 | ||||
-rw-r--r-- | vcl/inc/opengl/texture.hxx | 36 | ||||
-rw-r--r-- | vcl/inc/openglgdiimpl.hxx | 8 | ||||
-rw-r--r-- | vcl/inc/win/salgdi.h | 5 | ||||
-rw-r--r-- | vcl/opengl/FixedTextureAtlas.cxx | 20 | ||||
-rw-r--r-- | vcl/opengl/PackedTextureAtlas.cxx | 164 | ||||
-rw-r--r-- | vcl/opengl/gdiimpl.cxx | 114 | ||||
-rw-r--r-- | vcl/opengl/texture.cxx | 113 | ||||
-rw-r--r-- | vcl/win/source/gdi/salgdi.cxx | 8 | ||||
-rw-r--r-- | vcl/win/source/gdi/winlayout.cxx | 394 |
13 files changed, 729 insertions, 294 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 5b5c9698c44d..7f0b95d5bd20 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 000000000000..e74c06535f69 --- /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 5b22b619d945..8d104a788c83 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 000000000000..17501f368208 --- /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 938891848ab7..6afc17630431 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 ca8232ae2c45..6dfa73c7b742 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 085f77eed706..30b143814043 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 80c1cfe496c7..1ed83111d293 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 000000000000..60fa1e98fc41 --- /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 c9e59fdf3709..d3d6392bf96c 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 ffaaf092387d..61b65140b8e7 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 42ab90e9701a..85d54af7d9fd 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 d8073fd21fd0..49aa0c4a3034 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; } |