summaryrefslogtreecommitdiffstats
path: root/vcl/opengl
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/opengl/DeviceInfo.cxx16
-rw-r--r--vcl/opengl/FixedTextureAtlas.cxx137
-rw-r--r--vcl/opengl/LineRenderUtils.cxx189
-rw-r--r--vcl/opengl/PackedTextureAtlas.cxx192
-rw-r--r--vcl/opengl/README.deprecated23
-rw-r--r--vcl/opengl/README.opengl26
-rw-r--r--vcl/opengl/RenderList.cxx403
-rw-r--r--vcl/opengl/framebuffer.cxx108
-rw-r--r--vcl/opengl/gdiimpl.cxx2315
-rw-r--r--vcl/opengl/opengl_blacklist_windows.xml67
-rw-r--r--vcl/opengl/program.cxx390
-rw-r--r--vcl/opengl/salbmp.cxx783
-rw-r--r--vcl/opengl/scale.cxx423
-rw-r--r--vcl/opengl/shaders/areaHashCRC64TFragmentShader.glsl87
-rw-r--r--vcl/opengl/shaders/areaScaleFastFragmentShader.glsl59
-rw-r--r--vcl/opengl/shaders/areaScaleFragmentShader.glsl239
-rw-r--r--vcl/opengl/shaders/blendedTextureFragmentShader.glsl33
-rw-r--r--vcl/opengl/shaders/blendedTextureVertexShader.glsl28
-rw-r--r--vcl/opengl/shaders/combinedFragmentShader.glsl44
-rw-r--r--vcl/opengl/shaders/combinedTextureFragmentShader.glsl73
-rw-r--r--vcl/opengl/shaders/combinedTextureVertexShader.glsl43
-rw-r--r--vcl/opengl/shaders/combinedVertexShader.glsl76
-rw-r--r--vcl/opengl/shaders/convolutionFragmentShader.glsl30
-rw-r--r--vcl/opengl/shaders/diffTextureFragmentShader.glsl30
-rw-r--r--vcl/opengl/shaders/dumbVertexShader.glsl20
-rw-r--r--vcl/opengl/shaders/greyscaleFragmentShader.glsl20
-rw-r--r--vcl/opengl/shaders/invert50FragmentShader.glsl25
-rw-r--r--vcl/opengl/shaders/lineFragmentShader.glsl38
-rw-r--r--vcl/opengl/shaders/lineVertexShader.glsl37
-rw-r--r--vcl/opengl/shaders/linearGradientFragmentShader.glsl23
-rw-r--r--vcl/opengl/shaders/maskFragmentShader.glsl23
-rw-r--r--vcl/opengl/shaders/maskedTextureFragmentShader.glsl27
-rw-r--r--vcl/opengl/shaders/maskedTextureVertexShader.glsl26
-rw-r--r--vcl/opengl/shaders/radialGradientFragmentShader.glsl23
-rw-r--r--vcl/opengl/shaders/replaceColorFragmentShader.glsl25
-rw-r--r--vcl/opengl/shaders/solidFragmentShader.glsl19
-rw-r--r--vcl/opengl/shaders/textureFragmentShader.glsl20
-rw-r--r--vcl/opengl/shaders/textureVertexShader.glsl22
-rw-r--r--vcl/opengl/shaders/transformedTextureVertexShader.glsl28
-rw-r--r--vcl/opengl/texture.cxx606
-rw-r--r--vcl/opengl/win/WinDeviceInfo.cxx494
-rw-r--r--vcl/opengl/win/gdiimpl.cxx898
-rw-r--r--vcl/opengl/win/winlayout.cxx60
-rw-r--r--vcl/opengl/x11/X11DeviceInfo.cxx363
-rw-r--r--vcl/opengl/x11/cairotextrender.cxx84
-rw-r--r--vcl/opengl/x11/gdiimpl.cxx598
-rw-r--r--vcl/opengl/x11/salvd.cxx90
47 files changed, 9383 insertions, 0 deletions
diff --git a/vcl/opengl/DeviceInfo.cxx b/vcl/opengl/DeviceInfo.cxx
new file mode 100644
index 000000000..24f42e34e
--- /dev/null
+++ b/vcl/opengl/DeviceInfo.cxx
@@ -0,0 +1,16 @@
+/* -*- 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 <opengl/DeviceInfo.hxx>
+
+OpenGLDeviceInfo::~OpenGLDeviceInfo()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/FixedTextureAtlas.cxx b/vcl/opengl/FixedTextureAtlas.cxx
new file mode 100644
index 000000000..7425942d1
--- /dev/null
+++ b/vcl/opengl/FixedTextureAtlas.cxx
@@ -0,0 +1,137 @@
+/* -*- 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/FixedTextureAtlas.hxx>
+
+struct FixedTexture
+{
+ std::shared_ptr<ImplOpenGLTexture> mpTexture;
+ int mnFreeSlots;
+ std::vector<bool> maAllocatedSlots;
+
+ FixedTexture(int nTextureWidth, int nTextureHeight, int nNumberOfSlots)
+ : mpTexture(std::make_shared<ImplOpenGLTexture>(nTextureWidth, nTextureHeight, true))
+ , mnFreeSlots(nNumberOfSlots)
+ , maAllocatedSlots(nNumberOfSlots, false)
+ {
+ auto aDeallocateFunction = [this] (int nSlotNumber)
+ {
+ deallocateSlot(nSlotNumber);
+ };
+
+ mpTexture->SetSlotDeallocateCallback(aDeallocateFunction);
+ mpTexture->InitializeSlotMechanism(nNumberOfSlots);
+ }
+
+ ~FixedTexture()
+ {
+ mpTexture->ResetSlotDeallocateCallback();
+ }
+
+ void allocateSlot(int nSlot)
+ {
+ maAllocatedSlots[nSlot] = true;
+ mnFreeSlots--;
+ }
+
+ void deallocateSlot(int nSlot)
+ {
+ maAllocatedSlots[nSlot] = false;
+ mnFreeSlots++;
+ }
+
+ int findAndAllocateFreeSlot()
+ {
+ for (size_t i = 0; i < maAllocatedSlots.size(); ++i)
+ {
+ if (!maAllocatedSlots[i])
+ {
+ allocateSlot(i);
+ return i;
+ }
+ }
+ return -1;
+ }
+
+private:
+ FixedTexture(const FixedTexture&) = delete;
+ FixedTexture& operator=(const FixedTexture&) = delete;
+};
+
+FixedTextureAtlasManager::FixedTextureAtlasManager(int nWidthFactor, int nHeightFactor, int nSubTextureSize)
+ : mWidthFactor(nWidthFactor)
+ , mHeightFactor(nHeightFactor)
+ , mSubTextureSize(nSubTextureSize)
+{
+}
+
+FixedTextureAtlasManager::~FixedTextureAtlasManager()
+{
+}
+
+void FixedTextureAtlasManager::CreateNewTexture()
+{
+ int nTextureWidth = mWidthFactor * mSubTextureSize;
+ int nTextureHeight = mHeightFactor * mSubTextureSize;
+ maFixedTextures.push_back(std::make_unique<FixedTexture>(nTextureWidth, nTextureHeight, mWidthFactor * mHeightFactor));
+}
+
+OpenGLTexture FixedTextureAtlasManager::Reserve(int nWidth, int nHeight)
+{
+ FixedTexture* pFixedTexture = nullptr;
+
+ auto funFreeSlot = [] (std::unique_ptr<FixedTexture>& inFixedTexture)
+ {
+ return inFixedTexture->mnFreeSlots > 0;
+ };
+
+ auto it = std::find_if(maFixedTextures.begin(), maFixedTextures.end(), funFreeSlot);
+
+ if (it != maFixedTextures.end())
+ {
+ pFixedTexture = (*it).get();
+ }
+ else
+ {
+ CreateNewTexture();
+ pFixedTexture = maFixedTextures.back().get();
+ }
+
+ int nSlot = pFixedTexture->findAndAllocateFreeSlot();
+
+ // Calculate coordinates in texture
+ int nX = (nSlot % mWidthFactor) * mSubTextureSize;
+ int nY = (nSlot / mWidthFactor) * mSubTextureSize;
+
+ tools::Rectangle aRectangle(Point(nX, nY), Size(nWidth, nHeight));
+
+ return OpenGLTexture(pFixedTexture->mpTexture, aRectangle, nSlot);
+}
+
+OpenGLTexture FixedTextureAtlasManager::InsertBuffer(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8 const * 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/LineRenderUtils.cxx b/vcl/opengl/LineRenderUtils.cxx
new file mode 100644
index 000000000..e130fb93b
--- /dev/null
+++ b/vcl/opengl/LineRenderUtils.cxx
@@ -0,0 +1,189 @@
+/* -*- 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 <opengl/LineRenderUtils.hxx>
+#include <opengl/VertexUtils.hxx>
+
+namespace vcl
+{
+
+LineBuilder::LineBuilder(std::vector<Vertex>& rVertices, std::vector<GLuint>& rIndices,
+ Color nColor, GLfloat fTransparency,
+ GLfloat fLineWidth, bool bUseAA)
+ : mrVertices(rVertices)
+ , mrIndices(rIndices)
+ , mR(nColor.GetRed())
+ , mG(nColor.GetGreen())
+ , mB(nColor.GetBlue())
+ , mA((1.0f - fTransparency) * 255.0f)
+ , mfLineWidth(fLineWidth)
+ , mfLineWidthAndAA(bUseAA ? fLineWidth : -fLineWidth)
+ , mnInitialIndexSize(rIndices.size())
+ , mbIncomplete(false)
+{
+}
+
+void LineBuilder::appendLineSegment(const glm::vec2& rPoint1, const glm::vec2& rNormal1, GLfloat aExtrusion1,
+ const glm::vec2& rPoint2, const glm::vec2& rNormal2, GLfloat aExtrusion2)
+{
+ GLuint zero = mrVertices.size();
+
+ mrVertices.insert(mrVertices.end(), {
+ {rPoint1, glm::vec4{mR, mG, mB, mA}, glm::vec4{-rNormal1.x, -rNormal1.y, -aExtrusion1, mfLineWidthAndAA}},
+ {rPoint1, glm::vec4{mR, mG, mB, mA}, glm::vec4{ rNormal1.x, rNormal1.y, aExtrusion1, mfLineWidthAndAA}},
+ {rPoint2, glm::vec4{mR, mG, mB, mA}, glm::vec4{-rNormal2.x, -rNormal2.y, -aExtrusion2, mfLineWidthAndAA}},
+ {rPoint2, glm::vec4{mR, mG, mB, mA}, glm::vec4{ rNormal2.x, rNormal2.y, aExtrusion2, mfLineWidthAndAA}},
+ });
+
+ mrIndices.insert(mrIndices.end(), {
+ zero + 0, zero + 1, zero + 2,
+ zero + 2, zero + 1, zero + 3
+ });
+
+}
+
+void LineBuilder::appendLine(const glm::vec2& rPoint1, const glm::vec2& rPoint2)
+{
+ glm::vec2 aLineVector = vcl::vertex::normalize(rPoint2 - rPoint1);
+ glm::vec2 aNormal = vcl::vertex::perpendicular(aLineVector);
+
+ appendLineSegment(rPoint1, aNormal, 1.0f,
+ rPoint2, aNormal, 1.0f);
+}
+
+void LineBuilder::appendAndConnectLinePoint(const glm::vec2& rPoint, const glm::vec2& aNormal, GLfloat aExtrusion)
+{
+ GLuint zero = mrVertices.size();
+
+ mrVertices.insert(mrVertices.end(), {
+ {rPoint, glm::vec4{mR, mG, mB, mA}, glm::vec4{-aNormal.x, -aNormal.y, -aExtrusion, mfLineWidthAndAA}},
+ {rPoint, glm::vec4{mR, mG, mB, mA}, glm::vec4{ aNormal.x, aNormal.y, aExtrusion, mfLineWidthAndAA}},
+ });
+
+ if (mnInitialIndexSize == mrIndices.size())
+ {
+ mrIndices.insert(mrIndices.end(), {
+ zero + 0, zero + 1
+ });
+ mbIncomplete = true;
+ }
+ else
+ {
+ if (mbIncomplete)
+ {
+ mrIndices.insert(mrIndices.end(), {
+ zero + 0,
+ zero + 0, zero - 1, zero + 1
+ });
+ mbIncomplete = false;
+ }
+ else
+ {
+ mrIndices.insert(mrIndices.end(), {
+ zero - 2, zero - 1, zero + 0,
+ zero + 0, zero - 1, zero + 1
+ });
+ }
+ }
+}
+
+void LineBuilder::appendMiterJoint(glm::vec2 const& point, const glm::vec2& prevLineVector,
+ glm::vec2 const& nextLineVector)
+{
+ // With miter join we calculate the extrusion vector by adding normals of
+ // previous and next line segment. The vector shows the way but we also
+ // need the length (otherwise the line will be deformed). Length factor is
+ // calculated as dot product of extrusion vector and one of the normals.
+ // The value we get is the inverse length (used in the shader):
+ // length = line_width / dot(extrusionVector, normal)
+
+ glm::vec2 normal(-prevLineVector.y, prevLineVector.x);
+
+ glm::vec2 tangent = vcl::vertex::normalize(nextLineVector + prevLineVector);
+ glm::vec2 extrusionVector(-tangent.y, tangent.x);
+ GLfloat length = glm::dot(extrusionVector, normal);
+
+ appendAndConnectLinePoint(point, extrusionVector, length);
+}
+
+void LineBuilder::appendBevelJoint(glm::vec2 const& point, const glm::vec2& prevLineVector,
+ const glm::vec2& nextLineVector)
+{
+ // For bevel join we just add 2 additional vertices and use previous
+ // line segment normal and next line segment normal as extrusion vector.
+ // All the magic is done by the fact that we draw triangle strips, so we
+ // cover the joins correctly.
+
+ glm::vec2 prevNormal(-prevLineVector.y, prevLineVector.x);
+ glm::vec2 nextNormal(-nextLineVector.y, nextLineVector.x);
+
+ appendAndConnectLinePoint(point, prevNormal, 1.0f);
+ appendAndConnectLinePoint(point, nextNormal, 1.0f);
+}
+
+void LineBuilder::appendRoundJoint(glm::vec2 const& point, const glm::vec2& prevLineVector,
+ const glm::vec2& nextLineVector)
+{
+ // For round join we do a similar thing as in bevel, we add more intermediate
+ // vertices and add normals to get extrusion vectors in the between the
+ // both normals.
+
+ // 3 additional extrusion vectors + normals are enough to make most
+ // line joins look round. Ideally the number of vectors could be
+ // calculated.
+
+ glm::vec2 prevNormal(-prevLineVector.y, prevLineVector.x);
+ glm::vec2 nextNormal(-nextLineVector.y, nextLineVector.x);
+
+ glm::vec2 middle = vcl::vertex::normalize(prevNormal + nextNormal);
+ glm::vec2 middleLeft = vcl::vertex::normalize(prevNormal + middle);
+ glm::vec2 middleRight = vcl::vertex::normalize(middle + nextNormal);
+
+ appendAndConnectLinePoint(point, prevNormal, 1.0f);
+ appendAndConnectLinePoint(point, middleLeft, 1.0f);
+ appendAndConnectLinePoint(point, middle, 1.0f);
+ appendAndConnectLinePoint(point, middleRight, 1.0f);
+ appendAndConnectLinePoint(point, nextNormal, 1.0f);
+}
+
+void LineBuilder::appendRoundLineCapVertices(const glm::vec2& rPoint1, const glm::vec2& rPoint2)
+{
+ constexpr int nRoundCapIteration = 12;
+
+ glm::vec2 lineVector = vcl::vertex::normalize(rPoint2 - rPoint1);
+ glm::vec2 normal(-lineVector.y, lineVector.x);
+ glm::vec2 previousRoundNormal = normal;
+
+ for (int nFactor = 1; nFactor <= nRoundCapIteration; nFactor++)
+ {
+ float angle = float(nFactor) * (M_PI / float(nRoundCapIteration));
+ glm::vec2 roundNormal(normal.x * glm::cos(angle) - normal.y * glm::sin(angle),
+ normal.x * glm::sin(angle) + normal.y * glm::cos(angle));
+
+ appendLineSegment(rPoint1, previousRoundNormal, 1.0f,
+ rPoint1, roundNormal, 1.0f);
+ previousRoundNormal = roundNormal;
+ }
+}
+
+void LineBuilder::appendSquareLineCapVertices(const glm::vec2& rPoint1, const glm::vec2& rPoint2)
+{
+ glm::vec2 lineVector = vcl::vertex::normalize(rPoint2 - rPoint1);
+ glm::vec2 normal(-lineVector.y, lineVector.x);
+
+ glm::vec2 extrudedPoint = rPoint1 + -lineVector * (mfLineWidth / 2.0f);
+
+ appendLineSegment(extrudedPoint, normal, 1.0f,
+ rPoint1, normal, 1.0f);
+}
+
+} // end vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
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: */
diff --git a/vcl/opengl/README.deprecated b/vcl/opengl/README.deprecated
new file mode 100644
index 000000000..eb033a0fd
--- /dev/null
+++ b/vcl/opengl/README.deprecated
@@ -0,0 +1,23 @@
+deprecated features
+
+GL_LIGHTING
+GL_TEXTURE_2D
+GL_POINT_SMOOTH
+GL_TEXTURE_WRAP_S
+GL_TEXTURE_WRAP_T
+glBegin
+glEnd
+
+
+GLSL
+
+texture*D
+varying
+attribute
+missing version string
+
+gl_FragColor
+gl_FragData
+gl_Normal
+gl_NormalMatrix
+gl_Vertex
diff --git a/vcl/opengl/README.opengl b/vcl/opengl/README.opengl
new file mode 100644
index 000000000..231abbf36
--- /dev/null
+++ b/vcl/opengl/README.opengl
@@ -0,0 +1,26 @@
+Run LO with OpenGL enabled
+--------------------------
+SAL_USE_VCLPLUGIN=gen SAL_FORCEGL=1 ./soffice
+
+Environment variables used:
+
+SAL_USE_VCLPLUGIN - use the specified VCL plugin (GTK2 in this case - currently
+needed on Linux because the default GTK3 doesn't support OpenGL yet)
+
+SAL_FORCEGL - enable OpenGL even if the card is blacklisted.
+
+Other variables:
+
+LIBGL_ALWAYS_SOFTWARE=1 - on Linux+Mesa forces the software renderer to be used
+(this is useful as an alternative to spot driver specific bugs)
+
+SAL_LOG=+INFO.vcl.opengl - if "--enable-dbgutil" is used, this can show OpenGL
+various rendering messages.
+
+LD_PRELOAD=/usr/lib64/apitrace/wrappers/glxtrace.so - preload the wrapper for
+APItrace. The path is the default used in Fedora 21+.
+
+Run VCLDemo
+-----------
+
+SAL_USE_VCLPLUGIN=gen SAL_FORCEGL=1 ./bin/run vcldemo
diff --git a/vcl/opengl/RenderList.cxx b/vcl/opengl/RenderList.cxx
new file mode 100644
index 000000000..4830f1040
--- /dev/null
+++ b/vcl/opengl/RenderList.cxx
@@ -0,0 +1,403 @@
+/* -*- 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 <opengl/RenderList.hxx>
+#include <opengl/VertexUtils.hxx>
+#include <opengl/LineRenderUtils.hxx>
+
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontriangulator.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <basegfx/polygon/b2dtrapezoid.hxx>
+
+namespace
+{
+
+/** Append vertices for the polyline
+ *
+ * OpenGL polyline drawing algorithm inspired by:
+ * - http://mattdesl.svbtle.com/drawing-lines-is-hard
+ * - https://www.mapbox.com/blog/drawing-antialiased-lines/
+ * - https://cesiumjs.org/2013/04/22/Robust-Polyline-Rendering-with-WebGL/
+ * - http://artgrammer.blogspot.si/2011/05/drawing-nearly-perfect-2d-line-segments.html
+ * - http://artgrammer.blogspot.si/2011/07/drawing-polylines-by-tessellation.html
+ *
+ */
+void appendPolyLine(vcl::LineBuilder& rBuilder, const basegfx::B2DPolygon& rPolygon,
+ basegfx::B2DLineJoin eLineJoin, css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle)
+{
+ sal_uInt32 nPoints = rPolygon.count();
+ bool bClosed = rPolygon.isClosed();
+
+ if (nPoints == 2 || eLineJoin == basegfx::B2DLineJoin::NONE)
+ {
+ // If line joint is NONE or a simple line with 2 points, draw the polyline
+ // each line segment separately.
+
+ for (sal_uInt32 i = 0; i < (bClosed ? nPoints : nPoints - 1); ++i)
+ {
+ sal_uInt32 index1 = (i + 0) % nPoints; // loop indices - important when polyline is closed
+ sal_uInt32 index2 = (i + 1) % nPoints;
+
+ glm::vec2 aPoint1(rPolygon.getB2DPoint(index1).getX(), rPolygon.getB2DPoint(index1).getY());
+ glm::vec2 aPoint2(rPolygon.getB2DPoint(index2).getX(), rPolygon.getB2DPoint(index2).getY());
+
+ rBuilder.appendLine(aPoint1, aPoint2);
+ }
+ }
+ else if (nPoints > 2)
+ {
+ int i = 0;
+ int lastPoint = int(nPoints);
+
+ glm::vec2 p0(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY());
+ glm::vec2 p1(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY());
+ glm::vec2 p2(rPolygon.getB2DPoint(1).getX(), rPolygon.getB2DPoint(1).getY());
+
+ glm::vec2 nextLineVector;
+ glm::vec2 previousLineVector;
+ glm::vec2 normal; // perpendicular to the line vector
+
+ nextLineVector = vcl::vertex::normalize(p2 - p1);
+
+ if (!bClosed)
+ {
+ normal = glm::vec2(-nextLineVector.y, nextLineVector.x); // make perpendicular
+ rBuilder.appendAndConnectLinePoint(p1, normal, 1.0f);
+
+ i++; // first point done already
+ lastPoint--; // last point will be calculated separately from the loop
+
+ p0 = p1;
+ previousLineVector = nextLineVector;
+ }
+ else
+ {
+ lastPoint++; // we need to connect last point to first point so one more line segment to calculate
+ previousLineVector = vcl::vertex::normalize(p1 - p0);
+ }
+
+ for (; i < lastPoint; ++i)
+ {
+ int index1 = (i + 0) % nPoints; // loop indices - important when polyline is closed
+ int index2 = (i + 1) % nPoints;
+
+ p1 = glm::vec2(rPolygon.getB2DPoint(index1).getX(), rPolygon.getB2DPoint(index1).getY());
+ p2 = glm::vec2(rPolygon.getB2DPoint(index2).getX(), rPolygon.getB2DPoint(index2).getY());
+
+ if (p1 == p2) // skip equal points, normals could div-by-0
+ continue;
+
+ nextLineVector = vcl::vertex::normalize(p2 - p1);
+
+ if (eLineJoin == basegfx::B2DLineJoin::Miter)
+ {
+ if (vcl::vertex::lineVectorAngle(previousLineVector, nextLineVector) < fMiterMinimumAngle)
+ rBuilder.appendBevelJoint(p1, previousLineVector, nextLineVector);
+ else
+ rBuilder.appendMiterJoint(p1, previousLineVector, nextLineVector);
+ }
+ else if (eLineJoin == basegfx::B2DLineJoin::Bevel)
+ {
+ rBuilder.appendBevelJoint(p1, previousLineVector, nextLineVector);
+ }
+ else if (eLineJoin == basegfx::B2DLineJoin::Round)
+ {
+ rBuilder.appendRoundJoint(p1, previousLineVector, nextLineVector);
+ }
+ p0 = p1;
+ previousLineVector = nextLineVector;
+ }
+
+ if (!bClosed)
+ {
+ // Create vertices for the last point. There is no line join so just
+ // use the last line segment normal as the extrusion vector.
+ p1 = glm::vec2(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY());
+ normal = glm::vec2(-previousLineVector.y, previousLineVector.x);
+ rBuilder.appendAndConnectLinePoint(p1, normal, 1.0f);
+ }
+ }
+
+ if (!bClosed && nPoints >= 2 && (eLineCap == css::drawing::LineCap_ROUND || eLineCap == css::drawing::LineCap_SQUARE))
+ {
+ glm::vec2 aBeginCapPoint1(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY());
+ glm::vec2 aBeginCapPoint2(rPolygon.getB2DPoint(1).getX(), rPolygon.getB2DPoint(1).getY());
+
+ glm::vec2 aEndCapPoint1(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY());
+ glm::vec2 aEndCapPoint2(rPolygon.getB2DPoint(nPoints - 2).getX(), rPolygon.getB2DPoint(nPoints - 2).getY());
+
+ if (eLineCap == css::drawing::LineCap_ROUND)
+ {
+ rBuilder.appendRoundLineCapVertices(aBeginCapPoint1, aBeginCapPoint2);
+ rBuilder.appendRoundLineCapVertices(aEndCapPoint1, aEndCapPoint2);
+ }
+ else if (eLineCap == css::drawing::LineCap_SQUARE)
+ {
+ rBuilder.appendSquareLineCapVertices(aBeginCapPoint1, aBeginCapPoint2);
+ rBuilder.appendSquareLineCapVertices(aEndCapPoint1, aEndCapPoint2);
+ }
+ }
+}
+
+void appendTrapezoid(std::vector<Vertex>& rVertices, std::vector<GLuint>& rIndices,
+ GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2,
+ GLfloat x3, GLfloat y3, GLfloat x4, GLfloat y4,
+ Color nColor, GLfloat fTransparency)
+{
+ GLubyte nR, nG, nB, nA;
+ vcl::vertex::createColor(nColor, fTransparency, nR, nG, nB, nA);
+
+ GLuint zero = rVertices.size();
+
+ rVertices.insert(rVertices.end(), {
+ {glm::vec2{x1, y1}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}},
+ {glm::vec2{x2, y2}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}},
+ {glm::vec2{x3, y3}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}},
+ {glm::vec2{x4, y4}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}},
+ });
+
+ rIndices.insert(rIndices.end(), {
+ zero + 0, zero + 1, zero + 2,
+ zero + 2, zero + 1, zero + 3
+ });
+}
+
+void appendRectangle(std::vector<Vertex>& rVertices, std::vector<GLuint>& rIndices,
+ GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2,
+ Color nColor, GLfloat fTransparency)
+{
+ GLubyte nR, nG, nB, nA;
+ vcl::vertex::createColor(nColor, fTransparency, nR, nG, nB, nA);
+
+ GLuint zero = rVertices.size();
+
+ rVertices.insert(rVertices.end(), {
+ {glm::vec2{x1, y1}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}},
+ {glm::vec2{x2, y1}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}},
+ {glm::vec2{x1, y2}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}},
+ {glm::vec2{x2, y2}, glm::vec4{nR, nG, nB, nA}, glm::vec4{0.0f, 0.0f, 0.0f, 0.0f}},
+ });
+
+ rIndices.insert(rIndices.end(), {
+ zero + 0, zero + 1, zero + 2,
+ zero + 2, zero + 1, zero + 3
+ });
+}
+
+} // end anonymous namespace
+
+void RenderList::addDrawPixel(long nX, long nY, Color nColor)
+{
+ if (nColor == SALCOLOR_NONE)
+ return;
+
+ checkOverlapping(basegfx::B2DRange(nX, nY, nX, nY));
+
+ RenderParameters& rRenderParameter = maRenderEntries.back().maTriangleParameters;
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ nX - 0.5f, nY - 0.5f, nX + 0.5f, nY + 0.5f, nColor, 0.0f);
+}
+
+void RenderList::addDrawRectangle(long nX, long nY, long nWidth, long nHeight, double fTransparency,
+ Color nLineColor, Color nFillColor)
+{
+ if (nLineColor == SALCOLOR_NONE && nFillColor == SALCOLOR_NONE)
+ return;
+ if (fTransparency == 1.0f)
+ return;
+
+ GLfloat fX1(nX);
+ GLfloat fY1(nY);
+ GLfloat fX2(nX + nWidth - 1);
+ GLfloat fY2(nY + nHeight - 1);
+
+ checkOverlapping(basegfx::B2DRange(fX1, fY1, fX2, fY2));
+
+ RenderParameters& rRenderParameter = maRenderEntries.back().maTriangleParameters;
+
+ // Draw rectangle stroke with line color
+ if (nLineColor != SALCOLOR_NONE)
+ {
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ fX1 - 0.5f, fY1 - 0.5f, fX1 + 0.5f, fY2 + 0.5f, nLineColor, fTransparency);
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ fX1 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY1 + 0.5f, nLineColor, fTransparency);
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ fX2 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY2 + 0.5f, nLineColor, fTransparency);
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ fX1 - 0.5f, fY2 - 0.5f, fX2 + 0.5f, fY2 + 0.5f, nLineColor, fTransparency);
+ }
+
+ if (nFillColor != SALCOLOR_NONE)
+ {
+ if (nLineColor == SALCOLOR_NONE)
+ {
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ fX1 - 0.5f, fY1 - 0.5f, fX1 + 0.5f, fY2 + 0.5f, nFillColor, fTransparency);
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ fX1 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY1 + 0.5f, nFillColor, fTransparency);
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ fX2 - 0.5f, fY1 - 0.5f, fX2 + 0.5f, fY2 + 0.5f, nFillColor, fTransparency);
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ fX1 - 0.5f, fY2 - 0.5f, fX2 + 0.5f, fY2 + 0.5f, nFillColor, fTransparency);
+ }
+ // Draw rectangle fill with fill color
+ appendRectangle(rRenderParameter.maVertices, rRenderParameter.maIndices,
+ fX1 + 0.5f, fY1 + 0.5f, fX2 - 0.5f, fY2 - 0.5f, nFillColor, fTransparency);
+ }
+}
+
+void RenderList::addDrawLine(long nX1, long nY1, long nX2, long nY2, Color nLineColor, bool bUseAA)
+{
+ if (nLineColor == SALCOLOR_NONE)
+ return;
+
+ checkOverlapping(basegfx::B2DRange(nX1, nY1, nX2, nY2));
+
+ RenderParameters& rRenderParameter = maRenderEntries.back().maLineParameters;
+
+ glm::vec2 aPoint1(nX1, nY1);
+ glm::vec2 aPoint2(nX2, nY2);
+
+ vcl::LineBuilder aBuilder(rRenderParameter.maVertices, rRenderParameter.maIndices, nLineColor, 0.0f, 1.0f, bUseAA);
+ aBuilder.appendLine(aPoint1, aPoint2);
+}
+
+void RenderList::addDrawPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency,
+ Color nLineColor, Color nFillColor, bool bUseAA)
+{
+ if (rPolyPolygon.count() <= 0)
+ return;
+ if (nLineColor == SALCOLOR_NONE && nFillColor == SALCOLOR_NONE)
+ return;
+ if (fTransparency == 1.0)
+ return;
+
+ checkOverlapping(rPolyPolygon.getB2DRange());
+
+ if (nFillColor != SALCOLOR_NONE)
+ {
+ basegfx::B2DTrapezoidVector aTrapezoidVector;
+ basegfx::utils::trapezoidSubdivide(aTrapezoidVector, rPolyPolygon);
+
+ if (!aTrapezoidVector.empty())
+ {
+ RenderParameters& rTriangleRenderParameter = maRenderEntries.back().maTriangleParameters;
+
+ for (const basegfx::B2DTrapezoid & rTrapezoid : aTrapezoidVector)
+ {
+ GLfloat topX1 = rTrapezoid.getTopXLeft();
+ GLfloat topX2 = rTrapezoid.getTopXRight();
+ GLfloat topY = rTrapezoid.getTopY();
+
+ GLfloat bottomX1 = rTrapezoid.getBottomXLeft();
+ GLfloat bottomX2 = rTrapezoid.getBottomXRight();
+ GLfloat bottomY = rTrapezoid.getBottomY();
+
+ appendTrapezoid(rTriangleRenderParameter.maVertices, rTriangleRenderParameter.maIndices,
+ topX1, topY, topX2, topY,
+ bottomX1, bottomY, bottomX2, bottomY,
+ nFillColor, fTransparency);
+ }
+ }
+ }
+
+ if (nLineColor != SALCOLOR_NONE || bUseAA)
+ {
+ RenderParameters& rLineRenderParameter = maRenderEntries.back().maLineParameters;
+ Color nColor = (nLineColor == SALCOLOR_NONE) ? nFillColor : nLineColor;
+
+ vcl::LineBuilder aBuilder(rLineRenderParameter.maVertices, rLineRenderParameter.maIndices,
+ nColor, fTransparency, 1.0f, bUseAA);
+
+ for (const basegfx::B2DPolygon& rPolygon : rPolyPolygon)
+ {
+ basegfx::B2DPolygon aPolygon(rPolygon);
+ if (rPolygon.areControlPointsUsed())
+ aPolygon = rPolygon.getDefaultAdaptiveSubdivision();
+
+ sal_uInt32 nPoints = aPolygon.count();
+ if (nPoints <= 1)
+ continue;
+
+ GLfloat x1, y1, x2, y2;
+ sal_uInt32 index1, index2;
+
+ for (sal_uInt32 i = 0; i <= nPoints; ++i)
+ {
+ index1 = i % nPoints;
+ index2 = (i + 1) % nPoints;
+
+ x1 = aPolygon.getB2DPoint(index1).getX();
+ y1 = aPolygon.getB2DPoint(index1).getY();
+ x2 = aPolygon.getB2DPoint(index2).getX();
+ y2 = aPolygon.getB2DPoint(index2).getY();
+
+ aBuilder.appendLine(glm::vec2(x1, y1), glm::vec2(x2, y2));
+ }
+ }
+ }
+}
+
+void RenderList::addDrawTextureWithMaskColor(OpenGLTexture const & rTexture, Color nColor, const SalTwoRect& r2Rect)
+{
+ if (!rTexture)
+ return;
+
+ GLfloat fX1 = r2Rect.mnDestX;
+ GLfloat fY1 = r2Rect.mnDestY;
+ GLfloat fX2 = fX1 + r2Rect.mnDestWidth;
+ GLfloat fY2 = fY1 + r2Rect.mnDestHeight;
+
+ checkOverlapping(basegfx::B2DRange(fX1, fY1, fX2, fY2));
+
+ GLuint nTextureId = rTexture.Id();
+
+ RenderTextureParameters& rTextureParameter = maRenderEntries.back().maTextureParametersMap[nTextureId];
+ rTextureParameter.maTexture = rTexture;
+
+ rTexture.FillCoords<GL_TRIANGLES>(rTextureParameter.maTextureCoords, r2Rect);
+
+ vcl::vertex::addRectangle<GL_TRIANGLES>(rTextureParameter.maVertices, fX1, fY1, fX2, fY2);
+ vcl::vertex::addQuadColors<GL_TRIANGLES>(rTextureParameter.maColors, nColor, 0.0f);
+}
+
+void RenderList::addDrawPolyLine(const basegfx::B2DPolygon& rPolygon, double fTransparency,
+ double fLineWidth, basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap, double fMiterMinimumAngle,
+ Color nLineColor, bool bUseAA)
+{
+ if (rPolygon.count() <= 1)
+ return;
+ if (nLineColor == SALCOLOR_NONE)
+ return;
+ if (fTransparency == 1.0)
+ return;
+
+ const bool bIsHairline = fLineWidth <= 1.2;
+ fLineWidth = bIsHairline ? 1.0f : fLineWidth;
+
+ basegfx::B2DPolygon aPolygon(rPolygon);
+ if (rPolygon.areControlPointsUsed())
+ aPolygon = rPolygon.getDefaultAdaptiveSubdivision();
+
+ checkOverlapping(aPolygon.getB2DRange());
+
+ RenderParameters& rParameter = maRenderEntries.back().maLineParameters;
+
+ vcl::LineBuilder aBuilder(rParameter.maVertices, rParameter.maIndices,
+ nLineColor, fTransparency, fLineWidth, bUseAA);
+
+ appendPolyLine(aBuilder, aPolygon, eLineJoin, eLineCap, fMiterMinimumAngle);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/framebuffer.cxx b/vcl/opengl/framebuffer.cxx
new file mode 100644
index 000000000..db957d1a6
--- /dev/null
+++ b/vcl/opengl/framebuffer.cxx
@@ -0,0 +1,108 @@
+/* -*- 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/log.hxx>
+
+#include <opengl/framebuffer.hxx>
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+OpenGLFramebuffer::OpenGLFramebuffer() :
+ mnId( 0 ),
+ mnWidth( 0 ),
+ mnHeight( 0 ),
+ mnAttachedTexture( 0 ),
+ mpPrevFramebuffer( nullptr )
+{
+ glGenFramebuffers( 1, &mnId );
+ CHECK_GL_ERROR();
+ VCL_GL_INFO( "Created framebuffer " << static_cast<int>(mnId) );
+}
+
+OpenGLFramebuffer::~OpenGLFramebuffer()
+{
+ glDeleteFramebuffers( 1, &mnId );
+ VCL_GL_INFO( "Deleted framebuffer " << static_cast<int>(mnId) );
+ CHECK_GL_ERROR();
+}
+
+void OpenGLFramebuffer::Bind(GLenum eTarget)
+{
+ VCL_GL_INFO( "Binding framebuffer " << static_cast<int>(mnId) );
+ glBindFramebuffer(eTarget, mnId);
+ CHECK_GL_ERROR();
+}
+
+void OpenGLFramebuffer::Unbind(GLenum eTarget)
+{
+ glBindFramebuffer(eTarget, 0);
+ CHECK_GL_ERROR();
+ VCL_GL_INFO( "Binding default framebuffer" );
+}
+
+bool OpenGLFramebuffer::IsFree() const
+{
+ return !mnAttachedTexture;
+}
+
+bool OpenGLFramebuffer::IsAttached( GLuint nTexture ) const
+{
+ return mnAttachedTexture == nTexture;
+}
+
+bool OpenGLFramebuffer::IsAttached( const OpenGLTexture& rTexture ) const
+{
+ return mnAttachedTexture == rTexture.Id();
+}
+
+void OpenGLFramebuffer::AttachTexture( const OpenGLTexture& rTexture )
+{
+ if( rTexture.Id() == mnAttachedTexture )
+ return;
+
+ VCL_GL_INFO( "Attaching texture " << rTexture.Id() << " to framebuffer " << static_cast<int>(mnId) );
+ mnAttachedTexture = rTexture.Id();
+ mnWidth = rTexture.GetWidth();
+ mnHeight = rTexture.GetHeight();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mnAttachedTexture, 0);
+ CHECK_GL_ERROR();
+
+ GLuint nStencil = rTexture.StencilId();
+ if( nStencil )
+ {
+ VCL_GL_INFO( "Attaching stencil " << nStencil << " to framebuffer " << static_cast<int>(mnId) );
+ glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, nStencil );
+ CHECK_GL_ERROR();
+ }
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ CHECK_GL_ERROR();
+ if (status != GL_FRAMEBUFFER_COMPLETE)
+ {
+ SAL_WARN("vcl.opengl", "Framebuffer incomplete");
+ }
+}
+
+void OpenGLFramebuffer::DetachTexture()
+{
+ if( mnAttachedTexture != 0 )
+ {
+ mnAttachedTexture = 0;
+ glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0 );
+ CHECK_GL_ERROR();
+
+ // FIXME: we could make this conditional on having a stencil ?
+ glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, 0 );
+ CHECK_GL_ERROR();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
new file mode 100644
index 000000000..6c76154ea
--- /dev/null
+++ b/vcl/opengl/gdiimpl.cxx
@@ -0,0 +1,2315 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <opengl/gdiimpl.hxx>
+#include <opengl/framebuffer.hxx>
+
+#include <vcl/gradient.hxx>
+#include <vcl/idle.hxx>
+#include <salframe.hxx>
+#include <salvd.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dlinegeometry.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontriangulator.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <basegfx/polygon/b2dtrapezoid.hxx>
+#include <sal/log.hxx>
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <salgdi.hxx>
+#include <svdata.hxx>
+#include <opengl/zone.hxx>
+#include <opengl/salbmp.hxx>
+#include <opengl/RenderState.hxx>
+#include <opengl/VertexUtils.hxx>
+#include <opengl/BufferObject.hxx>
+
+#include <cmath>
+#include <vector>
+#include <numeric>
+
+#include <glm/gtc/type_ptr.hpp>
+#include <glm/gtx/norm.hpp>
+
+#include <stdlib.h>
+
+class OpenGLFlushIdle : public Idle
+{
+ OpenGLSalGraphicsImpl *m_pImpl;
+public:
+ explicit OpenGLFlushIdle( OpenGLSalGraphicsImpl *pImpl )
+ : Idle( "gl idle swap" )
+ , m_pImpl( pImpl )
+ {
+ // We don't want to be swapping before we've painted.
+ SetPriority( TaskPriority::POST_PAINT );
+ }
+
+ virtual void Invoke() override
+ {
+ m_pImpl->doFlush();
+ Stop();
+ SetPriority(TaskPriority::HIGHEST);
+ }
+};
+
+OpenGLSalGraphicsImpl::OpenGLSalGraphicsImpl(SalGraphics& rParent, SalGeometryProvider *pProvider)
+ : mrParent(rParent)
+ , mpProvider(pProvider)
+ , mpProgram(nullptr)
+ , mpFlush(new OpenGLFlushIdle(this))
+ , mbUseScissor(false)
+ , mbUseStencil(false)
+ , mbXORMode(false)
+ , mbAcquiringOpenGLContext(false)
+ , mnLineColor(SALCOLOR_NONE)
+ , mnFillColor(SALCOLOR_NONE)
+#ifdef DBG_UTIL
+ , mProgramIsSolidColor(false)
+#endif
+ , mnDrawCount(0)
+ , mnDrawCountAtFlush(0)
+ , mProgramSolidColor(SALCOLOR_NONE)
+ , mProgramSolidTransparency(0.0)
+ , mpRenderList(new RenderList)
+{
+}
+
+OpenGLSalGraphicsImpl::~OpenGLSalGraphicsImpl()
+{
+ if( !IsOffscreen() && mnDrawCountAtFlush != mnDrawCount )
+ VCL_GL_INFO( "Destroying un-flushed on-screen graphics" );
+
+ mpFlush.reset();
+
+ ReleaseContext();
+}
+
+rtl::Reference<OpenGLContext> OpenGLSalGraphicsImpl::GetOpenGLContext()
+{
+ if (mbAcquiringOpenGLContext)
+ return nullptr;
+ mbAcquiringOpenGLContext = true;
+ bool bSuccess = AcquireContext(true);
+ mbAcquiringOpenGLContext = false;
+ if (!bSuccess)
+ return nullptr;
+ return mpContext;
+}
+
+bool OpenGLSalGraphicsImpl::AcquireContext( bool bForceCreate )
+{
+ mpContext = OpenGLContext::getVCLContext( false );
+
+ if( !mpContext.is() && mpWindowContext.is() )
+ {
+ mpContext = mpWindowContext;
+ }
+ else if( bForceCreate && !IsOffscreen() )
+ {
+ mpWindowContext = CreateWinContext();
+ mpContext = mpWindowContext;
+ }
+
+ if( !mpContext.is() )
+ mpContext = OpenGLContext::getVCLContext();
+
+ return mpContext.is();
+}
+
+void OpenGLSalGraphicsImpl::ReleaseContext()
+{
+ mpContext.clear();
+}
+
+void OpenGLSalGraphicsImpl::Init()
+{
+ // Our init phase is strange ::Init is called twice for vdevs.
+ // the first time around with a NULL geometry provider.
+ if( !mpProvider )
+ return;
+
+ // check if we can simply re-use the same context
+ if( mpContext.is() )
+ {
+ if( !UseContext( mpContext ) )
+ ReleaseContext();
+ }
+
+ // Always create the offscreen texture
+ if( maOffscreenTex.GetWidth() != GetWidth() ||
+ maOffscreenTex.GetHeight() != GetHeight() )
+ {
+ // We don't want to be swapping before we've painted.
+ mpFlush->SetPriority( TaskPriority::POST_PAINT );
+
+ if( maOffscreenTex && // don't work to release empty textures
+ mpContext.is() ) // valid context
+ {
+ mpContext->makeCurrent();
+ mpContext->ReleaseFramebuffer( maOffscreenTex );
+ }
+ maOffscreenTex = OpenGLTexture();
+ VCL_GL_INFO("::Init - re-size offscreen texture");
+ }
+
+ if( mpWindowContext.is() )
+ {
+ mpWindowContext->reset();
+ mpWindowContext.clear();
+ }
+}
+
+// Currently only used to get windows ordering right.
+void OpenGLSalGraphicsImpl::DeInit()
+{
+ VCL_GL_INFO("::DeInit");
+
+ FlushDeferredDrawing();
+
+ // tdf#93839:
+ // Our window handles and resources are being free underneath us.
+ // These can be bound into a context, which relies on them. So
+ // let it know. Other eg. VirtualDevice contexts which have
+ // references on and rely on this context continuing to work will
+ // get a shiny new context in AcquireContext:: next PreDraw.
+ if( mpWindowContext.is() )
+ {
+ mpWindowContext->reset();
+ mpWindowContext.clear();
+ }
+ mpContext.clear();
+}
+
+void OpenGLSalGraphicsImpl::PreDraw(XOROption eOpt)
+{
+ FlushDeferredDrawing();
+
+ InitializePreDrawState(eOpt);
+}
+
+void OpenGLSalGraphicsImpl::InitializePreDrawState(XOROption eOpt)
+{
+ OpenGLZone::enter();
+
+ mnDrawCount++;
+
+ if( !AcquireContext() )
+ {
+ SAL_WARN( "vcl.opengl", "Couldn't acquire context" );
+ return;
+ }
+
+ mpContext->makeCurrent();
+ CHECK_GL_ERROR();
+
+ CheckOffscreenTexture();
+ CHECK_GL_ERROR();
+
+ mpContext->state().viewport(tools::Rectangle(Point(0, 0), Size(GetWidth(), GetHeight())));
+
+ ImplInitClipRegion();
+ CHECK_GL_ERROR();
+
+ if (eOpt == IMPLEMENT_XOR && mbXORMode)
+ {
+ glEnable(GL_COLOR_LOGIC_OP);
+ CHECK_GL_ERROR();
+
+ glLogicOp(GL_XOR);
+ CHECK_GL_ERROR();
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
+ CHECK_GL_ERROR();
+ }
+}
+
+void OpenGLSalGraphicsImpl::PostDraw()
+{
+ if (mbXORMode)
+ {
+ glDisable(GL_COLOR_LOGIC_OP);
+ CHECK_GL_ERROR();
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ CHECK_GL_ERROR();
+ }
+
+ if( mpProgram )
+ {
+ mpProgram->Clean();
+ mpProgram = nullptr;
+#ifdef DBG_UTIL
+ mProgramIsSolidColor = false;
+#endif
+ }
+
+ assert (maOffscreenTex);
+
+ // Always queue the flush.
+ if( !IsOffscreen() )
+ flush();
+
+ OpenGLZone::leave();
+}
+
+void OpenGLSalGraphicsImpl::PostBatchDraw()
+{
+ if (IsOffscreen())
+ return;
+
+ if (!mpFlush->IsActive())
+ mpFlush->Start();
+}
+
+void OpenGLSalGraphicsImpl::ApplyProgramMatrices(float fPixelOffset)
+{
+ mpProgram->ApplyMatrix(GetWidth(), GetHeight(), fPixelOffset);
+}
+
+void OpenGLSalGraphicsImpl::freeResources()
+{
+ // TODO Delete shaders, programs and textures if not shared
+ if( mpContext.is() && mpContext->isInitialized() )
+ {
+ VCL_GL_INFO( "freeResources" );
+ mpContext->makeCurrent();
+ FlushDeferredDrawing();
+ mpContext->ReleaseFramebuffer( maOffscreenTex );
+ }
+ ReleaseContext();
+}
+
+void OpenGLSalGraphicsImpl::ImplSetClipBit( const vcl::Region& rClip, GLuint nMask )
+{
+ mpContext->state().scissor().disable();
+ mpContext->state().stencil().enable();
+
+ VCL_GL_INFO( "Adding complex clip / stencil" );
+ GLuint nStencil = maOffscreenTex.StencilId();
+ if( nStencil == 0 )
+ {
+ nStencil = maOffscreenTex.AddStencil();
+ glFramebufferRenderbuffer(
+ GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+ GL_RENDERBUFFER, nStencil );
+ CHECK_GL_ERROR();
+ }
+ // else - we associated the stencil in
+ // AcquireFrameBuffer / AttachTexture
+
+ CHECK_GL_ERROR();
+ glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
+ CHECK_GL_ERROR();
+ glStencilMask( nMask );
+ CHECK_GL_ERROR();
+ glStencilFunc( GL_NEVER, nMask, 0xFF );
+ CHECK_GL_ERROR();
+ glStencilOp( GL_REPLACE, GL_KEEP, GL_KEEP );
+ CHECK_GL_ERROR();
+
+ glClear( GL_STENCIL_BUFFER_BIT );
+ CHECK_GL_ERROR();
+ if( UseSolid( Color( 0xFF, 0xFF, 0xFF ) ) )
+ {
+ if( rClip.getRegionBand() )
+ DrawRegionBand( *rClip.getRegionBand() );
+ else
+ DrawPolyPolygon( rClip.GetAsB2DPolyPolygon(), true );
+ }
+
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+ CHECK_GL_ERROR();
+ glStencilMask( 0x00 );
+ CHECK_GL_ERROR();
+
+ mpContext->state().stencil().disable();
+}
+
+void OpenGLSalGraphicsImpl::ImplInitClipRegion()
+{
+ // make sure the context has the right clipping set
+ if (maClipRegion != mpContext->maClipRegion)
+ {
+ mpContext->maClipRegion = maClipRegion;
+ if (mbUseStencil)
+ {
+ ImplSetClipBit(maClipRegion, 0x01);
+ }
+ }
+
+ if (mbUseScissor)
+ {
+ tools::Rectangle aRect(maClipRegion.GetBoundRect());
+ mpContext->state().scissor().set(aRect.Left(), GetHeight() - aRect.Bottom() - 1, aRect.GetWidth(), aRect.GetHeight());
+ mpContext->state().scissor().enable();
+ }
+ else
+ {
+ mpContext->state().scissor().disable();
+ }
+
+ if (mbUseStencil)
+ {
+ glStencilFunc( GL_EQUAL, 1, 0x1 );
+ CHECK_GL_ERROR();
+ mpContext->state().stencil().enable();
+ }
+ else
+ {
+ mpContext->state().stencil().disable();
+ }
+}
+
+const vcl::Region& OpenGLSalGraphicsImpl::getClipRegion() const
+{
+ return maClipRegion;
+}
+
+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;
+ mbUseScissor = false;
+ if (maClipRegion.IsRectangle())
+ mbUseScissor = true;
+ else if (!maClipRegion.IsEmpty())
+ mbUseStencil = true;
+
+ return true;
+}
+
+// set the clip region to empty
+void OpenGLSalGraphicsImpl::ResetClipRegion()
+{
+ if (maClipRegion.IsEmpty())
+ {
+ VCL_GL_INFO("::ResetClipRegion (no change) ");
+ return;
+ }
+
+ FlushDeferredDrawing();
+
+ VCL_GL_INFO("::ResetClipRegion");
+
+ maClipRegion.SetEmpty();
+ mbUseScissor = false;
+ mbUseStencil = false;
+}
+
+// get the depth of the device
+sal_uInt16 OpenGLSalGraphicsImpl::GetBitCount() const
+{
+ return 32;
+}
+
+// get the width of the device
+long OpenGLSalGraphicsImpl::GetGraphicsWidth() const
+{
+ return GetWidth();
+}
+
+// set the line color to transparent (= don't draw lines)
+void OpenGLSalGraphicsImpl::SetLineColor()
+{
+ if( mnLineColor != SALCOLOR_NONE )
+ {
+ mnLineColor = SALCOLOR_NONE;
+ }
+}
+
+// set the line color to a specific color
+void OpenGLSalGraphicsImpl::SetLineColor( Color nColor )
+{
+ if( mnLineColor != nColor )
+ {
+ mnLineColor = nColor;
+ }
+}
+
+// set the fill color to transparent (= don't fill)
+void OpenGLSalGraphicsImpl::SetFillColor()
+{
+ if( mnFillColor != SALCOLOR_NONE )
+ {
+ mnFillColor = SALCOLOR_NONE;
+ }
+}
+
+// set the fill color to a specific color, shapes will be
+// filled accordingly
+void OpenGLSalGraphicsImpl::SetFillColor( Color nColor )
+{
+ if( mnFillColor != nColor )
+ {
+ mnFillColor = nColor;
+ }
+}
+
+// enable/disable XOR drawing
+void OpenGLSalGraphicsImpl::SetXORMode( bool bSet, bool )
+{
+ if (mbXORMode != bSet)
+ {
+ FlushDeferredDrawing();
+ mbXORMode = bSet;
+ }
+}
+
+void OpenGLSalGraphicsImpl::SetROPLineColor(SalROPColor nROPColor)
+{
+ switch (nROPColor)
+ {
+ case SalROPColor::N0:
+ mnLineColor = Color(0, 0, 0);
+ break;
+ case SalROPColor::N1:
+ mnLineColor = Color(0xff, 0xff, 0xff);
+ break;
+ case SalROPColor::Invert:
+ mnLineColor = Color(0xff, 0xff, 0xff);
+ break;
+ }
+}
+
+void OpenGLSalGraphicsImpl::SetROPFillColor(SalROPColor nROPColor)
+{
+ switch (nROPColor)
+ {
+ case SalROPColor::N0:
+ mnFillColor = Color(0, 0, 0);
+ break;
+ case SalROPColor::N1:
+ mnFillColor = Color(0xff, 0xff, 0xff);
+ break;
+ case SalROPColor::Invert:
+ mnFillColor = Color(0xff, 0xff, 0xff);
+ break;
+ }
+}
+
+void OpenGLSalGraphicsImpl::CheckOffscreenTexture()
+{
+ bool bClearTexture = false;
+
+ VCL_GL_INFO( "Check Offscreen texture" );
+
+ // Always create the offscreen texture
+ if( maOffscreenTex )
+ {
+ if( maOffscreenTex.GetWidth() != GetWidth() ||
+ maOffscreenTex.GetHeight() != GetHeight() )
+ {
+ VCL_GL_INFO( "re-size offscreen texture " << maOffscreenTex.Id() );
+ mpFlush->SetPriority( TaskPriority::POST_PAINT );
+ mpContext->ReleaseFramebuffer( maOffscreenTex );
+ maOffscreenTex = OpenGLTexture();
+ }
+ }
+
+ if( !maOffscreenTex )
+ {
+ VCL_GL_INFO( "create texture of size "
+ << GetWidth() << " x " << GetHeight() );
+ maOffscreenTex = OpenGLTexture( GetWidth(), GetHeight() );
+ bClearTexture = true;
+ }
+
+ if( !maOffscreenTex.IsUnique() )
+ {
+ GLfloat fWidth = GetWidth();
+ GLfloat fHeight = GetHeight();
+ SalTwoRect aPosAry(0, 0, fWidth, fHeight, 0,0, fWidth, fHeight);
+
+ // TODO: lfrb: User GL_ARB_copy_image?
+ OpenGLTexture aNewTex( GetWidth(), GetHeight() );
+
+ mpContext->state().scissor().disable();
+ mpContext->state().stencil().disable();
+
+ mpContext->AcquireFramebuffer( aNewTex );
+ DrawTexture( maOffscreenTex, aPosAry );
+ maOffscreenTex = aNewTex;
+ }
+ else
+ {
+ mpContext->AcquireFramebuffer( maOffscreenTex );
+ CHECK_GL_ERROR();
+
+ if( bClearTexture )
+ {
+ glDrawBuffer( GL_COLOR_ATTACHMENT0 );
+#if OSL_DEBUG_LEVEL > 0 // lets have some red debugging background.
+ GLfloat const clearColor[4] = { 1.0, 0, 0, 0 };
+#else
+ GLfloat const clearColor[4] = { 1.0, 1.0, 1.0, 0 };
+#endif
+ glClearBufferfv( GL_COLOR, 0, clearColor );
+ // FIXME: use glClearTexImage if we have it ?
+ }
+ }
+
+ assert( maOffscreenTex );
+
+ CHECK_GL_ERROR();
+}
+
+bool OpenGLSalGraphicsImpl::UseProgram( const OUString& rVertexShader, const OUString& rFragmentShader, const OString& preamble )
+{
+ if( mpProgram != nullptr )
+ mpProgram->Clean();
+ mpProgram = mpContext->UseProgram( rVertexShader, rFragmentShader, preamble );
+#ifdef DBG_UTIL
+ mProgramIsSolidColor = false; // UseSolid() will set to true if needed
+#endif
+ return ( mpProgram != nullptr );
+}
+
+bool OpenGLSalGraphicsImpl::UseSolid( Color nColor, sal_uInt8 nTransparency )
+{
+ if( nColor == SALCOLOR_NONE )
+ return false;
+ UseSolid();
+ mpProgram->SetColor( "color", nColor, nTransparency );
+#ifdef DBG_UTIL
+ mProgramIsSolidColor = true;
+#endif
+ mProgramSolidColor = nColor;
+ mProgramSolidTransparency = nTransparency / 100.0;
+
+ return true;
+}
+
+bool OpenGLSalGraphicsImpl::UseSolid( Color nColor, double fTransparency )
+{
+ if( nColor == SALCOLOR_NONE )
+ return false;
+ UseSolid();
+ mpProgram->SetColorf( "color", nColor, fTransparency );
+#ifdef DBG_UTIL
+ mProgramIsSolidColor = true;
+#endif
+ mProgramSolidColor = nColor;
+ mProgramSolidTransparency = fTransparency;
+ return true;
+}
+
+void OpenGLSalGraphicsImpl::UseSolid()
+{
+ if (!UseProgram("combinedVertexShader", "combinedFragmentShader"))
+ return;
+ mpProgram->SetShaderType(DrawShaderType::Normal);
+}
+
+bool OpenGLSalGraphicsImpl::UseInvert50()
+{
+ return UseProgram( "dumbVertexShader", "invert50FragmentShader" );
+}
+
+bool OpenGLSalGraphicsImpl::UseSolid( Color nColor )
+{
+ return UseSolid( nColor, 0.0f );
+}
+
+bool OpenGLSalGraphicsImpl::UseInvert( SalInvert nFlags )
+{
+ OpenGLZone aZone;
+
+ if( ( nFlags & SalInvert::N50 ) ||
+ ( nFlags & SalInvert::TrackFrame ) )
+ {
+ // FIXME: Trackframe really should be 2 pix. on/off stipple.
+ if( !UseInvert50() )
+ return false;
+ mpProgram->SetBlendMode( GL_ONE_MINUS_DST_COLOR,
+ GL_ONE_MINUS_SRC_COLOR );
+ }
+ else
+ {
+ if( !UseSolid( Color( 255, 255, 255 ) ) )
+ return false;
+ mpProgram->SetBlendMode( GL_ONE_MINUS_DST_COLOR, GL_ZERO );
+ }
+ return true;
+}
+
+void OpenGLSalGraphicsImpl::DrawLineSegment(float x1, float y1, float x2, float y2)
+{
+ std::vector<GLfloat> aVertices;
+ std::vector<GLfloat> aExtrusionVectors;
+
+ OpenGLZone aZone;
+
+ glm::vec2 aPoint1(x1, y1);
+ glm::vec2 aPoint2(x2, y2);
+
+ glm::vec2 aLineVector = vcl::vertex::normalize(aPoint2 - aPoint1);
+ glm::vec2 aNormal(-aLineVector.y, aLineVector.x);
+
+ vcl::vertex::addLineSegmentVertices(aVertices, aExtrusionVectors,
+ aPoint1, aNormal, 1.0f,
+ aPoint2, aNormal, 1.0f);
+
+ ApplyProgramMatrices(0.5f);
+ mpProgram->SetExtrusionVectors(aExtrusionVectors.data());
+ mpProgram->DrawArrays(GL_TRIANGLES, aVertices);
+
+ CHECK_GL_ERROR();
+}
+
+bool OpenGLSalGraphicsImpl::UseLine(Color nColor, double fTransparency, GLfloat fLineWidth, bool bUseAA)
+{
+ if( nColor == SALCOLOR_NONE )
+ return false;
+ UseLine(fLineWidth, bUseAA);
+ mpProgram->SetColorf("color", nColor, fTransparency);
+#ifdef DBG_UTIL
+ mProgramIsSolidColor = true;
+#endif
+ mProgramSolidColor = nColor;
+ mProgramSolidTransparency = fTransparency;
+ return true;
+}
+
+void OpenGLSalGraphicsImpl::UseLine(GLfloat fLineWidth, bool bUseAA)
+{
+ if (!UseProgram("combinedVertexShader", "combinedFragmentShader"))
+ return;
+ mpProgram->SetShaderType(DrawShaderType::Line);
+ mpProgram->SetUniform1f("line_width", fLineWidth);
+ // The width of the feather - area we make linearly transparent in VS.
+ // Good AA value is 0.5f, no AA if feather 0.0f
+ mpProgram->SetUniform1f("feather", bUseAA ? 0.5f : 0.0f);
+ // We need blending or AA won't work correctly
+ mpProgram->SetBlendMode(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+void OpenGLSalGraphicsImpl::DrawConvexPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry, bool blockAA )
+{
+ OpenGLZone aZone;
+
+ std::vector<GLfloat> aVertices(nPoints * 2);
+ sal_uInt32 i, j;
+
+ for( i = 0, j = 0; i < nPoints; i++, j += 2 )
+ {
+ aVertices[j] = GLfloat(pPtAry[i].mnX);
+ aVertices[j+1] = GLfloat(pPtAry[i].mnY);
+ }
+
+ ApplyProgramMatrices();
+ std::vector<GLfloat> aExtrusion(nPoints * 3, 0);
+ mpProgram->SetExtrusionVectors(aExtrusion.data());
+ mpProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
+ CHECK_GL_ERROR();
+
+ if( !blockAA && mrParent.getAntiAliasB2DDraw())
+ {
+ // Make the edges antialiased by drawing the edge lines again with AA.
+ // TODO: If transparent drawing is set up, drawing the lines themselves twice
+ // may be a problem, if that is a real problem, the polygon areas itself needs to be
+ // masked out for this or something.
+#ifdef DBG_UTIL
+ assert( mProgramIsSolidColor );
+#endif
+ Color lastSolidColor = mProgramSolidColor;
+ double lastSolidTransparency = mProgramSolidTransparency;
+ if (UseLine(lastSolidColor, lastSolidTransparency, 1.0f, true))
+ {
+ for( i = 0; i < nPoints; ++i )
+ {
+ const SalPoint& rPt1 = pPtAry[ i ];
+ const SalPoint& rPt2 = pPtAry[ ( i + 1 ) % nPoints ];
+ DrawLineSegment(rPt1.mnX, rPt1.mnY, rPt2.mnX, rPt2.mnY);
+ }
+ UseSolid( lastSolidColor, lastSolidTransparency );
+ }
+ }
+}
+
+void OpenGLSalGraphicsImpl::DrawConvexPolygon( const tools::Polygon& rPolygon, bool blockAA )
+{
+ OpenGLZone aZone;
+
+ sal_uInt16 nPoints = rPolygon.GetSize() - 1;
+ std::vector<GLfloat> aVertices(nPoints * 2);
+ sal_uInt32 i, j;
+
+ for( i = 0, j = 0; i < nPoints; i++, j += 2 )
+ {
+ const Point& rPt = rPolygon.GetPoint( i );
+ aVertices[j] = GLfloat(rPt.X());
+ aVertices[j+1] = GLfloat(rPt.Y());
+ }
+
+ ApplyProgramMatrices();
+ std::vector<GLfloat> aExtrusion(nPoints * 3, 0);
+ mpProgram->SetExtrusionVectors(aExtrusion.data());
+ mpProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
+ CHECK_GL_ERROR();
+
+ if( !blockAA && mrParent.getAntiAliasB2DDraw())
+ {
+ // Make the edges antialiased by drawing the edge lines again with AA.
+ // TODO: If transparent drawing is set up, drawing the lines themselves twice
+ // may be a problem, if that is a real problem, the polygon areas itself needs to be
+ // masked out for this or something.
+#ifdef DBG_UTIL
+ assert( mProgramIsSolidColor );
+#endif
+ Color lastSolidColor = mProgramSolidColor;
+ double lastSolidTransparency = mProgramSolidTransparency;
+ if (UseLine(lastSolidColor, lastSolidTransparency, 1.0f, true))
+ {
+ for( i = 0; i < nPoints; ++i )
+ {
+ const Point& rPt1 = rPolygon.GetPoint( i );
+ const Point& rPt2 = rPolygon.GetPoint(( i + 1 ) % nPoints );
+ DrawLineSegment(rPt1.getX(), rPt1.getY(), rPt2.getX(), rPt2.getY());
+ }
+ UseSolid( lastSolidColor, lastSolidTransparency );
+ }
+ }
+}
+
+void OpenGLSalGraphicsImpl::DrawTrapezoid( const basegfx::B2DTrapezoid& trapezoid, bool blockAA )
+{
+ OpenGLZone aZone;
+
+ const basegfx::B2DPolygon& rPolygon = trapezoid.getB2DPolygon();
+ sal_uInt16 nPoints = rPolygon.count();
+ std::vector<GLfloat> aVertices(nPoints * 2);
+ sal_uInt32 i, j;
+
+ for( i = 0, j = 0; i < nPoints; i++, j += 2 )
+ {
+ const basegfx::B2DPoint& rPt = rPolygon.getB2DPoint( i );
+ aVertices[j] = GLfloat(rPt.getX());
+ aVertices[j+1] = GLfloat(rPt.getY());
+ }
+
+ if (!mpProgram)
+ {
+ SAL_WARN("vcl.opengl", "OpenGLSalGraphicsImpl::DrawTrapezoid: mpProgram is 0");
+ return;
+ }
+
+ ApplyProgramMatrices();
+ std::vector<GLfloat> aExtrusion(nPoints * 3, 0);
+ mpProgram->SetExtrusionVectors(aExtrusion.data());
+ mpProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
+ CHECK_GL_ERROR();
+
+ if( !blockAA && mrParent.getAntiAliasB2DDraw())
+ {
+ // Make the edges antialiased by drawing the edge lines again with AA.
+ // TODO: If transparent drawing is set up, drawing the lines themselves twice
+ // may be a problem, if that is a real problem, the polygon areas itself needs to be
+ // masked out for this or something.
+#ifdef DBG_UTIL
+ assert( mProgramIsSolidColor );
+#endif
+ Color lastSolidColor = mProgramSolidColor;
+ double lastSolidTransparency = mProgramSolidTransparency;
+ if (UseLine(lastSolidColor, lastSolidTransparency, 1.0f, true))
+ {
+ for( i = 0; i < nPoints; ++i )
+ {
+ const basegfx::B2DPoint& rPt1 = rPolygon.getB2DPoint( i );
+ const basegfx::B2DPoint& rPt2 = rPolygon.getB2DPoint(( i + 1 ) % nPoints );
+ DrawLineSegment(rPt1.getX(), rPt1.getY(), rPt2.getX(), rPt2.getY());
+ }
+ UseSolid( lastSolidColor, lastSolidTransparency );
+ }
+ }
+}
+
+void OpenGLSalGraphicsImpl::DrawRect( long nX, long nY, long nWidth, long nHeight )
+{
+ long nX1( nX );
+ long nY1( nY );
+ long nX2( nX + nWidth );
+ long nY2( nY + nHeight );
+ const SalPoint aPoints[] = { { nX1, nY2 }, { nX1, nY1 },
+ { nX2, nY1 }, { nX2, nY2 }};
+
+ DrawConvexPolygon( 4, aPoints, true );
+}
+
+void OpenGLSalGraphicsImpl::DrawRect( const tools::Rectangle& rRect )
+{
+ long nX1( rRect.Left() );
+ long nY1( rRect.Top() );
+ long nX2( rRect.Right() );
+ long nY2( rRect.Bottom() );
+ const SalPoint aPoints[] = { { nX1, nY2 }, { nX1, nY1 },
+ { nX2, nY1 }, { nX2, nY2 }};
+
+ DrawConvexPolygon( 4, aPoints, true );
+}
+
+void OpenGLSalGraphicsImpl::DrawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
+{
+ basegfx::B2DPolygon aPolygon;
+
+ for( sal_uInt32 i = 0; i < nPoints; i++ )
+ aPolygon.append( basegfx::B2DPoint( pPtAry[i].mnX, pPtAry[i].mnY ) );
+ aPolygon.setClosed( true );
+
+ if( basegfx::utils::isConvex( aPolygon ) )
+ {
+ if( nPoints > 2 )
+ DrawConvexPolygon( nPoints, pPtAry );
+ }
+ else
+ {
+ const basegfx::B2DPolyPolygon aPolyPolygon( aPolygon );
+ DrawPolyPolygon( aPolyPolygon );
+ }
+}
+
+void OpenGLSalGraphicsImpl::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon, bool blockAA )
+{
+ const basegfx::B2DPolyPolygon& aSimplePolyPolygon = ::basegfx::utils::solveCrossovers( rPolyPolygon );
+ basegfx::B2DTrapezoidVector aB2DTrapVector;
+ basegfx::utils::trapezoidSubdivide( aB2DTrapVector, aSimplePolyPolygon );
+ // draw tessellation result
+ for(const basegfx::B2DTrapezoid & i : aB2DTrapVector)
+ DrawTrapezoid( i, blockAA );
+}
+
+void OpenGLSalGraphicsImpl::DrawRegionBand( const RegionBand& rRegion )
+{
+ OpenGLZone aZone;
+
+ RectangleVector aRects;
+ std::vector<GLfloat> aVertices;
+ rRegion.GetRegionRectangles( aRects );
+
+ if( aRects.empty() )
+ return;
+
+#define ADD_VERTICE(pt) \
+ aVertices.push_back(GLfloat(pt.X())); \
+ aVertices.push_back(GLfloat(pt.Y()));
+
+ for(tools::Rectangle & rRect : aRects)
+ {
+ rRect.AdjustBottom(1 );
+ rRect.AdjustRight(1 );
+ ADD_VERTICE( rRect.TopLeft() );
+ ADD_VERTICE( rRect.TopRight() );
+ ADD_VERTICE( rRect.BottomLeft() );
+ ADD_VERTICE( rRect.BottomLeft() );
+ ADD_VERTICE( rRect.TopRight() );
+ ADD_VERTICE( rRect.BottomRight() );
+ }
+#undef ADD_VERTICE
+ std::vector<GLfloat> aExtrusion(aRects.size() * 6 * 3, 0);
+ mpProgram->SetExtrusionVectors(aExtrusion.data());
+ ApplyProgramMatrices();
+ mpProgram->DrawArrays(GL_TRIANGLES, aVertices);
+ CHECK_GL_ERROR();
+}
+
+void OpenGLSalGraphicsImpl::DrawTextureRect( const SalTwoRect& rPosAry )
+{
+ OpenGLZone aZone;
+
+ SAL_INFO("vcl.opengl", "draw texture rect");
+
+ long nX = rPosAry.mnDestX;
+ long nY = rPosAry.mnDestY;
+ long nWidth = rPosAry.mnDestWidth;
+ long nHeight = rPosAry.mnDestHeight;
+
+ std::vector<GLfloat> aVertices;
+ aVertices.reserve(8);
+ vcl::vertex::addRectangle<GL_TRIANGLE_FAN>(aVertices, nX, nY, nX + nWidth, nY + nHeight);
+
+ ApplyProgramMatrices();
+ mpProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
+ CHECK_GL_ERROR();
+}
+
+void OpenGLSalGraphicsImpl::DrawTexture( OpenGLTexture& rTexture, const SalTwoRect& rPosAry, bool bInverted )
+{
+ OpenGLZone aZone;
+
+ SAL_INFO("vcl.opengl", "draw texture");
+
+ if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
+ return;
+ mpProgram->SetShaderType(TextureShaderType::Normal);
+ mpProgram->SetIdentityTransform("transform");
+ mpProgram->SetTexture("texture", rTexture);
+
+ GLfloat aTexCoord[8];
+ rTexture.GetCoord(aTexCoord, rPosAry, bInverted);
+ mpProgram->SetTextureCoord(aTexCoord);
+ mpProgram->SetMaskCoord(aTexCoord);
+ mpProgram->SetAlphaCoord(aTexCoord);
+
+ DrawTextureRect( rPosAry );
+ mpProgram->Clean();
+}
+
+namespace {
+
+bool scaleTexture(const rtl::Reference< OpenGLContext > &xContext,
+ OpenGLTexture& rOutTexture, const double& ixscale, const double& iyscale, OpenGLTexture& rTexture)
+{
+ int nWidth = rTexture.GetWidth();
+ int nHeight = rTexture.GetHeight();
+ if (nWidth == 0 || nHeight == 0)
+ return false;
+
+ int nNewWidth = nWidth / ixscale;
+ int nNewHeight = nHeight / iyscale;
+
+ OString sUseReducedRegisterVariantDefine;
+ if (xContext->getOpenGLCapabilitySwitch().mbLimitedShaderRegisters)
+ sUseReducedRegisterVariantDefine = OString("#define USE_REDUCED_REGISTER_VARIANT\n");
+
+ OpenGLProgram* pProgram = xContext->UseProgram("textureVertexShader", "areaScaleFragmentShader", sUseReducedRegisterVariantDefine);
+ if (pProgram == nullptr)
+ return false;
+
+ OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
+ OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer(aScratchTex);
+
+ // From OpenGLSalBitmap::ImplScaleArea().
+ pProgram->SetUniform1f("xscale", ixscale);
+ pProgram->SetUniform1f("yscale", iyscale);
+ pProgram->SetUniform1i("swidth", nWidth);
+ pProgram->SetUniform1i("sheight", nHeight);
+ // For converting between <0,nWidth> and <0.0,1.0> coordinate systems.
+ GLfloat srcCoords[ 8 ];
+ rTexture.GetWholeCoord( srcCoords );
+ pProgram->SetUniform1f( "xoffset", srcCoords[ 0 ] );
+ pProgram->SetUniform1f( "yoffset", srcCoords[ 1 ] );
+ pProgram->SetUniform1f( "xtopixelratio", nNewWidth / ( srcCoords[ 4 ] - srcCoords[ 0 ] ));
+ pProgram->SetUniform1f( "ytopixelratio", nNewHeight / ( srcCoords[ 5 ] - srcCoords[ 1 ] ));
+ pProgram->SetUniform1f( "xfrompixelratio", ( srcCoords[ 4 ] - srcCoords[ 0 ] ) / nWidth );
+ pProgram->SetUniform1f( "yfrompixelratio", ( srcCoords[ 5 ] - srcCoords[ 1 ] ) / nHeight );
+
+ pProgram->SetTexture("sampler", rTexture);
+ pProgram->DrawTexture(rTexture);
+ pProgram->Clean();
+
+ OpenGLContext::ReleaseFramebuffer(pFramebuffer);
+
+ CHECK_GL_ERROR();
+
+ rOutTexture = aScratchTex;
+ return true;
+}
+
+}
+
+void OpenGLSalGraphicsImpl::DrawTransformedTexture(
+ OpenGLTexture& rTexture,
+ OpenGLTexture& rMask,
+ const basegfx::B2DPoint& rNull,
+ const basegfx::B2DPoint& rX,
+ const basegfx::B2DPoint& rY )
+{
+ OpenGLZone aZone;
+
+ std::vector<GLfloat> aVertices = {
+ 0, GLfloat(rTexture.GetHeight()),
+ 0, 0,
+ GLfloat(rTexture.GetWidth()), 0,
+ GLfloat(rTexture.GetWidth()), GLfloat(rTexture.GetHeight())
+ };
+
+ GLfloat aTexCoord[8];
+
+ const long nDestWidth = basegfx::fround(basegfx::B2DVector(rX - rNull).getLength());
+ const long nDestHeight = basegfx::fround(basegfx::B2DVector(rY - rNull).getLength());
+
+ // Invisibly small images shouldn't divide by zero.
+ if( nDestHeight == 0 || nDestWidth == 0 )
+ return;
+
+ // inverted scale ratios
+ double ixscale = rTexture.GetWidth() / double(nDestWidth);
+ double iyscale = rTexture.GetHeight() / double(nDestHeight);
+
+ // If downscaling at a higher scale ratio, use the area scaling algorithm rather
+ // than plain OpenGL's scaling (texture mapping), for better results.
+ // See OpenGLSalBitmap::ImplScaleArea().
+ bool areaScaling = false;
+ bool fastAreaScaling = false;
+
+ OString sUseReducedRegisterVariantDefine;
+ if (mpContext->getOpenGLCapabilitySwitch().mbLimitedShaderRegisters)
+ sUseReducedRegisterVariantDefine = OString("#define USE_REDUCED_REGISTER_VARIANT\n");
+
+ OUString textureFragmentShader;
+ if( ixscale >= 2 && iyscale >= 2 ) // scale ratio less than 50%
+ {
+ areaScaling = true;
+ fastAreaScaling = ( ixscale == std::trunc( ixscale ) && iyscale == std::trunc( iyscale ));
+ // The generic case has arrays only up to 16 ratio downscaling and is performed in 2 passes,
+ // when the ratio is in the 16-100 range, which is hopefully enough in practice, but protect
+ // against buffer overflows in case such an extreme case happens (and in such case the precision
+ // of the generic algorithm probably doesn't matter anyway).
+ if( ixscale > 100 || iyscale > 100 )
+ fastAreaScaling = true;
+ if( fastAreaScaling )
+ textureFragmentShader = "areaScaleFastFragmentShader";
+ else
+ textureFragmentShader = "areaScaleFragmentShader";
+ }
+
+ OpenGLTexture aInTexture = rTexture;
+ OpenGLTexture aInMask = rMask;
+
+ // When using the area scaling algorithm we need to reduce the texture size in 2 passes
+ // in order to not use a big array inside the fragment shader.
+ if (areaScaling && !fastAreaScaling)
+ {
+ // Perform a first texture downscaling by an inverted scale ratio equal to
+ // the square root of the whole inverted scale ratio.
+ if (ixscale > 16 || iyscale > 16)
+ {
+ // The scissor area is set to the current window size in PreDraw,
+ // so if we do not disable the scissor test, the texture produced
+ // by the first downscaling is clipped to the current window size.
+ mpContext->state().scissor().disable();
+ mpContext->state().stencil().disable();
+
+ // the square root of the whole inverted scale ratio
+ double ixscalesqrt = std::floor(std::sqrt(ixscale));
+ double iyscalesqrt = std::floor(std::sqrt(iyscale));
+ ixscale /= ixscalesqrt; // second pass inverted x-scale factor
+ iyscale /= iyscalesqrt; // second pass inverted y-scale factor
+
+ scaleTexture(mpContext, aInTexture, ixscalesqrt, iyscalesqrt, rTexture);
+
+ if (rMask) // we need to downscale the mask too
+ {
+ scaleTexture(mpContext, aInMask, ixscalesqrt, iyscalesqrt, rMask);
+ }
+
+ // We need to re-acquire the off-screen texture.
+ CheckOffscreenTexture();
+ CHECK_GL_ERROR();
+
+ // Re-enable scissor and stencil tests if needed.
+ if (mbUseScissor)
+ mpContext->state().scissor().enable();
+
+ if (mbUseStencil)
+ mpContext->state().stencil().enable();
+ }
+ }
+
+ if( aInMask )
+ {
+ if( !UseProgram( "transformedTextureVertexShader",
+ textureFragmentShader.isEmpty() ? "maskedTextureFragmentShader" : textureFragmentShader,
+ "#define MASKED\n" + sUseReducedRegisterVariantDefine))
+ return;
+ mpProgram->SetTexture( "mask", aInMask );
+ GLfloat aMaskCoord[8];
+ aInMask.GetWholeCoord(aMaskCoord);
+ mpProgram->SetMaskCoord(aMaskCoord);
+ aInMask.SetFilter( GL_LINEAR );
+ mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ }
+ else
+ {
+ if( !UseProgram( "transformedTextureVertexShader",
+ textureFragmentShader.isEmpty() ? "textureFragmentShader" : textureFragmentShader,
+ sUseReducedRegisterVariantDefine))
+ return;
+ }
+
+ if(areaScaling)
+ {
+ int nWidth = aInTexture.GetWidth();
+ int nHeight = aInTexture.GetHeight();
+
+ // From OpenGLSalBitmap::ImplScaleArea().
+ if (fastAreaScaling && nWidth && nHeight)
+ {
+ mpProgram->SetUniform1i( "xscale", ixscale );
+ mpProgram->SetUniform1i( "yscale", iyscale );
+ GLfloat srcCoords[ 8 ];
+ aInTexture.GetWholeCoord( srcCoords );
+ mpProgram->SetUniform1f( "xstep", ( srcCoords[ 4 ] - srcCoords[ 0 ] ) / nWidth );
+ mpProgram->SetUniform1f( "ystep", ( srcCoords[ 5 ] - srcCoords[ 1 ] ) / nHeight );
+ mpProgram->SetUniform1f( "ratio", 1.0 / ( ixscale * iyscale ));
+ }
+ else if (nHeight > 1 && nWidth > 1)
+ {
+ mpProgram->SetUniform1f( "xscale", ixscale );
+ mpProgram->SetUniform1f( "yscale", iyscale );
+ mpProgram->SetUniform1i( "swidth", nWidth );
+ mpProgram->SetUniform1i( "sheight", nHeight );
+ // For converting between <0,nWidth-1> and <0.0,1.0> coordinate systems.
+ GLfloat srcCoords[ 8 ];
+ aInTexture.GetWholeCoord( srcCoords );
+ mpProgram->SetUniform1f( "xoffset", srcCoords[ 0 ] );
+ mpProgram->SetUniform1f( "yoffset", srcCoords[ 1 ] );
+ mpProgram->SetUniform1f( "xtopixelratio", ( nWidth / ixscale ) / ( srcCoords[ 4 ] - srcCoords[ 0 ] ));
+ mpProgram->SetUniform1f( "ytopixelratio", ( nHeight / iyscale ) / ( srcCoords[ 5 ] - srcCoords[ 1 ] ));
+ mpProgram->SetUniform1f( "xfrompixelratio", ( srcCoords[ 4 ] - srcCoords[ 0 ] ) / nWidth );
+ mpProgram->SetUniform1f( "yfrompixelratio", ( srcCoords[ 5 ] - srcCoords[ 1 ] ) / nHeight );
+ }
+ }
+
+ ApplyProgramMatrices();
+ mpProgram->SetUniform2f( "viewport", GetWidth(), GetHeight() );
+ // Here, in order to get the correct transformation we need to pass the original texture,
+ // since it has been used for initializing the rectangle vertices.
+ mpProgram->SetTransform( "transform", rTexture, rNull, rX, rY );
+ aInTexture.GetWholeCoord(aTexCoord);
+ mpProgram->SetTexture("sampler", aInTexture);
+ aInTexture.SetFilter(GL_LINEAR);
+ mpProgram->SetTextureCoord( aTexCoord );
+ mpProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
+
+ CHECK_GL_ERROR();
+ mpProgram->Clean();
+}
+
+void OpenGLSalGraphicsImpl::DrawAlphaTexture( OpenGLTexture& rTexture, const SalTwoRect& rPosAry, bool bInverted, bool bPremultiplied )
+{
+ OpenGLZone aZone;
+
+ if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
+ return;
+ mpProgram->SetShaderType(TextureShaderType::Normal);
+ mpProgram->SetIdentityTransform("transform");
+ mpProgram->SetTexture("texture", rTexture);
+ mpProgram->SetBlendMode( bPremultiplied ? GL_ONE : GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC_ALPHA );
+
+ GLfloat aTexCoord[8];
+ rTexture.GetCoord(aTexCoord, rPosAry, bInverted);
+ mpProgram->SetTextureCoord(aTexCoord);
+ mpProgram->SetMaskCoord(aTexCoord);
+ mpProgram->SetAlphaCoord(aTexCoord);
+
+ DrawTextureRect( rPosAry );
+ mpProgram->Clean();
+}
+
+void OpenGLSalGraphicsImpl::DrawTextureDiff( OpenGLTexture& rTexture, OpenGLTexture& rMask, const SalTwoRect& rPosAry, bool bInverted )
+{
+ OpenGLZone aZone;
+
+ if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
+ return;
+ mpProgram->SetShaderType(TextureShaderType::Diff);
+ mpProgram->SetIdentityTransform("transform");
+ mpProgram->SetTexture( "texture", rTexture );
+ mpProgram->SetTexture( "mask", rMask );
+ mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+
+ GLfloat aTexCoord[8];
+ rTexture.GetCoord(aTexCoord, rPosAry, bInverted);
+ mpProgram->SetTextureCoord(aTexCoord);
+ mpProgram->SetAlphaCoord(aTexCoord);
+
+ GLfloat aMaskCoord[8];
+ rMask.GetCoord(aMaskCoord, rPosAry, bInverted);
+ mpProgram->SetMaskCoord(aMaskCoord);
+
+ DrawTextureRect( rPosAry );
+ mpProgram->Clean();
+}
+
+void OpenGLSalGraphicsImpl::DrawTextureWithMask( OpenGLTexture& rTexture, OpenGLTexture& rMask, const SalTwoRect& rPosAry )
+{
+ OpenGLZone aZone;
+
+ if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
+ return;
+ mpProgram->SetShaderType(TextureShaderType::Masked);
+ mpProgram->SetIdentityTransform("transform");
+ mpProgram->SetTexture( "texture", rTexture );
+ mpProgram->SetTexture( "mask", rMask );
+ mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+
+ GLfloat aTexCoord[8];
+ rTexture.GetCoord(aTexCoord, rPosAry);
+ mpProgram->SetTextureCoord(aTexCoord);
+ mpProgram->SetAlphaCoord(aTexCoord);
+
+ GLfloat aMaskCoord[8];
+ rMask.GetCoord(aMaskCoord, rPosAry);
+ mpProgram->SetMaskCoord(aMaskCoord);
+
+ DrawTextureRect(rPosAry);
+ mpProgram->Clean();
+}
+
+void OpenGLSalGraphicsImpl::DrawBlendedTexture( OpenGLTexture& rTexture, OpenGLTexture& rMask, OpenGLTexture& rAlpha, const SalTwoRect& rPosAry )
+{
+ OpenGLZone aZone;
+
+ if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
+ return;
+ mpProgram->SetShaderType(TextureShaderType::Blend);
+ mpProgram->SetTexture( "texture", rTexture );
+ mpProgram->SetTexture( "mask", rMask );
+ mpProgram->SetTexture( "alpha", rAlpha );
+
+ GLfloat aTexCoord[8];
+ rTexture.GetCoord(aTexCoord, rPosAry);
+ mpProgram->SetTextureCoord(aTexCoord);
+
+ GLfloat aAlphaCoord[8];
+ rAlpha.GetCoord(aAlphaCoord, rPosAry);
+ mpProgram->SetAlphaCoord(aAlphaCoord);
+
+ GLfloat aMaskCoord[8];
+ rMask.GetCoord(aMaskCoord, rPosAry);
+ mpProgram->SetMaskCoord(aMaskCoord);
+
+ mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ DrawTextureRect( rPosAry );
+ mpProgram->Clean();
+}
+
+void OpenGLSalGraphicsImpl::DrawMask( OpenGLTexture& rMask, Color nMaskColor, const SalTwoRect& rPosAry )
+{
+ OpenGLZone aZone;
+
+ if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
+ return;
+ mpProgram->SetShaderType(TextureShaderType::MaskedColor);
+ mpProgram->SetIdentityTransform("transform");
+ mpProgram->SetColor( "color", nMaskColor, 0 );
+ mpProgram->SetTexture("texture", rMask);
+
+ GLfloat aTexCoord[8];
+ rMask.GetCoord(aTexCoord, rPosAry);
+ mpProgram->SetTextureCoord(aTexCoord);
+ mpProgram->SetMaskCoord(aTexCoord);
+ mpProgram->SetAlphaCoord(aTexCoord);
+
+ mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ DrawTextureRect(rPosAry);
+ mpProgram->Clean();
+}
+
+void OpenGLSalGraphicsImpl::FlushLinesOrTriangles(DrawShaderType eType, RenderParameters const & rParameters)
+{
+ if (!UseProgram("combinedVertexShader", "combinedFragmentShader", "#define USE_VERTEX_COLORS"))
+ return;
+
+ mpProgram->SetShaderType(eType);
+ mpProgram->SetBlendMode(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ ApplyProgramMatrices(0.5f);
+
+ vcl::VertexBufferObject<Vertex> vbo;
+ vbo.upload(rParameters.maVertices);
+
+ GLuint positionAttrib = SAL_MAX_UINT32;
+ GLuint colorAttrib = SAL_MAX_UINT32;
+ GLuint lineDataAttrib = SAL_MAX_UINT32;
+
+ mpProgram->SetVertexAttrib(positionAttrib, "position", 2, GL_FLOAT, GL_FALSE,
+ sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, position)));
+
+ mpProgram->SetVertexAttrib(colorAttrib, "vertex_color_in", 4, GL_FLOAT, GL_FALSE,
+ sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, color)));
+
+ mpProgram->SetVertexAttrib(lineDataAttrib, "extrusion_vectors", 4, GL_FLOAT, GL_FALSE,
+ sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, lineData)));
+
+ vcl::IndexBufferObject ibo;
+ ibo.upload(rParameters.maIndices);
+ ibo.bind();
+
+ mpProgram->DrawElements(GL_TRIANGLES, rParameters.maIndices.size());
+ CHECK_GL_ERROR();
+
+ mpProgram->Clean();
+}
+
+void OpenGLSalGraphicsImpl::FlushDeferredDrawing()
+{
+ if (mpRenderList->empty())
+ return;
+
+ VCL_GL_INFO("FlushDeferredDrawing: " << mpRenderList->getEntries().size());
+
+ InitializePreDrawState(XOROption::IMPLEMENT_XOR);
+
+ OpenGLZone aZone;
+ for (RenderEntry& rRenderEntry : mpRenderList->getEntries())
+ {
+ if (rRenderEntry.hasTriangles())
+ {
+ RenderParameters& rParameters = rRenderEntry.maTriangleParameters;
+ VCL_GL_INFO("Flush Triangles: " << rParameters.maVertices.size());
+ FlushLinesOrTriangles(DrawShaderType::Normal, rParameters);
+ }
+ if (rRenderEntry.hasLines())
+ {
+ RenderParameters& rParameters = rRenderEntry.maLineParameters;
+ VCL_GL_INFO("Flush Lines: " << rParameters.maVertices.size());
+ FlushLinesOrTriangles(DrawShaderType::Line, rParameters);
+ }
+ if (rRenderEntry.hasTextures() && UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader", "#define USE_VERTEX_COLORS"))
+ {
+ mpProgram->SetShaderType(TextureShaderType::MaskedColor);
+ mpProgram->SetIdentityTransform("transform");
+ mpProgram->SetBlendMode(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ for (auto& rPair : rRenderEntry.maTextureParametersMap)
+ {
+ RenderTextureParameters& rParameters = rPair.second;
+ mpProgram->SetTexture("texture", rParameters.maTexture);
+ ApplyProgramMatrices();
+ mpProgram->SetTextureCoord(rParameters.maTextureCoords.data());
+ mpProgram->SetMaskCoord(rParameters.maTextureCoords.data());
+ mpProgram->SetAlphaCoord(rParameters.maTextureCoords.data());
+ mpProgram->SetVertexColors(rParameters.maColors);
+ mpProgram->DrawArrays(GL_TRIANGLES, rParameters.maVertices);
+ CHECK_GL_ERROR();
+ }
+ mpProgram->Clean();
+ }
+ }
+
+ mpRenderList->clear();
+ PostDraw();
+
+ VCL_GL_INFO("End FlushDeferredDrawing");
+}
+
+void OpenGLSalGraphicsImpl::DrawLinearGradient( const Gradient& rGradient, const tools::Rectangle& rRect )
+{
+ OpenGLZone aZone;
+
+ if( !UseProgram( "textureVertexShader", "linearGradientFragmentShader" ) )
+ return;
+ Color aStartCol = rGradient.GetStartColor();
+ Color aEndCol = rGradient.GetEndColor();
+ long nFactor = rGradient.GetStartIntensity();
+ mpProgram->SetColorWithIntensity( "start_color", aStartCol, nFactor );
+ nFactor = rGradient.GetEndIntensity();
+ mpProgram->SetColorWithIntensity( "end_color", aEndCol, nFactor );
+
+ tools::Rectangle aBoundRect;
+ Point aCenter;
+ rGradient.GetBoundRect( rRect, aBoundRect, aCenter );
+ tools::Polygon aPoly( aBoundRect );
+ aPoly.Rotate( aCenter, rGradient.GetAngle() % 3600 );
+
+ GLfloat aTexCoord[8] = { 0, 1, 1, 1, 1, 0, 0, 0 };
+ GLfloat fMin = 1.0 - 100.0 / (100.0 - rGradient.GetBorder());
+ aTexCoord[5] = aTexCoord[7] = fMin;
+ mpProgram->SetTextureCoord( aTexCoord );
+ DrawConvexPolygon( aPoly, true );
+}
+
+void OpenGLSalGraphicsImpl::DrawAxialGradient( const Gradient& rGradient, const tools::Rectangle& rRect )
+{
+ OpenGLZone aZone;
+
+ if( !UseProgram( "textureVertexShader", "linearGradientFragmentShader" ) )
+ return;
+ Color aStartCol = rGradient.GetStartColor();
+ Color aEndCol = rGradient.GetEndColor();
+ long nFactor = rGradient.GetStartIntensity();
+ mpProgram->SetColorWithIntensity( "start_color", aStartCol, nFactor );
+ nFactor = rGradient.GetEndIntensity();
+ mpProgram->SetColorWithIntensity( "end_color", aEndCol, nFactor );
+
+ /**
+ * Draw two rectangles with linear gradient.
+ *
+ * 1 *---* 2
+ * | /|
+ * | / | Points 0 and 3 have start color
+ * 0 |/__| 3 Points 1, 2, 4 and 5 have end color
+ * |\ |
+ * | \ |
+ * | \|
+ * 5 *---* 4
+ *
+ */
+
+ tools::Rectangle aRect;
+ Point aCenter;
+ rGradient.GetBoundRect( rRect, aRect, aCenter );
+
+ // determine points 0 and 3
+ Point aPt0( aRect.Left(), (aRect.Top() + aRect.Bottom() + 1) / 2 );
+ Point aPt3( aRect.Right(), (aRect.Top() + aRect.Bottom() + 1) / 2 );
+
+ tools::Polygon aPoly( 7 );
+ aPoly.SetPoint( aPt0, 0 );
+ aPoly.SetPoint( aRect.TopLeft(), 1 );
+ aPoly.SetPoint( aRect.TopRight(), 2 );
+ aPoly.SetPoint( aPt3, 3 );
+ aPoly.SetPoint( aRect.BottomRight(), 4 );
+ aPoly.SetPoint( aRect.BottomLeft(), 5 );
+ aPoly.SetPoint( aPt0, 6 );
+ aPoly.Rotate( aCenter, rGradient.GetAngle() % 3600 );
+
+ GLfloat aTexCoord[12] = { 0, 1, 1, 0, 2, 0, 3, 1, 4, 0, 5, 0 };
+ GLfloat fMin = 1.0 - 100.0 / (100.0 - rGradient.GetBorder());
+ aTexCoord[3] = aTexCoord[5] = aTexCoord[9] = aTexCoord[11] = fMin;
+ mpProgram->SetTextureCoord( aTexCoord );
+ DrawConvexPolygon( aPoly, true );
+}
+
+void OpenGLSalGraphicsImpl::DrawRadialGradient( const Gradient& rGradient, const tools::Rectangle& rRect )
+{
+ OpenGLZone aZone;
+
+ if( !UseProgram( "textureVertexShader", "radialGradientFragmentShader" ) )
+ return;
+ Color aStartCol = rGradient.GetStartColor();
+ Color aEndCol = rGradient.GetEndColor();
+ long nFactor = rGradient.GetStartIntensity();
+ mpProgram->SetColorWithIntensity( "start_color", aStartCol, nFactor );
+ nFactor = rGradient.GetEndIntensity();
+ mpProgram->SetColorWithIntensity( "end_color", aEndCol, nFactor );
+
+ tools::Rectangle aRect;
+ Point aCenter;
+ rGradient.GetBoundRect( rRect, aRect, aCenter );
+
+ // adjust coordinates so that radius has distance equals to 1.0
+ double fRadius = aRect.GetWidth() / 2.0f;
+ GLfloat fWidth = rRect.GetWidth() / fRadius;
+ GLfloat fHeight = rRect.GetHeight() / fRadius;
+ GLfloat aTexCoord[8] = { 0, 0, 0, fHeight, fWidth, fHeight, fWidth, 0 };
+ mpProgram->SetTextureCoord( aTexCoord );
+ mpProgram->SetUniform2f( "center", (aCenter.X() - rRect.Left()) / fRadius,
+ (aCenter.Y() - rRect.Top()) / fRadius );
+ DrawRect( rRect );
+}
+
+void OpenGLSalGraphicsImpl::drawPixel(long nX, long nY)
+{
+ VCL_GL_INFO("::drawPixel: (" << nX << ", " << nY << ")");
+ mpRenderList->addDrawPixel(nX, nY, mnLineColor);
+ PostBatchDraw();
+}
+
+void OpenGLSalGraphicsImpl::drawPixel(long nX, long nY, Color nColor)
+{
+ VCL_GL_INFO("::drawPixel: (" << nX << ", " << nY << ")");
+ mpRenderList->addDrawPixel(nX, nY, nColor);
+ PostBatchDraw();
+}
+
+void OpenGLSalGraphicsImpl::drawLine(long nX1, long nY1, long nX2, long nY2)
+{
+ VCL_GL_INFO("::drawLine (" << nX1 << ", " << nY1 << ") (" << nX2 << ", " << nY2 << ")");
+ mpRenderList->addDrawLine(nX1, nY1, nX2, nY2, mnLineColor, mrParent.getAntiAliasB2DDraw());
+ PostBatchDraw();
+}
+
+void OpenGLSalGraphicsImpl::drawRect( long nX, long nY, long nWidth, long nHeight )
+{
+ VCL_GL_INFO("::drawRect (" << nX << ", " << nY << ") [" << nWidth << ", " << nHeight << "]");
+ mpRenderList->addDrawRectangle(nX, nY, nWidth, nHeight, 0.0, mnLineColor, mnFillColor);
+ PostBatchDraw();
+}
+
+void OpenGLSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const SalPoint* pPtAry )
+{
+ VCL_GL_INFO("::drawPolyLine legacy -> redirecting to drawPolyLine");
+ basegfx::B2DPolygon aPoly;
+ aPoly.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
+ for (sal_uInt32 i = 1; i < nPoints; ++i)
+ aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
+ aPoly.setClosed(false);
+
+ drawPolyLine(
+ basegfx::B2DHomMatrix(),
+ aPoly,
+ 0.0,
+ 1.0,
+ nullptr, // MM01
+ basegfx::B2DLineJoin::Miter,
+ css::drawing::LineCap_BUTT,
+ basegfx::deg2rad(15.0) /*default*/,
+ false);
+}
+
+void OpenGLSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
+{
+ VCL_GL_INFO("::drawPolygon legacy -> redirecting to drawPolyPolygon with transparency");
+ basegfx::B2DPolygon aPoly;
+ aPoly.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
+ for (sal_uInt32 i = 1; i < nPoints; ++i)
+ aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
+
+ drawPolyPolygon(
+ basegfx::B2DHomMatrix(),
+ basegfx::B2DPolyPolygon(aPoly),
+ 0.0);
+}
+
+void OpenGLSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPointCounts, PCONSTSALPOINT* pPtAry )
+{
+ VCL_GL_INFO("::drawPolyPolygon legacy -> redirecting to drawPolyPolygon with transparency");
+ basegfx::B2DPolyPolygon aPolyPoly;
+ for(sal_uInt32 nPolygon = 0; nPolygon < nPoly; ++nPolygon)
+ {
+ sal_uInt32 nPoints = pPointCounts[nPolygon];
+ if (nPoints)
+ {
+ PCONSTSALPOINT pPoints = pPtAry[nPolygon];
+ basegfx::B2DPolygon aPoly;
+ aPoly.append( basegfx::B2DPoint(pPoints->mnX, pPoints->mnY), nPoints);
+ for (sal_uInt32 i = 1; i < nPoints; ++i)
+ aPoly.setB2DPoint(i, basegfx::B2DPoint( pPoints[i].mnX, pPoints[i].mnY));
+
+ aPolyPoly.append(aPoly);
+ }
+ }
+
+ drawPolyPolygon(
+ basegfx::B2DHomMatrix(),
+ aPolyPoly,
+ 0.0);
+}
+
+bool OpenGLSalGraphicsImpl::drawPolyPolygon(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fTransparency)
+{
+ VCL_GL_INFO("::drawPolyPolygon " << rPolyPolygon.getB2DRange());
+
+ // Fallback: Transform to DeviceCoordinates
+ basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
+ aPolyPolygon.transform(rObjectToDevice);
+
+ // FlushLinesOrTriangles() works with a 0.5 pixel offset, compensate for that here.
+ basegfx::B2DHomMatrix aMatrix;
+ aMatrix.translate(-0.5f, -0.5f);
+ aPolyPolygon.transform(aMatrix);
+
+ mpRenderList->addDrawPolyPolygon(
+ aPolyPolygon,
+ fTransparency,
+ mnLineColor,
+ mnFillColor,
+ mrParent.getAntiAliasB2DDraw());
+
+ PostBatchDraw();
+ return true;
+}
+
+bool OpenGLSalGraphicsImpl::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& rPolygon,
+ double fTransparency,
+ double fLineWidth,
+ const std::vector< double >* pStroke, // MM01
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
+{
+ VCL_GL_INFO("::drawPolyLine " << rPolygon.getB2DRange());
+
+ // MM01 check done for simple reasons
+ if(!rPolygon.count() || fTransparency < 0.0 || fTransparency > 1.0)
+ {
+ return true;
+ }
+
+ // MM01 need to do line dashing as fallback stuff here now
+ const double fDotDashLength(nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
+ const bool bStrokeUsed(0.0 != fDotDashLength);
+ assert(!bStrokeUsed || (bStrokeUsed && pStroke));
+ basegfx::B2DPolyPolygon aPolyPolygonLine;
+
+ if(bStrokeUsed)
+ {
+ // apply LineStyle
+ basegfx::utils::applyLineDashing(
+ rPolygon, // source
+ *pStroke, // pattern
+ &aPolyPolygonLine, // target for lines
+ nullptr, // target for gaps
+ fDotDashLength); // full length if available
+ }
+ else
+ {
+ // no line dashing, just copy
+ aPolyPolygonLine.append(rPolygon);
+ }
+
+ // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline
+ aPolyPolygonLine.transform(rObjectToDevice);
+ if(bPixelSnapHairline) { aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine); }
+
+ // tdf#124848 get correct LineWidth in discrete coordinates,
+ if(fLineWidth == 0) // hairline
+ fLineWidth = 1.0;
+ else // Adjust line width for object-to-device scale.
+ fLineWidth = (rObjectToDevice * basegfx::B2DVector(fLineWidth, 0)).getLength();
+
+ for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
+ {
+ // addDrawPolyLine() assumes that there are no duplicate points in the polygon
+ basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
+ basegfx::utils::simplifyCurveSegments(aPolyLine);
+ aPolyLine.removeDoublePoints();
+
+ mpRenderList->addDrawPolyLine(
+ aPolyLine,
+ fTransparency,
+ fLineWidth,
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle,
+ mnLineColor,
+ mrParent.getAntiAliasB2DDraw());
+
+ // MM01: not sure - maybe this can be moved out of this loop, but to
+ // keep on the safe side for now, do not really change something for now
+ PostBatchDraw();
+ }
+
+ return true;
+}
+
+bool OpenGLSalGraphicsImpl::drawPolyLineBezier(
+ sal_uInt32 /*nPoints*/,
+ const SalPoint* /*pPtAry*/,
+ const PolyFlags* /*pFlgAry*/ )
+{
+ return false;
+}
+
+bool OpenGLSalGraphicsImpl::drawPolygonBezier(
+ sal_uInt32 /*nPoints*/,
+ const SalPoint* /*pPtAry*/,
+ const PolyFlags* /*pFlgAry*/ )
+{
+ return false;
+}
+
+bool OpenGLSalGraphicsImpl::drawPolyPolygonBezier(
+ sal_uInt32 /*nPoly*/,
+ const sal_uInt32* /*pPoints*/,
+ const SalPoint* const* /*pPtAry*/,
+ const PolyFlags* const* /*pFlgAry*/ )
+{
+ return false;
+}
+
+// CopyArea --> No RasterOp, but ClipRegion
+void OpenGLSalGraphicsImpl::copyArea(
+ long nDestX, long nDestY,
+ long nSrcX, long nSrcY,
+ long nSrcWidth, long nSrcHeight, bool /*bWindowInvalidate*/ )
+{
+ VCL_GL_INFO( "::copyArea " << nSrcX << "," << nSrcY << " >> " << nDestX << "," << nDestY << " (" << nSrcWidth << "," << nSrcHeight << ")" );
+ OpenGLTexture aTexture;
+ SalTwoRect aPosAry(0, 0, nSrcWidth, nSrcHeight, nDestX, nDestY, nSrcWidth, nSrcHeight);
+
+ PreDraw();
+ // TODO offscreen case
+ aTexture = OpenGLTexture( nSrcX, GetHeight() - nSrcY - nSrcHeight,
+ nSrcWidth, nSrcHeight );
+ DrawTexture( aTexture, aPosAry );
+ PostDraw();
+}
+
+// CopyBits and DrawBitmap --> RasterOp and ClipRegion
+// CopyBits() --> pSrcGraphics == NULL, then CopyBits on same Graphics
+void OpenGLSalGraphicsImpl::DoCopyBits( const SalTwoRect& rPosAry, OpenGLSalGraphicsImpl& rImpl )
+{
+ VCL_GL_INFO( "::copyBits" );
+
+ rImpl.FlushDeferredDrawing();
+
+ if( !rImpl.maOffscreenTex )
+ {
+ VCL_GL_INFO( "::copyBits - skipping copy of un-initialized framebuffer contents of size "
+ << rImpl.GetWidth() << "x" << rImpl.GetHeight() );
+ return;
+ }
+
+ if( &rImpl == this &&
+ (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
+ (rPosAry.mnSrcHeight == rPosAry.mnDestHeight))
+ {
+ // short circuit if there is nothing to do
+ if( (rPosAry.mnSrcX == rPosAry.mnDestX) &&
+ (rPosAry.mnSrcY == rPosAry.mnDestY))
+ return;
+ // use copyArea() if source and destination context are identical
+ copyArea( rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX, rPosAry.mnSrcY,
+ rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, false/*bWindowInvalidate*/ );
+ return;
+ }
+
+ PreDraw();
+ DrawTexture( rImpl.maOffscreenTex, rPosAry );
+ PostDraw();
+}
+
+void OpenGLSalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap )
+{
+ // check that carefully only in the debug mode
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBitmap));
+
+ OpenGLZone aZone;
+
+ const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSalBitmap);
+ OpenGLTexture& rTexture = rBitmap.GetTexture();
+
+ VCL_GL_INFO( "::drawBitmap" );
+ PreDraw();
+ if (rPosAry.mnSrcWidth != rPosAry.mnDestWidth ||
+ rPosAry.mnSrcHeight != rPosAry.mnDestHeight)
+ {
+ basegfx::B2DPoint aNull(rPosAry.mnDestX,rPosAry.mnDestY);
+ basegfx::B2DPoint aX(rPosAry.mnDestX + rPosAry.mnDestWidth, rPosAry.mnDestY);
+ basegfx::B2DPoint aY(rPosAry.mnDestX, rPosAry.mnDestY + rPosAry.mnDestHeight);
+ OpenGLTexture mask; // no mask set
+ DrawTransformedTexture(rTexture, mask, aNull, aX, aY);
+ }
+ else
+ {
+ DrawTexture( rTexture, rPosAry );
+ }
+ PostDraw();
+}
+
+void OpenGLSalGraphicsImpl::drawBitmap(
+ const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap,
+ const SalBitmap& rMaskBitmap )
+{
+ VCL_GL_INFO("::drawBitmap with MASK -> redirect to ::drawAlphaBitmap");
+ drawAlphaBitmap(rPosAry, rSalBitmap, rMaskBitmap);
+}
+
+void OpenGLSalGraphicsImpl::drawMask(
+ const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap,
+ Color nMaskColor )
+{
+ VCL_GL_INFO("::drawMask");
+
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBitmap));
+ const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSalBitmap);
+ mpRenderList->addDrawTextureWithMaskColor(rBitmap.GetTexture(), nMaskColor, rPosAry);
+ PostBatchDraw();
+}
+
+std::shared_ptr<SalBitmap> OpenGLSalGraphicsImpl::getBitmap( long nX, long nY, long nWidth, long nHeight )
+{
+ FlushDeferredDrawing();
+
+ OpenGLZone aZone;
+
+ std::shared_ptr<OpenGLSalBitmap> pBitmap(std::make_shared<OpenGLSalBitmap>());
+ VCL_GL_INFO( "::getBitmap " << nX << "," << nY <<
+ " " << nWidth << "x" << nHeight );
+ //TODO really needed?
+ PreDraw();
+ pBitmap->Create( maOffscreenTex, nX, nY, nWidth, nHeight );
+ PostDraw();
+ return pBitmap;
+}
+
+Color OpenGLSalGraphicsImpl::getPixel( long nX, long nY )
+{
+ FlushDeferredDrawing();
+
+ char pixel[3] = { 0, 0, 0 };
+
+ PreDraw( XOROption::IMPLEMENT_XOR );
+ nY = GetHeight() - nY - 1;
+ glReadPixels( nX, nY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixel);
+ CHECK_GL_ERROR();
+ PostDraw();
+
+ return Color( pixel[0], pixel[1], pixel[2] );
+}
+
+// invert --> ClipRegion (only Windows or VirDevs)
+void OpenGLSalGraphicsImpl::invert(
+ long nX, long nY,
+ long nWidth, long nHeight,
+ SalInvert nFlags)
+{
+ PreDraw();
+
+ if( UseInvert( nFlags ) )
+ {
+ if( nFlags & SalInvert::TrackFrame )
+ { // FIXME: could be more efficient.
+ DrawRect( nX, nY, nWidth, 1 );
+ DrawRect( nX, nY + nHeight, nWidth, 1 );
+ DrawRect( nX, nY, 1, nHeight );
+ DrawRect( nX + nWidth, nY, 1, nHeight );
+ }
+ else
+ DrawRect( nX, nY, nWidth, nHeight );
+ }
+
+ PostDraw();
+}
+
+void OpenGLSalGraphicsImpl::invert( sal_uInt32 nPoints, const SalPoint* pPtAry, SalInvert nFlags )
+{
+ PreDraw();
+
+ if( UseInvert( nFlags ) )
+ {
+ if (nFlags & SalInvert::TrackFrame)
+ {
+ // Track frame means the invert50FragmentShader must remain active
+ // (to draw what looks like a dashed line), so DrawLineSegment()
+ // can't be used. Draw the edge of the polygon as polygons instead.
+ for (size_t nPoint = 0; nPoint < nPoints; ++nPoint)
+ {
+ const SalPoint& rFrom = pPtAry[nPoint];
+ const SalPoint& rTo = pPtAry[(nPoint + 1) % nPoints];
+ if (rFrom.mnX == rTo.mnX)
+ {
+ // Extend to the right, comments assuming "to" is above
+ // "from":
+ const SalPoint aPoints[] = { { rFrom.mnX + 1, rFrom.mnY }, // bottom right
+ { rFrom.mnX, rFrom.mnY }, // bottom left
+ { rTo.mnX, rTo.mnY }, // top left
+ { rTo.mnX + 1, rTo.mnY } }; // top right
+ DrawConvexPolygon(4, aPoints, true);
+ }
+ else
+ {
+ // Otherwise can extend downwards, comments assuming "to"
+ // is above and on the right of "from":
+ const SalPoint aPoints[] = { { rFrom.mnX, rFrom.mnY + 1 }, // bottom left
+ { rFrom.mnX, rFrom.mnY }, // top left
+ { rTo.mnX, rTo.mnY }, // top right
+ { rTo.mnX, rTo.mnY + 1 } }; // bottom right
+ DrawConvexPolygon(4, aPoints, true);
+ }
+ }
+ }
+ else
+ DrawPolygon(nPoints, pPtAry);
+ }
+
+ PostDraw();
+}
+
+bool OpenGLSalGraphicsImpl::drawEPS(
+ long /*nX*/, long /*nY*/,
+ long /*nWidth*/, long /*nHeight*/,
+ void* /*pPtr*/,
+ sal_uInt32 /*nSize*/ )
+{
+ return false;
+}
+
+bool OpenGLSalGraphicsImpl::blendBitmap(
+ const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap )
+{
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBitmap));
+
+ OpenGLZone aZone;
+
+ const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSalBitmap);
+ OpenGLTexture& rTexture( rBitmap.GetTexture() );
+
+ VCL_GL_INFO( "::blendBitmap" );
+ PreDraw();
+
+ if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
+ return true;
+
+ mpProgram->SetShaderType(TextureShaderType::Normal);
+ mpProgram->SetIdentityTransform("transform");
+ mpProgram->SetTexture("texture", rTexture);
+
+ GLfloat aTexCoord[8];
+ rTexture.GetCoord(aTexCoord, rPosAry);
+ mpProgram->SetTextureCoord(aTexCoord);
+ mpProgram->SetMaskCoord(aTexCoord);
+ mpProgram->SetAlphaCoord(aTexCoord);
+
+ mpProgram->SetBlendMode(GL_ZERO, GL_SRC_COLOR);
+ DrawTextureRect(rPosAry);
+ mpProgram->Clean();
+
+ PostDraw();
+ return true;
+}
+
+bool OpenGLSalGraphicsImpl::blendAlphaBitmap(
+ const SalTwoRect& rPosAry,
+ const SalBitmap& rSalSrcBitmap,
+ const SalBitmap& rSalMaskBitmap,
+ const SalBitmap& rSalAlphaBitmap )
+{
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalSrcBitmap));
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalMaskBitmap));
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalAlphaBitmap));
+
+ OpenGLZone aZone;
+
+ const OpenGLSalBitmap& rSrcBitmap = static_cast<const OpenGLSalBitmap&>(rSalSrcBitmap);
+ const OpenGLSalBitmap& rMaskBitmap = static_cast<const OpenGLSalBitmap&>(rSalMaskBitmap);
+ const OpenGLSalBitmap& rAlphaBitmap = static_cast<const OpenGLSalBitmap&>(rSalAlphaBitmap);
+ OpenGLTexture& rTexture( rSrcBitmap.GetTexture() );
+ OpenGLTexture& rMask( rMaskBitmap.GetTexture() );
+ OpenGLTexture& rAlpha( rAlphaBitmap.GetTexture() );
+
+ VCL_GL_INFO( "::blendAlphaBitmap" );
+ PreDraw();
+ DrawBlendedTexture( rTexture, rMask, rAlpha, rPosAry );
+ PostDraw();
+ return true;
+}
+
+/** Render bitmap with alpha channel
+
+ @param rSourceBitmap
+ Source bitmap to blit
+
+ @param rAlphaBitmap
+ Alpha channel to use for blitting
+
+ @return true, if the operation succeeded, and false
+ otherwise. In this case, clients should try to emulate alpha
+ compositing themselves
+ */
+bool OpenGLSalGraphicsImpl::drawAlphaBitmap(
+ const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap,
+ const SalBitmap& rAlphaBitmap )
+{
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBitmap));
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rAlphaBitmap));
+
+ OpenGLZone aZone;
+
+ const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSalBitmap);
+ const OpenGLSalBitmap& rAlpha = static_cast<const OpenGLSalBitmap&>(rAlphaBitmap);
+ OpenGLTexture& rTexture(rBitmap.GetTexture());
+ OpenGLTexture& rAlphaTexture(rAlpha.GetTexture());
+
+ VCL_GL_INFO( "::drawAlphaBitmap" );
+ PreDraw();
+
+ if (rPosAry.mnSrcWidth != rPosAry.mnDestWidth ||
+ rPosAry.mnSrcHeight != rPosAry.mnDestHeight)
+ {
+ basegfx::B2DPoint aNull(rPosAry.mnDestX,rPosAry.mnDestY);
+ basegfx::B2DPoint aX(rPosAry.mnDestX + rPosAry.mnDestWidth, rPosAry.mnDestY);
+ basegfx::B2DPoint aY(rPosAry.mnDestX, rPosAry.mnDestY + rPosAry.mnDestHeight);
+ DrawTransformedTexture(rTexture, rAlphaTexture, aNull, aX, aY);
+ }
+ else
+ {
+ DrawTextureWithMask( rTexture, rAlphaTexture, rPosAry );
+ }
+
+ PostDraw();
+ return true;
+}
+
+/** draw transformed bitmap (maybe with alpha) where Null, X, Y define the coordinate system */
+bool OpenGLSalGraphicsImpl::drawTransformedBitmap(
+ const basegfx::B2DPoint& rNull,
+ const basegfx::B2DPoint& rX,
+ const basegfx::B2DPoint& rY,
+ const SalBitmap& rSrcBitmap,
+ const SalBitmap* pAlphaBitmap)
+{
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rSrcBitmap));
+ assert(!pAlphaBitmap || dynamic_cast<const OpenGLSalBitmap*>(pAlphaBitmap));
+
+ OpenGLZone aZone;
+
+ const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSrcBitmap);
+ const OpenGLSalBitmap* pMaskBitmap = static_cast<const OpenGLSalBitmap*>(pAlphaBitmap);
+ OpenGLTexture& rTexture( rBitmap.GetTexture() );
+ OpenGLTexture aMask; // no texture
+
+ if( pMaskBitmap != nullptr )
+ aMask = pMaskBitmap->GetTexture();
+
+ VCL_GL_INFO( "::drawTransformedBitmap" );
+ PreDraw();
+ DrawTransformedTexture( rTexture, aMask, rNull, rX, rY );
+ PostDraw();
+
+ return true;
+}
+
+/** Render solid rectangle with given transparency
+
+ @param nTransparency
+ Transparency value (0-255) to use. 0 blits and opaque, 255 a
+ fully transparent rectangle
+ */
+bool OpenGLSalGraphicsImpl::drawAlphaRect(
+ long nX, long nY,
+ long nWidth, long nHeight,
+ sal_uInt8 nTransparency )
+{
+ VCL_GL_INFO("::drawAlphaRect (" << nX << ", " << nY << ") [" << nWidth << ", " << nHeight << "]");
+ mpRenderList->addDrawRectangle(nX, nY, nWidth, nHeight, nTransparency / 100.0, mnLineColor, mnFillColor);
+ PostBatchDraw();
+ return true;
+}
+
+bool OpenGLSalGraphicsImpl::drawGradient(const tools::PolyPolygon& rPolyPoly,
+ const Gradient& rGradient)
+{
+ tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
+
+ VCL_GL_INFO("::drawGradient " << rPolyPoly.GetBoundRect());
+
+ if (aBoundRect.IsEmpty())
+ {
+ VCL_GL_INFO("::drawGradient nothing to draw");
+ return true;
+ }
+
+ if (rGradient.GetStyle() != GradientStyle::Linear &&
+ rGradient.GetStyle() != GradientStyle::Axial &&
+ rGradient.GetStyle() != GradientStyle::Radial )
+ {
+ VCL_GL_INFO("::drawGradient unsupported gradient type");
+ return false;
+ }
+
+ aBoundRect.AdjustLeft( -1 );
+ aBoundRect.AdjustTop( -1 );
+ aBoundRect.AdjustRight( 1 );
+ aBoundRect.AdjustBottom( 1 );
+
+ PreDraw( XOROption::IMPLEMENT_XOR );
+
+#define FIXME_BROKEN_STENCIL_FOR_GRADIENTS 0
+#if FIXME_BROKEN_STENCIL_FOR_GRADIENTS
+ ImplSetClipBit( vcl::Region( rPolyPoly ), 0x02 );
+ if( mbUseStencil )
+ {
+ mpContext->state().stencil().enable();
+ CHECK_GL_ERROR();
+ glStencilFunc( GL_EQUAL, 3, 0xFF );
+ CHECK_GL_ERROR();
+ }
+ else
+ {
+ mpContext->state().stencil().enable();
+ CHECK_GL_ERROR();
+ glStencilFunc( GL_EQUAL, 2, 0xFF );
+ CHECK_GL_ERROR();
+ }
+#endif
+
+ // if border >= 100%, draw solid rectangle with start color
+ if (rGradient.GetBorder() >= 100.0)
+ {
+ VCL_GL_INFO("::drawGradient -> DrawRect (no gradient)");
+
+ Color aColor = rGradient.GetStartColor();
+ long nIntensity = rGradient.GetStartIntensity();
+ if (UseSolid(Color(aColor.GetRed() * nIntensity / 100.0,
+ aColor.GetGreen()* nIntensity / 100.0,
+ aColor.GetBlue() * nIntensity / 100.0)))
+ {
+ DrawRect(aBoundRect);
+ }
+ }
+ else if (rGradient.GetStyle() == GradientStyle::Linear)
+ {
+ VCL_GL_INFO("::drawGradient -> DrawLinearGradient");
+ DrawLinearGradient(rGradient, aBoundRect);
+ }
+ else if (rGradient.GetStyle() == GradientStyle::Axial)
+ {
+ VCL_GL_INFO("::drawGradient -> DrawAxialGradient");
+ DrawAxialGradient(rGradient, aBoundRect);
+ }
+ else if (rGradient.GetStyle() == GradientStyle::Radial)
+ {
+ VCL_GL_INFO("::drawGradient -> DrawRadialGradient");
+ DrawRadialGradient(rGradient, aBoundRect);
+ }
+
+#if FIXME_BROKEN_STENCIL_FOR_GRADIENTS
+ if( !mbUseStencil )
+ {
+ mpContext->state().stencil().disable();
+ CHECK_GL_ERROR();
+ }
+#endif
+ PostDraw();
+
+ return true;
+}
+
+void OpenGLSalGraphicsImpl::flush()
+{
+ FlushDeferredDrawing();
+
+ if( IsOffscreen() )
+ return;
+
+ if( !Application::IsInExecute() )
+ {
+ // otherwise nothing would trigger idle rendering
+ doFlush();
+ }
+ else if( !mpFlush->IsActive() )
+ mpFlush->Start();
+}
+
+void OpenGLSalGraphicsImpl::doFlush()
+{
+ FlushDeferredDrawing();
+
+ if (OpenGLContext::hasCurrent())
+ {
+ mpContext->state().scissor().disable();
+ mpContext->state().stencil().disable();
+ }
+
+ if( IsOffscreen() )
+ return;
+
+ if( !maOffscreenTex )
+ {
+ VCL_GL_INFO( "doFlush - odd no texture !" );
+ return;
+ }
+
+ if( mnDrawCountAtFlush == mnDrawCount )
+ {
+ VCL_GL_INFO( "eliding redundant doFlush, no drawing since last!" );
+ return;
+ }
+
+ mnDrawCountAtFlush = mnDrawCount;
+
+ OpenGLZone aZone;
+
+ VCL_GL_INFO( "doFlush" );
+
+ if( !mpWindowContext.is() )
+ {
+ // ensure everything is released from the old context.
+ OpenGLContext::clearCurrent();
+ mpWindowContext = CreateWinContext();
+ VCL_GL_INFO( "late creation of window context" );
+ }
+
+ assert( mpWindowContext.is() );
+
+ if( !mpWindowContext.is() )
+ {
+ // failed to create a GL context for this window:
+ // eg. mis-matching pixel formats, underlying window
+ // resource lifecycle, etc.
+ VCL_GL_INFO( "Failed to create window context" );
+ return;
+ }
+
+ // Interesting ! -> this destroys a context [ somehow ] ...
+ mpWindowContext->makeCurrent();
+ CHECK_GL_ERROR();
+
+ VCL_GL_INFO( "doFlush - acquire default framebuffer" );
+
+ mpWindowContext->AcquireDefaultFramebuffer();
+
+ CHECK_GL_ERROR();
+
+ mpWindowContext->state().sync();
+ mpWindowContext->state().viewport(tools::Rectangle(Point(0, 0), Size(GetWidth(), GetHeight())));
+ mpWindowContext->state().scissor().disable();
+ mpWindowContext->state().stencil().disable();
+
+#if OSL_DEBUG_LEVEL > 0 // random background glClear
+ glClearColor(static_cast<float>(double(rand())/RAND_MAX),
+ static_cast<float>(double(rand())/RAND_MAX),
+ static_cast<float>(double(rand())/RAND_MAX), 1.0);
+ glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
+ CHECK_GL_ERROR();
+#endif
+
+ VCL_GL_INFO( "Texture height " << maOffscreenTex.GetHeight() << " vs. window height " << GetHeight() );
+
+ OpenGLFramebuffer* pFrameBuffer = mpWindowContext->AcquireFramebuffer(maOffscreenTex);
+ CHECK_GL_ERROR();
+ if (pFrameBuffer)
+ {
+ OpenGLFramebuffer::Unbind(GL_DRAW_FRAMEBUFFER);
+ pFrameBuffer->Bind(GL_READ_FRAMEBUFFER);
+
+ glBlitFramebuffer(0, 0, GetWidth(), GetHeight(),
+ 0, 0, GetWidth(), GetHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ CHECK_GL_ERROR();
+
+ pFrameBuffer->Bind();
+ }
+
+ static bool bNoSwap = getenv("SAL_GL_NO_SWAP");
+ if (!bNoSwap)
+ mpWindowContext->swapBuffers();
+
+ VCL_GL_INFO( "doFlush - end." );
+}
+
+bool OpenGLSalGraphicsImpl::supportsOperation(OutDevSupportType eType) const
+{
+ switch (eType)
+ {
+ case OutDevSupportType::B2DDraw:
+ case OutDevSupportType::TransparentRect:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/opengl_blacklist_windows.xml b/vcl/opengl/opengl_blacklist_windows.xml
new file mode 100644
index 000000000..71e562fa9
--- /dev/null
+++ b/vcl/opengl/opengl_blacklist_windows.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* 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/.
+-->
+
+<!--
+ entry attributes:
+ os - "all", "7", "8", "8_1", "10"
+ vendor - "all", "intel", "amd", "nvidia", "microsoft"
+ compare - "less", "less_equal", "greater", "greater_equal", "equal", "not_equal", "between_exclusive", "between_inclusive", "between_inclusive_start"
+ version
+ minVersion
+ maxVersion
+-->
+
+<root>
+ <whitelist>
+ </whitelist>
+ <blacklist>
+ <entry os="all" vendor="intel" compare="less" version="10.18.14.4264">
+ <device id="all"/>
+ </entry>
+ <!-- tdf#99919 -->
+ <entry os="all" vendor="intel" compare="equal" version="20.19.15.4352">
+ <device id="0x1927"/>
+ </entry>
+ <!-- tdf#100243 -->
+ <!-- tdf#117477 -->
+ <entry os="all" vendor="intel" compare="equal" version="21.20.16.4664">
+ <device id="0x591b"/>
+ <device id="0x5916"/>
+ </entry>
+ <!-- tdf#115092 -->
+ <entry os="all" vendor="intel" compare="equal" version="22.20.16.4735">
+ <device id="0x1912"/>
+ </entry>
+ <entry os="7" vendor="intel">
+ <device id="all"/>
+ </entry>
+ <entry os="8" vendor="intel" compare="equal" version="10.18.10.3308"><!-- Intel(R) HD Graphics 4000 -->
+ <device id="0x0166"/>
+ </entry>
+ <entry os="10" vendor="intel" compare="between_inclusive_start" minVersion="26.20.100.6861" maxVersion="26.20.100.7584"><!-- tdf#125516 -->
+ <device id="all"/>
+ </entry>
+ <!-- tdf#131221 -->
+ <entry os="10" vendor="intel">
+ <device id="0x5917"/>
+ </entry>
+ <entry os="all" vendor="amd" compare="less" version="15.200.1062.1004"> <!-- 150.200 -->
+ <device id="all"/>
+ </entry>
+ <entry os="all" vendor="nvidia" compare="less" version="10.18.13.5362"> <!-- 353.62 -->
+ <device id="all"/>
+ </entry>
+ <entry os="10" vendor="nvidia"> <!-- tdf#128441 -->
+ <device id="0x2182"/>
+ </entry>
+ <entry os="all" vendor="microsoft" compare="less" version="6.2.0.0"> <!-- 6.2.0.0 -->
+ <device id="all"/>
+ </entry>
+ </blacklist>
+</root>
diff --git a/vcl/opengl/program.cxx b/vcl/opengl/program.cxx
new file mode 100644
index 000000000..6557eccf8
--- /dev/null
+++ b/vcl/opengl/program.cxx
@@ -0,0 +1,390 @@
+/* -*- 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 <opengl/program.hxx>
+#include <opengl/RenderState.hxx>
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <vcl/opengl/OpenGLContext.hxx>
+
+#include <glm/glm.hpp>
+#include <glm/gtc/type_ptr.hpp>
+#include <glm/gtc/matrix_transform.hpp>
+
+OpenGLProgram::OpenGLProgram() :
+ mnId( 0 ),
+ mnEnabledAttribs( 0 ),
+ mnPositionAttrib( SAL_MAX_UINT32 ),
+ mnTexCoordAttrib( SAL_MAX_UINT32 ),
+ mnAlphaCoordAttrib( SAL_MAX_UINT32 ),
+ mnMaskCoordAttrib( SAL_MAX_UINT32 ),
+ mnExtrusionVectorsAttrib( SAL_MAX_UINT32 ),
+ mnVertexColorsAttrib( SAL_MAX_UINT32 ),
+ mbBlending(false),
+ mfLastWidth(0.0),
+ mfLastHeight(0.0),
+ mfLastPixelOffset(0.0)
+{
+}
+
+OpenGLProgram::~OpenGLProgram()
+{
+ maUniformLocations.clear();
+ if( mnId != 0 )
+ {
+ glDeleteProgram( mnId );
+ CHECK_GL_ERROR();
+ }
+}
+
+bool OpenGLProgram::Load( const OUString& rVertexShader,
+ const OUString& rFragmentShader,
+ const OString& preamble,
+ const OString& rDigest )
+{
+ mnId = OpenGLHelper::LoadShaders( rVertexShader, rFragmentShader, preamble, rDigest );
+ return ( mnId != 0 );
+}
+
+void OpenGLProgram::Reuse()
+{
+ mbBlending = false;
+}
+
+void OpenGLProgram::Use()
+{
+ if (!mnId)
+ return;
+
+ glUseProgram(mnId);
+ CHECK_GL_ERROR();
+ Reuse();
+}
+
+void OpenGLProgram::Clean()
+{
+ // unbind all textures
+ for (OpenGLTexture& rTexture : maTextures)
+ {
+ rTexture.Unbind();
+ }
+ maTextures.clear();
+
+ // disable any enabled vertex attrib array
+ if( mnEnabledAttribs )
+ {
+ for( int i = 0; i < 32; i++ )
+ {
+ if( mnEnabledAttribs & ( 1 << i ) )
+ {
+ glDisableVertexAttribArray( i );
+ CHECK_GL_ERROR();
+ }
+ }
+ mnEnabledAttribs = 0;
+ }
+}
+
+bool OpenGLProgram::EnableVertexAttrib(GLuint& rAttrib, const OString& rName)
+{
+ if( rAttrib == SAL_MAX_UINT32 )
+ {
+ GLint aLocation = glGetAttribLocation(mnId, rName.getStr());
+ CHECK_GL_ERROR();
+ if (aLocation < 0)
+ return false;
+ rAttrib = GLuint(aLocation);
+ }
+ if( (mnEnabledAttribs & ( 1 << rAttrib )) == 0 )
+ {
+ glEnableVertexAttribArray( rAttrib );
+ CHECK_GL_ERROR();
+ mnEnabledAttribs |= ( 1 << rAttrib );
+ }
+ return true;
+}
+
+void OpenGLProgram::SetVertexAttrib(GLuint& rAttrib, const OString& rName, GLint nSize,
+ GLenum eType, GLboolean bNormalized, GLsizei aStride,
+ const GLvoid* pPointer)
+{
+ if (EnableVertexAttrib(rAttrib, rName))
+ {
+ glVertexAttribPointer(rAttrib, nSize, eType, bNormalized, aStride, pPointer);
+ CHECK_GL_ERROR();
+ }
+ else
+ {
+ VCL_GL_INFO("Vertex attribute '" << rName << "' doesn't exist in this program (" << mnId << ")");
+ }
+}
+
+void OpenGLProgram::SetVertices( const GLvoid* pData )
+{
+ SetVertexAttrib(mnPositionAttrib, "position", 2, GL_FLOAT, GL_FALSE, 0, pData);
+}
+
+void OpenGLProgram::SetTextureCoord( const GLvoid* pData )
+{
+ SetVertexAttrib(mnTexCoordAttrib, "tex_coord_in", 2, GL_FLOAT, GL_FALSE, 0, pData);
+}
+
+void OpenGLProgram::SetAlphaCoord( const GLvoid* pData )
+{
+ SetVertexAttrib(mnAlphaCoordAttrib, "alpha_coord_in", 2, GL_FLOAT, GL_FALSE, 0, pData);
+}
+
+void OpenGLProgram::SetMaskCoord(const GLvoid* pData)
+{
+ SetVertexAttrib(mnMaskCoordAttrib, "mask_coord_in", 2, GL_FLOAT, GL_FALSE, 0, pData);
+}
+
+void OpenGLProgram::SetExtrusionVectors(const GLvoid* pData)
+{
+ SetVertexAttrib(mnExtrusionVectorsAttrib, "extrusion_vectors", 3, GL_FLOAT, GL_FALSE, 0, pData);
+}
+
+void OpenGLProgram::SetVertexColors(std::vector<GLubyte>& rColorVector)
+{
+ SetVertexAttrib(mnVertexColorsAttrib, "vertex_color_in", 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, rColorVector.data());
+}
+
+void OpenGLProgram::SetShaderType(TextureShaderType eTextureShaderType)
+{
+ SetUniform1i("type", GLint(eTextureShaderType));
+}
+
+void OpenGLProgram::SetShaderType(DrawShaderType eDrawShaderType)
+{
+ SetUniform1i("type", GLint(eDrawShaderType));
+}
+
+GLuint OpenGLProgram::GetUniformLocation( const OString& rName )
+{
+ auto it = maUniformLocations.find( rName );
+ if( it == maUniformLocations.end() )
+ {
+ GLuint nLocation = glGetUniformLocation( mnId, rName.getStr() );
+ CHECK_GL_ERROR();
+ maUniformLocations[rName] = nLocation;
+ return nLocation;
+ }
+
+ return it->second;
+}
+
+void OpenGLProgram::DrawArrays(GLenum aMode, std::vector<GLfloat>& aVertices)
+{
+ if (!mbBlending)
+ OpenGLContext::getVCLContext()->state().blend().disable();
+
+ SetVertices(aVertices.data());
+ glDrawArrays(aMode, 0, aVertices.size() / 2);
+}
+
+void OpenGLProgram::DrawElements(GLenum aMode, GLuint nNumberOfVertices)
+{
+ if (!mbBlending)
+ OpenGLContext::getVCLContext()->state().blend().disable();
+
+ glDrawElements(aMode, nNumberOfVertices, GL_UNSIGNED_INT, nullptr);
+}
+
+void OpenGLProgram::SetUniform1f( const OString& rName, GLfloat v1 )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ glUniform1f( nUniform, v1 );
+ CHECK_GL_ERROR();
+}
+
+void OpenGLProgram::SetUniform2f( const OString& rName, GLfloat v1, GLfloat v2 )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ glUniform2f( nUniform, v1, v2 );
+ CHECK_GL_ERROR();
+}
+
+void OpenGLProgram::SetUniform1fv( const OString& rName, GLsizei nCount, GLfloat const * aValues )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ glUniform1fv( nUniform, nCount, aValues );
+ CHECK_GL_ERROR();
+}
+
+void OpenGLProgram::SetUniform2fv( const OString& rName, GLsizei nCount, GLfloat const * aValues )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ glUniform2fv( nUniform, nCount, aValues );
+ CHECK_GL_ERROR();
+}
+
+void OpenGLProgram::SetUniform1i( const OString& rName, GLint v1 )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ glUniform1i( nUniform, v1 );
+ CHECK_GL_ERROR();
+}
+
+void OpenGLProgram::SetColor( const OString& rName, Color nColor, sal_uInt8 nTransparency )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ glUniform4f( nUniform,
+ nColor.GetRed() / 255.0f,
+ nColor.GetGreen() / 255.0f,
+ nColor.GetBlue() / 255.0f,
+ (100 - nTransparency) * (1.0 / 100) );
+ CHECK_GL_ERROR();
+
+ if( nTransparency > 0 )
+ SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+}
+
+void OpenGLProgram::SetColorf( const OString& rName, Color nColor, double fTransparency )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ glUniform4f( nUniform,
+ nColor.GetRed() / 255.0f,
+ nColor.GetGreen() / 255.0f,
+ nColor.GetBlue() / 255.0f,
+ (1.0f - fTransparency) );
+ CHECK_GL_ERROR();
+
+ if( fTransparency > 0.0 )
+ SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+}
+
+void OpenGLProgram::SetColor( const OString& rName, const Color& rColor )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ glUniform4f( nUniform,
+ static_cast<float>(rColor.GetRed()) / 255,
+ static_cast<float>(rColor.GetGreen()) / 255,
+ static_cast<float>(rColor.GetBlue()) / 255,
+ 1.0f - static_cast<float>(rColor.GetTransparency()) / 255 );
+ CHECK_GL_ERROR();
+
+ if( rColor.GetTransparency() > 0 )
+ SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+}
+
+void OpenGLProgram::SetColorWithIntensity( const OString& rName, const Color& rColor, long nFactor )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ glUniform4f( nUniform,
+ static_cast<float>(rColor.GetRed()) * nFactor / 25500.0,
+ static_cast<float>(rColor.GetGreen()) * nFactor / 25500.0,
+ static_cast<float>(rColor.GetBlue()) * nFactor / 25500.0,
+ 1.0f );
+ CHECK_GL_ERROR();
+}
+
+void OpenGLProgram::SetTexture( const OString& rName, OpenGLTexture& rTexture )
+{
+ GLuint nUniform = GetUniformLocation( rName );
+ int nIndex = maTextures.size();
+
+ glUniform1i( nUniform, nIndex );
+ CHECK_GL_ERROR();
+
+ OpenGLContext::getVCLContext()->state().texture().active(nIndex);
+
+ rTexture.Bind();
+ maTextures.push_back(rTexture);
+}
+
+void OpenGLProgram::SetTransform(
+ const OString& rName,
+ const OpenGLTexture& rTexture,
+ const basegfx::B2DPoint& rNull,
+ const basegfx::B2DPoint& rX,
+ const basegfx::B2DPoint& rY )
+{
+ auto nTexWidth = rTexture.GetWidth();
+ auto nTexHeight = rTexture.GetHeight();
+ if (nTexWidth == 0 || nTexHeight == 0)
+ return;
+
+ GLuint nUniform = GetUniformLocation( rName );
+ const basegfx::B2DVector aXRel = rX - rNull;
+ const basegfx::B2DVector aYRel = rY - rNull;
+ const float aValues[] = {
+ static_cast<float>(aXRel.getX())/nTexWidth, static_cast<float>(aXRel.getY())/nTexWidth, 0, 0,
+ static_cast<float>(aYRel.getX())/nTexHeight, static_cast<float>(aYRel.getY())/nTexHeight, 0, 0,
+ 0, 0, 1, 0,
+ static_cast<float>(rNull.getX()), static_cast<float>(rNull.getY()), 0, 1 };
+ glm::mat4 aMatrix = glm::make_mat4( aValues );
+ glUniformMatrix4fv( nUniform, 1, GL_FALSE, glm::value_ptr( aMatrix ) );
+ CHECK_GL_ERROR();
+}
+
+void OpenGLProgram::SetIdentityTransform(const OString& rName)
+{
+ GLuint nUniform = GetUniformLocation(rName);
+ glm::mat4 aMatrix {};
+ glUniformMatrix4fv(nUniform, 1, GL_FALSE, glm::value_ptr( aMatrix ) );
+ CHECK_GL_ERROR();
+}
+
+void OpenGLProgram::ApplyMatrix(float fWidth, float fHeight, float fPixelOffset)
+{
+
+ if (mfLastWidth == fWidth && mfLastHeight == fHeight && mfLastPixelOffset == fPixelOffset)
+ return;
+
+ mfLastWidth = fWidth;
+ mfLastHeight = fHeight;
+ mfLastPixelOffset = fPixelOffset;
+
+ GLuint nUniform = GetUniformLocation("mvp");
+
+ glm::mat4 aMVP = glm::ortho(0.0f, fWidth, fHeight, 0.0f, 0.0f, 1.0f);
+
+ if (fPixelOffset != 0.0f)
+ aMVP = glm::translate(aMVP, glm::vec3(fPixelOffset, fPixelOffset, 0.0f));
+
+ glUniformMatrix4fv(nUniform, 1, GL_FALSE, glm::value_ptr(aMVP));
+ CHECK_GL_ERROR();
+}
+
+void OpenGLProgram::SetBlendMode(GLenum nSFactor, GLenum nDFactor)
+{
+ OpenGLContext::getVCLContext()->state().blend().enable();
+ OpenGLContext::getVCLContext()->state().blend().func(nSFactor, nDFactor);
+ mbBlending = true;
+}
+
+void OpenGLProgram::DrawTexture( const OpenGLTexture& rTexture )
+{
+ if (!rTexture)
+ return;
+
+ float fWidth = rTexture.GetWidth();
+ float fHeight = rTexture.GetHeight();
+
+ float fMinX = 0.0f;
+ float fMaxX = fWidth;
+ float fMinY = 0.0f;
+ float fMaxY = fHeight;
+
+ std::vector<GLfloat> aPosition {
+ fMinX, fMaxY,
+ fMinX, fMinY,
+ fMaxX, fMinY,
+ fMaxX, fMaxY
+ };
+ GLfloat aTexCoord[8];
+
+ rTexture.GetWholeCoord( aTexCoord );
+ SetTextureCoord( aTexCoord );
+ ApplyMatrix(fWidth, fHeight);
+ DrawArrays(GL_TRIANGLE_FAN, aPosition);
+ CHECK_GL_ERROR();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/salbmp.cxx b/vcl/opengl/salbmp.cxx
new file mode 100644
index 000000000..e9b1bca73
--- /dev/null
+++ b/vcl/opengl/salbmp.cxx
@@ -0,0 +1,783 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+#include <vcl/bitmap.hxx>
+#include <vcl/checksum.hxx>
+#include <vcl/outdev.hxx>
+#include <svdata.hxx>
+#include <salgdi.hxx>
+#include <vcleventlisteners.hxx>
+#include <vcl/lazydelete.hxx>
+#include <scanlinewriter.hxx>
+
+#include <o3tl/make_shared.hxx>
+
+#include <opengl/zone.hxx>
+#include <opengl/program.hxx>
+#include <opengl/salbmp.hxx>
+#include <opengl/RenderState.hxx>
+#include <opengl/FixedTextureAtlas.hxx>
+
+#if OSL_DEBUG_LEVEL > 0
+# define CANARY "tex-canary"
+#endif
+
+namespace
+{
+
+bool determineTextureFormat(sal_uInt16 nBits, GLenum& nFormat, GLenum& nType)
+{
+ switch(nBits)
+ {
+ case 8:
+ nFormat = GL_LUMINANCE;
+ nType = GL_UNSIGNED_BYTE;
+ return true;
+ case 24:
+ nFormat = GL_RGB;
+ nType = GL_UNSIGNED_BYTE;
+ return true;
+ case 32:
+ nFormat = GL_RGBA;
+ nType = GL_UNSIGNED_BYTE;
+ return true;
+ default:
+ break;
+ }
+ SAL_WARN("vcl.opengl", "Could not determine the appropriate texture format for input bits '" << nBits << "'");
+ return false;
+}
+
+bool isValidBitCount( sal_uInt16 nBitCount )
+{
+ return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 24) || (nBitCount == 32);
+}
+
+sal_uInt32 lclBytesPerRow(sal_uInt16 nBits, int nWidth)
+{
+ switch(nBits)
+ {
+ case 1: return (nWidth + 7) >> 3;
+ case 4: return (nWidth + 1) >> 1;
+ case 8: return nWidth;
+ case 24: return nWidth * 3;
+ case 32: return nWidth * 4;
+ default:
+ OSL_FAIL("vcl::OpenGLSalBitmap::AllocateUserData(), illegal bitcount!");
+ }
+ return 0;
+}
+}
+
+OpenGLSalBitmap::OpenGLSalBitmap()
+: mbDirtyTexture(true)
+, mnBits(0)
+, mnBytesPerRow(0)
+, mnWidth(0)
+, mnHeight(0)
+{
+}
+
+OpenGLSalBitmap::~OpenGLSalBitmap()
+{
+ Destroy();
+ VCL_GL_INFO( "~OpenGLSalBitmap" );
+}
+
+void OpenGLSalBitmap::Create( const OpenGLTexture& rTex, long nX, long nY, long nWidth, long nHeight )
+{
+ DBG_TESTSOLARMUTEX();
+ static const BitmapPalette aEmptyPalette;
+ OpenGLVCLContextZone aContextZone;
+
+ Destroy();
+ VCL_GL_INFO( "OpenGLSalBitmap::Create from FBO: ["
+ << nX << ", " << nY << "] " << nWidth << "x" << nHeight );
+
+ GLint nMaxTextureSize;
+ glGetIntegerv( GL_MAX_TEXTURE_SIZE, &nMaxTextureSize );
+ if ( nWidth > nMaxTextureSize )
+ {
+ nWidth = nMaxTextureSize;
+ VCL_GL_INFO( "Width limited to " << nMaxTextureSize );
+ }
+
+ if ( nHeight > nMaxTextureSize )
+ {
+ nHeight = nMaxTextureSize;
+ VCL_GL_INFO( "Height limited to " << nMaxTextureSize );
+ }
+
+ mnWidth = nWidth;
+ mnHeight = nHeight;
+
+ // TODO Check the framebuffer configuration
+ mnBits = 32;
+ maPalette = aEmptyPalette;
+
+ if( rTex )
+ maTexture = OpenGLTexture( rTex, nX, nY, nWidth, nHeight );
+ else
+ maTexture = OpenGLTexture( nX, nY, nWidth, nHeight );
+ mbDirtyTexture = false;
+ VCL_GL_INFO( "Created texture " << maTexture.Id() );
+
+ assert(mnWidth == maTexture.GetWidth() &&
+ mnHeight == maTexture.GetHeight());
+}
+
+bool OpenGLSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
+{
+ DBG_TESTSOLARMUTEX();
+ OpenGLVCLContextZone aContextZone;
+
+ Destroy();
+ VCL_GL_INFO( "OpenGLSalBitmap::Create with size: " << rSize );
+
+ if( !isValidBitCount( nBits ) )
+ return false;
+ maPalette = rBitmapPalette;
+ mnBits = nBits;
+ mnWidth = rSize.Width();
+ mnHeight = rSize.Height();
+
+ // Limit size to what GL allows, so later glTexImage2D() won't fail.
+ GLint nMaxTextureSize;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxTextureSize);
+ if (mnWidth > nMaxTextureSize)
+ mnWidth = nMaxTextureSize;
+ if (mnHeight > nMaxTextureSize)
+ mnHeight = nMaxTextureSize;
+
+ return false;
+}
+
+bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp )
+{
+ DBG_TESTSOLARMUTEX();
+ return Create( rSalBmp, rSalBmp.GetBitCount() );
+}
+
+bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
+{
+ DBG_TESTSOLARMUTEX();
+ return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() );
+}
+
+bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount )
+{
+ DBG_TESTSOLARMUTEX();
+ OpenGLZone aZone;
+
+ // check that carefully only in the debug mode
+ assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBmp));
+
+ const OpenGLSalBitmap& rSourceBitmap = static_cast<const OpenGLSalBitmap&>(rSalBmp);
+
+ VCL_GL_INFO("OpenGLSalBitmap::Create from BMP: "
+ << rSourceBitmap.mnWidth << "x" << rSourceBitmap.mnHeight
+ << " Bits old: " << mnBits << " new:" << nNewBitCount );
+
+ if( isValidBitCount( nNewBitCount ) )
+ {
+ // TODO: lfrb: What about the pending operations?!
+ mnBits = nNewBitCount;
+ mnBytesPerRow = rSourceBitmap.mnBytesPerRow;
+ mnWidth = rSourceBitmap.mnWidth;
+ mnHeight = rSourceBitmap.mnHeight;
+ maPalette = rSourceBitmap.maPalette;
+ // execute any pending operations on the source bitmap
+ maTexture = rSourceBitmap.GetTexture();
+ mbDirtyTexture = false;
+
+ // be careful here, we are share & reference-count the
+ // mpUserBuffer, BUT this Create() is called from
+ // Bitmap::ImplMakeUnique().
+ // Consequently, there might be cases when this needs to be made
+ // unique later (when we don't do that right away here), like when
+ // using the BitmapWriteAccess.
+ mpUserBuffer = rSourceBitmap.mpUserBuffer;
+
+ return true;
+ }
+ return false;
+}
+
+bool OpenGLSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/, Size& /*rSize*/, bool /*bMask*/ )
+{
+ DBG_TESTSOLARMUTEX();
+ // TODO Is this method needed?
+ return false;
+}
+
+OpenGLTexture& OpenGLSalBitmap::GetTexture() const
+{
+ OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
+ if( !maTexture || mbDirtyTexture )
+ pThis->CreateTexture();
+ VCL_GL_INFO( "Got texture " << maTexture.Id() );
+ return pThis->maTexture;
+}
+
+void OpenGLSalBitmap::Destroy()
+{
+ OpenGLZone aZone;
+
+ VCL_GL_INFO("Destroy OpenGLSalBitmap texture:" << maTexture.Id());
+ maTexture = OpenGLTexture();
+ DeallocateUserData();
+}
+
+bool OpenGLSalBitmap::AllocateUserData()
+{
+ VCL_GL_INFO( "OpenGLSalBitmap::AllocateUserData" );
+
+ if( mnWidth && mnHeight )
+ {
+ mnBytesPerRow = lclBytesPerRow(mnBits, mnWidth);
+ }
+
+ bool alloc = false;
+ if (mnBytesPerRow != 0 && mnHeight &&
+ mnBytesPerRow <= std::numeric_limits<sal_uInt32>::max() / mnHeight)
+ {
+ try
+ {
+ size_t nToAllocate = mnBytesPerRow * mnHeight;
+#if OSL_DEBUG_LEVEL > 0
+ nToAllocate += sizeof(CANARY);
+#endif
+ mpUserBuffer = o3tl::make_shared_array<sal_uInt8>(nToAllocate);
+#if OSL_DEBUG_LEVEL > 0
+ memcpy(mpUserBuffer.get() + nToAllocate - sizeof(CANARY),
+ CANARY, sizeof(CANARY));
+#endif
+ alloc = true;
+ }
+ catch (const std::bad_alloc &) {}
+ }
+ if (!alloc)
+ {
+ SAL_WARN("vcl.opengl", "bad alloc " << mnBytesPerRow << "x" << mnHeight);
+ DeallocateUserData();
+ }
+#ifdef DBG_UTIL
+ else
+ {
+ for (size_t i = 0; i < size_t(mnBytesPerRow * mnHeight); i++)
+ mpUserBuffer.get()[i] = (i & 0xFF);
+ }
+#endif
+
+ return mpUserBuffer != nullptr;
+}
+
+void OpenGLSalBitmap::DeallocateUserData()
+{
+ mpUserBuffer.reset();
+ mnBytesPerRow = 0;
+}
+
+namespace {
+
+void lclInstantiateTexture(OpenGLTexture& rTexture, const int nWidth, const int nHeight,
+ const GLenum nFormat, const GLenum nType, sal_uInt8 const * pData)
+{
+ if (nWidth == nHeight)
+ {
+ typedef std::vector<std::unique_ptr<FixedTextureAtlasManager>> TextureAtlasVector;
+ static vcl::DeleteOnDeinit<TextureAtlasVector> aTextureAtlases([]() {
+ TextureAtlasVector* p = new TextureAtlasVector;
+ p->reserve(5);
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 16));
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 24));
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 32));
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 48));
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 64));
+ return p;
+ }());
+ for (std::unique_ptr<FixedTextureAtlasManager>& pTextureAtlas : *aTextureAtlases.get())
+ {
+ if (nWidth == pTextureAtlas->GetSubtextureSize())
+ {
+ rTexture = pTextureAtlas->InsertBuffer(nWidth, nHeight, nFormat, nType, pData);
+ return;
+ }
+ }
+ }
+ rTexture = OpenGLTexture (nWidth, nHeight, nFormat, nType, pData);
+}
+
+} // end anonymous namespace
+
+Size OpenGLSalBitmap::GetSize() const
+{
+ return Size(mnWidth, mnHeight);
+}
+
+GLuint OpenGLSalBitmap::CreateTexture()
+{
+ VCL_GL_INFO( "::CreateTexture bits: " << mnBits);
+ GLenum nFormat = GL_RGBA;
+ GLenum nType = GL_UNSIGNED_BYTE;
+ sal_uInt8* pData( nullptr );
+ bool bAllocated( false );
+
+ if (mpUserBuffer != nullptr)
+ {
+ if( mnBits == 24 || mnBits == 32 )
+ {
+ // no conversion needed for truecolor
+ pData = mpUserBuffer.get();
+
+ determineTextureFormat(mnBits, nFormat, nType);
+ }
+ else if( mnBits == 8 && maPalette.IsGreyPalette8Bit() )
+ {
+ // no conversion needed for 8bit grayscale
+ pData = mpUserBuffer.get();
+ nFormat = GL_LUMINANCE;
+ nType = GL_UNSIGNED_BYTE;
+ }
+ else
+ {
+ VCL_GL_INFO( "::CreateTexture - convert from " << mnBits << " to 24 bits" );
+ // convert to 24 bits RGB using palette
+ determineTextureFormat(24, nFormat, nType);
+ pData = convertDataBitCount( mpUserBuffer.get(), mnWidth, mnHeight,
+ mnBits, mnBytesPerRow, maPalette,
+ nFormat == GL_BGR ? BitConvert::BGR : BitConvert::RGB ).release();
+ bAllocated = true;
+ }
+ }
+
+ OpenGLVCLContextZone aContextZone;
+
+ lclInstantiateTexture(maTexture, mnWidth, mnHeight, nFormat, nType, pData);
+
+ VCL_GL_INFO("Created texture " << maTexture.Id() << " bits: " << mnBits);
+
+ if( bAllocated )
+ delete[] pData;
+
+ mbDirtyTexture = false;
+
+ CHECK_GL_ERROR();
+ return maTexture.Id();
+}
+
+bool OpenGLSalBitmap::ReadTexture()
+{
+ sal_uInt8* pData = mpUserBuffer.get();
+
+ GLenum nFormat = GL_RGBA;
+ GLenum nType = GL_UNSIGNED_BYTE;
+
+ VCL_GL_INFO( "::ReadTexture " << mnWidth << "x" << mnHeight << " bits: " << mnBits);
+
+ if( pData == nullptr )
+ return false;
+
+ OpenGLVCLContextZone aContextZone;
+
+ rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
+ xContext->state().scissor().disable();
+ xContext->state().stencil().disable();
+
+ if ((mnBits == 8 && maPalette.IsGreyPalette8Bit()) || mnBits == 24 || mnBits == 32)
+ {
+ determineTextureFormat(mnBits, nFormat, nType);
+
+#if OSL_DEBUG_LEVEL > 0
+ // help valgrind & drmemory rescue us - touch last and first bits.
+ pData[0] = 0;
+ pData[mnBits/8*mnWidth*mnHeight-1] = 0;
+ // if this fails we can read too much into pData
+ assert(mnWidth == maTexture.GetWidth() &&
+ mnHeight == maTexture.GetHeight());
+#endif
+
+ maTexture.Read(nFormat, nType, pData);
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ // If we read over the end of pData we have a real hidden memory
+ // corruption problem !
+ size_t nCanary = mnBytesPerRow * mnHeight;
+ assert(!memcmp(pData + nCanary, CANARY, sizeof (CANARY)));
+#endif
+ return true;
+ }
+ else if (mnBits == 1 || mnBits == 4 || mnBits == 8)
+ { // convert buffers from 24-bit RGB to 1,4 or 8-bit buffer
+ std::vector<sal_uInt8> aBuffer(mnWidth * mnHeight * 3);
+
+ sal_uInt8* pBuffer = aBuffer.data();
+ determineTextureFormat(24, nFormat, nType);
+ maTexture.Read(nFormat, nType, pBuffer);
+ sal_uInt32 nSourceBytesPerRow = lclBytesPerRow(24, mnWidth);
+
+ std::unique_ptr<vcl::ScanlineWriter> pWriter = vcl::ScanlineWriter::Create(mnBits, maPalette);
+ for (int y = 0; y < mnHeight; ++y)
+ {
+ sal_uInt8* pSource = &pBuffer[y * nSourceBytesPerRow];
+ sal_uInt8* pDestination = &pData[y * mnBytesPerRow];
+
+ pWriter->nextLine(pDestination);
+
+ for (int x = 0; x < mnWidth; ++x)
+ {
+ // read source
+ sal_uInt8 nR = *pSource++;
+ sal_uInt8 nG = *pSource++;
+ sal_uInt8 nB = *pSource++;
+
+ pWriter->writeRGB(nR, nG, nB);
+ }
+ }
+ return true;
+ }
+
+ SAL_WARN("vcl.opengl", "::ReadTexture - tx:" << maTexture.Id() << " @ "
+ << mnWidth << "x" << mnHeight << "- unimplemented bit depth: "
+ << mnBits);
+ return false;
+}
+
+sal_uInt16 OpenGLSalBitmap::GetBitCount() const
+{
+ return mnBits;
+}
+
+bool OpenGLSalBitmap::calcChecksumGL(OpenGLTexture& rInputTexture, BitmapChecksum& rChecksum) const
+{
+ OUString FragShader("areaHashCRC64TFragmentShader");
+
+ rtl::Reference< OpenGLContext > xContext = OpenGLContext::getVCLContext();
+ xContext->state().scissor().disable();
+ xContext->state().stencil().disable();
+
+ static vcl::DeleteOnDeinit<OpenGLTexture> gCRCTableTexture(
+ new OpenGLTexture(512, 1, GL_RGBA, GL_UNSIGNED_BYTE,
+ vcl_get_crc64_table()));
+ OpenGLTexture &rCRCTableTexture = *gCRCTableTexture.get();
+
+ // First Pass
+
+ int nWidth = rInputTexture.GetWidth();
+ int nHeight = rInputTexture.GetHeight();
+
+ OpenGLProgram* pProgram = xContext->UseProgram("textureVertexShader", FragShader);
+ if (pProgram == nullptr)
+ return false;
+
+ int nNewWidth = ceil( nWidth / 4.0 );
+ int nNewHeight = ceil( nHeight / 4.0 );
+
+ OpenGLTexture aFirstPassTexture(nNewWidth, nNewHeight);
+ OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer(aFirstPassTexture);
+
+ pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
+ pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
+
+ pProgram->SetTexture("crc_table", rCRCTableTexture);
+ pProgram->SetTexture("sampler", rInputTexture);
+ pProgram->DrawTexture(rInputTexture);
+ pProgram->Clean();
+
+ OpenGLContext::ReleaseFramebuffer(pFramebuffer);
+
+ CHECK_GL_ERROR();
+
+ // Second Pass
+
+ nWidth = aFirstPassTexture.GetWidth();
+ nHeight = aFirstPassTexture.GetHeight();
+
+ pProgram = xContext->UseProgram("textureVertexShader", FragShader);
+ if (pProgram == nullptr)
+ return false;
+
+ nNewWidth = ceil( nWidth / 4.0 );
+ nNewHeight = ceil( nHeight / 4.0 );
+
+ OpenGLTexture aSecondPassTexture(nNewWidth, nNewHeight);
+ pFramebuffer = xContext->AcquireFramebuffer(aSecondPassTexture);
+
+ pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
+ pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
+
+ pProgram->SetTexture("crc_table", rCRCTableTexture);
+ pProgram->SetTexture("sampler", aFirstPassTexture);
+ pProgram->DrawTexture(aFirstPassTexture);
+ pProgram->Clean();
+
+ OpenGLContext::ReleaseFramebuffer(pFramebuffer);
+
+ CHECK_GL_ERROR();
+
+ // Final CRC on CPU
+ OpenGLTexture& aFinalTexture = aSecondPassTexture;
+ std::vector<sal_uInt8> aBuf( aFinalTexture.GetWidth() * aFinalTexture.GetHeight() * 4 );
+ aFinalTexture.Read(GL_RGBA, GL_UNSIGNED_BYTE, aBuf.data());
+
+ BitmapChecksum nCrc = vcl_get_checksum(0, aBuf.data(), aBuf.size());
+
+ rChecksum = nCrc;
+ return true;
+}
+
+void OpenGLSalBitmap::updateChecksum() const
+{
+ if (mbChecksumValid)
+ return;
+
+ if( (mnWidth * mnHeight) < (1024*768) || mnWidth < 128 || mnHeight < 128 )
+ {
+ SalBitmap::updateChecksum();
+ return;
+ }
+
+ OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
+
+ OpenGLVCLContextZone aContextZone;
+ OpenGLTexture& rInputTexture = GetTexture();
+ pThis->mbChecksumValid = calcChecksumGL(rInputTexture, pThis->mnChecksum);
+ if (!pThis->mbChecksumValid)
+ SalBitmap::updateChecksum();
+}
+
+BitmapBuffer* OpenGLSalBitmap::AcquireBuffer( BitmapAccessMode nMode )
+{
+ OpenGLVCLContextZone aContextZone;
+
+ if( nMode != BitmapAccessMode::Info )
+ {
+ if (!mpUserBuffer)
+ {
+ if( !AllocateUserData() )
+ return nullptr;
+ if( maTexture && !ReadTexture() )
+ {
+ DeallocateUserData();
+ return nullptr;
+ }
+ }
+ }
+
+ // mpUserBuffer must be unique when we are doing the write access
+ if (nMode == BitmapAccessMode::Write && mpUserBuffer && mpUserBuffer.use_count() > 1)
+ {
+ std::shared_ptr<sal_uInt8> aBuffer(mpUserBuffer);
+
+ mpUserBuffer.reset();
+ AllocateUserData();
+ memcpy(mpUserBuffer.get(), aBuffer.get(), mnBytesPerRow * mnHeight);
+ }
+
+ BitmapBuffer* pBuffer = new BitmapBuffer;
+ pBuffer->mnWidth = mnWidth;
+ pBuffer->mnHeight = mnHeight;
+ pBuffer->maPalette = maPalette;
+ pBuffer->mnScanlineSize = mnBytesPerRow;
+ pBuffer->mpBits = mpUserBuffer.get();
+ pBuffer->mnBitCount = mnBits;
+
+ switch (mnBits)
+ {
+ case 1:
+ pBuffer->mnFormat = ScanlineFormat::N1BitMsbPal;
+ break;
+ case 4:
+ pBuffer->mnFormat = ScanlineFormat::N4BitMsnPal;
+ break;
+ case 8:
+ pBuffer->mnFormat = ScanlineFormat::N8BitPal;
+ break;
+ case 24:
+ {
+ pBuffer->mnFormat = ScanlineFormat::N24BitTcRgb;
+ break;
+ }
+ case 32:
+ {
+ pBuffer->mnFormat = ScanlineFormat::N32BitTcRgba;
+ ColorMaskElement aRedMask(0xff000000);
+ aRedMask.CalcMaskShift();
+ ColorMaskElement aGreenMask(0x00ff0000);
+ aGreenMask.CalcMaskShift();
+ ColorMaskElement aBlueMask(0x0000ff00);
+ aBlueMask.CalcMaskShift();
+ pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
+ break;
+ }
+ default: assert(false);
+ }
+
+ return pBuffer;
+}
+
+void OpenGLSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, BitmapAccessMode nMode )
+{
+ OpenGLVCLContextZone aContextZone;
+
+ if( nMode == BitmapAccessMode::Write )
+ {
+ maTexture = OpenGLTexture();
+ mbDirtyTexture = true;
+ mbChecksumValid = false;
+ }
+ // The palette is modified on read during the BitmapWriteAccess,
+ // but of course, often it is not modified; interesting.
+ maPalette = pBuffer->maPalette;
+
+ // Are there any more ground movements underneath us ?
+ assert( pBuffer->mnWidth == mnWidth );
+ assert( pBuffer->mnHeight == mnHeight );
+ assert( pBuffer->mnBitCount == mnBits );
+
+ delete pBuffer;
+}
+
+bool OpenGLSalBitmap::GetSystemData( BitmapSystemData& /*rData*/ )
+{
+ SAL_WARN( "vcl.opengl", "*** NOT IMPLEMENTED *** GetSystemData" );
+#if 0
+ // TODO Implement for ANDROID/OSX/IOS/WIN32
+ X11SalBitmap rBitmap;
+ BitmapBuffer* pBuffer;
+
+ rBitmap.Create( GetSize(), mnBits, maPalette );
+ pBuffer = rBitmap.AcquireBuffer( false );
+ if( pBuffer == NULL )
+ return false;
+
+ if (!mpUserBuffer.get())
+ {
+ if( !AllocateUserData() || !ReadTexture() )
+ {
+ rBitmap.ReleaseBuffer( pBuffer, false );
+ DeallocateUserData();
+ return false;
+ }
+ }
+
+ // TODO Might be more efficient to add a static method to SalBitmap
+ // to get system data from a buffer
+ memcpy( pBuffer->mpBits, mpUserBuffer.get(), mnBytesPerRow * mnHeight );
+
+ rBitmap.ReleaseBuffer( pBuffer, false );
+ return rBitmap.GetSystemData( rData );
+#else
+ return false;
+#endif
+}
+
+bool OpenGLSalBitmap::Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol )
+{
+ VCL_GL_INFO("::Replace");
+
+ OpenGLZone aZone;
+ rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
+ xContext->state().scissor().disable();
+ xContext->state().stencil().disable();
+
+ OpenGLFramebuffer* pFramebuffer;
+ OpenGLProgram* pProgram;
+
+ GetTexture();
+ pProgram = xContext->UseProgram( "textureVertexShader",
+ "replaceColorFragmentShader" );
+ if( !pProgram )
+ return false;
+
+ OpenGLTexture aNewTex( mnWidth, mnHeight );
+ pFramebuffer = xContext->AcquireFramebuffer( aNewTex );
+
+ pProgram->SetTexture( "sampler", maTexture );
+ pProgram->SetColor( "search_color", rSearchColor );
+ pProgram->SetColor( "replace_color", rReplaceColor );
+ pProgram->SetUniform1f( "epsilon", nTol / 255.0f );
+ pProgram->DrawTexture( maTexture );
+ pProgram->Clean();
+
+ OpenGLContext::ReleaseFramebuffer( pFramebuffer );
+ maTexture = aNewTex;
+
+ CHECK_GL_ERROR();
+ return true;
+}
+
+// Convert texture to greyscale and adjust bitmap metadata
+bool OpenGLSalBitmap::ConvertToGreyscale()
+{
+ VCL_GL_INFO("::ConvertToGreyscale");
+
+ // avoid re-converting to 8bits.
+ if ( mnBits == 8 && maPalette.IsGreyPalette8Bit())
+ return true;
+
+ OpenGLZone aZone;
+ rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
+ xContext->state().scissor().disable();
+ xContext->state().stencil().disable();
+
+ OpenGLFramebuffer* pFramebuffer;
+ OpenGLProgram* pProgram;
+
+ GetTexture();
+ pProgram = xContext->UseProgram("textureVertexShader", "greyscaleFragmentShader");
+
+ if (!pProgram)
+ return false;
+
+ OpenGLTexture aNewTex(mnWidth, mnHeight);
+ pFramebuffer = xContext->AcquireFramebuffer(aNewTex);
+ pProgram->SetTexture("sampler", maTexture);
+ pProgram->DrawTexture(maTexture);
+ pProgram->Clean();
+
+ OpenGLContext::ReleaseFramebuffer( pFramebuffer );
+ maTexture = aNewTex;
+ mnBits = 8;
+ maPalette = Bitmap::GetGreyPalette(256);
+
+ // AllocateUserData will handle the rest.
+ DeallocateUserData();
+ mbDirtyTexture = false;
+
+ CHECK_GL_ERROR();
+ return true;
+}
+
+// This is needed to just make the bitmap usable as an alpha channel.
+// Converting to 8bit grey will do.
+bool OpenGLSalBitmap::InterpretAs8Bit()
+{
+ return ConvertToGreyscale();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/scale.cxx b/vcl/opengl/scale.cxx
new file mode 100644
index 000000000..98f0f5ea7
--- /dev/null
+++ b/vcl/opengl/scale.cxx
@@ -0,0 +1,423 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <cmath>
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+#include <vcl/bitmap.hxx>
+
+#include <opengl/zone.hxx>
+#include <opengl/salbmp.hxx>
+#include <opengl/program.hxx>
+#include <opengl/texture.hxx>
+#include <opengl/RenderState.hxx>
+
+#include <ResampleKernel.hxx>
+
+using vcl::Kernel;
+using vcl::Lanczos3Kernel;
+
+bool OpenGLSalBitmap::ImplScaleFilter(
+ const rtl::Reference< OpenGLContext > &xContext,
+ const double& rScaleX,
+ const double& rScaleY,
+ GLenum nFilter )
+{
+ OpenGLFramebuffer* pFramebuffer;
+ OpenGLProgram* pProgram;
+ GLenum nOldFilter;
+ int nNewWidth( mnWidth * rScaleX );
+ int nNewHeight( mnHeight * rScaleY );
+
+ pProgram = xContext->UseProgram( "textureVertexShader",
+ "textureFragmentShader" );
+ if( !pProgram )
+ return false;
+
+ OpenGLTexture aNewTex(nNewWidth, nNewHeight);
+ pFramebuffer = xContext->AcquireFramebuffer( aNewTex );
+
+ pProgram->SetTexture( "sampler", maTexture );
+ nOldFilter = maTexture.GetFilter();
+ maTexture.SetFilter( nFilter );
+ pProgram->ApplyMatrix(mnWidth, mnHeight);
+ pProgram->DrawTexture( maTexture );
+ maTexture.SetFilter( nOldFilter );
+ pProgram->Clean();
+
+ OpenGLContext::ReleaseFramebuffer( pFramebuffer );
+
+ mnWidth = nNewWidth;
+ mnHeight = nNewHeight;
+ maTexture = aNewTex;
+
+ CHECK_GL_ERROR();
+ return true;
+}
+
+void OpenGLSalBitmap::ImplCreateKernel(
+ const double& fScale,
+ const Kernel& rKernel,
+ GLfloat*& pWeights,
+ sal_uInt32& aKernelSize )
+{
+ const double fSamplingRadius(rKernel.GetWidth());
+ const double fScaledRadius((fScale < 1.0) ? fSamplingRadius / fScale : fSamplingRadius);
+ const double fFilterFactor(std::min(fScale, 1.0));
+ int aNumberOfContributions;
+ double aSum( 0 );
+
+ aNumberOfContributions = (static_cast< sal_uInt32 >(fabs(ceil(fScaledRadius))) * 2) + 1 - 6;
+ aKernelSize = aNumberOfContributions / 2 + 1;
+
+ // avoid a crash for now; re-think me.
+ if (aKernelSize > 16)
+ aKernelSize = 16;
+
+ pWeights = new GLfloat[16];
+ memset( pWeights, 0, 16 * sizeof( GLfloat ) );
+
+ for( sal_uInt32 i(0); i < aKernelSize; i++ )
+ {
+ const GLfloat aWeight( rKernel.Calculate( fFilterFactor * i ) );
+ if( fabs( aWeight ) >= 0.0001 )
+ {
+ pWeights[i] = aWeight;
+ aSum += i > 0 ? aWeight * 2 : aWeight;
+ }
+ }
+
+ for( sal_uInt32 i(0); i < aKernelSize; i++ )
+ {
+ pWeights[i] /= aSum;
+ }
+}
+
+bool OpenGLSalBitmap::ImplScaleConvolution(
+ const rtl::Reference< OpenGLContext > &xContext,
+ const double& rScaleX,
+ const double& rScaleY,
+ const Kernel& aKernel )
+{
+ OpenGLFramebuffer* pFramebuffer;
+ OpenGLProgram* pProgram;
+ GLfloat* pWeights( nullptr );
+ sal_uInt32 nKernelSize;
+ GLfloat aOffsets[32];
+ int nNewWidth( mnWidth * rScaleX );
+ int nNewHeight( mnHeight * rScaleY );
+
+ // TODO Make sure the framebuffer is alright
+
+ pProgram = xContext->UseProgram( "textureVertexShader",
+ "convolutionFragmentShader" );
+ if( pProgram == nullptr )
+ return false;
+
+ // horizontal scaling in scratch texture
+ if( mnWidth != nNewWidth )
+ {
+ OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
+
+ pFramebuffer = xContext->AcquireFramebuffer( aScratchTex );
+
+ for( sal_uInt32 i = 0; i < 16; i++ )
+ {
+ aOffsets[i * 2] = i / static_cast<double>(mnWidth);
+ aOffsets[i * 2 + 1] = 0;
+ }
+ ImplCreateKernel( rScaleX, aKernel, pWeights, nKernelSize );
+ pProgram->SetUniform1fv( "kernel", 16, pWeights );
+ pProgram->SetUniform2fv( "offsets", 16, aOffsets );
+ pProgram->SetTexture( "sampler", maTexture );
+ pProgram->DrawTexture( maTexture );
+ pProgram->Clean();
+
+ maTexture = aScratchTex;
+ OpenGLContext::ReleaseFramebuffer( pFramebuffer );
+ }
+
+ // vertical scaling in final texture
+ if( mnHeight != nNewHeight )
+ {
+ OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
+
+ pFramebuffer = xContext->AcquireFramebuffer( aScratchTex );
+
+ for( sal_uInt32 i = 0; i < 16; i++ )
+ {
+ aOffsets[i * 2] = 0;
+ aOffsets[i * 2 + 1] = i / static_cast<double>(mnHeight);
+ }
+ ImplCreateKernel( rScaleY, aKernel, pWeights, nKernelSize );
+ pProgram->SetUniform1fv( "kernel", 16, pWeights );
+ pProgram->SetUniform2fv( "offsets", 16, aOffsets );
+ pProgram->SetTexture( "sampler", maTexture );
+ pProgram->DrawTexture( maTexture );
+ pProgram->Clean();
+
+ maTexture = aScratchTex;
+ OpenGLContext::ReleaseFramebuffer( pFramebuffer );
+ }
+
+ mnWidth = nNewWidth;
+ mnHeight = nNewHeight;
+
+ CHECK_GL_ERROR();
+ return true;
+}
+
+/*
+ "Area" scaling algorithm, which seems to give better results for downscaling
+ than other algorithms. The principle (taken from opencv, see resize.cl)
+ is that each resulting pixel is the average of all the source pixel values
+ it represents. Which is trivial in the case of exact multiples for downscaling,
+ the generic case needs to also consider that some source pixels contribute
+ only partially to their resulting pixels (because of non-integer multiples).
+*/
+bool OpenGLSalBitmap::ImplScaleArea( const rtl::Reference< OpenGLContext > &xContext,
+ double rScaleX, double rScaleY )
+{
+ int nNewWidth( mnWidth * rScaleX );
+ int nNewHeight( mnHeight * rScaleY );
+
+ if( nNewWidth == mnWidth && nNewHeight == mnHeight )
+ return true;
+
+ double ixscale = 1 / rScaleX;
+ double iyscale = 1 / rScaleY;
+ bool fast = ( ixscale == std::trunc( ixscale ) && iyscale == std::trunc( iyscale )
+ && int( nNewWidth * ixscale ) == mnWidth && int( nNewHeight * iyscale ) == mnHeight );
+
+ bool bTwoPasses = false;
+
+ // The generic case has arrays only up to 100 ratio downscaling, which is hopefully enough
+ // in practice, but protect against buffer overflows in case such an extreme case happens
+ // (and in such case the precision of the generic algorithm probably doesn't matter anyway).
+ if( ixscale > 100 || iyscale > 100 )
+ {
+ fast = true;
+ }
+ else
+ {
+ if (ixscale > 16 || iyscale > 16)
+ {
+ ixscale = std::floor(std::sqrt(ixscale));
+ iyscale = std::floor(std::sqrt(iyscale));
+ nNewWidth = int(mnWidth / ixscale);
+ rScaleX *= ixscale; // second pass x-scale factor
+ nNewHeight = int(mnHeight / iyscale);
+ rScaleY *= iyscale; // second pass y-scale factor
+ bTwoPasses = true;
+ }
+ }
+
+ // TODO Make sure the framebuffer is alright
+
+ OString sUseReducedRegisterVariantDefine;
+ if (xContext->getOpenGLCapabilitySwitch().mbLimitedShaderRegisters)
+ sUseReducedRegisterVariantDefine = OString("#define USE_REDUCED_REGISTER_VARIANT\n");
+
+ OpenGLProgram* pProgram = xContext->UseProgram( "textureVertexShader",
+ fast ? OUString( "areaScaleFastFragmentShader" ) : OUString( "areaScaleFragmentShader" ),
+ sUseReducedRegisterVariantDefine);
+
+ if( pProgram == nullptr )
+ return false;
+
+ OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
+
+ OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer( aScratchTex );
+
+ // NOTE: This setup is also done in OpenGLSalGraphicsImpl::DrawTransformedTexture().
+ if( fast )
+ {
+ pProgram->SetUniform1i( "xscale", ixscale );
+ pProgram->SetUniform1i( "yscale", iyscale );
+ // The shader operates on pixels in the surrounding area, so it's necessary
+ // to know the step in texture coordinates to get to the next pixel.
+ // With a texture atlas the "texture" is just a subtexture of a larger texture,
+ // so while with a normal texture we'd map between <0.0,1.0> and <0,mnWidth>,
+ // with a subtexture the texture coordinates range is smaller.
+ GLfloat srcCoords[ 8 ];
+ maTexture.GetWholeCoord( srcCoords );
+ pProgram->SetUniform1f( "xstep", ( srcCoords[ 4 ] - srcCoords[ 0 ] ) / mnWidth );
+ pProgram->SetUniform1f( "ystep", ( srcCoords[ 5 ] - srcCoords[ 1 ] ) / mnHeight );
+ pProgram->SetUniform1f( "ratio", 1.0 / ( ixscale * iyscale ));
+ }
+ else
+ {
+ pProgram->SetUniform1f( "xscale", ixscale );
+ pProgram->SetUniform1f( "yscale", iyscale );
+ pProgram->SetUniform1i( "swidth", mnWidth );
+ pProgram->SetUniform1i( "sheight", mnHeight );
+ // The shader internally actually operates on pixel coordinates,
+ // so it needs to know how to convert to those from the texture coordinates.
+ // With a simple texture that would mean converting e.g. between
+ // <0,mnWidth-1> and <0.0,1.0> coordinates.
+ // However with a texture atlas the "texture" is just a subtexture
+ // of a larger texture, so the texture coordinates need offset and ratio
+ // conversion too.
+ GLfloat srcCoords[ 8 ];
+ maTexture.GetWholeCoord( srcCoords );
+ pProgram->SetUniform1f( "xoffset", srcCoords[ 0 ] );
+ pProgram->SetUniform1f( "yoffset", srcCoords[ 1 ] );
+ pProgram->SetUniform1f( "xtopixelratio", nNewWidth / ( srcCoords[ 4 ] - srcCoords[ 0 ] ));
+ pProgram->SetUniform1f( "ytopixelratio", nNewHeight / ( srcCoords[ 5 ] - srcCoords[ 1 ] ));
+ pProgram->SetUniform1f( "xfrompixelratio", ( srcCoords[ 4 ] - srcCoords[ 0 ] ) / mnWidth );
+ pProgram->SetUniform1f( "yfrompixelratio", ( srcCoords[ 5 ] - srcCoords[ 1 ] ) / mnHeight );
+ }
+
+ pProgram->SetTexture( "sampler", maTexture );
+ pProgram->DrawTexture( maTexture );
+ pProgram->Clean();
+
+ OpenGLContext::ReleaseFramebuffer(pFramebuffer);
+
+ CHECK_GL_ERROR();
+
+ if (bTwoPasses)
+ {
+ mnWidth = nNewWidth;
+ mnHeight = nNewHeight;
+
+ nNewWidth = round(mnWidth * rScaleX);
+ nNewHeight = round(mnHeight * rScaleY);
+
+ ixscale = 1 / rScaleX;
+ iyscale = 1 / rScaleY;
+
+ pProgram = xContext->UseProgram("textureVertexShader", "areaScaleFragmentShader", sUseReducedRegisterVariantDefine);
+ if (pProgram == nullptr)
+ return false;
+
+ OpenGLTexture aScratchTex2(nNewWidth, nNewHeight);
+
+ pFramebuffer = xContext->AcquireFramebuffer(aScratchTex2);
+
+ pProgram->SetUniform1f("xscale", ixscale);
+ pProgram->SetUniform1f("yscale", iyscale);
+ pProgram->SetUniform1i("swidth", mnWidth);
+ pProgram->SetUniform1i("sheight", mnHeight);
+
+ GLfloat srcCoords[ 8 ];
+ aScratchTex.GetWholeCoord( srcCoords );
+ pProgram->SetUniform1f( "xoffset", srcCoords[ 0 ] );
+ pProgram->SetUniform1f( "yoffset", srcCoords[ 1 ] );
+ pProgram->SetUniform1f( "xtopixelratio", nNewWidth / ( srcCoords[ 4 ] - srcCoords[ 0 ] ));
+ pProgram->SetUniform1f( "ytopixelratio", nNewHeight / ( srcCoords[ 5 ] - srcCoords[ 1 ] ));
+ pProgram->SetUniform1f( "xfrompixelratio", ( srcCoords[ 4 ] - srcCoords[ 0 ] ) / mnWidth );
+ pProgram->SetUniform1f( "yfrompixelratio", ( srcCoords[ 5 ] - srcCoords[ 1 ] ) / mnHeight );
+
+ pProgram->SetTexture("sampler", aScratchTex);
+ pProgram->DrawTexture(aScratchTex);
+ pProgram->Clean();
+
+ OpenGLContext::ReleaseFramebuffer(pFramebuffer);
+
+ CHECK_GL_ERROR();
+
+ maTexture = aScratchTex2;
+ mnWidth = nNewWidth;
+ mnHeight = nNewHeight;
+ }
+ else
+ {
+ maTexture = aScratchTex;
+ mnWidth = nNewWidth;
+ mnHeight = nNewHeight;
+ }
+
+ return true;
+}
+
+void OpenGLSalBitmap::ImplScale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
+{
+ VCL_GL_INFO( "::ImplScale" );
+
+ mpUserBuffer.reset();
+ OpenGLVCLContextZone aContextZone;
+ rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
+ xContext->state().scissor().disable();
+ xContext->state().stencil().disable();
+
+ if (rScaleX <= 1 && rScaleY <= 1)
+ {
+ nScaleFlag = BmpScaleFlag::BestQuality;
+ }
+
+ if( nScaleFlag == BmpScaleFlag::Fast )
+ {
+ ImplScaleFilter( xContext, rScaleX, rScaleY, GL_NEAREST );
+ }
+ else if( nScaleFlag == BmpScaleFlag::BiLinear )
+ {
+ ImplScaleFilter( xContext, rScaleX, rScaleY, GL_LINEAR );
+ }
+ else if( nScaleFlag == BmpScaleFlag::Default )
+ {
+ const Lanczos3Kernel aKernel;
+
+ ImplScaleConvolution( xContext, rScaleX, rScaleY, aKernel );
+ }
+ else if( nScaleFlag == BmpScaleFlag::BestQuality && rScaleX <= 1 && rScaleY <= 1 )
+ { // Use area scaling for best quality, but only if downscaling.
+ ImplScaleArea( xContext, rScaleX, rScaleY );
+ }
+ else if( nScaleFlag == BmpScaleFlag::Lanczos || nScaleFlag == BmpScaleFlag::BestQuality )
+ {
+ const Lanczos3Kernel aKernel;
+
+ ImplScaleConvolution( xContext, rScaleX, rScaleY, aKernel );
+ }
+ else
+ SAL_WARN( "vcl.opengl", "Invalid flag for scaling operation" );
+}
+
+bool OpenGLSalBitmap::ScalingSupported() const
+{
+ return true;
+}
+
+bool OpenGLSalBitmap::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
+{
+ OpenGLVCLContextZone aContextZone;
+
+ VCL_GL_INFO("::Scale " << int(nScaleFlag)
+ << " from " << mnWidth << "x" << mnHeight
+ << " to " << (mnWidth * rScaleX) << "x" << (mnHeight * rScaleY) );
+
+ if( nScaleFlag == BmpScaleFlag::Fast ||
+ nScaleFlag == BmpScaleFlag::BiLinear ||
+ nScaleFlag == BmpScaleFlag::Lanczos ||
+ nScaleFlag == BmpScaleFlag::Default ||
+ nScaleFlag == BmpScaleFlag::BestQuality )
+ {
+ ImplScale( rScaleX, rScaleY, nScaleFlag );
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/areaHashCRC64TFragmentShader.glsl b/vcl/opengl/shaders/areaHashCRC64TFragmentShader.glsl
new file mode 100644
index 000000000..901b481d8
--- /dev/null
+++ b/vcl/opengl/shaders/areaHashCRC64TFragmentShader.glsl
@@ -0,0 +1,87 @@
+/* -*- 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/.
+ */
+
+/* TODO Use textureOffset for newest version of GLSL */
+
+
+#version 130
+
+uniform sampler2D crc_table;
+uniform sampler2D sampler;
+uniform float xstep;
+uniform float ystep;
+
+varying vec2 tex_coord;
+
+const int scale = 4;
+const float ratio = 16.0;
+
+
+ivec2 crc64( ivec2 hval, int color )
+{
+ int dx = 2 * ((hval[0] ^ color) & 0xff);
+ float s = dx / 255.0;
+ vec4 table_value_lo = round(texture2D( crc_table, vec2( s, 0.0 ) ) * 255.0);
+ s = (dx+1) / 255.0;
+ vec4 table_value_hi = round(texture2D( crc_table, vec2( s, 0.0 ) ) * 255.0);
+
+ int tvalue_lo = int(table_value_lo[0]) | (int(table_value_lo[1]) << 8) | (int(table_value_lo[2]) << 16) | (int(table_value_lo[3]) << 24);
+ int tvalue_hi = int(table_value_hi[0]) | (int(table_value_hi[1]) << 8) | (int(table_value_hi[2]) << 16) | (int(table_value_hi[3]) << 24);
+
+ hval[1] = tvalue_hi ^ (hval[1] >> 8);
+ hval[0] = tvalue_lo ^ ( (hval[1] << 24) | (hval[0] >> 8) );
+
+ return hval;
+}
+
+
+void main(void)
+{
+ ivec2 Crc = ivec2( 0xffffffff, 0xffffffff );
+ vec2 offset = vec2( 0.0, 0.0 );
+ vec2 next_coord = tex_coord.st;
+ for( int y = 0; y < scale && next_coord.y <= 1.0; ++y )
+ {
+ for( int x = 0; x < scale && next_coord.x <= 1.0; ++x )
+ {
+ vec4 pixel = round(texture2D( sampler, next_coord ) * 255.0);
+
+ int r = int(pixel.r); // 0..255
+ int g = int(pixel.g); // 0..255
+ int b = int(pixel.b); // 0..255
+ int a = int(pixel.a); // 0..255
+
+ Crc = crc64( Crc, r );
+ Crc = crc64( Crc, g );
+ Crc = crc64( Crc, b );
+ Crc = crc64( Crc, a );
+
+ offset.x += xstep;
+ next_coord = tex_coord.st + offset;
+ }
+ offset.y += ystep;
+ offset.x = 0.0;
+ next_coord = tex_coord.st + offset;
+ }
+
+ Crc[0] = ~Crc[0];
+ Crc[1] = ~Crc[1];
+
+ int Hash = Crc[0] ^ Crc[1];
+
+ float fr = ( Hash & 0xff) / 255.0;
+ float fg = ((Hash >> 8) & 0xff) / 255.0;
+ float fb = ((Hash >> 16) & 0xff) / 255.0;
+ float fa = ((Hash >> 24) & 0xff) / 255.0;
+
+
+ gl_FragColor = vec4(fr, fg, fb, fa);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/areaScaleFastFragmentShader.glsl b/vcl/opengl/shaders/areaScaleFastFragmentShader.glsl
new file mode 100644
index 000000000..57ad8fa97
--- /dev/null
+++ b/vcl/opengl/shaders/areaScaleFastFragmentShader.glsl
@@ -0,0 +1,59 @@
+/* -*- 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/.
+ */
+
+/* TODO Use textureOffset for newest version of GLSL */
+
+#version 130
+
+uniform sampler2D sampler;
+uniform int xscale;
+uniform int yscale;
+uniform float xstep;
+uniform float ystep;
+uniform float ratio; // = 1.0/(xscale*yscale)
+
+varying vec2 tex_coord;
+
+// This mode makes the scaling work like maskedTextureFragmentShader.glsl
+// (instead of like plain textureVertexShader.glsl).
+#ifdef MASKED
+varying vec2 mask_coord;
+uniform sampler2D mask;
+#endif
+
+/*
+ Just make the resulting color the average of all the source pixels
+ (which is an area (xscale)x(yscale) ).
+*/
+void main(void)
+{
+ vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );
+ vec2 offset = vec2( 0.0, 0.0 );
+ for( int y = 0; y < yscale; ++y )
+ {
+ for( int x = 0; x < xscale; ++x )
+ {
+#ifndef MASKED
+ sum += texture2D( sampler, tex_coord.st + offset );
+#else
+ vec4 texel;
+ texel = texture2D( sampler, tex_coord.st + offset );
+ texel.a = 1.0 - texture2D( mask, mask_coord.st + offset ).r;
+ sum += texel;
+#endif
+ offset.x += xstep;
+ }
+ offset.y += ystep;
+ offset.x = 0.0;
+ }
+ sum *= ratio;
+ gl_FragColor = sum;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/areaScaleFragmentShader.glsl b/vcl/opengl/shaders/areaScaleFragmentShader.glsl
new file mode 100644
index 000000000..5dab5ba01
--- /dev/null
+++ b/vcl/opengl/shaders/areaScaleFragmentShader.glsl
@@ -0,0 +1,239 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+uniform sampler2D sampler;
+uniform int swidth;
+uniform int sheight;
+uniform float xscale;
+uniform float yscale;
+uniform float xoffset;
+uniform float yoffset;
+uniform float xfrompixelratio;
+uniform float yfrompixelratio;
+uniform float xtopixelratio;
+uniform float ytopixelratio;
+
+varying vec2 tex_coord;
+
+// This mode makes the scaling work like maskedTextureFragmentShader.glsl
+// (instead of like plain textureVertexShader.glsl).
+#ifdef MASKED
+varying vec2 mask_coord;
+uniform sampler2D mask;
+#endif
+
+#ifdef USE_REDUCED_REGISTER_VARIANT
+
+vec4 getTexel(int x, int y)
+{
+ vec2 pos = vec2( x * xfrompixelratio + xoffset, y * yfrompixelratio + yoffset );
+ vec4 texel = texture2D(sampler, pos);
+#ifdef MASKED
+ texel.a = 1.0 - texture2D(mask, pos - tex_coord.st + mask_coord.st).r;
+#endif
+ return texel;
+}
+
+void main(void)
+{
+ // Convert to pixel coordinates again.
+ int dx = int(( tex_coord.s - xoffset ) * xtopixelratio );
+ int dy = int(( tex_coord.t - yoffset ) * ytopixelratio );
+
+ // Compute the range of source pixels which will make up this destination pixel.
+ float fsx1 = min(dx * xscale, float(swidth - 1));
+ float fsx2 = min(fsx1 + xscale, float(swidth - 1));
+
+ float fsy1 = min(dy * yscale, float(sheight - 1));
+ float fsy2 = min(fsy1 + yscale, float(sheight - 1));
+
+ // To whole pixel coordinates.
+ int xstart = int(floor(fsx1));
+ int xend = int(floor(fsx2));
+
+ int ystart = int(floor(fsy1));
+ int yend = int(floor(fsy2));
+
+ float xlength = fsx2 - fsx1;
+ float ylength = fsy2 - fsy1;
+
+ float xContribution[3];
+ xContribution[0] = (1.0 - max(0.0, fsx1 - xstart)) / xlength;
+ xContribution[1] = 1.0 / xlength;
+ xContribution[2] = (1.0 - max(0.0, (xend + 1) - fsx2)) / xlength;
+
+ float yContribution[3];
+ yContribution[0] = (1.0 - max(0.0, fsy1 - ystart)) / ylength;
+ yContribution[1] = 1.0 / ylength;
+ yContribution[2] = (1.0 - max(0.0, (yend + 1) - fsy2)) / ylength;
+
+ vec4 sumAll = vec4(0.0, 0.0, 0.0, 0.0);
+ vec4 texel;
+ // First Y pass
+ {
+ vec4 sumX = vec4(0.0, 0.0, 0.0, 0.0);
+
+ sumX += getTexel(xstart, ystart) * xContribution[0];
+ for (int x = xstart + 1; x < xend; ++x)
+ {
+ sumX += getTexel(x, ystart) * xContribution[1];
+ }
+ sumX += getTexel(xend, ystart) * xContribution[2];
+
+ sumAll += sumX * yContribution[0];
+ }
+
+ // Middle Y Passes
+ for (int y = ystart + 1; y < yend; ++y)
+ {
+ vec4 sumX = vec4(0.0, 0.0, 0.0, 0.0);
+
+ sumX += getTexel(xstart, y) * xContribution[0];
+ for (int x = xstart + 1; x < xend; ++x)
+ {
+ sumX += getTexel(x, y) * xContribution[1];
+ }
+ sumX += getTexel(xend, y) * xContribution[2];
+
+ sumAll += sumX * yContribution[1];
+ }
+
+ // Last Y pass
+ {
+ vec4 sumX = vec4(0.0, 0.0, 0.0, 0.0);
+
+ sumX += getTexel(xstart, yend) * xContribution[0];
+ for (int x = xstart + 1; x < xend; ++x)
+ {
+ sumX += getTexel(x, yend) * xContribution[1];
+ }
+ sumX += getTexel(xend, yend) * xContribution[2];
+
+ sumAll += sumX * yContribution[2];
+ }
+
+ gl_FragColor = sumAll;
+}
+#else
+void main(void)
+{
+ // Convert to pixel coordinates again.
+ int dx = int(( tex_coord.s - xoffset ) * xtopixelratio );
+ int dy = int(( tex_coord.t - yoffset ) * ytopixelratio );
+
+ // How much each column/row will contribute to the resulting pixel.
+ // Note: These values are always the same for the same X (or Y),
+ // so they could be precalculated in C++ and passed to the shader,
+ // but GLSL has limits on the size of uniforms passed to it,
+ // so it'd need something like texture buffer objects from newer
+ // GLSL versions, and it seems the hassle is not really worth it.
+ float xratio[ 16 + 2 ];
+ float yratio[ 16 + 2 ];
+
+ // For finding the first and last source pixel.
+ int xpixel[ 16 + 2 ];
+ int ypixel[ 16 + 2 ];
+
+ int xpos = 0;
+ int ypos = 0;
+
+ // Compute the range of source pixels which will make up this destination pixel.
+ float fsx1 = dx * xscale;
+ float fsx2 = fsx1 + xscale;
+ // To whole pixel coordinates.
+ int sx1 = int( ceil( fsx1 ) );
+ int sx2 = int( floor( fsx2 ) );
+ // Range checking.
+ sx2 = min( sx2, swidth - 1 );
+ sx1 = min( sx1, sx2 );
+
+ // How much one full column contributes to the resulting pixel.
+ float width = min( xscale, swidth - fsx1 );
+
+ if( sx1 - fsx1 > 0.001 )
+ { // The first column contributes only partially.
+ xpixel[ xpos ] = sx1 - 1;
+ xratio[ xpos ] = ( sx1 - fsx1 ) / width;
+ ++xpos;
+ }
+ for( int sx = sx1; sx < sx2; ++sx )
+ { // Columns that fully contribute to the resulting pixel.
+ xpixel[ xpos ] = sx;
+ xratio[ xpos ] = 1.0 / width;
+ ++xpos;
+ }
+ if( fsx2 - sx2 > 0.001 )
+ { // The last column contributes only partially.
+ xpixel[ xpos ] = sx2;
+ xratio[ xpos ] = min( min( fsx2 - sx2, 1.0 ) / width, 1.0 );
+ ++xpos;
+ }
+
+ // The same for Y.
+ float fsy1 = dy * yscale;
+ float fsy2 = fsy1 + yscale;
+ int sy1 = int( ceil( fsy1 ) );
+ int sy2 = int( floor( fsy2 ) );
+ sy2 = min( sy2, sheight - 1 );
+ sy1 = min( sy1, sy2 );
+
+ float height = min( yscale, sheight - fsy1 );
+
+ if( sy1 - fsy1 > 0.001 )
+ {
+ ypixel[ ypos ] = sy1 - 1;
+ yratio[ ypos ] = ( sy1 - fsy1 ) / height;
+ ++ypos;
+ }
+ for( int sy = sy1; sy < sy2; ++sy )
+ {
+ ypixel[ ypos ] = sy;
+ yratio[ ypos ] = 1.0 / height;
+ ++ypos;
+ }
+ if( fsy2 - sy2 > 0.001 )
+ {
+ ypixel[ ypos ] = sy2;
+ yratio[ ypos ] = min( min( fsy2 - sy2, 1.0 ) / height, 1.0 );
+ ++ypos;
+ }
+
+ int xstart = xpixel[ 0 ];
+ int xend = xpixel[ xpos - 1 ];
+ int ystart = ypixel[ 0 ];
+ int yend = ypixel[ ypos - 1 ];
+
+ vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );
+
+ ypos = 0;
+ for( int y = ystart; y <= yend; ++y, ++ypos )
+ {
+ vec4 tmp = vec4( 0.0, 0.0, 0.0, 0.0 );
+ xpos = 0;
+ for( int x = xstart; x <= xend; ++x, ++xpos )
+ {
+ vec2 pos = vec2( x * xfrompixelratio + xoffset, y * yfrompixelratio + yoffset );
+#ifndef MASKED
+ tmp += texture2D( sampler, pos ) * xratio[ xpos ];
+#else
+ vec4 texel;
+ texel = texture2D( sampler, pos );
+ texel.a = 1.0 - texture2D( mask, pos - tex_coord.st + mask_coord.st ).r;
+ tmp += texel * xratio[ xpos ];
+#endif
+ }
+ sum += tmp * yratio[ ypos ];
+ }
+
+ gl_FragColor = sum;
+}
+#endif
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/blendedTextureFragmentShader.glsl b/vcl/opengl/shaders/blendedTextureFragmentShader.glsl
new file mode 100644
index 000000000..15dfcf7e7
--- /dev/null
+++ b/vcl/opengl/shaders/blendedTextureFragmentShader.glsl
@@ -0,0 +1,33 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+varying vec2 tex_coord;
+varying vec2 alpha_coord;
+varying vec2 mask_coord;
+
+uniform sampler2D sampler;
+uniform sampler2D mask;
+uniform sampler2D alpha;
+
+void main()
+{
+ vec4 texel0, texel1, texel2;
+
+ texel0 = texture2D(sampler, tex_coord);
+ texel1 = texture2D(mask, mask_coord);
+ texel2 = texture2D(alpha, alpha_coord);
+ gl_FragColor = texel0;
+
+ /* Only blend if the alpha texture wasn't fully transparent */
+ gl_FragColor.a = 1.0 - (1.0 - floor(texel2.r)) * texel1.r;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/blendedTextureVertexShader.glsl b/vcl/opengl/shaders/blendedTextureVertexShader.glsl
new file mode 100644
index 000000000..3e60d0e22
--- /dev/null
+++ b/vcl/opengl/shaders/blendedTextureVertexShader.glsl
@@ -0,0 +1,28 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+attribute vec4 position;
+attribute vec2 tex_coord_in;
+attribute vec2 alpha_coord_in;
+attribute vec2 mask_coord_in;
+varying vec2 tex_coord;
+varying vec2 alpha_coord;
+varying vec2 mask_coord;
+uniform mat4 mvp;
+
+void main() {
+ gl_Position = mvp * position;
+ tex_coord = tex_coord_in;
+ alpha_coord = alpha_coord_in;
+ mask_coord = mask_coord_in;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/combinedFragmentShader.glsl b/vcl/opengl/shaders/combinedFragmentShader.glsl
new file mode 100644
index 000000000..2515b174f
--- /dev/null
+++ b/vcl/opengl/shaders/combinedFragmentShader.glsl
@@ -0,0 +1,44 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+varying float fade_factor; // 0->1 fade factor used for AA
+varying float multiply;
+
+#ifdef USE_VERTEX_COLORS
+varying vec4 vertex_color;
+#endif
+
+uniform vec4 color;
+
+#define TYPE_NORMAL 0
+#define TYPE_LINE 1
+
+uniform int type;
+
+void main()
+{
+#ifdef USE_VERTEX_COLORS
+ vec4 result = vertex_color;
+#else
+ vec4 result = color;
+#endif
+
+ if (type == TYPE_LINE)
+ {
+ float dist = (1.0 - abs(fade_factor)) * multiply;
+ float alpha = clamp(dist, 0.0, 1.0);
+ result.a = result.a * alpha;
+ }
+
+ gl_FragColor = result;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/combinedTextureFragmentShader.glsl b/vcl/opengl/shaders/combinedTextureFragmentShader.glsl
new file mode 100644
index 000000000..2990de8c4
--- /dev/null
+++ b/vcl/opengl/shaders/combinedTextureFragmentShader.glsl
@@ -0,0 +1,73 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+varying vec2 tex_coord;
+varying vec2 alpha_coord;
+varying vec2 mask_coord;
+#ifdef USE_VERTEX_COLORS
+varying vec4 vertex_color;
+#endif
+
+uniform sampler2D texture;
+uniform sampler2D mask;
+uniform sampler2D alpha;
+
+uniform vec4 color;
+
+uniform int type;
+
+#define TYPE_NORMAL 0
+#define TYPE_BLEND 1
+#define TYPE_MASKED 2
+#define TYPE_DIFF 3
+#define TYPE_MASKED_COLOR 4
+
+void main()
+{
+ vec4 texelTexture = texture2D(texture, tex_coord);
+
+ if (type == TYPE_NORMAL)
+ {
+ gl_FragColor = texelTexture;
+ }
+ else if (type == TYPE_BLEND)
+ {
+ vec4 texelMask = texture2D(mask, mask_coord);
+ vec4 texelAlpha = texture2D(alpha, alpha_coord);
+ gl_FragColor = texelTexture;
+ gl_FragColor.a = 1.0 - (1.0 - floor(texelAlpha.r)) * texelMask.r;
+ }
+ else if (type == TYPE_MASKED)
+ {
+ vec4 texelMask = texture2D(mask, mask_coord);
+ gl_FragColor = texelTexture;
+ gl_FragColor.a = 1.0 - texelMask.r;
+ }
+ else if (type == TYPE_DIFF)
+ {
+ vec4 texelMask = texture2D(mask, mask_coord);
+ float alpha = 1.0 - abs(texelTexture.r - texelMask.r);
+ if (alpha > 0.0)
+ gl_FragColor = texelMask / alpha;
+ gl_FragColor.a = alpha;
+ }
+ else if (type == TYPE_MASKED_COLOR)
+ {
+#ifdef USE_VERTEX_COLORS
+ gl_FragColor = vertex_color;
+#else
+ gl_FragColor = color;
+#endif
+ gl_FragColor.a = 1.0 - texelTexture.r;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/combinedTextureVertexShader.glsl b/vcl/opengl/shaders/combinedTextureVertexShader.glsl
new file mode 100644
index 000000000..52d44d553
--- /dev/null
+++ b/vcl/opengl/shaders/combinedTextureVertexShader.glsl
@@ -0,0 +1,43 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+attribute vec4 position;
+attribute vec2 tex_coord_in;
+attribute vec2 mask_coord_in;
+attribute vec2 alpha_coord_in;
+#ifdef USE_VERTEX_COLORS
+attribute vec4 vertex_color_in;
+#endif
+
+varying vec2 tex_coord;
+varying vec2 mask_coord;
+varying vec2 alpha_coord;
+#ifdef USE_VERTEX_COLORS
+varying vec4 vertex_color;
+#endif
+
+uniform mat4 mvp;
+uniform mat4 transform;
+
+uniform int type;
+
+void main()
+{
+ gl_Position = mvp * transform * position;
+ tex_coord = tex_coord_in;
+ mask_coord = mask_coord_in;
+ alpha_coord = alpha_coord_in;
+#ifdef USE_VERTEX_COLORS
+ vertex_color = vertex_color_in / 255.0;
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/combinedVertexShader.glsl b/vcl/opengl/shaders/combinedVertexShader.glsl
new file mode 100644
index 000000000..16fc4a942
--- /dev/null
+++ b/vcl/opengl/shaders/combinedVertexShader.glsl
@@ -0,0 +1,76 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+attribute vec2 position;
+attribute vec4 extrusion_vectors;
+#ifdef USE_VERTEX_COLORS
+attribute vec4 vertex_color_in;
+#endif
+
+varying float fade_factor; // fade factor for anti-aliasing
+varying float multiply;
+
+#ifdef USE_VERTEX_COLORS
+varying vec4 vertex_color;
+#endif
+
+uniform float line_width;
+uniform float feather; // width where we fade the line
+
+uniform mat4 mvp;
+
+#define TYPE_NORMAL 0
+#define TYPE_LINE 1
+
+uniform int type;
+
+void main()
+{
+ vec2 extrusion_vector = extrusion_vectors.xy;
+
+ float render_thickness = 0.0;
+
+ if (type == TYPE_LINE)
+ {
+ // miter factor to additionally lengthen the distance of vertex (needed for miter)
+ // if 1.0 - miter_factor has no effect
+ float miter_factor = 1.0 / abs(extrusion_vectors.z);
+ // fade factor is always -1.0 or 1.0 -> we transport that info together with length
+ fade_factor = sign(extrusion_vectors.z);
+#ifdef USE_VERTEX_COLORS
+ float the_feather = (1.0 + sign(extrusion_vectors.w)) / 4.0;
+ float the_line_width = abs(extrusion_vectors.w);
+#else
+ float the_feather = feather;
+ float the_line_width = line_width;
+#endif
+ render_thickness = (the_line_width * miter_factor + the_feather * 2.0 * miter_factor);
+
+ // Calculate the multiplier so we can transform the 0->1 fade factor
+ // to take feather and line width into account.
+
+ float start = mix(0.0, (the_line_width / 2.0) - the_feather, the_feather * 2.0);
+ float end = mix(1.0, (the_line_width / 2.0) + the_feather, the_feather * 2.0);
+
+ multiply = 1.0 / (1.0 - (start / end));
+ }
+
+ // lengthen the vertex in direction of the extrusion vector by line width.
+ vec4 final_position = vec4(position + (extrusion_vector * (render_thickness / 2.0) ), 0.0, 1.0);
+
+ gl_Position = mvp * final_position;
+
+#ifdef USE_VERTEX_COLORS
+ vertex_color = vertex_color_in / 255.0;
+#endif
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/convolutionFragmentShader.glsl b/vcl/opengl/shaders/convolutionFragmentShader.glsl
new file mode 100644
index 000000000..4b2f316e0
--- /dev/null
+++ b/vcl/opengl/shaders/convolutionFragmentShader.glsl
@@ -0,0 +1,30 @@
+/* -*- 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/.
+ */
+
+/* TODO Use textureOffset for newest version of GLSL */
+
+#version 130
+
+uniform sampler2D sampler;
+uniform vec2 offsets[16];
+uniform float kernel[16];
+
+varying vec2 tex_coord;
+
+void main(void)
+{
+ vec4 sum = texture2D(sampler, tex_coord.st) * kernel[0];
+ for (int i = 1; i < 16; i++) {
+ sum += texture2D(sampler, tex_coord.st - offsets[i]) * kernel[i];
+ sum += texture2D(sampler, tex_coord.st + offsets[i]) * kernel[i];
+ }
+ gl_FragColor = sum;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/diffTextureFragmentShader.glsl b/vcl/opengl/shaders/diffTextureFragmentShader.glsl
new file mode 100644
index 000000000..8c50ddf98
--- /dev/null
+++ b/vcl/opengl/shaders/diffTextureFragmentShader.glsl
@@ -0,0 +1,30 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+/*precision mediump float;*/
+varying vec2 tex_coord;
+varying vec2 mask_coord;
+uniform sampler2D texture; /* white background */
+uniform sampler2D mask; /* black background */
+
+void main()
+{
+ float alpha;
+ vec4 texel0, texel1;
+ texel0 = texture2D(texture, tex_coord);
+ texel1 = texture2D(mask, mask_coord);
+ alpha = 1.0 - abs(texel0.r - texel1.r);
+ if(alpha > 0.0)
+ gl_FragColor = texel1 / alpha;
+ gl_FragColor.a = alpha;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/dumbVertexShader.glsl b/vcl/opengl/shaders/dumbVertexShader.glsl
new file mode 100644
index 000000000..80341b614
--- /dev/null
+++ b/vcl/opengl/shaders/dumbVertexShader.glsl
@@ -0,0 +1,20 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+attribute vec4 position;
+uniform mat4 mvp;
+
+void main() {
+ gl_Position = mvp * position;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/greyscaleFragmentShader.glsl b/vcl/opengl/shaders/greyscaleFragmentShader.glsl
new file mode 100644
index 000000000..c37f0d5df
--- /dev/null
+++ b/vcl/opengl/shaders/greyscaleFragmentShader.glsl
@@ -0,0 +1,20 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+varying vec2 tex_coord;
+uniform sampler2D sampler;
+
+void main() {
+ vec4 texel = texture2D(sampler, tex_coord);
+ gl_FragColor = vec4(vec3(dot(texel.rgb, vec3(0.301, 0.591, 0.108))), 1.0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/invert50FragmentShader.glsl b/vcl/opengl/shaders/invert50FragmentShader.glsl
new file mode 100644
index 000000000..9222888f0
--- /dev/null
+++ b/vcl/opengl/shaders/invert50FragmentShader.glsl
@@ -0,0 +1,25 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+/*precision mediump float;*/
+
+void main() {
+ vec2 tex_mod = mod(gl_FragCoord, 2).xy;
+ bool bLeft = (tex_mod.x > 0.0) && (tex_mod.x < 1.0);
+ bool bTop = (tex_mod.y > 0.0) && (tex_mod.y < 1.0);
+ // horrors - where is the XOR operator ?
+ if ((bTop && bLeft) || (!bTop && !bLeft))
+ gl_FragColor = vec4(255,255,255,0);
+ else
+ gl_FragColor = vec4(0,0,0,0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/lineFragmentShader.glsl b/vcl/opengl/shaders/lineFragmentShader.glsl
new file mode 100644
index 000000000..c49570be3
--- /dev/null
+++ b/vcl/opengl/shaders/lineFragmentShader.glsl
@@ -0,0 +1,38 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+varying float fade_factor; // 0->1 fade factor used for AA
+uniform vec4 color;
+
+uniform float line_width;
+uniform float feather;
+
+void main()
+{
+ float start = (line_width / 2.0) - feather; // where we start to apply alpha
+ float end = (line_width / 2.0) + feather; // where we end to apply alpha
+
+ // Calculate the multiplier so we can transform the 0->1 fade factor
+ // to take feather and line width into account.
+ float multiplied = 1.0 / (1.0 - (start / end));
+
+ float dist = (1.0 - abs(fade_factor)) * multiplied;
+
+ float alpha = clamp(dist, 0.0, 1.0);
+
+ // modify the alpha channel only
+ vec4 result_color = color;
+ result_color.a = result_color.a * alpha;
+
+ gl_FragColor = result_color;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/lineVertexShader.glsl b/vcl/opengl/shaders/lineVertexShader.glsl
new file mode 100644
index 000000000..e26be78d0
--- /dev/null
+++ b/vcl/opengl/shaders/lineVertexShader.glsl
@@ -0,0 +1,37 @@
+/* -*- 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/.
+ */
+
+attribute vec2 position;
+attribute vec4 extrusion_vectors;
+
+varying float fade_factor; // fade factor for anti-aliasing
+
+uniform float line_width;
+uniform float feather; // width where we fade the line
+
+uniform mat4 mvp;
+
+void main()
+{
+ vec2 extrusion_vector = extrusion_vectors.xy;
+ // miter factor to additionally lengthen the distance of vertex (needed for miter)
+ // if 1.0 - miter_factor has no effect
+ float miter_factor = 1.0f / abs(extrusion_vectors.z);
+ // fade factor is always -1.0 or 1.0 -> we transport that info together with length
+ fade_factor = sign(extrusion_vectors.z);
+
+ float rendered_thickness = (line_width + feather * 2.0) * miter_factor;
+
+ // lengthen the vertex in direction of the extrusion vector by line width.
+ vec4 position = vec4(position + (extrusion_vector * (rendered_thickness / 2.0) ), 0.0, 1.0);
+
+ gl_Position = mvp * position;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/linearGradientFragmentShader.glsl b/vcl/opengl/shaders/linearGradientFragmentShader.glsl
new file mode 100644
index 000000000..bd1137c16
--- /dev/null
+++ b/vcl/opengl/shaders/linearGradientFragmentShader.glsl
@@ -0,0 +1,23 @@
+/* -*- 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/.
+ */
+
+#version 120
+
+uniform vec4 start_color;
+uniform vec4 end_color;
+uniform mat3x3 transform;
+varying vec2 tex_coord;
+
+void main(void)
+{
+ gl_FragColor = mix(end_color, start_color,
+ clamp(tex_coord.t, 0.0, 1.0));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/maskFragmentShader.glsl b/vcl/opengl/shaders/maskFragmentShader.glsl
new file mode 100644
index 000000000..864869c89
--- /dev/null
+++ b/vcl/opengl/shaders/maskFragmentShader.glsl
@@ -0,0 +1,23 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+varying vec2 tex_coord;
+uniform sampler2D sampler;
+uniform vec4 color;
+
+void main() {
+ vec4 texel0;
+ texel0 = texture2D(sampler, tex_coord);
+ gl_FragColor = color;
+ gl_FragColor.a = 1.0 - texel0.r;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/maskedTextureFragmentShader.glsl b/vcl/opengl/shaders/maskedTextureFragmentShader.glsl
new file mode 100644
index 000000000..31c793897
--- /dev/null
+++ b/vcl/opengl/shaders/maskedTextureFragmentShader.glsl
@@ -0,0 +1,27 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+/*precision mediump float;*/
+varying vec2 tex_coord;
+varying vec2 mask_coord;
+uniform sampler2D sampler;
+uniform sampler2D mask;
+
+void main()
+{
+ vec4 texel0, texel1;
+ texel0 = texture2D(sampler, tex_coord);
+ texel1 = texture2D(mask, mask_coord);
+ gl_FragColor = texel0;
+ gl_FragColor.a = 1.0 - texel1.r;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/maskedTextureVertexShader.glsl b/vcl/opengl/shaders/maskedTextureVertexShader.glsl
new file mode 100644
index 000000000..6b5f327da
--- /dev/null
+++ b/vcl/opengl/shaders/maskedTextureVertexShader.glsl
@@ -0,0 +1,26 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+attribute vec4 position;
+attribute vec2 tex_coord_in;
+attribute vec2 mask_coord_in;
+varying vec2 tex_coord;
+varying vec2 mask_coord;
+uniform mat4 mvp;
+
+void main()
+{
+ gl_Position = mvp * position;
+ tex_coord = tex_coord_in;
+ mask_coord = mask_coord_in;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/radialGradientFragmentShader.glsl b/vcl/opengl/shaders/radialGradientFragmentShader.glsl
new file mode 100644
index 000000000..94a86eb95
--- /dev/null
+++ b/vcl/opengl/shaders/radialGradientFragmentShader.glsl
@@ -0,0 +1,23 @@
+/* -*- 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/.
+ */
+
+#version 120
+
+uniform vec4 start_color;
+uniform vec4 end_color;
+uniform vec2 center;
+varying vec2 tex_coord;
+
+void main(void)
+{
+ gl_FragColor = mix(end_color, start_color,
+ clamp(distance(tex_coord, center), 0.0, 1.0));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/replaceColorFragmentShader.glsl b/vcl/opengl/shaders/replaceColorFragmentShader.glsl
new file mode 100644
index 000000000..24f6008e2
--- /dev/null
+++ b/vcl/opengl/shaders/replaceColorFragmentShader.glsl
@@ -0,0 +1,25 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+varying vec2 tex_coord;
+uniform sampler2D sampler;
+uniform vec4 search_color;
+uniform vec4 replace_color;
+uniform float epsilon;
+
+void main() {
+ vec4 texel = texture2D(sampler, tex_coord);
+ vec4 diff = clamp(abs(texel - search_color) - epsilon, 0.0, 1.0);
+ float bump = max(0.0, 1.0 - ceil(diff.x + diff.y + diff.z));
+ gl_FragColor = texel + bump * (replace_color - search_color);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/solidFragmentShader.glsl b/vcl/opengl/shaders/solidFragmentShader.glsl
new file mode 100644
index 000000000..b77e2578d
--- /dev/null
+++ b/vcl/opengl/shaders/solidFragmentShader.glsl
@@ -0,0 +1,19 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+/*precision mediump float;*/
+
+uniform vec4 color;
+void main() {
+ gl_FragColor = color;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/textureFragmentShader.glsl b/vcl/opengl/shaders/textureFragmentShader.glsl
new file mode 100644
index 000000000..b1fedcba5
--- /dev/null
+++ b/vcl/opengl/shaders/textureFragmentShader.glsl
@@ -0,0 +1,20 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+/* precision mediump float; */
+varying vec2 tex_coord;
+uniform sampler2D sampler;
+
+void main() {
+ gl_FragColor = texture2D(sampler, tex_coord);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/textureVertexShader.glsl b/vcl/opengl/shaders/textureVertexShader.glsl
new file mode 100644
index 000000000..7fbdcf1eb
--- /dev/null
+++ b/vcl/opengl/shaders/textureVertexShader.glsl
@@ -0,0 +1,22 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+attribute vec4 position;
+attribute vec2 tex_coord_in;
+varying vec2 tex_coord;
+uniform mat4 mvp;
+
+void main() {
+ gl_Position = mvp * position;
+ tex_coord = tex_coord_in;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/shaders/transformedTextureVertexShader.glsl b/vcl/opengl/shaders/transformedTextureVertexShader.glsl
new file mode 100644
index 000000000..3d67f78e0
--- /dev/null
+++ b/vcl/opengl/shaders/transformedTextureVertexShader.glsl
@@ -0,0 +1,28 @@
+/* -*- 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/.
+ */
+
+#version 130
+
+attribute vec4 position;
+attribute vec2 tex_coord_in;
+attribute vec2 mask_coord_in;
+uniform vec2 viewport;
+uniform mat4 transform;
+uniform mat4 mvp;
+varying vec2 tex_coord;
+varying vec2 mask_coord;
+
+void main() {
+ vec4 pos = mvp * transform * position;
+ gl_Position = pos;
+ tex_coord = tex_coord_in;
+ mask_coord = mask_coord_in;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/texture.cxx b/vcl/opengl/texture.cxx
new file mode 100644
index 000000000..9f4acc0fc
--- /dev/null
+++ b/vcl/opengl/texture.cxx
@@ -0,0 +1,606 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+#include <vcl/opengl/OpenGLContext.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+#include <svdata.hxx>
+
+#include <vcl/pngwrite.hxx>
+
+#include <opengl/framebuffer.hxx>
+#include <opengl/texture.hxx>
+#include <opengl/zone.hxx>
+#include <opengl/RenderState.hxx>
+
+namespace
+{
+
+constexpr GLenum constInternalFormat = GL_RGBA8;
+
+} // end anonymous namespace
+
+// texture with allocated size
+ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, bool bAllocate ) :
+ mnTexture( 0 ),
+ mnWidth( nWidth ),
+ mnHeight( nHeight ),
+ mnFilter( GL_NEAREST ),
+ mnOptStencil( 0 )
+{
+ OpenGLVCLContextZone aContextZone;
+
+ auto& rState = OpenGLContext::getVCLContext()->state();
+ TextureState::generate(mnTexture);
+ rState.texture().active(0);
+ rState.texture().bind(mnTexture);
+
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ CHECK_GL_ERROR();
+ if( bAllocate )
+ {
+#ifdef DBG_UTIL
+ std::vector< sal_uInt8 > buffer;
+ buffer.resize( nWidth * nHeight * 4 );
+ for( int i = 0; i < nWidth * nHeight; ++i )
+ { // pre-fill the texture with deterministic garbage
+ bool odd = (i & 0x01);
+ buffer[ i * 4 ] = odd ? 0x40 : 0xBF;
+ buffer[ i * 4 + 1 ] = 0x80;
+ buffer[ i * 4 + 2 ] = odd ? 0xBF : 0x40;
+ buffer[ i * 4 + 3 ] = 0xFF;
+ }
+ glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer.data());
+#else
+ glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nWidth, nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
+#endif
+ CHECK_GL_ERROR();
+ }
+
+ VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " allocate" );
+}
+
+// texture with content retrieved from FBO
+ImplOpenGLTexture::ImplOpenGLTexture( int nX, int nY, int nWidth, int nHeight ) :
+ mnTexture( 0 ),
+ mnWidth( nWidth ),
+ mnHeight( nHeight ),
+ mnFilter( GL_NEAREST ),
+ mnOptStencil( 0 )
+{
+ OpenGLVCLContextZone aContextZone;
+
+ // FIXME We need the window height here
+ // nY = GetHeight() - nHeight - nY;
+
+ auto& rState = OpenGLContext::getVCLContext()->state();
+ TextureState::generate(mnTexture);
+ rState.texture().active(0);
+ rState.texture().bind(mnTexture);
+
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ CHECK_GL_ERROR();
+ glCopyTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, nX, nY, nWidth, nHeight, 0 );
+ CHECK_GL_ERROR();
+
+ VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " from x" << nX << ", y" << nY );
+}
+
+// texture from buffer data
+ImplOpenGLTexture::ImplOpenGLTexture( int nWidth, int nHeight, int nFormat, int nType, void const * pData ) :
+ mnTexture( 0 ),
+ mnWidth( nWidth ),
+ mnHeight( nHeight ),
+ mnFilter( GL_NEAREST ),
+ mnOptStencil( 0 )
+{
+ OpenGLVCLContextZone aContextZone;
+
+ auto& rState = OpenGLContext::getVCLContext()->state();
+ TextureState::generate(mnTexture);
+ rState.texture().active(0);
+ rState.texture().bind(mnTexture);
+
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
+ CHECK_GL_ERROR();
+ glTexImage2D( GL_TEXTURE_2D, 0, constInternalFormat, mnWidth, mnHeight, 0, nFormat, nType, pData );
+ CHECK_GL_ERROR();
+
+ VCL_GL_INFO( "OpenGLTexture " << mnTexture << " " << nWidth << "x" << nHeight << " from data" );
+}
+
+GLuint ImplOpenGLTexture::AddStencil()
+{
+ assert( mnOptStencil == 0 );
+
+ glGenRenderbuffers( 1, &mnOptStencil );
+ CHECK_GL_ERROR();
+ glBindRenderbuffer( GL_RENDERBUFFER, mnOptStencil );
+ CHECK_GL_ERROR();
+ VCL_GL_INFO( "Allocate stencil " << mnWidth << " x " << mnHeight );
+ glRenderbufferStorage( GL_RENDERBUFFER, GL_STENCIL_INDEX,
+ mnWidth, mnHeight );
+ CHECK_GL_ERROR();
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ CHECK_GL_ERROR();
+
+ return mnOptStencil;
+}
+
+ImplOpenGLTexture::~ImplOpenGLTexture()
+{
+ VCL_GL_INFO( "~OpenGLTexture " << mnTexture );
+ if( mnTexture != 0 )
+ {
+ // During shutdown GL is already de-initialized, so we should not try to create a new context.
+ OpenGLZone aZone;
+ rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext(false);
+ if( xContext.is() )
+ {
+ // FIXME: this is really not optimal performance-wise.
+
+ // Check we have been correctly un-bound from all framebuffers.
+ ImplSVData* pSVData = ImplGetSVData();
+ rtl::Reference<OpenGLContext> pContext = pSVData->maGDIData.mpLastContext;
+
+ if( pContext.is() )
+ {
+ pContext->makeCurrent();
+ pContext->UnbindTextureFromFramebuffers( mnTexture );
+ }
+
+ if( mnOptStencil != 0 )
+ {
+ glDeleteRenderbuffers( 1, &mnOptStencil );
+ mnOptStencil = 0;
+ }
+ auto& rState = pContext->state();
+ rState.texture().unbindAndDelete(mnTexture);
+ mnTexture = 0;
+ }
+ else
+ {
+ mnOptStencil = 0;
+ mnTexture = 0;
+ }
+ }
+}
+
+bool ImplOpenGLTexture::InsertBuffer(int nX, int nY, int nWidth, int nHeight, int nFormat, int nType, sal_uInt8 const * pData)
+{
+ if (!pData || mnTexture == 0)
+ return false;
+
+ rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
+ xContext->state().texture().active(0);
+ xContext->state().texture().bind(mnTexture);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ CHECK_GL_ERROR();
+ glTexSubImage2D(GL_TEXTURE_2D, 0, nX, mnHeight - nY - nHeight, nWidth, nHeight, nFormat, nType, pData);
+ CHECK_GL_ERROR();
+
+ VCL_GL_INFO( "OpenGLTexture " << mnTexture << " Insert buff. to " << nX << " " << nY
+ << " size " << nWidth << "x" << nHeight << " from data" );
+
+ return true;
+}
+
+void ImplOpenGLTexture::InitializeSlotMechanism(int nInitialSlotSize)
+{
+ if (mpSlotReferences)
+ return;
+
+ mpSlotReferences.reset(new std::vector<int>(nInitialSlotSize, 0));
+}
+
+void ImplOpenGLTexture::IncreaseRefCount(int nSlotNumber)
+{
+ if (mpSlotReferences && nSlotNumber >= 0)
+ {
+ if (nSlotNumber >= int(mpSlotReferences->size()))
+ mpSlotReferences->resize(nSlotNumber + 1, 0);
+
+ mpSlotReferences->at(nSlotNumber)++;
+ }
+}
+
+void ImplOpenGLTexture::DecreaseRefCount(int nSlotNumber)
+{
+ if (mpSlotReferences && nSlotNumber >= 0)
+ {
+ if (nSlotNumber >= int(mpSlotReferences->size()))
+ mpSlotReferences->resize(nSlotNumber, 0);
+
+ mpSlotReferences->at(nSlotNumber)--;
+
+ if (mpSlotReferences->at(nSlotNumber) == 0 && mFunctSlotDeallocateCallback)
+ {
+ mFunctSlotDeallocateCallback(nSlotNumber);
+ }
+ }
+}
+
+OpenGLTexture::OpenGLTexture() :
+ maRect( 0, 0, 0, 0 ),
+ mpImpl(),
+ mnSlotNumber(-1)
+{
+}
+
+OpenGLTexture::OpenGLTexture(const std::shared_ptr<ImplOpenGLTexture>& rpImpl, tools::Rectangle aRectangle, int nSlotNumber)
+ : maRect(aRectangle)
+ , mpImpl(rpImpl)
+ , mnSlotNumber(nSlotNumber)
+{
+ if (mpImpl)
+ mpImpl->IncreaseRefCount(nSlotNumber);
+}
+
+OpenGLTexture::OpenGLTexture( int nWidth, int nHeight, bool bAllocate )
+ : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
+ , mpImpl(std::make_shared<ImplOpenGLTexture>(nWidth, nHeight, bAllocate))
+ , mnSlotNumber(-1)
+{
+}
+
+OpenGLTexture::OpenGLTexture( int nX, int nY, int nWidth, int nHeight )
+ : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
+ , mpImpl(std::make_shared<ImplOpenGLTexture>(nX, nY, nWidth, nHeight))
+ , mnSlotNumber(-1)
+{
+}
+
+OpenGLTexture::OpenGLTexture( int nWidth, int nHeight, int nFormat, int nType, void const * pData )
+ : maRect( Point( 0, 0 ), Size( nWidth, nHeight ) )
+ , mpImpl(std::make_shared<ImplOpenGLTexture>(nWidth, nHeight, nFormat, nType, pData))
+ , mnSlotNumber(-1)
+{
+
+}
+
+OpenGLTexture::OpenGLTexture(const OpenGLTexture& rTexture)
+ : maRect(rTexture.maRect)
+ , mpImpl(rTexture.mpImpl)
+ , mnSlotNumber(rTexture.mnSlotNumber)
+{
+ if (mpImpl)
+ mpImpl->IncreaseRefCount(mnSlotNumber);
+}
+
+OpenGLTexture::OpenGLTexture(OpenGLTexture&& rTexture) noexcept
+ : maRect(rTexture.maRect)
+ , mpImpl(std::move(rTexture.mpImpl))
+ , mnSlotNumber(rTexture.mnSlotNumber)
+{
+}
+
+OpenGLTexture::OpenGLTexture( const OpenGLTexture& rTexture,
+ int nX, int nY, int nWidth, int nHeight )
+{
+ maRect = tools::Rectangle( Point( rTexture.maRect.Left() + nX, rTexture.maRect.Top() + nY ),
+ Size( nWidth, nHeight ) );
+ mpImpl = rTexture.mpImpl;
+ mnSlotNumber = rTexture.mnSlotNumber;
+ if (mpImpl)
+ mpImpl->IncreaseRefCount(mnSlotNumber);
+ VCL_GL_INFO( "Copying texture " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
+}
+
+OpenGLTexture::~OpenGLTexture()
+{
+ if (mpImpl)
+ mpImpl->DecreaseRefCount(mnSlotNumber);
+}
+
+bool OpenGLTexture::IsUnique() const
+{
+ return !mpImpl || (mpImpl.use_count() == 1);
+}
+
+GLuint OpenGLTexture::Id() const
+{
+ if (mpImpl)
+ return mpImpl->mnTexture;
+ return 0;
+}
+
+int OpenGLTexture::GetWidth() const
+{
+ return maRect.GetWidth();
+}
+
+int OpenGLTexture::GetHeight() const
+{
+ return maRect.GetHeight();
+}
+
+GLuint OpenGLTexture::StencilId() const
+{
+ return mpImpl ? mpImpl->mnOptStencil : 0;
+}
+
+GLuint OpenGLTexture::AddStencil()
+{
+ if (mpImpl)
+ return mpImpl->AddStencil();
+ else
+ return 0;
+}
+
+void OpenGLTexture::GetCoord( GLfloat* pCoord, const SalTwoRect& rPosAry, bool bInverted ) const
+{
+ VCL_GL_INFO( "Getting coord " << Id() << " [" << maRect.Left() << "," << maRect.Top() << "] " << GetWidth() << "x" << GetHeight() );
+
+ if (!IsValid())
+ {
+ pCoord[0] = pCoord[1] = pCoord[2] = pCoord[3] = 0.0f;
+ pCoord[4] = pCoord[5] = pCoord[6] = pCoord[7] = 0.0f;
+ return;
+ }
+
+ pCoord[0] = pCoord[2] = (maRect.Left() + rPosAry.mnSrcX) / static_cast<double>(mpImpl->mnWidth);
+ pCoord[4] = pCoord[6] = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / static_cast<double>(mpImpl->mnWidth);
+
+ if( !bInverted )
+ {
+ pCoord[3] = pCoord[5] = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / static_cast<double>(mpImpl->mnHeight);
+ pCoord[1] = pCoord[7] = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / static_cast<double>(mpImpl->mnHeight);
+ }
+ else
+ {
+ pCoord[1] = pCoord[7] = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / static_cast<double>(mpImpl->mnHeight);
+ pCoord[3] = pCoord[5] = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / static_cast<double>(mpImpl->mnHeight);
+ }
+}
+
+void OpenGLTexture::GetTextureRect(const SalTwoRect& rPosAry, GLfloat& x1, GLfloat& x2, GLfloat& y1, GLfloat& y2) const
+{
+ if (IsValid())
+ {
+ double fTextureWidth(mpImpl->mnWidth);
+ double fTextureHeight(mpImpl->mnHeight);
+
+ x1 = (maRect.Left() + rPosAry.mnSrcX) / fTextureWidth;
+ x2 = (maRect.Left() + rPosAry.mnSrcX + rPosAry.mnSrcWidth) / fTextureWidth;
+
+ y1 = 1.0f - (maRect.Top() + rPosAry.mnSrcY) / fTextureHeight;
+ y2 = 1.0f - (maRect.Top() + rPosAry.mnSrcY + rPosAry.mnSrcHeight) / fTextureHeight;
+ }
+}
+
+template <>
+void OpenGLTexture::FillCoords<GL_TRIANGLE_FAN>(std::vector<GLfloat>& rCoords, const SalTwoRect& rPosAry) const
+{
+ GLfloat x1 = 0.0f;
+ GLfloat x2 = 0.0f;
+ GLfloat y1 = 0.0f;
+ GLfloat y2 = 0.0f;
+
+ GetTextureRect(rPosAry, x1, x2, y1, y2);
+
+ rCoords.insert(rCoords.end(), {
+ x1, y2, x1, y1,
+ x2, y1, x2, y2
+ });
+}
+
+template <>
+void OpenGLTexture::FillCoords<GL_TRIANGLES>(std::vector<GLfloat>& rCoords, const SalTwoRect& rPosAry) const
+{
+ GLfloat x1 = 0.0f;
+ GLfloat x2 = 0.0f;
+ GLfloat y1 = 0.0f;
+ GLfloat y2 = 0.0f;
+
+ GetTextureRect(rPosAry, x1, x2, y1, y2);
+
+ rCoords.insert(rCoords.end(), {
+ x1, y1, x2, y1, x1, y2,
+ x1, y2, x2, y1, x2, y2
+ });
+}
+
+void OpenGLTexture::GetWholeCoord( GLfloat* pCoord ) const
+{
+ if( GetWidth() != mpImpl->mnWidth || GetHeight() != mpImpl->mnHeight )
+ {
+ pCoord[0] = pCoord[2] = maRect.Left() / static_cast<double>(mpImpl->mnWidth);
+ pCoord[4] = pCoord[6] = maRect.Right() / static_cast<double>(mpImpl->mnWidth);
+ pCoord[3] = pCoord[5] = 1.0f - maRect.Top() / static_cast<double>(mpImpl->mnHeight);
+ pCoord[1] = pCoord[7] = 1.0f - maRect.Bottom() / static_cast<double>(mpImpl->mnHeight);
+ }
+ else
+ {
+ pCoord[0] = pCoord[2] = 0;
+ pCoord[4] = pCoord[6] = 1;
+ pCoord[1] = pCoord[7] = 0;
+ pCoord[3] = pCoord[5] = 1;
+ }
+}
+
+GLenum OpenGLTexture::GetFilter() const
+{
+ if( mpImpl )
+ return mpImpl->mnFilter;
+ return GL_NEAREST;
+}
+
+bool OpenGLTexture::CopyData(int nWidth, int nHeight, int nFormat, int nType, sal_uInt8 const * pData)
+{
+ if (!pData || !IsValid())
+ 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 )
+ {
+ mpImpl->mnFilter = nFilter;
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, nFilter );
+ CHECK_GL_ERROR();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, nFilter );
+ CHECK_GL_ERROR();
+ }
+}
+
+void OpenGLTexture::Bind()
+{
+ if (IsValid())
+ {
+ OpenGLContext::getVCLContext()->state().texture().bind(mpImpl->mnTexture);
+ }
+ else
+ VCL_GL_INFO( "OpenGLTexture::Binding invalid texture" );
+
+ CHECK_GL_ERROR();
+}
+
+void OpenGLTexture::Unbind()
+{
+ if (IsValid())
+ {
+ OpenGLContext::getVCLContext()->state().texture().unbind(mpImpl->mnTexture);
+ }
+}
+
+void OpenGLTexture::SaveToFile(const OUString& rFileName)
+{
+ std::vector<sal_uInt8> aBuffer(GetWidth() * GetHeight() * 4);
+ Read(OpenGLHelper::OptimalBufferFormat(), GL_UNSIGNED_BYTE, aBuffer.data());
+ BitmapEx aBitmap = OpenGLHelper::ConvertBufferToBitmapEx(aBuffer.data(), GetWidth(), GetHeight());
+ try
+ {
+ vcl::PNGWriter aWriter(aBitmap);
+ SvFileStream sOutput(rFileName, StreamMode::WRITE);
+ aWriter.Write(sOutput);
+ sOutput.Close();
+ }
+ catch (...)
+ {
+ SAL_WARN("vcl.opengl", "Error writing png to " << rFileName);
+ }
+}
+
+void OpenGLTexture::Read( GLenum nFormat, GLenum nType, sal_uInt8* pData )
+{
+ if (!IsValid())
+ {
+ SAL_WARN( "vcl.opengl", "Can't read invalid texture" );
+ return;
+ }
+
+ OpenGLVCLContextZone aContextZone;
+
+ VCL_GL_INFO( "Reading texture " << Id() << " " << GetWidth() << "x" << GetHeight() );
+
+ if( GetWidth() == mpImpl->mnWidth && GetHeight() == mpImpl->mnHeight )
+ {
+ Bind();
+ glPixelStorei( GL_PACK_ALIGNMENT, 1 );
+ CHECK_GL_ERROR();
+ // XXX: Call not available with GLES 2.0
+ glGetTexImage( GL_TEXTURE_2D, 0, nFormat, nType, pData );
+ CHECK_GL_ERROR();
+ Unbind();
+ }
+ else
+ {
+ long nWidth = maRect.GetWidth();
+ long nHeight = maRect.GetHeight();
+ long nX = maRect.Left();
+ long nY = mpImpl->mnHeight - maRect.Top() - nHeight;
+
+ // Retrieve current context
+ ImplSVData* pSVData = ImplGetSVData();
+ rtl::Reference<OpenGLContext> pContext = pSVData->maGDIData.mpLastContext;
+ OpenGLFramebuffer* pFramebuffer = pContext->AcquireFramebuffer(*this);
+ glPixelStorei(GL_PACK_ALIGNMENT, 1);
+ CHECK_GL_ERROR();
+ glReadPixels(nX, nY, nWidth, nHeight, nFormat, nType, pData);
+ CHECK_GL_ERROR();
+ OpenGLContext::ReleaseFramebuffer(pFramebuffer);
+ }
+}
+
+OpenGLTexture::operator bool() const
+{
+ return IsValid();
+}
+
+OpenGLTexture& OpenGLTexture::operator=(const OpenGLTexture& rTexture)
+{
+ OpenGLTexture aTemp(rTexture);
+ *this = std::move(aTemp);
+ return *this;
+}
+
+OpenGLTexture& OpenGLTexture::operator=(OpenGLTexture&& rTexture)
+{
+ if (mpImpl)
+ mpImpl->DecreaseRefCount(mnSlotNumber);
+
+ maRect = rTexture.maRect;
+ mpImpl = std::move(rTexture.mpImpl);
+ mnSlotNumber = rTexture.mnSlotNumber;
+
+ return *this;
+}
+
+bool OpenGLTexture::operator==( const OpenGLTexture& rTexture ) const
+{
+ return (mpImpl == rTexture.mpImpl
+ && maRect == rTexture.maRect
+ && mnSlotNumber == rTexture.mnSlotNumber);
+}
+
+bool OpenGLTexture::operator!=( const OpenGLTexture& rTexture ) const
+{
+ return !( *this == rTexture );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/win/WinDeviceInfo.cxx b/vcl/opengl/win/WinDeviceInfo.cxx
new file mode 100644
index 000000000..301c8e74d
--- /dev/null
+++ b/vcl/opengl/win/WinDeviceInfo.cxx
@@ -0,0 +1,494 @@
+/* -*- 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 <opengl/win/WinDeviceInfo.hxx>
+
+#include <driverblocklist.hxx>
+#include <config_folders.h>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <objbase.h>
+#include <setupapi.h>
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+
+#include <osl/file.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <desktop/crashreport.hxx>
+
+namespace {
+
+bool GetKeyValue(const WCHAR* keyLocation, const WCHAR* keyName, OUString& destString, int type)
+{
+ HKEY key;
+ DWORD dwcbData;
+ DWORD dValue;
+ DWORD resultType;
+ LONG result;
+ bool retval = true;
+
+ result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyLocation, 0, KEY_QUERY_VALUE, &key);
+ if (result != ERROR_SUCCESS)
+ {
+ return false;
+ }
+
+ switch (type)
+ {
+ case REG_DWORD:
+ {
+ // We only use this for vram size
+ dwcbData = sizeof(dValue);
+ result = RegQueryValueExW(key, keyName, nullptr, &resultType,
+ reinterpret_cast<LPBYTE>(&dValue), &dwcbData);
+ if (result == ERROR_SUCCESS && resultType == REG_DWORD)
+ {
+ dValue = dValue / 1024 / 1024;
+ destString += OUString::number(int32_t(dValue));
+ }
+ else
+ {
+ retval = false;
+ }
+ break;
+ }
+ case REG_MULTI_SZ:
+ {
+ // A chain of null-separated strings; we convert the nulls to spaces
+ WCHAR wCharValue[1024];
+ dwcbData = sizeof(wCharValue);
+
+ result = RegQueryValueExW(key, keyName, nullptr, &resultType,
+ reinterpret_cast<LPBYTE>(wCharValue), &dwcbData);
+ if (result == ERROR_SUCCESS && resultType == REG_MULTI_SZ)
+ {
+ // This bit here could probably be cleaner.
+ bool isValid = false;
+
+ DWORD strLen = dwcbData/sizeof(wCharValue[0]);
+ for (DWORD i = 0; i < strLen; i++)
+ {
+ if (wCharValue[i] == '\0')
+ {
+ if (i < strLen - 1 && wCharValue[i + 1] == '\0')
+ {
+ isValid = true;
+ break;
+ }
+ else
+ {
+ wCharValue[i] = ' ';
+ }
+ }
+ }
+
+ // ensure wCharValue is null terminated
+ wCharValue[strLen-1] = '\0';
+
+ if (isValid)
+ destString = OUString(o3tl::toU(wCharValue));
+
+ }
+ else
+ {
+ retval = false;
+ }
+
+ break;
+ }
+ }
+ RegCloseKey(key);
+
+ return retval;
+}
+
+// The device ID is a string like PCI\VEN_15AD&DEV_0405&SUBSYS_040515AD
+// this function is used to extract the id's out of it
+uint32_t ParseIDFromDeviceID(const OUString &key, const char *prefix, int length)
+{
+ OUString id = key.toAsciiUpperCase();
+ OUString aPrefix = OUString::fromUtf8(prefix);
+ int32_t start = id.indexOf(aPrefix);
+ if (start != -1)
+ {
+ id = id.copy(start + aPrefix.getLength(), length);
+ }
+ return id.toUInt32(16);
+}
+
+/* Other interesting places for info:
+ * IDXGIAdapter::GetDesc()
+ * IDirectDraw7::GetAvailableVidMem()
+ * e->GetAvailableTextureMem()
+ * */
+
+template<typename T> void appendIntegerWithPadding(OUString& rString, T value, sal_uInt32 nChars)
+{
+ rString += "0x";
+ OUString aValue = OUString::number(value, 16);
+ sal_Int32 nLength = aValue.getLength();
+ sal_uInt32 nPadLength = nChars - nLength;
+ assert(nPadLength >= 0);
+ OUStringBuffer aBuffer;
+ for (sal_uInt32 i = 0; i < nPadLength; ++i)
+ {
+ aBuffer.append("0");
+ }
+ rString += aBuffer.makeStringAndClear() + aValue;
+}
+
+#define DEVICE_KEY_PREFIX L"\\Registry\\Machine\\"
+}
+
+WinOpenGLDeviceInfo::WinOpenGLDeviceInfo():
+ mbHasDualGPU(false),
+ mbRDP(false)
+{
+ GetData();
+}
+
+WinOpenGLDeviceInfo::~WinOpenGLDeviceInfo()
+{
+}
+
+static OUString getBlacklistFile()
+{
+ OUString url("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER);
+ rtl::Bootstrap::expandMacros(url);
+
+ return url + "/opengl/opengl_blacklist_windows.xml";
+}
+
+bool WinOpenGLDeviceInfo::FindBlocklistedDeviceInList()
+{
+ return DriverBlocklist::IsDeviceBlocked( getBlacklistFile(), DriverBlocklist::VersionType::OpenGL,
+ maDriverVersion, maAdapterVendorID, maAdapterDeviceID);
+}
+
+namespace {
+
+OUString getCacheFolder()
+{
+ OUString url("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache/");
+ rtl::Bootstrap::expandMacros(url);
+
+ osl::Directory::create(url);
+
+ return url;
+}
+
+void writeToLog(SvStream& rStrm, const char* pKey, const OUString & rVal)
+{
+ rStrm.WriteCharPtr(pKey);
+ rStrm.WriteCharPtr(": ");
+ rStrm.WriteOString(OUStringToOString(rVal, RTL_TEXTENCODING_UTF8));
+ rStrm.WriteChar('\n');
+}
+
+}
+
+bool WinOpenGLDeviceInfo::isDeviceBlocked()
+{
+ CrashReporter::addKeyValue("OpenGLVendor", maAdapterVendorID, CrashReporter::AddItem);
+ CrashReporter::addKeyValue("OpenGLDevice", maAdapterDeviceID, CrashReporter::AddItem);
+ CrashReporter::addKeyValue("OpenGLDriver", maDriverVersion, CrashReporter::Write);
+
+ SAL_INFO("vcl.opengl", maDriverVersion);
+ SAL_INFO("vcl.opengl", maDriverDate);
+ SAL_INFO("vcl.opengl", maDeviceID);
+ SAL_INFO("vcl.opengl", maAdapterVendorID);
+ SAL_INFO("vcl.opengl", maAdapterDeviceID);
+ SAL_INFO("vcl.opengl", maAdapterSubsysID);
+ SAL_INFO("vcl.opengl", maDeviceKey);
+ SAL_INFO("vcl.opengl", maDeviceString);
+
+ OUString aCacheFolder = getCacheFolder();
+
+ OUString aCacheFile(aCacheFolder + "/opengl_device.log");
+ SvFileStream aOpenGLLogFile(aCacheFile, StreamMode::WRITE|StreamMode::TRUNC);
+
+ writeToLog(aOpenGLLogFile, "DriverVersion", maDriverVersion);
+ writeToLog(aOpenGLLogFile, "DriverDate", maDriverDate);
+ writeToLog(aOpenGLLogFile, "DeviceID", maDeviceID);
+ writeToLog(aOpenGLLogFile, "AdapterVendorID", maAdapterVendorID);
+ writeToLog(aOpenGLLogFile, "AdapterDeviceID", maAdapterDeviceID);
+ writeToLog(aOpenGLLogFile, "AdapterSubsysID", maAdapterSubsysID);
+ writeToLog(aOpenGLLogFile, "DeviceKey", maDeviceKey);
+ writeToLog(aOpenGLLogFile, "DeviceString", maDeviceString);
+
+ // Check if the device is blocked from the downloaded blocklist. If not, check
+ // the static list after that. This order is used so that we can later escape
+ // out of static blocks (i.e. if we were wrong or something was patched, we
+ // can back out our static block without doing a release).
+ if (mbRDP)
+ {
+ SAL_WARN("vcl.opengl", "all OpenGL blocked for RDP sessions");
+ return true;
+ }
+
+ return FindBlocklistedDeviceInList();
+}
+
+void WinOpenGLDeviceInfo::GetData()
+{
+ DISPLAY_DEVICEW displayDevice;
+ displayDevice.cb = sizeof(displayDevice);
+
+ int deviceIndex = 0;
+
+ while (EnumDisplayDevicesW(nullptr, deviceIndex, &displayDevice, 0))
+ {
+ if (displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
+ {
+ break;
+ }
+ deviceIndex++;
+ }
+
+ // make sure the string is null terminated
+ // (using the term "null" here to mean a zero UTF-16 unit)
+ if (wcsnlen(displayDevice.DeviceKey, SAL_N_ELEMENTS(displayDevice.DeviceKey))
+ == SAL_N_ELEMENTS(displayDevice.DeviceKey))
+ {
+ // we did not find a null
+ SAL_WARN("vcl.opengl", "string not null terminated");
+ return;
+ }
+
+ /* DeviceKey is "reserved" according to MSDN so we'll be careful with it */
+ /* check that DeviceKey begins with DEVICE_KEY_PREFIX */
+ /* some systems have a DeviceKey starting with \REGISTRY\Machine\ so we need to compare case insensitively */
+ if (_wcsnicmp(displayDevice.DeviceKey, DEVICE_KEY_PREFIX, SAL_N_ELEMENTS(DEVICE_KEY_PREFIX)-1) != 0)
+ {
+ SAL_WARN("vcl.opengl", "incorrect DeviceKey");
+ return;
+ }
+
+ // chop off DEVICE_KEY_PREFIX
+ maDeviceKey = o3tl::toU(displayDevice.DeviceKey) + SAL_N_ELEMENTS(DEVICE_KEY_PREFIX)-1;
+
+ maDeviceID = o3tl::toU(displayDevice.DeviceID);
+ maDeviceString = o3tl::toU(displayDevice.DeviceString);
+
+ if (maDeviceID.isEmpty() &&
+ (maDeviceString == "RDPDD Chained DD" ||
+ (maDeviceString == "RDPUDD Chained DD")))
+ {
+ // we need to block RDP as it does not provide OpenGL 2.1+
+ mbRDP = true;
+ SAL_WARN("vcl.opengl", "RDP => blocked");
+ return;
+ }
+
+ /* create a device information set composed of the current display device */
+ HDEVINFO devinfo = SetupDiGetClassDevsW(nullptr, o3tl::toW(maDeviceID.getStr()), nullptr,
+ DIGCF_PRESENT | DIGCF_PROFILE | DIGCF_ALLCLASSES);
+
+ if (devinfo != INVALID_HANDLE_VALUE)
+ {
+ HKEY key;
+ LONG result;
+ WCHAR value[255];
+ DWORD dwcbData;
+ SP_DEVINFO_DATA devinfoData;
+ DWORD memberIndex = 0;
+
+ devinfoData.cbSize = sizeof(devinfoData);
+ /* enumerate device information elements in the device information set */
+ while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
+ {
+ /* get a string that identifies the device's driver key */
+ if (SetupDiGetDeviceRegistryPropertyW(devinfo,
+ &devinfoData,
+ SPDRP_DRIVER,
+ nullptr,
+ reinterpret_cast<PBYTE>(value),
+ sizeof(value),
+ nullptr))
+ {
+ OUString driverKey(OUStringLiteral("System\\CurrentControlSet\\Control\\Class\\") + o3tl::toU(value));
+ result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, o3tl::toW(driverKey.getStr()), 0, KEY_QUERY_VALUE, &key);
+ if (result == ERROR_SUCCESS)
+ {
+ /* we've found the driver we're looking for */
+ dwcbData = sizeof(value);
+ result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
+ reinterpret_cast<LPBYTE>(value), &dwcbData);
+ if (result == ERROR_SUCCESS)
+ {
+ maDriverVersion = OUString(o3tl::toU(value));
+ }
+ else
+ {
+ // If the entry wasn't found, assume the worst (0.0.0.0).
+ maDriverVersion = OUString("0.0.0.0");
+ }
+ dwcbData = sizeof(value);
+ result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
+ reinterpret_cast<LPBYTE>(value), &dwcbData);
+ if (result == ERROR_SUCCESS)
+ {
+ maDriverDate = o3tl::toU(value);
+ }
+ else
+ {
+ // Again, assume the worst
+ maDriverDate = OUString("01-01-1970");
+ }
+ RegCloseKey(key);
+ break;
+ }
+ }
+ }
+
+ SetupDiDestroyDeviceInfoList(devinfo);
+ }
+ else
+ {
+ SAL_WARN("vcl.opengl", "invalid handle value");
+ }
+
+ appendIntegerWithPadding(maAdapterVendorID, ParseIDFromDeviceID(maDeviceID, "VEN_", 4), 4);
+ appendIntegerWithPadding(maAdapterDeviceID, ParseIDFromDeviceID(maDeviceID, "&DEV_", 4), 4);
+ appendIntegerWithPadding(maAdapterSubsysID, ParseIDFromDeviceID(maDeviceID, "&SUBSYS_", 8), 8);
+
+ // We now check for second display adapter.
+
+ // Device interface class for display adapters.
+ CLSID GUID_DISPLAY_DEVICE_ARRIVAL;
+ HRESULT hresult = CLSIDFromString(L"{1CA05180-A699-450A-9A0C-DE4FBE3DDD89}",
+ &GUID_DISPLAY_DEVICE_ARRIVAL);
+ if (hresult == NOERROR)
+ {
+ devinfo = SetupDiGetClassDevsW(&GUID_DISPLAY_DEVICE_ARRIVAL,
+ nullptr, nullptr,
+ DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
+
+ if (devinfo != INVALID_HANDLE_VALUE)
+ {
+ HKEY key;
+ LONG result;
+ WCHAR value[255];
+ DWORD dwcbData;
+ SP_DEVINFO_DATA devinfoData;
+ DWORD memberIndex = 0;
+ devinfoData.cbSize = sizeof(devinfoData);
+
+ OUString aAdapterDriver2;
+ OUString aDeviceID2;
+ OUString aDriverVersion2;
+ OUString aDriverDate2;
+ uint32_t adapterVendorID2;
+ uint32_t adapterDeviceID2;
+
+ /* enumerate device information elements in the device information set */
+ while (SetupDiEnumDeviceInfo(devinfo, memberIndex++, &devinfoData))
+ {
+ /* get a string that identifies the device's driver key */
+ if (SetupDiGetDeviceRegistryPropertyW(devinfo,
+ &devinfoData,
+ SPDRP_DRIVER,
+ nullptr,
+ reinterpret_cast<PBYTE>(value),
+ sizeof(value),
+ nullptr))
+ {
+ OUString driverKey2(OUStringLiteral("System\\CurrentControlSet\\Control\\Class\\") + o3tl::toU(value));
+ result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, o3tl::toW(driverKey2.getStr()), 0, KEY_QUERY_VALUE, &key);
+ if (result == ERROR_SUCCESS)
+ {
+ dwcbData = sizeof(value);
+ result = RegQueryValueExW(key, L"MatchingDeviceId", nullptr,
+ nullptr, reinterpret_cast<LPBYTE>(value), &dwcbData);
+ if (result != ERROR_SUCCESS)
+ {
+ continue;
+ }
+ aDeviceID2 = o3tl::toU(value);
+ OUString aAdapterVendorID2String;
+ OUString aAdapterDeviceID2String;
+ adapterVendorID2 = ParseIDFromDeviceID(aDeviceID2, "VEN_", 4);
+ appendIntegerWithPadding(aAdapterVendorID2String, adapterVendorID2, 4);
+ adapterDeviceID2 = ParseIDFromDeviceID(aDeviceID2, "&DEV_", 4);
+ appendIntegerWithPadding(aAdapterDeviceID2String, adapterDeviceID2, 4);
+ if (maAdapterVendorID == aAdapterVendorID2String &&
+ maAdapterDeviceID == aAdapterDeviceID2String)
+ {
+ RegCloseKey(key);
+ continue;
+ }
+
+ // If this device is missing driver information, it is unlikely to
+ // be a real display adapter.
+ if (!GetKeyValue(o3tl::toW(driverKey2.getStr()), L"InstalledDisplayDrivers",
+ aAdapterDriver2, REG_MULTI_SZ))
+ {
+ RegCloseKey(key);
+ continue;
+ }
+ dwcbData = sizeof(value);
+ result = RegQueryValueExW(key, L"DriverVersion", nullptr, nullptr,
+ reinterpret_cast<LPBYTE>(value), &dwcbData);
+ if (result != ERROR_SUCCESS)
+ {
+ RegCloseKey(key);
+ continue;
+ }
+ aDriverVersion2 = o3tl::toU(value);
+ dwcbData = sizeof(value);
+ result = RegQueryValueExW(key, L"DriverDate", nullptr, nullptr,
+ reinterpret_cast<LPBYTE>(value), &dwcbData);
+ if (result != ERROR_SUCCESS)
+ {
+ RegCloseKey(key);
+ continue;
+ }
+ aDriverDate2 = o3tl::toU(value);
+ dwcbData = sizeof(value);
+ result = RegQueryValueExW(key, L"Device Description", nullptr,
+ nullptr, reinterpret_cast<LPBYTE>(value), &dwcbData);
+ if (result != ERROR_SUCCESS)
+ {
+ dwcbData = sizeof(value);
+ result = RegQueryValueExW(key, L"DriverDesc", nullptr, nullptr,
+ reinterpret_cast<LPBYTE>(value), &dwcbData);
+ }
+ RegCloseKey(key);
+ if (result == ERROR_SUCCESS)
+ {
+ mbHasDualGPU = true;
+ maDeviceString2 = o3tl::toU(value);
+ maDeviceID2 = aDeviceID2;
+ maDeviceKey2 = driverKey2;
+ maDriverVersion2 = aDriverVersion2;
+ maDriverDate2 = aDriverDate2;
+ appendIntegerWithPadding(maAdapterVendorID2, adapterVendorID2, 4);
+ appendIntegerWithPadding(maAdapterDeviceID2, adapterDeviceID2, 4);
+ appendIntegerWithPadding(maAdapterSubsysID2, ParseIDFromDeviceID(maDeviceID2, "&SUBSYS_", 8), 8);
+ break;
+ }
+ }
+ }
+ }
+
+ SetupDiDestroyDeviceInfoList(devinfo);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/win/gdiimpl.cxx b/vcl/opengl/win/gdiimpl.cxx
new file mode 100644
index 000000000..eabfe8a09
--- /dev/null
+++ b/vcl/opengl/win/gdiimpl.cxx
@@ -0,0 +1,898 @@
+/* -*- 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 <thread>
+#include <opengl/win/gdiimpl.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+#include <sal/log.hxx>
+#include <comphelper/windowserrorstring.hxx>
+#include <opengl/zone.hxx>
+#include <win/wincomp.hxx>
+#include <win/saldata.hxx>
+#include <win/salframe.h>
+#include <win/salinst.h>
+#include <epoxy/wgl.h>
+#include <ControlCacheKey.hxx>
+
+static std::vector<HGLRC> g_vShareList;
+static bool g_bAnyCurrent;
+
+namespace {
+
+class GLWinWindow : public GLWindow
+{
+public:
+ HWND hWnd;
+ HDC hDC;
+ HGLRC hRC;
+ GLWinWindow();
+};
+
+}
+
+GLWinWindow::GLWinWindow()
+ : hWnd(nullptr)
+ , hDC(nullptr)
+ , hRC(nullptr)
+{
+}
+
+namespace {
+
+class WinOpenGLContext : public OpenGLContext
+{
+public:
+ bool init( HDC hDC, HWND hWnd );
+ virtual void initWindow() override;
+private:
+ GLWinWindow m_aGLWin;
+ virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; }
+ virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; }
+ virtual bool ImplInit() override;
+ virtual void makeCurrent() override;
+ virtual void destroyCurrentContext() override;
+ virtual bool isCurrent() override;
+ virtual bool isAnyCurrent() override;
+ virtual void resetCurrent() override;
+ virtual void swapBuffers() override;
+};
+
+}
+
+void WinOpenGLContext::swapBuffers()
+{
+ OpenGLZone aZone;
+
+ SwapBuffers(m_aGLWin.hDC);
+
+ BuffersSwapped();
+}
+
+void WinOpenGLContext::resetCurrent()
+{
+ clearCurrent();
+
+ OpenGLZone aZone;
+
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+}
+
+static void ensureDispatchTable()
+{
+ thread_local bool bEpoxyDispatchMakeCurrentCalled = false;
+ if (!bEpoxyDispatchMakeCurrentCalled)
+ {
+ epoxy_handle_external_wglMakeCurrent();
+ bEpoxyDispatchMakeCurrentCalled = true;
+ }
+}
+
+bool WinOpenGLContext::isCurrent()
+{
+ OpenGLZone aZone;
+ if (!g_bAnyCurrent || !m_aGLWin.hRC)
+ return false;
+ ensureDispatchTable();
+ return wglGetCurrentContext() == m_aGLWin.hRC && wglGetCurrentDC() == m_aGLWin.hDC;
+}
+
+bool WinOpenGLContext::isAnyCurrent()
+{
+ return g_bAnyCurrent && wglGetCurrentContext() != nullptr;
+}
+
+void WinOpenGLContext::makeCurrent()
+{
+ if (isCurrent())
+ return;
+
+ OpenGLZone aZone;
+
+ clearCurrent();
+
+ ensureDispatchTable();
+
+ if (!wglMakeCurrent(m_aGLWin.hDC, m_aGLWin.hRC))
+ {
+ g_bAnyCurrent = false;
+ DWORD nLastError = GetLastError();
+ if (nLastError != ERROR_SUCCESS)
+ SAL_WARN("vcl.opengl", "wglMakeCurrent failed: " << WindowsErrorString(nLastError));
+ return;
+ }
+
+ g_bAnyCurrent = true;
+
+ registerAsCurrent();
+}
+
+bool WinOpenGLContext::init(HDC hDC, HWND hWnd)
+{
+ if (isInitialized())
+ return true;
+
+ m_aGLWin.hDC = hDC;
+ m_aGLWin.hWnd = hWnd;
+ return ImplInit();
+}
+
+void WinOpenGLContext::initWindow()
+{
+ if( !m_pChildWindow )
+ {
+ SystemWindowData winData = generateWinData(mpWindow, false);
+ m_pChildWindow = VclPtr<SystemChildWindow>::Create(mpWindow, 0, &winData, false);
+ }
+
+ if (m_pChildWindow)
+ {
+ InitChildWindow(m_pChildWindow.get());
+ const SystemEnvData* sysData(m_pChildWindow->GetSystemData());
+ m_aGLWin.hWnd = sysData->hWnd;
+ }
+
+ m_aGLWin.hDC = GetDC(m_aGLWin.hWnd);
+}
+
+void WinOpenGLContext::destroyCurrentContext()
+{
+ if (m_aGLWin.hRC)
+ {
+ std::vector<HGLRC>::iterator itr = std::remove(g_vShareList.begin(), g_vShareList.end(), m_aGLWin.hRC);
+ if (itr != g_vShareList.end())
+ g_vShareList.erase(itr);
+
+ if (wglGetCurrentContext() != nullptr)
+ {
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ }
+ wglDeleteContext( m_aGLWin.hRC );
+ ReleaseDC( m_aGLWin.hWnd, m_aGLWin.hDC );
+ m_aGLWin.hRC = nullptr;
+ }
+}
+
+static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_CREATE:
+ return 0;
+ case WM_CLOSE:
+ PostQuitMessage(0);
+ return 0;
+ case WM_DESTROY:
+ return 0;
+ default:
+ return DefWindowProcW(hwnd, message, wParam, lParam);
+ }
+}
+
+static bool InitTempWindow(HWND& hwnd, int width, int height, const PIXELFORMATDESCRIPTOR& inPfd, GLWinWindow& glWin)
+{
+ OpenGLZone aZone;
+
+ PIXELFORMATDESCRIPTOR pfd = inPfd;
+ int ret;
+ WNDCLASSW wc;
+ wc.style = 0;
+ wc.lpfnWndProc = WndProc;
+ wc.cbClsExtra = wc.cbWndExtra = 0;
+ wc.hInstance = nullptr;
+ wc.hIcon = nullptr;
+ wc.hCursor = nullptr;
+ wc.hbrBackground = nullptr;
+ wc.lpszMenuName = nullptr;
+ wc.lpszClassName = L"GLRenderer";
+ RegisterClassW(&wc);
+ hwnd = CreateWindowW(wc.lpszClassName, nullptr, WS_DISABLED, 0, 0, width, height, nullptr, nullptr, wc.hInstance, nullptr);
+ glWin.hDC = GetDC(hwnd);
+
+ int nPixelFormat = ChoosePixelFormat(glWin.hDC, &pfd);
+ if (!nPixelFormat)
+ {
+ ReleaseDC(hwnd, glWin.hDC);
+ DestroyWindow(hwnd);
+ return false;
+ }
+ ret = SetPixelFormat(glWin.hDC, nPixelFormat, &pfd);
+ if(!ret)
+ {
+ ReleaseDC(hwnd, glWin.hDC);
+ DestroyWindow(hwnd);
+ return false;
+ }
+ glWin.hRC = wglCreateContext(glWin.hDC);
+ if(!(glWin.hRC))
+ {
+ ReleaseDC(hwnd, glWin.hDC);
+ DestroyWindow(hwnd);
+ return false;
+ }
+ ret = wglMakeCurrent(glWin.hDC, glWin.hRC);
+ if(!ret)
+ {
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(glWin.hRC);
+ ReleaseDC(hwnd, glWin.hDC);
+ DestroyWindow(hwnd);
+ return false;
+ }
+ g_bAnyCurrent = false;
+
+ return true;
+}
+
+static bool WGLisExtensionSupported(const char *extension)
+{
+ OpenGLZone aZone;
+
+ const size_t extlen = strlen(extension);
+ const char *supported = nullptr;
+
+ // Try to use wglGetExtensionStringARB on current DC, if possible
+ PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");
+
+ if (wglGetExtString)
+ supported = reinterpret_cast<char*(__stdcall*)(HDC)>(wglGetExtString)(wglGetCurrentDC());
+ // If that failed, try standard OpenGL extensions string
+ if (supported == nullptr)
+ supported = reinterpret_cast<char const *>(glGetString(GL_EXTENSIONS));
+ // If that failed too, must be no extensions supported
+ if (supported == nullptr)
+ return false;
+
+ // Begin examination at start of string, increment by 1 on false match
+ for (const char* p = supported; ; p++)
+ {
+ // Advance p up to the next possible match
+ p = strstr(p, extension);
+
+ if (p == nullptr)
+ return false; // No Match
+
+ // Make sure that match is at the start of the string or that
+ // the previous char is a space, or else we could accidentally
+ // match "wglFunkywglExtension" with "wglExtension"
+
+ // Also, make sure that the following character is space or null
+ // or else "wglExtensionTwo" might match "wglExtension"
+ if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
+ return true; // Match
+ }
+}
+
+static bool InitMultisample(const PIXELFORMATDESCRIPTOR& pfd, int& rPixelFormat,
+ bool bUseDoubleBufferedRendering, bool bRequestVirtualDevice)
+{
+ OpenGLZone aZone;
+
+ HWND hWnd = nullptr;
+ GLWinWindow glWin;
+ // Create a temp window to check whether support multi-sample, if support, get the format
+ if (!InitTempWindow(hWnd, 32, 32, pfd, glWin))
+ {
+ SAL_WARN("vcl.opengl", "Can't create temp window to test");
+ return false;
+ }
+
+ // See if the string exists in WGL
+ if (!WGLisExtensionSupported("WGL_ARB_multisample"))
+ {
+ SAL_WARN("vcl.opengl", "Device doesn't support multisample");
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(glWin.hRC);
+ ReleaseDC(hWnd, glWin.hDC);
+ DestroyWindow(hWnd);
+ return false;
+ }
+ // Get our pixel format
+ PFNWGLCHOOSEPIXELFORMATARBPROC fn_wglChoosePixelFormatARB = reinterpret_cast<PFNWGLCHOOSEPIXELFORMATARBPROC>(wglGetProcAddress("wglChoosePixelFormatARB"));
+ if (!fn_wglChoosePixelFormatARB)
+ {
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(glWin.hRC);
+ ReleaseDC(hWnd, glWin.hDC);
+ DestroyWindow(hWnd);
+ return false;
+ }
+ // Get our current device context
+ HDC hDC = GetDC(hWnd);
+
+ int pixelFormat;
+ int valid;
+ UINT numFormats;
+ float fAttributes[] = {0,0};
+ // These attributes are the bits we want to test for in our sample.
+ // Everything is pretty standard, the only one we want to
+ // really focus on is the WGL_SAMPLE_BUFFERS_ARB and WGL_SAMPLES_ARB.
+ // These two are going to do the main testing for whether or not
+ // we support multisampling on this hardware.
+ int iAttributes[] =
+ {
+ WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
+ WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
+ WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
+ WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
+ WGL_COLOR_BITS_ARB,24,
+ WGL_ALPHA_BITS_ARB,8,
+ WGL_DEPTH_BITS_ARB,24,
+ WGL_STENCIL_BITS_ARB,0,
+ WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
+ WGL_SAMPLES_ARB,8,
+ 0,0
+ };
+
+ if (!bUseDoubleBufferedRendering)
+ {
+ // Use asserts to make sure the iAttributes array is not changed without changing these ugly
+ // hardcode indexes into it.
+ assert(iAttributes[0] == WGL_DOUBLE_BUFFER_ARB);
+ iAttributes[1] = GL_FALSE;
+ }
+
+ if (bRequestVirtualDevice)
+ {
+ assert(iAttributes[2] == WGL_DRAW_TO_WINDOW_ARB);
+ iAttributes[2] = WGL_DRAW_TO_BITMAP_ARB;
+ }
+
+ bool bArbMultisampleSupported = false;
+
+ // First we check to see if we can get a pixel format for 8 samples
+ valid = fn_wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
+ // If we returned true, and our format count is greater than 1
+ if (valid && numFormats >= 1)
+ {
+ bArbMultisampleSupported = true;
+ rPixelFormat = pixelFormat;
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(glWin.hRC);
+ ReleaseDC(hWnd, glWin.hDC);
+ DestroyWindow(hWnd);
+ return bArbMultisampleSupported;
+ }
+ // Our pixel format with 8 samples failed, test for 2 samples
+ assert(iAttributes[18] == WGL_SAMPLES_ARB);
+ iAttributes[19] = 2;
+ valid = fn_wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
+ if (valid && numFormats >= 1)
+ {
+ bArbMultisampleSupported = true;
+ rPixelFormat = pixelFormat;
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(glWin.hRC);
+ ReleaseDC(hWnd, glWin.hDC);
+ DestroyWindow(hWnd);
+ return bArbMultisampleSupported;
+ }
+ // Return the valid format
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(glWin.hRC);
+ ReleaseDC(hWnd, glWin.hDC);
+ DestroyWindow(hWnd);
+
+ return bArbMultisampleSupported;
+}
+
+namespace
+{
+
+bool tryShaders(const OUString& rVertexShader, const OUString& rFragmentShader, const OUString& rGeometryShader = "", const OString& rPreamble = "")
+{
+ GLint nId;
+
+ // Somewhat mysteriously, the OpenGLHelper::LoadShaders() API saves a compiled binary of the
+ // shader only if you give it the digest of the shaders. We have API to calculate the digest
+ // only of the combination of vertex and fragment (but not geometry) shader. So if we have a
+ // geometry shader, we should not save the binary.
+ if (rGeometryShader.isEmpty())
+ {
+ nId = OpenGLHelper::LoadShaders(rVertexShader, rFragmentShader, rPreamble, OpenGLHelper::GetDigest( rVertexShader, rFragmentShader, rPreamble));
+ }
+ else
+ {
+ assert(rPreamble.isEmpty());
+ nId = OpenGLHelper::LoadShaders(rVertexShader, rFragmentShader, rGeometryShader);
+ }
+ if (!nId)
+ return false;
+
+ // We're interested in the error returned by glDeleteProgram().
+ glGetError();
+
+ glDeleteProgram(nId);
+ return glGetError() == GL_NO_ERROR;
+}
+
+bool compiledShaderBinariesWork()
+{
+ static bool bBeenHere = false;
+ static bool bResult;
+
+ if (bBeenHere)
+ return bResult;
+
+ bBeenHere = true;
+
+ bResult =
+ (
+#if 0 // Only look at shaders used by vcl for now
+ // canvas
+ tryShaders("dummyVertexShader", "linearMultiColorGradientFragmentShader") &&
+ tryShaders("dummyVertexShader", "linearTwoColorGradientFragmentShader") &&
+ tryShaders("dummyVertexShader", "radialMultiColorGradientFragmentShader") &&
+ tryShaders("dummyVertexShader", "radialTwoColorGradientFragmentShader") &&
+ tryShaders("dummyVertexShader", "rectangularMultiColorGradientFragmentShader") &&
+ tryShaders("dummyVertexShader", "rectangularTwoColorGradientFragmentShader") &&
+ // chart2
+ (GLEW_VERSION_3_3 ?
+ (tryShaders("shape3DVertexShader", "shape3DFragmentShader") &&
+ tryShaders("shape3DVertexShaderBatchScroll", "shape3DFragmentShaderBatchScroll") &&
+ tryShaders("shape3DVertexShaderBatch", "shape3DFragmentShaderBatch") &&
+ tryShaders("textVertexShaderBatch", "textFragmentShaderBatch")) :
+ (tryShaders("shape3DVertexShaderV300", "shape3DFragmentShaderV300"))) &&
+ tryShaders("textVertexShader", "textFragmentShader") &&
+ tryShaders("screenTextVertexShader", "screenTextFragmentShader") &&
+ tryShaders("commonVertexShader", "commonFragmentShader") &&
+ tryShaders("pickingVertexShader", "pickingFragmentShader") &&
+ tryShaders("backgroundVertexShader", "backgroundFragmentShader") &&
+ tryShaders("symbolVertexShader", "symbolFragmentShader") &&
+ tryShaders("symbolVertexShader", "symbolFragmentShader") &&
+ // slideshow
+ tryShaders("reflectionVertexShader", "reflectionFragmentShader") &&
+ tryShaders("basicVertexShader", "basicFragmentShader") &&
+ tryShaders("vortexVertexShader", "vortexFragmentShader", "vortexGeometryShader") &&
+ tryShaders("basicVertexShader", "rippleFragmentShader") &&
+ tryShaders("glitterVertexShader", "glitterFragmentShader") &&
+ tryShaders("honeycombVertexShader", "honeycombFragmentShader", "honeycombGeometryShader") &&
+#endif
+ // vcl
+ tryShaders("combinedVertexShader", "combinedFragmentShader") &&
+ tryShaders("dumbVertexShader", "invert50FragmentShader") &&
+ tryShaders("textureVertexShader", "areaScaleFragmentShader") &&
+ tryShaders("transformedTextureVertexShader", "maskedTextureFragmentShader") &&
+ tryShaders("transformedTextureVertexShader", "areaScaleFastFragmentShader") &&
+ tryShaders("transformedTextureVertexShader", "areaScaleFastFragmentShader", "", "#define MASKED") &&
+ tryShaders("transformedTextureVertexShader", "areaScaleFragmentShader") &&
+ tryShaders("transformedTextureVertexShader", "areaScaleFragmentShader", "", "#define MASKED") &&
+ tryShaders("transformedTextureVertexShader", "textureFragmentShader") &&
+ tryShaders("combinedTextureVertexShader", "combinedTextureFragmentShader") &&
+ tryShaders("combinedTextureVertexShader", "combinedTextureFragmentShader", "", "// flush shader\n") &&
+ tryShaders("textureVertexShader", "linearGradientFragmentShader") &&
+ tryShaders("textureVertexShader", "radialGradientFragmentShader") &&
+ tryShaders("textureVertexShader", "areaHashCRC64TFragmentShader") &&
+ tryShaders("textureVertexShader", "replaceColorFragmentShader") &&
+ tryShaders("textureVertexShader", "greyscaleFragmentShader") &&
+ tryShaders("textureVertexShader", "textureFragmentShader") &&
+ tryShaders("textureVertexShader", "convolutionFragmentShader") &&
+ tryShaders("textureVertexShader", "areaScaleFastFragmentShader"));
+
+ return bResult;
+}
+
+} // unnamed namespace
+
+bool WinOpenGLContext::ImplInit()
+{
+ static bool bFirstCall = true;
+
+ OpenGLZone aZone;
+
+ VCL_GL_INFO("OpenGLContext::ImplInit----start");
+ // PixelFormat tells Windows how we want things to be
+ PIXELFORMATDESCRIPTOR PixelFormatFront =
+ {
+ sizeof(PIXELFORMATDESCRIPTOR),
+ 1, // Version Number
+ PFD_SUPPORT_OPENGL,
+ PFD_TYPE_RGBA, // Request An RGBA Format
+ BYTE(32), // Select Our Color Depth
+ 0, 0, 0, 0, 0, 0, // Color Bits Ignored
+ 0, // No Alpha Buffer
+ 0, // Shift Bit Ignored
+ 0, // No Accumulation Buffer
+ 0, 0, 0, 0, // Accumulation Bits Ignored
+ 24, // 24 bit z-buffer
+ 8, // stencil buffer
+ 0, // No Auxiliary Buffer
+ 0, // now ignored
+ 0, // Reserved
+ 0, 0, 0 // Layer Masks Ignored
+ };
+
+ PixelFormatFront.dwFlags |= PFD_DOUBLEBUFFER;
+ PixelFormatFront.dwFlags |= PFD_DRAW_TO_WINDOW;
+
+ // we must check whether can set the MSAA
+ int WindowPix = 0;
+ bool bMultiSampleSupport = false;
+
+ if (!mbVCLOnly)
+ bMultiSampleSupport = InitMultisample(PixelFormatFront, WindowPix, /*bUseDoubleBufferedRendering*/true, false);
+ else
+ VCL_GL_INFO("Skipping multisample detection for VCL.");
+
+ if (bMultiSampleSupport && WindowPix != 0)
+ {
+ m_aGLWin.bMultiSampleSupported = true;
+ }
+ else
+ {
+ WindowPix = ChoosePixelFormat(m_aGLWin.hDC, &PixelFormatFront);
+#if OSL_DEBUG_LEVEL > 0
+ PIXELFORMATDESCRIPTOR pfd;
+ DescribePixelFormat(m_aGLWin.hDC, WindowPix, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
+ SAL_WARN("vcl.opengl", "Render Target: Window: " << static_cast<int>((pfd.dwFlags & PFD_DRAW_TO_WINDOW) != 0) << ", Bitmap: " << static_cast<int>((pfd.dwFlags & PFD_DRAW_TO_BITMAP) != 0));
+ SAL_WARN("vcl.opengl", "Supports OpenGL: " << static_cast<int>((pfd.dwFlags & PFD_SUPPORT_OPENGL) != 0));
+#endif
+ }
+
+ if (WindowPix == 0)
+ {
+ SAL_WARN("vcl.opengl", "Invalid pixelformat");
+ return false;
+ }
+
+ if (!SetPixelFormat(m_aGLWin.hDC, WindowPix, &PixelFormatFront))
+ {
+ SAL_WARN("vcl.opengl", "SetPixelFormat failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ HGLRC hTempRC = wglCreateContext(m_aGLWin.hDC);
+ if (hTempRC == nullptr)
+ {
+ SAL_WARN("vcl.opengl", "wglCreateContext failed: "<< WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ if (!wglMakeCurrent(m_aGLWin.hDC, hTempRC))
+ {
+ g_bAnyCurrent = false;
+ SAL_WARN("vcl.opengl", "wglMakeCurrent failed: "<< WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ g_bAnyCurrent = true;
+
+ if (!InitGL())
+ {
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(hTempRC);
+ return false;
+ }
+
+ HGLRC hSharedCtx = nullptr;
+ if (!g_vShareList.empty())
+ hSharedCtx = g_vShareList.front();
+
+ if (!wglCreateContextAttribsARB)
+ {
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(hTempRC);
+ return false;
+ }
+
+ // now setup the shared context; this needs a temporary context already
+ // set up in order to work
+ int const attribs [] =
+ {
+#ifdef DBG_UTIL
+ WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
+#endif
+ 0
+ };
+ m_aGLWin.hRC = wglCreateContextAttribsARB(m_aGLWin.hDC, hSharedCtx, attribs);
+ if (m_aGLWin.hRC == nullptr)
+ {
+ SAL_WARN("vcl.opengl", "wglCreateContextAttribsARB failed: "<< WindowsErrorString(GetLastError()));
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(hTempRC);
+ return false;
+ }
+
+ if (!compiledShaderBinariesWork())
+ {
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(hTempRC);
+ return false;
+ }
+
+ wglMakeCurrent(nullptr, nullptr);
+ g_bAnyCurrent = false;
+ wglDeleteContext(hTempRC);
+
+ if (!wglMakeCurrent(m_aGLWin.hDC, m_aGLWin.hRC))
+ {
+ g_bAnyCurrent = false;
+ SAL_WARN("vcl.opengl", "wglMakeCurrent failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ g_bAnyCurrent = true;
+
+ if (bFirstCall)
+ {
+ // Checking texture size
+ GLint nMaxTextureSize;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxTextureSize);
+ if (nMaxTextureSize <= 4096)
+ SAL_WARN("vcl.opengl", "Max texture size is " << nMaxTextureSize
+ << ". This may not be enough for normal operation.");
+ else
+ VCL_GL_INFO("Max texture size: " << nMaxTextureSize);
+
+ // Trying to make a texture and check its size
+ for (GLint nWidthHeight = 1023; nWidthHeight < nMaxTextureSize; nWidthHeight += (nWidthHeight + 1))
+ {
+ glTexImage2D(GL_PROXY_TEXTURE_2D, 0, 4, nWidthHeight, nWidthHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, nullptr);
+ CHECK_GL_ERROR();
+ if (glGetError() == GL_NO_ERROR)
+ {
+ GLint nWidth = 0;
+ GLint nHeight = 0;
+ glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &nWidth);
+ glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &nHeight);
+ VCL_GL_INFO("Created texture " << nWidthHeight << "," << nWidthHeight << " reports size: " << nWidth << ", " << nHeight);
+ }
+ else
+ {
+ SAL_WARN("vcl.opengl", "Error when creating a " << nWidthHeight << ", " << nWidthHeight << " test texture.");
+ }
+ }
+ }
+
+ InitGLDebugging();
+
+ g_vShareList.push_back(m_aGLWin.hRC);
+
+ RECT clientRect;
+ GetClientRect(WindowFromDC(m_aGLWin.hDC), &clientRect);
+ m_aGLWin.Width = clientRect.right - clientRect.left;
+ m_aGLWin.Height = clientRect.bottom - clientRect.top;
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ registerAsCurrent();
+
+ bFirstCall = false;
+
+ static OString aVendor(reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
+
+ if (aVendor.equalsIgnoreAsciiCase("intel"))
+ maOpenGLCapabilitySwitch.mbLimitedShaderRegisters = true;
+
+ return true;
+}
+
+OpenGLContext* WinSalInstance::CreateOpenGLContext()
+{
+ return new WinOpenGLContext;
+}
+
+WinOpenGLSalGraphicsImpl::WinOpenGLSalGraphicsImpl(WinSalGraphics& rGraphics,
+ SalGeometryProvider *mpProvider):
+ OpenGLSalGraphicsImpl(rGraphics,mpProvider),
+ mrWinParent(rGraphics)
+{
+}
+
+void WinOpenGLSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
+{
+ OpenGLSalGraphicsImpl *pImpl = pSrcGraphics ? static_cast< OpenGLSalGraphicsImpl* >(pSrcGraphics->GetImpl()) : static_cast< OpenGLSalGraphicsImpl *>(mrParent.GetImpl());
+ OpenGLSalGraphicsImpl::DoCopyBits( rPosAry, *pImpl );
+}
+
+rtl::Reference<OpenGLContext> WinOpenGLSalGraphicsImpl::CreateWinContext()
+{
+ rtl::Reference<WinOpenGLContext> xContext(new WinOpenGLContext);
+ xContext->setVCLOnly();
+ if (!xContext->init(mrWinParent.mhLocalDC, mrWinParent.mhWnd))
+ {
+ SAL_WARN("vcl.opengl", "Context could not be created.");
+ return rtl::Reference<OpenGLContext>();
+ }
+ return rtl::Reference<OpenGLContext>(xContext.get());
+}
+
+void WinOpenGLSalGraphicsImpl::Init()
+{
+ if (!IsOffscreen() && mpContext.is() && mpContext->isInitialized())
+ {
+ const GLWinWindow& rGLWindow = static_cast<const GLWinWindow&>(mpContext->getOpenGLWindow());
+ if (rGLWindow.hWnd != mrWinParent.mhWnd || rGLWindow.hDC == mrWinParent.mhLocalDC)
+ {
+ // This can legitimately happen, SalFrame keeps 2x
+ // SalGraphics which share the same hWnd and hDC.
+ // The shape 'Area' dialog does reparenting to trigger this.
+ SAL_WARN("vcl.opengl", "Unusual: Windows handle / DC changed without DeInit");
+ DeInit();
+ }
+ }
+
+ OpenGLSalGraphicsImpl::Init();
+}
+
+OpenGLControlsCache::OpenGLControlsCache(): cache(200) {}
+
+OpenGLControlCacheType & OpenGLControlsCache::get() {
+ SalData * data = GetSalData();
+ if (!data->m_pOpenGLControlsCache) {
+ data->m_pOpenGLControlsCache.reset(new OpenGLControlsCache);
+ }
+ return data->m_pOpenGLControlsCache->cache;
+}
+
+OpenGLCompatibleDC::OpenGLCompatibleDC(SalGraphics &rGraphics, int x, int y, int width, int height)
+: CompatibleDC( rGraphics, x, y, width, height, false )
+{
+}
+
+OpenGLTexture* OpenGLCompatibleDC::getOpenGLTexture() const
+{
+ if (!mpImpl)
+ return nullptr;
+
+ // turn what's in the mpData into a texture
+ return new OpenGLTexture(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, mpData);
+}
+
+std::unique_ptr<CompatibleDC::Texture> OpenGLCompatibleDC::getAsMaskTexture() const
+{
+ auto ret = std::make_unique<OpenGLCompatibleDC::Texture>();
+ ret->texture = OpenGLTexture(maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, mpData);
+ return ret;
+}
+
+bool OpenGLCompatibleDC::copyToTexture(CompatibleDC::Texture& aTexture) const
+{
+ if (!mpImpl)
+ return false;
+
+ assert(dynamic_cast<OpenGLCompatibleDC::Texture*>(&aTexture));
+ return static_cast<OpenGLCompatibleDC::Texture&>(aTexture).texture.CopyData(
+ maRects.mnSrcWidth, maRects.mnSrcHeight, GL_BGRA, GL_UNSIGNED_BYTE, reinterpret_cast<sal_uInt8*>(mpData));
+}
+
+bool WinOpenGLSalGraphicsImpl::TryRenderCachedNativeControl(ControlCacheKey const & rControlCacheKey, int nX, int nY)
+{
+ static bool gbCacheEnabled = !getenv("SAL_WITHOUT_WIDGET_CACHE");
+
+ if (!gbCacheEnabled)
+ return false;
+
+ auto & gTextureCache = OpenGLControlsCache::get();
+ OpenGLControlCacheType::const_iterator iterator = gTextureCache.find(rControlCacheKey);
+
+ if (iterator == gTextureCache.end())
+ return false;
+
+ const std::unique_ptr<TextureCombo>& pCombo = iterator->second;
+
+ bool bRet = false;
+
+ PreDraw();
+
+ bRet = RenderTextureCombo(*pCombo, nX, nY);
+
+ PostDraw();
+
+ return bRet;
+}
+
+bool WinOpenGLSalGraphicsImpl::RenderTextureCombo(TextureCombo const & rCombo, int nX, int nY)
+{
+ OpenGLTexture& rTexture = *rCombo.mpTexture;
+
+ SalTwoRect aPosAry(0, 0, rTexture.GetWidth(), rTexture.GetHeight(),
+ nX, nY, rTexture.GetWidth(), rTexture.GetHeight());
+
+ DrawTextureDiff(rTexture, *rCombo.mpMask, aPosAry, false);
+
+ return true;
+}
+
+bool WinOpenGLSalGraphicsImpl::RenderCompatibleDC(OpenGLCompatibleDC& rWhite, OpenGLCompatibleDC& rBlack,
+ int nX, int nY, TextureCombo& rCombo)
+{
+ bool bRet = false;
+
+ PreDraw();
+
+ rCombo.mpTexture.reset(rWhite.getOpenGLTexture());
+ rCombo.mpMask.reset(rBlack.getOpenGLTexture());
+
+ bRet = RenderTextureCombo(rCombo, nX, nY);
+
+ PostDraw();
+ return bRet;
+}
+
+bool WinOpenGLSalGraphicsImpl::RenderAndCacheNativeControl(CompatibleDC& rWhite, CompatibleDC& rBlack,
+ int nX, int nY , ControlCacheKey& aControlCacheKey)
+{
+ assert(dynamic_cast<OpenGLCompatibleDC*>(&rWhite));
+ assert(dynamic_cast<OpenGLCompatibleDC*>(&rBlack));
+
+ std::unique_ptr<TextureCombo> pCombo(new TextureCombo);
+
+ bool bResult = RenderCompatibleDC(static_cast<OpenGLCompatibleDC&>(rWhite),
+ static_cast<OpenGLCompatibleDC&>(rBlack), nX, nY, *pCombo);
+ if (!bResult)
+ return false;
+
+ if (!aControlCacheKey.canCacheControl())
+ return true;
+
+ OpenGLControlCachePair pair(aControlCacheKey, std::move(pCombo));
+ OpenGLControlsCache::get().insert(std::move(pair));
+
+ return bResult;
+}
+
+void WinOpenGLSalGraphicsImpl::PreDrawText()
+{
+ PreDraw();
+}
+
+void WinOpenGLSalGraphicsImpl::PostDrawText()
+{
+ PostDraw();
+}
+
+void WinOpenGLSalGraphicsImpl::DeferredTextDraw(const CompatibleDC::Texture* pTexture, Color aMaskColor, const SalTwoRect& rPosAry)
+{
+ assert(dynamic_cast<const OpenGLCompatibleDC::Texture*>(pTexture));
+ mpRenderList->addDrawTextureWithMaskColor(
+ static_cast<const OpenGLCompatibleDC::Texture*>(pTexture)->texture, aMaskColor, rPosAry);
+ PostBatchDraw();
+}
+
+void WinOpenGLSalGraphicsImpl::DrawTextMask( CompatibleDC::Texture* pTexture, Color nMaskColor, const SalTwoRect& rPosAry )
+{
+ assert(dynamic_cast<OpenGLCompatibleDC::Texture*>(pTexture));
+ DrawMask( static_cast<OpenGLCompatibleDC::Texture*>(pTexture)->texture, nMaskColor, rPosAry );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/win/winlayout.cxx b/vcl/opengl/win/winlayout.cxx
new file mode 100644
index 000000000..59bf12c25
--- /dev/null
+++ b/vcl/opengl/win/winlayout.cxx
@@ -0,0 +1,60 @@
+/* -*- 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 <opengl/win/winlayout.hxx>
+
+#include <opengl/win/gdiimpl.hxx>
+
+bool OpenGLGlobalWinGlyphCache::AllocateTexture(WinGlyphDrawElement& rElement, CompatibleDC* dc)
+{
+ assert(rElement.maTexture.get() == nullptr);
+ assert(dynamic_cast<OpenGLCompatibleDC*>(dc));
+ OpenGLCompatibleDC* odc = static_cast<OpenGLCompatibleDC*>(dc);
+ OpenGLCompatibleDC::Texture* texture = new OpenGLCompatibleDC::Texture;
+ rElement.maTexture.reset(texture);
+ texture->texture = maPackedTextureAtlas.Reserve(dc->getBitmapWidth(), dc->getBitmapHeight());
+ if (!texture->texture)
+ return false;
+ if (!odc->copyToTexture(*rElement.maTexture))
+ return false;
+ return true;
+}
+
+void OpenGLGlobalWinGlyphCache::Prune()
+{
+ std::vector<GLuint> aTextureIDs = maPackedTextureAtlas.ReduceTextureNumber(8);
+ if (!aTextureIDs.empty())
+ {
+ for (auto& pWinGlyphCache : maWinGlyphCaches)
+ static_cast<OpenGLWinGlyphCache*>(pWinGlyphCache)->RemoveTextures(aTextureIDs);
+ }
+}
+
+void OpenGLWinGlyphCache::RemoveTextures(std::vector<GLuint>& rTextureIDs)
+{
+ auto it = maWinTextureCache.begin();
+
+ while (it != maWinTextureCache.end())
+ {
+ assert(dynamic_cast<OpenGLCompatibleDC::Texture*>(it->second.maTexture.get()));
+ GLuint nTextureID
+ = static_cast<OpenGLCompatibleDC::Texture*>(it->second.maTexture.get())->texture.Id();
+
+ if (std::find(rTextureIDs.begin(), rTextureIDs.end(), nTextureID) != rTextureIDs.end())
+ {
+ it = maWinTextureCache.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/x11/X11DeviceInfo.cxx b/vcl/opengl/x11/X11DeviceInfo.cxx
new file mode 100644
index 000000000..7f671952f
--- /dev/null
+++ b/vcl/opengl/x11/X11DeviceInfo.cxx
@@ -0,0 +1,363 @@
+/* -*- 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 <opengl/x11/X11DeviceInfo.hxx>
+#include <opengl/x11/glxtest.hxx>
+
+#include <config_features.h>
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/utsname.h>
+
+#include <desktop/crashreport.hxx>
+
+namespace glx {
+
+static int glxtest_pipe = 0;
+
+static pid_t glxtest_pid = 0;
+
+}
+
+pid_t* getGlxPid()
+{
+ return &glx::glxtest_pid;
+}
+
+int* getGlxPipe()
+{
+ return &glx::glxtest_pipe;
+}
+
+namespace {
+
+const char*
+strspnp_wrapper(const char* aDelims, const char* aStr)
+{
+ const char* d;
+ do
+ {
+ for (d = aDelims; *d != '\0'; ++d)
+ {
+ if (*aStr == *d)
+ {
+ ++aStr;
+ break;
+ }
+ }
+ } while (*d);
+
+ return aStr;
+}
+
+char* strtok_wrapper(const char* aDelims, char** aStr)
+{
+ if (!*aStr)
+ {
+ return nullptr;
+ }
+
+ char* ret = const_cast<char*>(strspnp_wrapper(aDelims, *aStr));
+
+ if (!*ret)
+ {
+ *aStr = ret;
+ return nullptr;
+ }
+
+ char* i = ret;
+ do
+ {
+ for (const char* d = aDelims; *d != '\0'; ++d)
+ {
+ if (*i == *d) {
+ *i = '\0';
+ *aStr = ++i;
+ return ret;
+ }
+ }
+ ++i;
+ } while (*i);
+
+ *aStr = nullptr;
+ return ret;
+}
+
+uint64_t version(uint32_t major, uint32_t minor, uint32_t revision = 0)
+{
+ return (uint64_t(major) << 32) + (uint64_t(minor) << 16) + uint64_t(revision);
+}
+
+}
+
+X11OpenGLDeviceInfo::X11OpenGLDeviceInfo():
+ mbIsMesa(false),
+ mbIsNVIDIA(false),
+ mbIsFGLRX(false),
+ mbIsNouveau(false),
+ mbIsIntel(false),
+ mbIsOldSwrast(false),
+ mbIsLlvmpipe(false),
+ mnGLMajorVersion(0),
+ mnMajorVersion(0),
+ mnMinorVersion(0),
+ mnRevisionVersion(0)
+{
+ GetData();
+}
+
+void X11OpenGLDeviceInfo::GetData()
+{
+ if (!glx::glxtest_pipe)
+ return;
+
+ // to understand this function, see bug moz#639842. We retrieve the OpenGL driver information in a
+ // separate process to protect against bad drivers.
+ enum { buf_size = 1024 };
+ char buf[buf_size];
+ ssize_t bytesread = read(glx::glxtest_pipe,
+ &buf,
+ buf_size-1); // -1 because we'll append a zero
+ close(glx::glxtest_pipe);
+ glx::glxtest_pipe = 0;
+
+ // bytesread < 0 would mean that the above read() call failed.
+ // This should never happen. If it did, the outcome would be to blacklist anyway.
+ if (bytesread < 0)
+ bytesread = 0;
+
+ // let buf be a zero-terminated string
+ buf[bytesread] = 0;
+
+ // Wait for the glxtest process to finish. This serves 2 purposes:
+ // * avoid having a zombie glxtest process laying around
+ // * get the glxtest process status info.
+ int glxtest_status = 0;
+ bool wait_for_glxtest_process = true;
+ bool waiting_for_glxtest_process_failed = false;
+ int waitpid_errno = 0;
+ while(wait_for_glxtest_process)
+ {
+ wait_for_glxtest_process = false;
+ if (waitpid(glx::glxtest_pid, &glxtest_status, 0) == -1)
+ {
+ waitpid_errno = errno;
+ if (waitpid_errno == EINTR)
+ {
+ wait_for_glxtest_process = true;
+ }
+ else
+ {
+ // Bug moz#718629
+ // ECHILD happens when the glxtest process got reaped got reaped after a PR_CreateProcess
+ // as per bug moz#227246. This shouldn't matter, as we still seem to get the data
+ // from the pipe, and if we didn't, the outcome would be to blacklist anyway.
+ waiting_for_glxtest_process_failed = (waitpid_errno != ECHILD);
+ }
+ }
+ }
+
+ bool exited_with_error_code = !waiting_for_glxtest_process_failed &&
+ WIFEXITED(glxtest_status) &&
+ WEXITSTATUS(glxtest_status) != EXIT_SUCCESS;
+ bool received_signal = !waiting_for_glxtest_process_failed &&
+ WIFSIGNALED(glxtest_status);
+
+ bool error = waiting_for_glxtest_process_failed || exited_with_error_code || received_signal;
+
+ OString textureFromPixmap;
+ OString *stringToFill = nullptr;
+ char *bufptr = buf;
+ if (!error)
+ {
+ while(true)
+ {
+ char *line = strtok_wrapper("\n", &bufptr);
+ if (!line)
+ break;
+ if (stringToFill) {
+ *stringToFill = OString(line);
+ stringToFill = nullptr;
+ }
+ else if(!strcmp(line, "VENDOR"))
+ stringToFill = &maVendor;
+ else if(!strcmp(line, "RENDERER"))
+ stringToFill = &maRenderer;
+ else if(!strcmp(line, "VERSION"))
+ stringToFill = &maVersion;
+ else if(!strcmp(line, "TFP"))
+ stringToFill = &textureFromPixmap;
+ }
+ }
+
+ // only useful for Linux kernel version check for FGLRX driver.
+ // assumes X client == X server, which is sad.
+ struct utsname unameobj;
+ if (!uname(&unameobj))
+ {
+ maOS = OString(unameobj.sysname);
+ maOSRelease = OString(unameobj.release);
+ }
+
+ // determine the major OpenGL version. That's the first integer in the version string.
+ mnGLMajorVersion = strtol(maVersion.getStr(), nullptr, 10);
+
+ // determine driver type (vendor) and where in the version string
+ // the actual driver version numbers should be expected to be found (whereToReadVersionNumbers)
+ const char *whereToReadVersionNumbers = nullptr;
+ const char *Mesa_in_version_string = strstr(maVersion.getStr(), "Mesa");
+ if (Mesa_in_version_string)
+ {
+ mbIsMesa = true;
+ // with Mesa, the version string contains "Mesa major.minor" and that's all the version information we get:
+ // there is no actual driver version info.
+ whereToReadVersionNumbers = Mesa_in_version_string + strlen("Mesa");
+ if (strcasestr(maVendor.getStr(), "nouveau"))
+ mbIsNouveau = true;
+ if (strcasestr(maRenderer.getStr(), "intel")) // yes, intel is in the renderer string
+ mbIsIntel = true;
+ if (strcasestr(maRenderer.getStr(), "llvmpipe"))
+ mbIsLlvmpipe = true;
+ if (strcasestr(maRenderer.getStr(), "software rasterizer"))
+ mbIsOldSwrast = true;
+ }
+ else if (strstr(maVendor.getStr(), "NVIDIA Corporation"))
+ {
+ mbIsNVIDIA = true;
+ // with the NVIDIA driver, the version string contains "NVIDIA major.minor"
+ // note that here the vendor and version strings behave differently, that's why we don't put this above
+ // alongside Mesa_in_version_string.
+ const char *NVIDIA_in_version_string = strstr(maVersion.getStr(), "NVIDIA");
+ if (NVIDIA_in_version_string)
+ whereToReadVersionNumbers = NVIDIA_in_version_string + strlen("NVIDIA");
+ }
+ else if (strstr(maVendor.getStr(), "ATI Technologies Inc"))
+ {
+ mbIsFGLRX = true;
+ // with the FGLRX driver, the version string only gives an OpenGL version: so let's return that.
+ // that can at least give a rough idea of how old the driver is.
+ whereToReadVersionNumbers = maVersion.getStr();
+ }
+
+ // read major.minor version numbers of the driver (not to be confused with the OpenGL version)
+ if (whereToReadVersionNumbers)
+ {
+ // copy into writable buffer, for tokenization
+ strncpy(buf, whereToReadVersionNumbers, buf_size-1);
+ buf[buf_size-1] = 0;
+ bufptr = buf;
+
+ // now try to read major.minor version numbers. In case of failure, gracefully exit: these numbers have
+ // been initialized as 0 anyways
+ char *token = strtok_wrapper(".", &bufptr);
+ if (token)
+ {
+ mnMajorVersion = strtol(token, nullptr, 10);
+ token = strtok_wrapper(".", &bufptr);
+ if (token)
+ {
+ mnMinorVersion = strtol(token, nullptr, 10);
+ token = strtok_wrapper(".", &bufptr);
+ if (token)
+ mnRevisionVersion = strtol(token, nullptr, 10);
+ }
+ }
+ }
+}
+
+bool X11OpenGLDeviceInfo::isDeviceBlocked()
+{
+ // don't even try to use OpenGL 1.x
+ if (mnGLMajorVersion == 1)
+ return true;
+
+ CrashReporter::addKeyValue("AdapterVendorId", OStringToOUString(maVendor, RTL_TEXTENCODING_UTF8), CrashReporter::AddItem);
+ CrashReporter::addKeyValue("AdapterDeviceId", OStringToOUString(maRenderer, RTL_TEXTENCODING_UTF8), CrashReporter::Write);
+
+ SAL_INFO("vcl.opengl", "Vendor: " << maVendor);
+ SAL_INFO("vcl.opengl", "Renderer: " << maRenderer);
+ SAL_INFO("vcl.opengl", "Version: " << maVersion);
+ SAL_INFO("vcl.opengl", "OS: " << maOS);
+ SAL_INFO("vcl.opengl", "OSRelease: " << maOSRelease);
+
+ if (mbIsMesa)
+ {
+ if (mbIsNouveau && version(mnMajorVersion, mnMinorVersion) < version(8,0))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: old nouveau driver (requires mesa 8.0+)");
+ return true;
+ }
+ else if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(7,10,3))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: requires at least mesa 7.10.3");
+ return true;
+ }
+ else if (mbIsIntel && version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) == version(9,0,2))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: my broken intel driver Mesa 9.0.2");
+ return true;
+ }
+ else if (mbIsOldSwrast)
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: software rasterizer");
+ return true;
+ }
+ else if (mbIsLlvmpipe && version(mnMajorVersion, mnMinorVersion) < version(9, 1))
+ {
+ // bug moz#791905, Mesa bug 57733, fixed in Mesa 9.1 according to
+ // https://bugs.freedesktop.org/show_bug.cgi?id=57733#c3
+ SAL_WARN("vcl.opengl", "blocked driver version: fdo#57733");
+ return true;
+ }
+ }
+ else if (mbIsNVIDIA)
+ {
+ if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(257,21))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: nvidia requires at least 257.21");
+ return true;
+ }
+ }
+ else if (mbIsFGLRX)
+ {
+ // FGLRX does not report a driver version number, so we have the OpenGL version instead.
+ // by requiring OpenGL 3, we effectively require recent drivers.
+ if (version(mnMajorVersion, mnMinorVersion, mnRevisionVersion) < version(3, 0))
+ {
+ SAL_WARN("vcl.opengl", "blocked driver version: require at least OpenGL 3 for fglrx");
+ return true;
+ }
+ // Bug moz#724640: FGLRX + Linux 2.6.32 is a crashy combo
+ bool unknownOS = maOS.isEmpty() || maOSRelease.isEmpty();
+ bool badOS = maOS.indexOf("Linux") != -1 &&
+ maOSRelease.indexOf("2.6.32") != -1;
+ if (unknownOS || badOS)
+ {
+ SAL_WARN("vcl.opengl", "blocked OS version with fglrx");
+ return true;
+ }
+ }
+ else
+ {
+ // like on windows, let's block unknown vendors. Think of virtual machines.
+ // Also, this case is hit whenever the GLXtest probe failed to get driver info or crashed.
+ SAL_WARN("vcl.opengl", "unknown vendor => blocked");
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/x11/cairotextrender.cxx b/vcl/opengl/x11/cairotextrender.cxx
new file mode 100644
index 000000000..39b5f661d
--- /dev/null
+++ b/vcl/opengl/x11/cairotextrender.cxx
@@ -0,0 +1,84 @@
+/* -*- 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 <opengl/x11/cairotextrender.hxx>
+
+#include <opengl/gdiimpl.hxx>
+
+#include <cairo.h>
+
+OpenGLX11CairoTextRender::OpenGLX11CairoTextRender(X11SalGraphics& rParent)
+ : X11CairoTextRender(rParent)
+{
+}
+
+cairo_t* OpenGLX11CairoTextRender::getCairoContext()
+{
+ cairo_surface_t* surface = nullptr;
+ OpenGLSalGraphicsImpl *pImpl = dynamic_cast< OpenGLSalGraphicsImpl* >(mrParent.GetImpl());
+ if( pImpl )
+ {
+ tools::Rectangle aClipRect = pImpl->getClipRegion().GetBoundRect();
+ if( aClipRect.GetWidth() == 0 || aClipRect.GetHeight() == 0 )
+ {
+ aClipRect.setWidth( GetWidth() );
+ aClipRect.setHeight( GetHeight() );
+ }
+ surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, aClipRect.GetWidth(), aClipRect.GetHeight() );
+ }
+ if (!surface)
+ return nullptr;
+ cairo_t *cr = cairo_create(surface);
+ cairo_surface_destroy(surface);
+ return cr;
+}
+
+void OpenGLX11CairoTextRender::getSurfaceOffset( double& nDX, double& nDY )
+{
+ OpenGLSalGraphicsImpl *pImpl = dynamic_cast< OpenGLSalGraphicsImpl* >(mrParent.GetImpl());
+ if( pImpl )
+ {
+ tools::Rectangle aClipRect = pImpl->getClipRegion().GetBoundRect();
+ nDX = -aClipRect.Left();
+ nDY = -aClipRect.Top();
+ }
+}
+
+void OpenGLX11CairoTextRender::releaseCairoContext(cairo_t* cr)
+{
+ // XXX: lfrb: GLES 2.0 doesn't support GL_UNSIGNED_INT_8_8_8_8_REV
+ OpenGLSalGraphicsImpl *pImpl = dynamic_cast< OpenGLSalGraphicsImpl* >(mrParent.GetImpl());
+ if(!pImpl)
+ {
+ cairo_destroy(cr);
+ return;
+ }
+
+ cairo_surface_t* pSurface = cairo_get_target(cr);
+ int nWidth = cairo_image_surface_get_width( pSurface );
+ int nHeight = cairo_image_surface_get_height( pSurface );
+ cairo_surface_flush(pSurface);
+ unsigned char *pSrc = cairo_image_surface_get_data( pSurface );
+
+ // XXX: lfrb: GLES 2.0 doesn't support GL_UNSIGNED_INT_8_8_8_8_REV
+ tools::Rectangle aClipRect = pImpl->getClipRegion().GetBoundRect();
+
+ SalTwoRect aRect(0, 0, nWidth, nHeight,
+ aClipRect.Left(), aClipRect.Top(), nWidth, nHeight);
+
+ // Cairo surface data is ARGB with premultiplied alpha and is Y-inverted
+ OpenGLTexture aTexture( nWidth, nHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pSrc );
+ pImpl->PreDraw();
+ pImpl->DrawAlphaTexture( aTexture, aRect, true, true );
+ pImpl->PostDraw();
+
+ cairo_destroy(cr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/x11/gdiimpl.cxx b/vcl/opengl/x11/gdiimpl.cxx
new file mode 100644
index 000000000..c00ff76e8
--- /dev/null
+++ b/vcl/opengl/x11/gdiimpl.cxx
@@ -0,0 +1,598 @@
+/* -*- 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 <vcl/lazydelete.hxx>
+
+#include <svdata.hxx>
+
+#include <unx/saldisp.hxx>
+#include <unx/salframe.h>
+#include <unx/salgdi.h>
+#include <unx/salinst.h>
+#include <unx/salvd.h>
+#include <unx/x11/xlimits.hxx>
+
+#include <opengl/texture.hxx>
+#include <opengl/zone.hxx>
+#include <opengl/RenderState.hxx>
+#include <opengl/x11/gdiimpl.hxx>
+#include <opengl/x11/salvd.hxx>
+
+#include <vcl/opengl/OpenGLContext.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <sal/log.hxx>
+
+#include <o3tl/lru_map.hxx>
+#include <ControlCacheKey.hxx>
+
+static std::vector<GLXContext> g_vShareList;
+static bool g_bAnyCurrent;
+
+namespace {
+
+class X11OpenGLContext : public OpenGLContext
+{
+public:
+ void init(Display* dpy, Window win, int screen);
+ virtual void initWindow() override;
+private:
+ GLX11Window m_aGLWin;
+ virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; }
+ virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; }
+ virtual bool ImplInit() override;
+ void initGLWindow(Visual* pVisual);
+ virtual SystemWindowData generateWinData(vcl::Window* pParent, bool bRequestLegacyContext) override;
+ virtual void makeCurrent() override;
+ virtual void destroyCurrentContext() override;
+ virtual bool isCurrent() override;
+ virtual bool isAnyCurrent() override;
+ virtual void sync() override;
+ virtual void resetCurrent() override;
+ virtual void swapBuffers() override;
+};
+
+#ifdef DBG_UTIL
+ int unxErrorHandler(Display* dpy, XErrorEvent* event)
+ {
+ char err[256];
+ char req[256];
+ char minor[256];
+ XGetErrorText(dpy, event->error_code, err, 256);
+ XGetErrorText(dpy, event->request_code, req, 256);
+ XGetErrorText(dpy, event->minor_code, minor, 256);
+ SAL_WARN("vcl.opengl", "Error: " << err << ", Req: " << req << ", Minor: " << minor);
+ return 0;
+ }
+#endif
+
+ typedef int (*errorHandler)(Display* /*dpy*/, XErrorEvent* /*evnt*/);
+
+ class TempErrorHandler
+ {
+ private:
+ errorHandler oldErrorHandler;
+ Display* mdpy;
+
+ public:
+ TempErrorHandler(Display* dpy, errorHandler newErrorHandler)
+ : oldErrorHandler(nullptr)
+ , mdpy(dpy)
+ {
+ if (mdpy)
+ {
+ XLockDisplay(dpy);
+ XSync(dpy, false);
+ oldErrorHandler = XSetErrorHandler(newErrorHandler);
+ }
+ }
+
+ ~TempErrorHandler()
+ {
+ if (mdpy)
+ {
+ // sync so that we possibly get an XError
+ glXWaitGL();
+ XSync(mdpy, false);
+ XSetErrorHandler(oldErrorHandler);
+ XUnlockDisplay(mdpy);
+ }
+ }
+ };
+
+ static bool errorTriggered;
+ int oglErrorHandler( Display* /*dpy*/, XErrorEvent* /*evnt*/ )
+ {
+ errorTriggered = true;
+
+ return 0;
+ }
+
+ GLXFBConfig* getFBConfig(Display* dpy, Window win, int& nBestFBC)
+ {
+ OpenGLZone aZone;
+
+ if( dpy == nullptr || !glXQueryExtension( dpy, nullptr, nullptr ) )
+ return nullptr;
+
+ VCL_GL_INFO("window: " << win);
+
+ XWindowAttributes xattr;
+ if( !XGetWindowAttributes( dpy, win, &xattr ) )
+ {
+ SAL_WARN("vcl.opengl", "Failed to get window attributes for fbconfig " << win);
+ xattr.screen = nullptr;
+ xattr.visual = nullptr;
+ }
+
+ int screen = XScreenNumberOfScreen( xattr.screen );
+
+ // TODO: moggi: Select colour channel depth based on visual attributes, not hardcoded */
+ static int visual_attribs[] =
+ {
+ GLX_DOUBLEBUFFER, True,
+ GLX_X_RENDERABLE, True,
+ GLX_RED_SIZE, 8,
+ GLX_GREEN_SIZE, 8,
+ GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
+ GLX_DEPTH_SIZE, 24,
+ GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
+ None
+ };
+
+ int fbCount = 0;
+ GLXFBConfig* pFBC = glXChooseFBConfig( dpy,
+ screen,
+ visual_attribs, &fbCount );
+
+ if(!pFBC)
+ {
+ SAL_WARN("vcl.opengl", "no suitable fb format found");
+ return nullptr;
+ }
+
+ int best_num_samp = -1;
+ for(int i = 0; i < fbCount; ++i)
+ {
+ XVisualInfo* pVi = glXGetVisualFromFBConfig( dpy, pFBC[i] );
+ if(pVi && (xattr.visual && pVi->visualid == xattr.visual->visualid) )
+ {
+ // pick the one with the most samples per pixel
+ int nSampleBuf = 0;
+ int nSamples = 0;
+ glXGetFBConfigAttrib( dpy, pFBC[i], GLX_SAMPLE_BUFFERS, &nSampleBuf );
+ glXGetFBConfigAttrib( dpy, pFBC[i], GLX_SAMPLES , &nSamples );
+
+ if ( nBestFBC < 0 || (nSampleBuf && ( nSamples > best_num_samp )) )
+ {
+ nBestFBC = i;
+ best_num_samp = nSamples;
+ }
+ }
+ XFree( pVi );
+ }
+
+ return pFBC;
+ }
+
+ Visual* getVisual(Display* dpy, Window win)
+ {
+ OpenGLZone aZone;
+
+ XWindowAttributes xattr;
+ if( !XGetWindowAttributes( dpy, win, &xattr ) )
+ {
+ SAL_WARN("vcl.opengl", "Failed to get window attributes for getVisual " << win);
+ xattr.visual = nullptr;
+ }
+ VCL_GL_INFO("using VisualID " << xattr.visual);
+ return xattr.visual;
+ }
+}
+
+void X11OpenGLContext::sync()
+{
+ OpenGLZone aZone;
+ glXWaitGL();
+ XSync(m_aGLWin.dpy, false);
+}
+
+void X11OpenGLContext::swapBuffers()
+{
+ OpenGLZone aZone;
+
+ glXSwapBuffers(m_aGLWin.dpy, m_aGLWin.win);
+
+ BuffersSwapped();
+}
+
+void X11OpenGLContext::resetCurrent()
+{
+ clearCurrent();
+
+ OpenGLZone aZone;
+
+ if (m_aGLWin.dpy)
+ {
+ glXMakeCurrent(m_aGLWin.dpy, None, nullptr);
+ g_bAnyCurrent = false;
+ }
+}
+
+bool X11OpenGLContext::isCurrent()
+{
+ OpenGLZone aZone;
+ return g_bAnyCurrent && m_aGLWin.ctx && glXGetCurrentContext() == m_aGLWin.ctx &&
+ glXGetCurrentDrawable() == m_aGLWin.win;
+}
+
+bool X11OpenGLContext::isAnyCurrent()
+{
+ return g_bAnyCurrent && glXGetCurrentContext() != None;
+}
+
+SystemWindowData X11OpenGLContext::generateWinData(vcl::Window* pParent, bool /*bRequestLegacyContext*/)
+{
+ OpenGLZone aZone;
+
+ SystemWindowData aWinData;
+ aWinData.pVisual = nullptr;
+ aWinData.bClipUsingNativeWidget = false;
+
+ const SystemEnvData* sysData(pParent->GetSystemData());
+
+ Display *dpy = static_cast<Display*>(sysData->pDisplay);
+ Window win = sysData->aWindow;
+
+ if( dpy == nullptr || !glXQueryExtension( dpy, nullptr, nullptr ) )
+ return aWinData;
+
+ int best_fbc = -1;
+ GLXFBConfig* pFBC = getFBConfig(dpy, win, best_fbc);
+
+ if (!pFBC)
+ return aWinData;
+
+ XVisualInfo* vi = nullptr;
+ if( best_fbc != -1 )
+ vi = glXGetVisualFromFBConfig( dpy, pFBC[best_fbc] );
+
+ XFree(pFBC);
+
+ if( vi )
+ {
+ VCL_GL_INFO("using VisualID " << vi->visualid);
+ aWinData.pVisual = static_cast<void*>(vi->visual);
+ }
+
+ return aWinData;
+}
+
+bool X11OpenGLContext::ImplInit()
+{
+ if (!m_aGLWin.dpy)
+ return false;
+
+ OpenGLZone aZone;
+
+ GLXContext pSharedCtx( nullptr );
+#ifdef DBG_UTIL
+ TempErrorHandler aErrorHandler(m_aGLWin.dpy, unxErrorHandler);
+#endif
+
+ VCL_GL_INFO("OpenGLContext::ImplInit----start");
+
+ if (!g_vShareList.empty())
+ pSharedCtx = g_vShareList.front();
+
+ //tdf#112166 for, e.g. VirtualBox GL, claiming OpenGL 2.1
+ static bool hasCreateContextAttribsARB = glXGetProcAddress(reinterpret_cast<const GLubyte*>("glXCreateContextAttribsARB")) != nullptr;
+ if (hasCreateContextAttribsARB && !mbRequestLegacyContext)
+ {
+ int best_fbc = -1;
+ GLXFBConfig* pFBC = getFBConfig(m_aGLWin.dpy, m_aGLWin.win, best_fbc);
+
+ if (pFBC && best_fbc != -1)
+ {
+ int const pContextAttribs[] =
+ {
+#if 0 // defined(DBG_UTIL)
+ GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+ GLX_CONTEXT_MINOR_VERSION_ARB, 2,
+#endif
+ None
+
+ };
+ m_aGLWin.ctx = glXCreateContextAttribsARB(m_aGLWin.dpy, pFBC[best_fbc], pSharedCtx, /* direct, not via X */ GL_TRUE, pContextAttribs);
+ SAL_INFO_IF(m_aGLWin.ctx, "vcl.opengl", "created a 3.2 core context");
+ }
+ else
+ SAL_WARN("vcl.opengl", "unable to find correct FBC");
+ }
+
+ if (!m_aGLWin.ctx)
+ {
+ if (!m_aGLWin.vi)
+ return false;
+
+ SAL_WARN("vcl.opengl", "attempting to create a non-double-buffered "
+ "visual matching the context");
+
+ m_aGLWin.ctx = glXCreateContext(m_aGLWin.dpy,
+ m_aGLWin.vi,
+ pSharedCtx,
+ GL_TRUE /* direct, not via X server */);
+ }
+
+ if( m_aGLWin.ctx )
+ {
+ g_vShareList.push_back( m_aGLWin.ctx );
+ }
+ else
+ {
+ SAL_WARN("vcl.opengl", "unable to create GLX context");
+ return false;
+ }
+
+ if( !glXMakeCurrent( m_aGLWin.dpy, m_aGLWin.win, m_aGLWin.ctx ) )
+ {
+ g_bAnyCurrent = false;
+ SAL_WARN("vcl.opengl", "unable to select current GLX context");
+ return false;
+ }
+
+ g_bAnyCurrent = true;
+
+ int glxMinor, glxMajor;
+ double nGLXVersion = 0;
+ if( glXQueryVersion( m_aGLWin.dpy, &glxMajor, &glxMinor ) )
+ nGLXVersion = glxMajor + 0.1*glxMinor;
+ SAL_INFO("vcl.opengl", "available GLX version: " << nGLXVersion);
+
+ SAL_INFO("vcl.opengl", "available GL extensions: " << glGetString(GL_EXTENSIONS));
+
+ XWindowAttributes aWinAttr;
+ if( !XGetWindowAttributes( m_aGLWin.dpy, m_aGLWin.win, &aWinAttr ) )
+ {
+ SAL_WARN("vcl.opengl", "Failed to get window attributes on " << m_aGLWin.win);
+ m_aGLWin.Width = 0;
+ m_aGLWin.Height = 0;
+ }
+ else
+ {
+ m_aGLWin.Width = aWinAttr.width;
+ m_aGLWin.Height = aWinAttr.height;
+ }
+
+ if( m_aGLWin.HasGLXExtension("GLX_SGI_swap_control" ) )
+ {
+ // enable vsync
+ typedef GLint (*glXSwapIntervalProc)(GLint);
+ glXSwapIntervalProc glXSwapInterval = reinterpret_cast<glXSwapIntervalProc>(glXGetProcAddress( reinterpret_cast<const GLubyte*>("glXSwapIntervalSGI") ));
+ if( glXSwapInterval )
+ {
+ TempErrorHandler aLocalErrorHandler(m_aGLWin.dpy, oglErrorHandler);
+
+ errorTriggered = false;
+
+ glXSwapInterval( 1 );
+
+ if( errorTriggered )
+ SAL_WARN("vcl.opengl", "error when trying to set swap interval, NVIDIA or Mesa bug?");
+ else
+ VCL_GL_INFO("set swap interval to 1 (enable vsync)");
+ }
+ }
+
+ bool bRet = InitGL();
+ InitGLDebugging();
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ registerAsCurrent();
+
+ return bRet;
+}
+
+void X11OpenGLContext::makeCurrent()
+{
+ if (isCurrent())
+ return;
+
+ OpenGLZone aZone;
+
+ clearCurrent();
+
+#ifdef DBG_UTIL
+ TempErrorHandler aErrorHandler(m_aGLWin.dpy, unxErrorHandler);
+#endif
+
+ if (m_aGLWin.dpy)
+ {
+ if (!glXMakeCurrent( m_aGLWin.dpy, m_aGLWin.win, m_aGLWin.ctx ))
+ {
+ g_bAnyCurrent = false;
+ SAL_WARN("vcl.opengl", "OpenGLContext::makeCurrent failed "
+ "on drawable " << m_aGLWin.win);
+ return;
+ }
+ g_bAnyCurrent = true;
+ }
+
+ registerAsCurrent();
+}
+
+void X11OpenGLContext::destroyCurrentContext()
+{
+ if(m_aGLWin.ctx)
+ {
+ std::vector<GLXContext>::iterator itr = std::remove( g_vShareList.begin(), g_vShareList.end(), m_aGLWin.ctx );
+ if (itr != g_vShareList.end())
+ g_vShareList.erase(itr);
+
+ glXMakeCurrent(m_aGLWin.dpy, None, nullptr);
+ g_bAnyCurrent = false;
+ if( glGetError() != GL_NO_ERROR )
+ {
+ SAL_WARN("vcl.opengl", "glError: " << glGetError());
+ }
+ glXDestroyContext(m_aGLWin.dpy, m_aGLWin.ctx);
+ m_aGLWin.ctx = nullptr;
+ }
+}
+
+void X11OpenGLContext::init(Display* dpy, Window win, int screen)
+{
+ if (isInitialized())
+ return;
+
+ if (!dpy)
+ return;
+
+ OpenGLZone aZone;
+
+ m_aGLWin.dpy = dpy;
+ m_aGLWin.win = win;
+ m_aGLWin.screen = screen;
+
+ Visual* pVisual = getVisual(dpy, win);
+
+ initGLWindow(pVisual);
+
+ ImplInit();
+}
+
+void X11OpenGLContext::initGLWindow(Visual* pVisual)
+{
+ OpenGLZone aZone;
+
+ // Get visual info
+ {
+ XVisualInfo aTemplate;
+ aTemplate.visualid = XVisualIDFromVisual( pVisual );
+ int nVisuals = 0;
+ XVisualInfo* pInfo = XGetVisualInfo( m_aGLWin.dpy, VisualIDMask, &aTemplate, &nVisuals );
+ if( nVisuals != 1 )
+ SAL_WARN( "vcl.opengl", "match count for visual id is not 1" );
+ m_aGLWin.vi = pInfo;
+ }
+
+ // Check multisample support
+ /* TODO: moggi: This is not necessarily correct in the DBG_UTIL path, as it picks
+ * an FBConfig instead ... */
+ int nSamples = 0;
+ glXGetConfig(m_aGLWin.dpy, m_aGLWin.vi, GLX_SAMPLES, &nSamples);
+ if( nSamples > 0 )
+ m_aGLWin.bMultiSampleSupported = true;
+
+ m_aGLWin.GLXExtensions = glXQueryExtensionsString( m_aGLWin.dpy, m_aGLWin.screen );
+ SAL_INFO("vcl.opengl", "available GLX extensions: " << m_aGLWin.GLXExtensions);
+}
+
+void X11OpenGLContext::initWindow()
+{
+ const SystemEnvData* pChildSysData = nullptr;
+ SystemWindowData winData = generateWinData(mpWindow, false);
+ if( winData.pVisual )
+ {
+ if( !m_pChildWindow )
+ {
+ m_pChildWindow = VclPtr<SystemChildWindow>::Create(mpWindow, 0, &winData, false);
+ }
+ pChildSysData = m_pChildWindow->GetSystemData();
+ }
+
+ if (!m_pChildWindow || !pChildSysData)
+ return;
+
+ InitChildWindow(m_pChildWindow.get());
+
+ m_aGLWin.dpy = static_cast<Display*>(pChildSysData->pDisplay);
+ m_aGLWin.win = pChildSysData->aWindow;
+ m_aGLWin.screen = pChildSysData->nScreen;
+
+ Visual* pVisual = static_cast<Visual*>(pChildSysData->pVisual);
+ initGLWindow(pVisual);
+}
+
+GLX11Window::GLX11Window()
+ : dpy(nullptr)
+ , screen(0)
+ , win(0)
+ , vi(nullptr)
+ , ctx(nullptr)
+{
+}
+
+bool GLX11Window::HasGLXExtension( const char* name ) const
+{
+ for (sal_Int32 i = 0; i != -1;) {
+ if (GLXExtensions.getToken(0, ' ', i) == name) {
+ return true;
+ }
+ }
+ return false;
+}
+
+GLX11Window::~GLX11Window()
+{
+ XFree(vi);
+}
+
+bool GLX11Window::Synchronize(bool bOnoff) const
+{
+ XSynchronize(dpy, bOnoff);
+ return true;
+}
+
+OpenGLContext* X11SalInstance::CreateOpenGLContext()
+{
+ return new X11OpenGLContext;
+}
+
+X11OpenGLSalGraphicsImpl::X11OpenGLSalGraphicsImpl( X11SalGraphics& rParent ):
+ OpenGLSalGraphicsImpl(rParent,rParent.GetGeometryProvider()),
+ mrX11Parent(rParent)
+{
+}
+
+X11OpenGLSalGraphicsImpl::~X11OpenGLSalGraphicsImpl()
+{
+}
+
+void X11OpenGLSalGraphicsImpl::Init()
+{
+ // The m_pFrame and m_pVDev pointers are updated late in X11
+ mpProvider = mrX11Parent.GetGeometryProvider();
+ OpenGLSalGraphicsImpl::Init();
+}
+
+rtl::Reference<OpenGLContext> X11OpenGLSalGraphicsImpl::CreateWinContext()
+{
+ NativeWindowHandleProvider *pProvider = dynamic_cast<NativeWindowHandleProvider*>(mrX11Parent.m_pFrame);
+
+ if( !pProvider )
+ return nullptr;
+
+ sal_uIntPtr aWin = pProvider->GetNativeWindowHandle();
+ rtl::Reference<X11OpenGLContext> xContext = new X11OpenGLContext;
+ xContext->setVCLOnly();
+ xContext->init( mrX11Parent.GetXDisplay(), aWin,
+ mrX11Parent.m_nXScreen.getXScreen() );
+ return rtl::Reference<OpenGLContext>(xContext.get());
+}
+
+void X11OpenGLSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
+{
+ OpenGLSalGraphicsImpl *pImpl = pSrcGraphics ? static_cast< OpenGLSalGraphicsImpl* >(pSrcGraphics->GetImpl()) : static_cast< OpenGLSalGraphicsImpl *>(mrX11Parent.GetImpl());
+ OpenGLSalGraphicsImpl::DoCopyBits( rPosAry, *pImpl );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/opengl/x11/salvd.cxx b/vcl/opengl/x11/salvd.cxx
new file mode 100644
index 000000000..a6ed5602f
--- /dev/null
+++ b/vcl/opengl/x11/salvd.cxx
@@ -0,0 +1,90 @@
+/* -*- 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 <vcl/sysdata.hxx>
+
+#include <unx/salunx.h>
+#include <unx/saldisp.hxx>
+#include <unx/salgdi.h>
+#include <unx/salvd.h>
+
+#include <opengl/x11/salvd.hxx>
+
+void X11SalGraphics::Init( X11OpenGLSalVirtualDevice *pDevice )
+{
+ SalDisplay *pDisplay = pDevice->GetDisplay();
+
+ m_nXScreen = pDevice->GetXScreenNumber();
+ m_pColormap = &pDisplay->GetColormap( m_nXScreen );
+
+ m_pVDev = pDevice;
+ m_pFrame = nullptr;
+
+ bWindow_ = pDisplay->IsDisplay();
+ bVirDev_ = true;
+
+ mxImpl->Init();
+}
+
+X11OpenGLSalVirtualDevice::X11OpenGLSalVirtualDevice( SalGraphics const * pGraphics,
+ long nDX, long nDY,
+ const SystemGraphicsData *pData,
+ std::unique_ptr<X11SalGraphics> pNewGraphics) :
+ mpGraphics(std::move(pNewGraphics)),
+ mbGraphics( false ),
+ mnXScreen( 0 )
+{
+ assert(mpGraphics);
+
+ // TODO Check where a VirtualDevice is created from SystemGraphicsData
+ assert( pData == nullptr ); (void)pData;
+
+ mpDisplay = vcl_sal::getSalDisplay(GetGenericUnixSalData());
+ mnXScreen = pGraphics ? static_cast<X11SalGraphics const *>(pGraphics)->GetScreenNumber() :
+ vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDefaultXScreen();
+ mnWidth = nDX;
+ mnHeight = nDY;
+ mpGraphics->Init( this );
+}
+
+X11OpenGLSalVirtualDevice::~X11OpenGLSalVirtualDevice()
+{
+}
+
+SalGraphics* X11OpenGLSalVirtualDevice::AcquireGraphics()
+{
+ if( mbGraphics )
+ return nullptr;
+
+ if( mpGraphics )
+ mbGraphics = true;
+
+ return mpGraphics.get();
+}
+
+void X11OpenGLSalVirtualDevice::ReleaseGraphics( SalGraphics* )
+{
+ mbGraphics = false;
+}
+
+
+bool X11OpenGLSalVirtualDevice::SetSize( long nDX, long nDY )
+{
+ if( !nDX ) nDX = 1;
+ if( !nDY ) nDY = 1;
+
+ mnWidth = nDX;
+ mnHeight = nDY;
+ if( mpGraphics )
+ mpGraphics->Init( this );
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */