/* -*- 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 #include #include #include #include #include #include namespace { struct Node { tools::Rectangle const mRectangle; std::unique_ptr mLeftNode; std::unique_ptr 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 mpTexture; std::unique_ptr mpRootNode; PackedTexture(int nWidth, int nHeight) : mpTexture(new 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& pPackedTexture : maPackedTextures) { // Free texture early in VCL shutdown while we have a context. pPackedTexture->mpTexture.reset(); } } void PackedTextureAtlasManager::CreateNewTexture() { std::unique_ptr 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& pPackedTexture : maPackedTextures) { Node* pNode = pPackedTexture->mpRootNode->insert(nWidth, nHeight, 1); if (pNode != nullptr) { return OpenGLTexture(pPackedTexture->mpTexture, pNode->mRectangle, -1); } } CreateNewTexture(); std::unique_ptr& 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 PackedTextureAtlasManager::ReduceTextureNumber(int nMaxNumberOfTextures) { std::vector 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: */