summaryrefslogtreecommitdiffstats
path: root/vcl/opengl/gdiimpl.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /vcl/opengl/gdiimpl.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--vcl/opengl/gdiimpl.cxx2315
1 files changed, 2315 insertions, 0 deletions
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: */