summaryrefslogtreecommitdiffstats
path: root/vcl/opengl/PackedTextureAtlas.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/opengl/PackedTextureAtlas.cxx192
1 files changed, 192 insertions, 0 deletions
diff --git a/vcl/opengl/PackedTextureAtlas.cxx b/vcl/opengl/PackedTextureAtlas.cxx
new file mode 100644
index 000000000..8508bbe3c
--- /dev/null
+++ b/vcl/opengl/PackedTextureAtlas.cxx
@@ -0,0 +1,192 @@
+/* -*- 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 <memory>
+#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>
+
+namespace {
+
+struct Node
+{
+ tools::Rectangle mRectangle;
+ std::unique_ptr<Node> mLeftNode;
+ std::unique_ptr<Node> mRightNode;
+ bool mOccupied;
+
+ explicit Node(int nWidth, int nHeight);
+ explicit Node(tools::Rectangle const & aRectangle);
+
+ bool isLeaf() const;
+ Node* insert(int nWidth, int nHeight, int nPadding);
+};
+
+}
+
+Node::Node(int nWidth, int nHeight)
+ : mRectangle(tools::Rectangle(Point(), Size(nWidth, nHeight)))
+ , mLeftNode()
+ , mRightNode()
+ , mOccupied(false)
+{}
+
+Node::Node(tools::Rectangle const & aRectangle)
+ : mRectangle(aRectangle)
+ , mLeftNode()
+ , mRightNode()
+ , mOccupied(false)
+{}
+
+bool Node::isLeaf() const { return mLeftNode == nullptr && mRightNode == 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;
+
+ tools::Rectangle aLeftRect;
+ tools::Rectangle aRightRect;
+ if (dw > dh)
+ {
+ aLeftRect = tools::Rectangle(Point(mRectangle.Left(), mRectangle.Top()),
+ Size(nWidth, mRectangle.GetHeight()));
+ aRightRect = tools::Rectangle(Point(nPadding + mRectangle.Left() + nWidth, mRectangle.Top()),
+ Size(mRectangle.GetWidth() - nWidth - nPadding, mRectangle.GetHeight()));
+ }
+ else
+ {
+ aLeftRect = tools::Rectangle(Point(mRectangle.Left(), mRectangle.Top()),
+ Size(mRectangle.GetWidth(), nHeight));
+ aRightRect = tools::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::shared_ptr<ImplOpenGLTexture> mpTexture;
+ std::unique_ptr<Node> mpRootNode;
+
+ PackedTexture(int nWidth, int nHeight)
+ : mpTexture(std::make_shared<ImplOpenGLTexture>(nWidth, nHeight, true))
+ , mpRootNode(new Node(nWidth, nHeight))
+ {}
+};
+
+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.reset();
+ }
+}
+
+void PackedTextureAtlasManager::CreateNewTexture()
+{
+ std::unique_ptr<PackedTexture> pPackedTexture(new PackedTexture(mnTextureWidth, mnTextureHeight));
+ GLuint nTextureID = pPackedTexture->mpTexture->mnTexture;
+ maPackedTextures.push_back(std::move(pPackedTexture));
+ VCL_GL_INFO("PackedTextureAtlas::CreateNewTexture adding texture: " << nTextureID <<
+ " atlases: " << maPackedTextures.size());
+}
+
+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 const * pData)
+{
+ OpenGLTexture aTexture = Reserve(nWidth, nHeight);
+ if (aTexture && pData == nullptr)
+ return aTexture;
+
+ aTexture.CopyData(nWidth, nHeight, nFormat, nType, pData);
+
+ return aTexture;
+}
+
+std::vector<GLuint> PackedTextureAtlasManager::ReduceTextureNumber(int nMaxNumberOfTextures)
+{
+ std::vector<GLuint> aTextureIDs;
+ while (int(maPackedTextures.size()) > nMaxNumberOfTextures)
+ {
+ // Remove oldest created texture
+ GLuint nTextureID = maPackedTextures[0]->mpTexture->mnTexture;
+ aTextureIDs.push_back(nTextureID);
+ maPackedTextures.erase(maPackedTextures.begin());
+ VCL_GL_INFO("PackedTextureAtlas::ReduceTextureNumber removing texture: " << nTextureID <<
+ " atlases: " << maPackedTextures.size());
+ }
+ return aTextureIDs;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */