Version 5.2.0.0.beta1, tag libreoffice-5.2.0.0.beta1
[LibreOffice.git] / vcl / opengl / gdiimpl.cxx
blob58b4d62cca2ec388cbb971d1eba6f7e5a7d52128
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "openglgdiimpl.hxx"
22 #include <vcl/gradient.hxx>
23 #include <salframe.hxx>
24 #include "salvd.hxx"
25 #include <basegfx/matrix/b2dhommatrixtools.hxx>
26 #include <basegfx/polygon/b2dlinegeometry.hxx>
27 #include <basegfx/polygon/b2dpolygontools.hxx>
28 #include <basegfx/polygon/b2dpolygontriangulator.hxx>
29 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
30 #include <basegfx/polygon/b2dtrapezoid.hxx>
32 #include <vcl/opengl/OpenGLHelper.hxx>
33 #include "salgdi.hxx"
34 #include "svdata.hxx"
35 #include "opengl/zone.hxx"
36 #include "opengl/salbmp.hxx"
37 #include "opengl/RenderState.hxx"
39 #include <vector>
41 #include <glm/gtc/type_ptr.hpp>
42 #include <glm/gtx/norm.hpp>
44 #include <stdlib.h>
46 class OpenGLFlushIdle : public Idle
48 OpenGLSalGraphicsImpl *m_pImpl;
49 public:
50 explicit OpenGLFlushIdle( OpenGLSalGraphicsImpl *pImpl )
51 : Idle( "gl idle swap" )
52 , m_pImpl( pImpl )
54 // We don't want to be swapping before we've painted.
55 SetPriority( SchedulerPriority::POST_PAINT );
57 virtual ~OpenGLFlushIdle()
60 virtual void Invoke() override
62 m_pImpl->doFlush();
63 SetPriority( SchedulerPriority::HIGHEST );
64 Stop();
68 OpenGLSalGraphicsImpl::OpenGLSalGraphicsImpl(SalGraphics& rParent, SalGeometryProvider *pProvider)
69 : mpContext(nullptr)
70 , mrParent(rParent)
71 , mpProvider(pProvider)
72 , mpProgram(nullptr)
73 , mpFlush(new OpenGLFlushIdle(this))
74 , mbUseScissor(false)
75 , mbUseStencil(false)
76 , mbXORMode(false)
77 , mnLineColor(SALCOLOR_NONE)
78 , mnFillColor(SALCOLOR_NONE)
79 #ifdef DBG_UTIL
80 , mProgramIsSolidColor(false)
81 #endif
82 , mnDrawCount(0)
83 , mnDrawCountAtFlush(0)
84 , mProgramSolidColor(SALCOLOR_NONE)
85 , mProgramSolidTransparency(0.0)
86 , mpAccumulatedTextures(new AccumulatedTextures)
90 OpenGLSalGraphicsImpl::~OpenGLSalGraphicsImpl()
92 if( !IsOffscreen() && mnDrawCountAtFlush != mnDrawCount )
93 VCL_GL_INFO( "Destroying un-flushed on-screen graphics" );
95 delete mpFlush;
97 ReleaseContext();
100 rtl::Reference<OpenGLContext> OpenGLSalGraphicsImpl::GetOpenGLContext()
102 if( !AcquireContext(true) )
103 return nullptr;
104 return mpContext;
107 bool OpenGLSalGraphicsImpl::AcquireContext( bool bForceCreate )
109 mpContext = OpenGLContext::getVCLContext( false );
111 if( !mpContext.is() && mpWindowContext.is() )
113 mpContext = mpWindowContext;
115 else if( bForceCreate && !IsOffscreen() )
117 mpWindowContext = CreateWinContext();
118 mpContext = mpWindowContext;
121 if( !mpContext.is() )
122 mpContext = OpenGLContext::getVCLContext();
124 return mpContext.is();
127 bool OpenGLSalGraphicsImpl::ReleaseContext()
129 mpContext.clear();
131 return true;
134 void OpenGLSalGraphicsImpl::Init()
136 // Our init phase is strange ::Init is called twice for vdevs.
137 // the first time around with a NULL geometry provider.
138 if( !mpProvider )
139 return;
141 // check if we can simply re-use the same context
142 if( mpContext.is() )
144 if( !UseContext( mpContext ) )
145 ReleaseContext();
148 // Always create the offscreen texture
149 if( maOffscreenTex.GetWidth() != GetWidth() ||
150 maOffscreenTex.GetHeight() != GetHeight() )
152 // We don't want to be swapping before we've painted.
153 mpFlush->SetPriority( SchedulerPriority::POST_PAINT );
155 if( maOffscreenTex && // don't work to release empty textures
156 mpContext.is() ) // valid context
158 mpContext->makeCurrent();
159 mpContext->ReleaseFramebuffer( maOffscreenTex );
161 maOffscreenTex = OpenGLTexture();
162 VCL_GL_INFO("::Init - re-size offscreen texture");
165 if( mpWindowContext.is() )
167 mpWindowContext->reset();
168 mpWindowContext.clear();
172 // Currently only used to get windows ordering right.
173 void OpenGLSalGraphicsImpl::DeInit()
175 // tdf#93839:
176 // Our window handles and resources are being free underneath us.
177 // These can be bound into a context, which relies on them. So
178 // let it know. Other eg. VirtualDevice contexts which have
179 // references on and rely on this context continuing to work will
180 // get a shiny new context in AcquireContext:: next PreDraw.
181 if( mpWindowContext.is() )
183 mpWindowContext->reset();
184 mpWindowContext.clear();
186 mpContext.clear();
189 void OpenGLSalGraphicsImpl::PreDraw(XOROption eOpt)
191 FlushDeferredDrawing();
193 InitializePreDrawState(eOpt);
196 void OpenGLSalGraphicsImpl::InitializePreDrawState(XOROption eOpt)
198 OpenGLZone::enter();
200 mnDrawCount++;
202 if( !AcquireContext() )
204 SAL_WARN( "vcl.opengl", "Couldn't acquire context" );
205 return;
208 mpContext->makeCurrent();
209 CHECK_GL_ERROR();
211 CheckOffscreenTexture();
212 CHECK_GL_ERROR();
214 mpContext->state()->viewport(Rectangle(Point(0, 0), Size(GetWidth(), GetHeight())));
216 ImplInitClipRegion();
217 CHECK_GL_ERROR();
219 if (eOpt == IMPLEMENT_XOR && mbXORMode)
221 glEnable(GL_COLOR_LOGIC_OP);
222 CHECK_GL_ERROR();
224 glLogicOp(GL_XOR);
228 void OpenGLSalGraphicsImpl::PostDraw()
230 if (mbXORMode)
232 glDisable(GL_COLOR_LOGIC_OP);
233 CHECK_GL_ERROR();
236 if( mpProgram )
238 mpProgram->Clean();
239 mpProgram = nullptr;
240 #ifdef DBG_UTIL
241 mProgramIsSolidColor = false;
242 #endif
245 assert (maOffscreenTex);
247 // Always queue the flush.
248 if( !IsOffscreen() )
249 flush();
251 OpenGLZone::leave();
254 void OpenGLSalGraphicsImpl::ApplyProgramMatrices(float fPixelOffset)
256 mpProgram->ApplyMatrix(GetWidth(), GetHeight(), fPixelOffset);
259 void OpenGLSalGraphicsImpl::freeResources()
261 // TODO Delete shaders, programs and textures if not shared
262 if( mpContext.is() && mpContext->isInitialized() )
264 VCL_GL_INFO( "freeResources" );
265 mpContext->makeCurrent();
266 FlushDeferredDrawing();
267 mpContext->ReleaseFramebuffer( maOffscreenTex );
269 ReleaseContext();
272 void OpenGLSalGraphicsImpl::ImplSetClipBit( const vcl::Region& rClip, GLuint nMask )
274 mpContext->state()->scissor().disable();
275 mpContext->state()->stencil().enable();
277 VCL_GL_INFO( "Adding complex clip / stencil" );
278 GLuint nStencil = maOffscreenTex.StencilId();
279 if( nStencil == 0 )
281 nStencil = maOffscreenTex.AddStencil();
282 glFramebufferRenderbuffer(
283 GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
284 GL_RENDERBUFFER, nStencil );
285 CHECK_GL_ERROR();
287 // else - we associated the stencil in
288 // AcquireFrameBuffer / AttachTexture
290 CHECK_GL_ERROR();
291 glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE );
292 CHECK_GL_ERROR();
293 glStencilMask( nMask );
294 CHECK_GL_ERROR();
295 glStencilFunc( GL_NEVER, nMask, 0xFF );
296 CHECK_GL_ERROR();
297 glStencilOp( GL_REPLACE, GL_KEEP, GL_KEEP );
298 CHECK_GL_ERROR();
300 glClear( GL_STENCIL_BUFFER_BIT );
301 CHECK_GL_ERROR();
302 if( UseSolid( MAKE_SALCOLOR( 0xFF, 0xFF, 0xFF ) ) )
304 if( rClip.getRegionBand() )
305 DrawRegionBand( *rClip.getRegionBand() );
306 else
307 DrawPolyPolygon( rClip.GetAsB2DPolyPolygon(), true );
310 glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
311 CHECK_GL_ERROR();
312 glStencilMask( 0x00 );
313 CHECK_GL_ERROR();
315 mpContext->state()->stencil().disable();
318 void OpenGLSalGraphicsImpl::ImplInitClipRegion()
320 // make sure the context has the right clipping set
321 if (maClipRegion != mpContext->maClipRegion)
323 mpContext->maClipRegion = maClipRegion;
324 if (mbUseStencil)
326 ImplSetClipBit(maClipRegion, 0x01);
330 if (mbUseScissor)
332 Rectangle aRect(maClipRegion.GetBoundRect());
333 mpContext->state()->scissor().set(aRect.Left(), GetHeight() - aRect.Bottom() - 1, aRect.GetWidth(), aRect.GetHeight());
334 mpContext->state()->scissor().enable();
336 else
338 mpContext->state()->scissor().disable();
341 if (mbUseStencil)
343 glStencilFunc( GL_EQUAL, 1, 0x1 );
344 CHECK_GL_ERROR();
345 mpContext->state()->stencil().enable();
347 else
349 mpContext->state()->stencil().disable();
353 const vcl::Region& OpenGLSalGraphicsImpl::getClipRegion() const
355 return maClipRegion;
358 bool OpenGLSalGraphicsImpl::setClipRegion( const vcl::Region& rClip )
360 if (maClipRegion == rClip)
362 VCL_GL_INFO("::setClipRegion (no change) " << rClip);
363 return true;
366 FlushDeferredDrawing();
368 VCL_GL_INFO("::setClipRegion " << rClip);
370 maClipRegion = rClip;
372 mbUseStencil = false;
373 mbUseScissor = false;
374 if (maClipRegion.IsRectangle())
375 mbUseScissor = true;
376 else if (!maClipRegion.IsEmpty())
377 mbUseStencil = true;
379 return true;
382 // set the clip region to empty
383 void OpenGLSalGraphicsImpl::ResetClipRegion()
385 if (maClipRegion.IsEmpty())
387 VCL_GL_INFO("::ResetClipRegion (no change) ");
388 return;
391 FlushDeferredDrawing();
393 VCL_GL_INFO("::ResetClipRegion");
395 maClipRegion.SetEmpty();
396 mbUseScissor = false;
397 mbUseStencil = false;
400 // get the depth of the device
401 sal_uInt16 OpenGLSalGraphicsImpl::GetBitCount() const
403 return 32;
406 // get the width of the device
407 long OpenGLSalGraphicsImpl::GetGraphicsWidth() const
409 return GetWidth();
412 // set the line color to transparent (= don't draw lines)
413 void OpenGLSalGraphicsImpl::SetLineColor()
415 if( mnLineColor != SALCOLOR_NONE )
417 mnLineColor = SALCOLOR_NONE;
421 // set the line color to a specific color
422 void OpenGLSalGraphicsImpl::SetLineColor( SalColor nSalColor )
424 if( mnLineColor != nSalColor )
426 mnLineColor = nSalColor;
430 // set the fill color to transparent (= don't fill)
431 void OpenGLSalGraphicsImpl::SetFillColor()
433 if( mnFillColor != SALCOLOR_NONE )
435 mnFillColor = SALCOLOR_NONE;
439 // set the fill color to a specific color, shapes will be
440 // filled accordingly
441 void OpenGLSalGraphicsImpl::SetFillColor( SalColor nSalColor )
443 if( mnFillColor != nSalColor )
445 mnFillColor = nSalColor;
449 // enable/disable XOR drawing
450 void OpenGLSalGraphicsImpl::SetXORMode( bool bSet, bool )
452 mbXORMode = bSet;
455 // set line color for raster operations
456 void OpenGLSalGraphicsImpl::SetROPLineColor( SalROPColor /*nROPColor*/ )
460 // set fill color for raster operations
461 void OpenGLSalGraphicsImpl::SetROPFillColor( SalROPColor /*nROPColor*/ )
465 bool OpenGLSalGraphicsImpl::CheckOffscreenTexture()
467 bool bClearTexture = false;
469 VCL_GL_INFO( "Check Offscreen texture" );
471 // Always create the offscreen texture
472 if( maOffscreenTex )
474 if( maOffscreenTex.GetWidth() != GetWidth() ||
475 maOffscreenTex.GetHeight() != GetHeight() )
477 VCL_GL_INFO( "re-size offscreen texture " << maOffscreenTex.Id() );
478 mpFlush->SetPriority( SchedulerPriority::POST_PAINT );
479 mpContext->ReleaseFramebuffer( maOffscreenTex );
480 maOffscreenTex = OpenGLTexture();
484 if( !maOffscreenTex )
486 VCL_GL_INFO( "create texture of size "
487 << GetWidth() << " x " << GetHeight() );
488 maOffscreenTex = OpenGLTexture( GetWidth(), GetHeight() );
489 bClearTexture = true;
492 if( !maOffscreenTex.IsUnique() )
494 GLfloat fWidth = GetWidth();
495 GLfloat fHeight = GetHeight();
496 SalTwoRect aPosAry(0, 0, fWidth, fHeight, 0,0, fWidth, fHeight);
498 // TODO: lfrb: User GL_ARB_copy_image?
499 OpenGLTexture aNewTex = OpenGLTexture( GetWidth(), GetHeight() );
501 mpContext->state()->scissor().disable();
502 mpContext->state()->stencil().disable();
504 mpContext->AcquireFramebuffer( aNewTex );
505 DrawTexture( maOffscreenTex, aPosAry );
506 maOffscreenTex = aNewTex;
508 else
510 mpContext->AcquireFramebuffer( maOffscreenTex );
511 CHECK_GL_ERROR();
513 if( bClearTexture )
515 glDrawBuffer( GL_COLOR_ATTACHMENT0 );
516 #if OSL_DEBUG_LEVEL > 0 // lets have some red debugging background.
517 GLfloat clearColor[4] = { 1.0, 0, 0, 0 };
518 #else
519 GLfloat clearColor[4] = { 1.0, 1.0, 1.0, 0 };
520 #endif
521 glClearBufferfv( GL_COLOR, 0, clearColor );
522 // FIXME: use glClearTexImage if we have it ?
526 assert( maOffscreenTex );
528 CHECK_GL_ERROR();
529 return true;
532 bool OpenGLSalGraphicsImpl::UseProgram( const OUString& rVertexShader, const OUString& rFragmentShader, const OString& preamble )
534 if( mpProgram != nullptr )
535 mpProgram->Clean();
536 mpProgram = mpContext->UseProgram( rVertexShader, rFragmentShader, preamble );
537 #ifdef DBG_UTIL
538 mProgramIsSolidColor = false; // UseSolid() will set to true if needed
539 #endif
540 return ( mpProgram != nullptr );
543 bool OpenGLSalGraphicsImpl::UseSolid( SalColor nColor, sal_uInt8 nTransparency )
545 if( nColor == SALCOLOR_NONE )
546 return false;
547 if (!UseProgram("combinedVertexShader", "combinedFragmentShader"))
548 return false;
549 mpProgram->SetShaderType(DrawShaderType::Normal);
550 mpProgram->SetColor( "color", nColor, nTransparency );
551 #ifdef DBG_UTIL
552 mProgramIsSolidColor = true;
553 #endif
554 mProgramSolidColor = nColor;
555 mProgramSolidTransparency = nTransparency / 100.0;
557 return true;
560 bool OpenGLSalGraphicsImpl::UseSolid( SalColor nColor, double fTransparency )
562 if( nColor == SALCOLOR_NONE )
563 return false;
564 if (!UseProgram("combinedVertexShader", "combinedFragmentShader"))
565 return false;
566 mpProgram->SetShaderType(DrawShaderType::Normal);
567 mpProgram->SetColorf( "color", nColor, fTransparency );
568 #ifdef DBG_UTIL
569 mProgramIsSolidColor = true;
570 #endif
571 mProgramSolidColor = nColor;
572 mProgramSolidTransparency = fTransparency;
573 return true;
576 bool OpenGLSalGraphicsImpl::UseInvert50()
578 if( !UseProgram( "dumbVertexShader", "invert50FragmentShader" ) )
579 return false;
580 return true;
583 bool OpenGLSalGraphicsImpl::UseSolid( SalColor nColor )
585 return UseSolid( nColor, 0.0f );
588 bool OpenGLSalGraphicsImpl::UseInvert( SalInvert nFlags )
590 OpenGLZone aZone;
592 if( ( nFlags & SalInvert::N50 ) ||
593 ( nFlags & SalInvert::TrackFrame ) )
595 // FIXME: Trackframe really should be 2 pix. on/off stipple.
596 if( !UseInvert50() )
597 return false;
598 mpProgram->SetBlendMode( GL_ONE_MINUS_DST_COLOR,
599 GL_ONE_MINUS_SRC_COLOR );
601 else
603 if( !UseSolid( MAKE_SALCOLOR( 255, 255, 255 ) ) )
604 return false;
605 mpProgram->SetBlendMode( GL_ONE_MINUS_DST_COLOR, GL_ZERO );
607 return true;
610 void OpenGLSalGraphicsImpl::DrawPoint( long nX, long nY )
612 OpenGLZone aZone;
614 std::vector<GLfloat> pPoint {
615 GLfloat(nX), GLfloat(nY)
618 ApplyProgramMatrices(0.5f);
619 mpProgram->DrawArrays(GL_POINTS, pPoint);
620 CHECK_GL_ERROR();
623 void OpenGLSalGraphicsImpl::DrawLine( double nX1, double nY1, double nX2, double nY2 )
625 OpenGLZone aZone;
627 std::vector<GLfloat> pPoint {
628 GLfloat(nX1), GLfloat(nY1),
629 GLfloat(nX2), GLfloat(nY2)
632 ApplyProgramMatrices(0.5f);
633 mpProgram->DrawArrays(GL_LINES, pPoint);
634 CHECK_GL_ERROR();
637 namespace
640 inline void addVertex(std::vector<GLfloat>& rVertices, std::vector<GLfloat>& rExtrusionVectors, glm::vec2 point, glm::vec2 extrusionVector, float length)
642 rVertices.push_back(point.x);
643 rVertices.push_back(point.y);
645 rExtrusionVectors.push_back(extrusionVector.x);
646 rExtrusionVectors.push_back(extrusionVector.y);
647 rExtrusionVectors.push_back(length);
650 inline void addVertexPair(std::vector<GLfloat>& rVertices, std::vector<GLfloat>& rExtrusionVectors, const glm::vec2& point, const glm::vec2& extrusionVector, float length)
652 addVertex(rVertices, rExtrusionVectors, point, -extrusionVector, -length);
653 addVertex(rVertices, rExtrusionVectors, point, extrusionVector, length);
656 inline glm::vec2 normalize(const glm::vec2& vector)
658 if (glm::length(vector) > 0.0)
659 return glm::normalize(vector);
660 return vector;
663 } // end anonymous namespace
665 void OpenGLSalGraphicsImpl::DrawLineCap(float x1, float y1, float x2, float y2, css::drawing::LineCap eLineCap, float fLineWidth)
667 if (eLineCap != css::drawing::LineCap_ROUND && eLineCap != css::drawing::LineCap_SQUARE)
668 return;
670 OpenGLZone aZone;
672 const int nRoundCapIteration = 12;
674 std::vector<GLfloat> aVertices;
675 std::vector<GLfloat> aExtrusionVectors;
677 glm::vec2 p1(x1, y1);
678 glm::vec2 p2(x2, y2);
679 glm::vec2 lineVector = normalize(p2 - p1);
680 glm::vec2 normal = glm::vec2(-lineVector.y, lineVector.x);
682 if (eLineCap == css::drawing::LineCap_ROUND)
684 for (int nFactor = 0; nFactor <= nRoundCapIteration; nFactor++)
686 float angle = float(nFactor) * (M_PI / float(nRoundCapIteration));
687 glm::vec2 roundNormal(normal.x * glm::cos(angle) - normal.y * glm::sin(angle),
688 normal.x * glm::sin(angle) + normal.y * glm::cos(angle));
690 addVertexPair(aVertices, aExtrusionVectors, p1, roundNormal, 1.0f);
693 else if (eLineCap == css::drawing::LineCap_SQUARE)
695 glm::vec2 extrudedPoint = p1 + -lineVector * (fLineWidth / 2.0f);
697 addVertexPair(aVertices, aExtrusionVectors, extrudedPoint, normal, 1.0f);
698 addVertexPair(aVertices, aExtrusionVectors, p1, normal, 1.0f);
701 ApplyProgramMatrices(0.5f);
702 mpProgram->SetExtrusionVectors(aExtrusionVectors.data());
703 mpProgram->DrawArrays(GL_TRIANGLE_STRIP, aVertices);
705 CHECK_GL_ERROR();
708 void OpenGLSalGraphicsImpl::DrawLineSegment(float x1, float y1, float x2, float y2)
710 glm::vec2 p1(x1, y1);
711 glm::vec2 p2(x2, y2);
713 std::vector<GLfloat> aPoints;
714 std::vector<GLfloat> aExtrusionVectors;
716 OpenGLZone aZone;
718 glm::vec2 lineVector = normalize(p2 - p1);
719 glm::vec2 normal = glm::vec2(-lineVector.y, lineVector.x);
721 addVertexPair(aPoints, aExtrusionVectors, p1, normal, 1.0f);
722 addVertexPair(aPoints, aExtrusionVectors, p2, normal, 1.0f);
724 ApplyProgramMatrices(0.5f);
725 mpProgram->SetExtrusionVectors(aExtrusionVectors.data());
726 mpProgram->DrawArrays(GL_TRIANGLE_STRIP, aPoints);
728 CHECK_GL_ERROR();
731 /** Draw a simple (non bezier) polyline
733 * OpenGL polyline drawing algorithm inspired by:
734 * - http://mattdesl.svbtle.com/drawing-lines-is-hard
735 * - https://www.mapbox.com/blog/drawing-antialiased-lines/
736 * - https://cesiumjs.org/2013/04/22/Robust-Polyline-Rendering-with-WebGL/
737 * - http://artgrammer.blogspot.si/2011/05/drawing-nearly-perfect-2d-line-segments.html
738 * - http://artgrammer.blogspot.si/2011/07/drawing-polylines-by-tessellation.html
741 void OpenGLSalGraphicsImpl::DrawPolyLine(const basegfx::B2DPolygon& rPolygon, float fLineWidth, basegfx::B2DLineJoin eLineJoin, css::drawing::LineCap eLineCap, float fMiterMinimumAngle)
743 sal_uInt32 nPoints = rPolygon.count();
744 bool bClosed = rPolygon.isClosed();
746 if (!bClosed && nPoints >= 2)
748 // draw begin cap
750 glm::vec2 p1(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY());
751 glm::vec2 p2(rPolygon.getB2DPoint(1).getX(), rPolygon.getB2DPoint(1).getY());
752 DrawLineCap(p1.x, p1.y, p2.x, p2.y, eLineCap, fLineWidth);
755 // draw end cap
757 glm::vec2 p1(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY());
758 glm::vec2 p2(rPolygon.getB2DPoint(nPoints - 2).getX(), rPolygon.getB2DPoint(nPoints - 2).getY());
759 DrawLineCap(p1.x, p1.y, p2.x, p2.y, eLineCap, fLineWidth);
763 if (nPoints == 2 || eLineJoin == basegfx::B2DLineJoin::NONE)
765 // If line joint is NONE or a simple line with 2 points, draw the polyline
766 // each line segment separatly.
767 for (int i = 0; i < int(nPoints) - 1; ++i)
769 glm::vec2 p1(rPolygon.getB2DPoint(i+0).getX(), rPolygon.getB2DPoint(i+0).getY());
770 glm::vec2 p2(rPolygon.getB2DPoint(i+1).getX(), rPolygon.getB2DPoint(i+1).getY());
771 DrawLineSegment(p1.x, p1.y, p2.x, p2.y);
773 if (bClosed)
775 glm::vec2 p1(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY());
776 glm::vec2 p2(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY());
777 DrawLineSegment(p1.x, p1.y, p2.x, p2.y);
780 else if (nPoints > 2)
782 OpenGLZone aZone;
784 int i = 0;
785 int lastPoint = int(nPoints);
787 std::vector<GLfloat> aVertices;
788 std::vector<GLfloat> aExtrusionVectors;
790 // First guess on the size, but we could know relatively exactly
791 // how much vertices we need.
792 aVertices.reserve(nPoints * 4);
793 aExtrusionVectors.reserve(nPoints * 6);
795 // Handle first point
797 glm::vec2 nextLineVector;
798 glm::vec2 previousLineVector;
799 glm::vec2 normal; // perpendicular to the line vector
801 glm::vec2 p0(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY());
802 glm::vec2 p1(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY());
803 glm::vec2 p2(rPolygon.getB2DPoint(1).getX(), rPolygon.getB2DPoint(1).getY());
805 nextLineVector = normalize(p2 - p1);
807 if (!bClosed)
809 normal = glm::vec2(-nextLineVector.y, nextLineVector.x); // make perpendicular
810 addVertexPair(aVertices, aExtrusionVectors, p1, normal, 1.0f);
812 i++; // first point done already
813 lastPoint--; // last point will be calculated separatly from the loop
815 p0 = p1;
816 previousLineVector = nextLineVector;
818 else
820 lastPoint++; // we need to connect last point to first point so one more line segment to calculate
822 previousLineVector = normalize(p1 - p0);
825 for (; i < lastPoint; ++i)
827 int index1 = (i + 0) % nPoints; // loop indices - important when polyline is closed
828 int index2 = (i + 1) % nPoints;
830 p1 = glm::vec2(rPolygon.getB2DPoint(index1).getX(), rPolygon.getB2DPoint(index1).getY());
831 p2 = glm::vec2(rPolygon.getB2DPoint(index2).getX(), rPolygon.getB2DPoint(index2).getY());
833 if (p1 == p2) // skip equal points, normals could div-by-0
834 continue;
836 nextLineVector = normalize(p2 - p1);
838 if (eLineJoin == basegfx::B2DLineJoin::Miter)
840 float angle = std::atan2(previousLineVector.x * nextLineVector.y - previousLineVector.y * nextLineVector.x,
841 previousLineVector.x * nextLineVector.x + previousLineVector.y * nextLineVector.y);
843 angle = F_PI - std::fabs(angle);
845 if (angle < fMiterMinimumAngle)
846 eLineJoin = basegfx::B2DLineJoin::Bevel;
849 if (eLineJoin == basegfx::B2DLineJoin::Miter)
851 // With miter join we calculate the extrusion vector by adding normals of
852 // previous and next line segment. The vector shows the way but we also
853 // need the length (otherwise the line will be deformed). Length factor is
854 // calculated as dot product of extrusion vector and one of the normals.
855 // The value we get is the inverse length (used in the shader):
856 // length = line_width / dot(extrusionVector, normal)
858 normal = glm::vec2(-previousLineVector.y, previousLineVector.x);
860 glm::vec2 tangent = normalize(nextLineVector + previousLineVector);
861 glm::vec2 extrusionVector(-tangent.y, tangent.x);
862 GLfloat length = glm::dot(extrusionVector, normal);
864 addVertexPair(aVertices, aExtrusionVectors, p1, extrusionVector, length);
866 else if (eLineJoin == basegfx::B2DLineJoin::Bevel)
868 // For bevel join we just add 2 additional vertices and use previous
869 // line segment normal and next line segment normal as extrusion vector.
870 // All the magic is done by the fact that we draw triangle strips, so we
871 // cover the joins correctly.
873 glm::vec2 previousNormal = glm::vec2(-previousLineVector.y, previousLineVector.x);
874 glm::vec2 nextNormal = glm::vec2(-nextLineVector.y, nextLineVector.x);
876 addVertexPair(aVertices, aExtrusionVectors, p1, previousNormal, 1.0f);
877 addVertexPair(aVertices, aExtrusionVectors, p1, nextNormal, 1.0f);
879 else if (eLineJoin == basegfx::B2DLineJoin::Round)
881 // For round join we do a similar thing as in bevel, we add more intermediate
882 // vertices and add normals to get extrusion vectors in the between the
883 // both normals.
885 // 3 additional extrusion vectors + normals are enough to make most
886 // line joins look round. Ideally the number of vectors could be
887 // calculated.
889 glm::vec2 previousNormal = glm::vec2(-previousLineVector.y, previousLineVector.x);
890 glm::vec2 nextNormal = glm::vec2(-nextLineVector.y, nextLineVector.x);
892 glm::vec2 middle = normalize(previousNormal + nextNormal);
893 glm::vec2 middleLeft = normalize(previousNormal + middle);
894 glm::vec2 middleRight = normalize(middle + nextNormal);
896 addVertexPair(aVertices, aExtrusionVectors, p1, previousNormal, 1.0f);
897 addVertexPair(aVertices, aExtrusionVectors, p1, middleLeft, 1.0f);
898 addVertexPair(aVertices, aExtrusionVectors, p1, middle, 1.0f);
899 addVertexPair(aVertices, aExtrusionVectors, p1, middleRight, 1.0f);
900 addVertexPair(aVertices, aExtrusionVectors, p1, nextNormal, 1.0f);
902 p0 = p1;
903 previousLineVector = nextLineVector;
906 if (!bClosed)
908 // Create vertices for the last point. There is no line join so just
909 // use the last line segment normal as the extrusion vector.
911 p1 = glm::vec2(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY());
913 normal = glm::vec2(-previousLineVector.y, previousLineVector.x);
915 addVertexPair(aVertices, aExtrusionVectors, p1, normal, 1.0f);
918 ApplyProgramMatrices(0.5f);
919 mpProgram->SetExtrusionVectors(aExtrusionVectors.data());
920 mpProgram->DrawArrays(GL_TRIANGLE_STRIP, aVertices);
922 CHECK_GL_ERROR();
926 bool OpenGLSalGraphicsImpl::UseLine(SalColor nColor, double fTransparency, GLfloat fLineWidth, bool bUseAA)
928 if( nColor == SALCOLOR_NONE )
929 return false;
930 if (!UseProgram("combinedVertexShader", "combinedFragmentShader"))
931 return false;
932 mpProgram->SetShaderType(DrawShaderType::Line);
933 mpProgram->SetColorf("color", nColor, fTransparency);
934 mpProgram->SetUniform1f("line_width", fLineWidth);
935 // The width of the feather - area we make lineary transparent in VS.
936 // Good AA value is 0.5f, no AA if feather 0.0f
937 mpProgram->SetUniform1f("feather", bUseAA ? 0.5f : 0.0f);
938 // We need blending or AA won't work correctly
939 mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
940 #ifdef DBG_UTIL
941 mProgramIsSolidColor = true;
942 #endif
943 mProgramSolidColor = nColor;
944 mProgramSolidTransparency = fTransparency;
945 return true;
948 void OpenGLSalGraphicsImpl::DrawConvexPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry, bool blockAA )
950 OpenGLZone aZone;
952 std::vector<GLfloat> aVertices(nPoints * 2);
953 sal_uInt32 i, j;
955 for( i = 0, j = 0; i < nPoints; i++, j += 2 )
957 aVertices[j] = GLfloat(pPtAry[i].mnX);
958 aVertices[j+1] = GLfloat(pPtAry[i].mnY);
961 ApplyProgramMatrices();
962 mpProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
963 CHECK_GL_ERROR();
965 if( !blockAA && mrParent.getAntiAliasB2DDraw())
967 // Make the edges antialiased by drawing the edge lines again with AA.
968 // TODO: If transparent drawing is set up, drawing the lines themselves twice
969 // may be a problem, if that is a real problem, the polygon areas itself needs to be
970 // masked out for this or something.
971 #ifdef DBG_UTIL
972 assert( mProgramIsSolidColor );
973 #endif
974 SalColor lastSolidColor = mProgramSolidColor;
975 double lastSolidTransparency = mProgramSolidTransparency;
976 if (UseLine(lastSolidColor, lastSolidTransparency, 1.0f, true))
978 for( i = 0; i < nPoints; ++i )
980 const SalPoint& rPt1 = pPtAry[ i ];
981 const SalPoint& rPt2 = pPtAry[ ( i + 1 ) % nPoints ];
982 DrawLineSegment(rPt1.mnX, rPt1.mnY, rPt2.mnX, rPt2.mnY);
984 UseSolid( lastSolidColor, lastSolidTransparency );
989 void OpenGLSalGraphicsImpl::DrawConvexPolygon( const tools::Polygon& rPolygon, bool blockAA )
991 OpenGLZone aZone;
993 sal_uInt16 nPoints = rPolygon.GetSize() - 1;
994 std::vector<GLfloat> aVertices(nPoints * 2);
995 sal_uInt32 i, j;
997 for( i = 0, j = 0; i < nPoints; i++, j += 2 )
999 const Point& rPt = rPolygon.GetPoint( i );
1000 aVertices[j] = GLfloat(rPt.X());
1001 aVertices[j+1] = GLfloat(rPt.Y());
1004 ApplyProgramMatrices();
1005 mpProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
1006 CHECK_GL_ERROR();
1008 if( !blockAA && mrParent.getAntiAliasB2DDraw())
1010 // Make the edges antialiased by drawing the edge lines again with AA.
1011 // TODO: If transparent drawing is set up, drawing the lines themselves twice
1012 // may be a problem, if that is a real problem, the polygon areas itself needs to be
1013 // masked out for this or something.
1014 #ifdef DBG_UTIL
1015 assert( mProgramIsSolidColor );
1016 #endif
1017 SalColor lastSolidColor = mProgramSolidColor;
1018 double lastSolidTransparency = mProgramSolidTransparency;
1019 if (UseLine(lastSolidColor, lastSolidTransparency, 1.0f, true))
1021 for( i = 0; i < nPoints; ++i )
1023 const Point& rPt1 = rPolygon.GetPoint( i );
1024 const Point& rPt2 = rPolygon.GetPoint(( i + 1 ) % nPoints );
1025 DrawLineSegment(rPt1.getX(), rPt1.getY(), rPt2.getX(), rPt2.getY());
1027 UseSolid( lastSolidColor, lastSolidTransparency );
1032 void OpenGLSalGraphicsImpl::DrawTrapezoid( const basegfx::B2DTrapezoid& trapezoid, bool blockAA )
1034 OpenGLZone aZone;
1036 const basegfx::B2DPolygon& rPolygon = trapezoid.getB2DPolygon();
1037 sal_uInt16 nPoints = rPolygon.count();
1038 std::vector<GLfloat> aVertices(nPoints * 2);
1039 sal_uInt32 i, j;
1041 for( i = 0, j = 0; i < nPoints; i++, j += 2 )
1043 const basegfx::B2DPoint& rPt = rPolygon.getB2DPoint( i );
1044 aVertices[j] = GLfloat(rPt.getX());
1045 aVertices[j+1] = GLfloat(rPt.getY());
1048 if (!mpProgram)
1050 SAL_WARN("vcl.opengl", "OpenGLSalGraphicsImpl::DrawTrapezoid: mpProgram is 0");
1051 return;
1054 ApplyProgramMatrices();
1055 mpProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
1056 CHECK_GL_ERROR();
1058 if( !blockAA && mrParent.getAntiAliasB2DDraw())
1060 // Make the edges antialiased by drawing the edge lines again with AA.
1061 // TODO: If transparent drawing is set up, drawing the lines themselves twice
1062 // may be a problem, if that is a real problem, the polygon areas itself needs to be
1063 // masked out for this or something.
1064 #ifdef DBG_UTIL
1065 assert( mProgramIsSolidColor );
1066 #endif
1067 SalColor lastSolidColor = mProgramSolidColor;
1068 double lastSolidTransparency = mProgramSolidTransparency;
1069 if (UseLine(lastSolidColor, lastSolidTransparency, 1.0f, true))
1071 for( i = 0; i < nPoints; ++i )
1073 const basegfx::B2DPoint& rPt1 = rPolygon.getB2DPoint( i );
1074 const basegfx::B2DPoint& rPt2 = rPolygon.getB2DPoint(( i + 1 ) % nPoints );
1075 DrawLineSegment(rPt1.getX(), rPt1.getY(), rPt2.getX(), rPt2.getY());
1077 UseSolid( lastSolidColor, lastSolidTransparency );
1082 void OpenGLSalGraphicsImpl::DrawRect( long nX, long nY, long nWidth, long nHeight )
1084 long nX1( nX );
1085 long nY1( nY );
1086 long nX2( nX + nWidth );
1087 long nY2( nY + nHeight );
1088 const SalPoint aPoints[] = { { nX1, nY2 }, { nX1, nY1 },
1089 { nX2, nY1 }, { nX2, nY2 }};
1091 DrawConvexPolygon( 4, aPoints, true );
1094 void OpenGLSalGraphicsImpl::DrawRect( const Rectangle& rRect )
1096 long nX1( rRect.Left() );
1097 long nY1( rRect.Top() );
1098 long nX2( rRect.Right() );
1099 long nY2( rRect.Bottom() );
1100 const SalPoint aPoints[] = { { nX1, nY2 }, { nX1, nY1 },
1101 { nX2, nY1 }, { nX2, nY2 }};
1103 DrawConvexPolygon( 4, aPoints, true );
1106 void OpenGLSalGraphicsImpl::DrawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
1108 basegfx::B2DPolygon aPolygon;
1110 for( sal_uInt32 i = 0; i < nPoints; i++ )
1111 aPolygon.append( basegfx::B2DPoint( pPtAry[i].mnX, pPtAry[i].mnY ) );
1112 aPolygon.setClosed( true );
1114 if( basegfx::tools::isConvex( aPolygon ) )
1116 if( nPoints > 2L )
1117 DrawConvexPolygon( nPoints, pPtAry );
1119 else
1121 const basegfx::B2DPolyPolygon aPolyPolygon( aPolygon );
1122 DrawPolyPolygon( aPolyPolygon );
1126 void OpenGLSalGraphicsImpl::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon, bool blockAA )
1128 const basegfx::B2DPolyPolygon& aSimplePolyPolygon = ::basegfx::tools::solveCrossovers( rPolyPolygon );
1129 basegfx::B2DTrapezoidVector aB2DTrapVector;
1130 basegfx::tools::trapezoidSubdivide( aB2DTrapVector, aSimplePolyPolygon );
1131 // draw tesselation result
1132 if( aB2DTrapVector.size())
1134 for(basegfx::B2DTrapezoid & i : aB2DTrapVector)
1135 DrawTrapezoid( i, blockAA );
1139 void OpenGLSalGraphicsImpl::DrawRegionBand( const RegionBand& rRegion )
1141 OpenGLZone aZone;
1143 RectangleVector aRects;
1144 std::vector<GLfloat> aVertices;
1145 rRegion.GetRegionRectangles( aRects );
1147 if( aRects.empty() )
1148 return;
1150 #define ADD_VERTICE(pt) \
1151 aVertices.push_back(GLfloat(pt.X())); \
1152 aVertices.push_back(GLfloat(pt.Y()));
1154 for(Rectangle & rRect : aRects)
1156 rRect.Bottom() += 1;
1157 rRect.Right() += 1;
1158 ADD_VERTICE( rRect.TopLeft() );
1159 ADD_VERTICE( rRect.TopRight() );
1160 ADD_VERTICE( rRect.BottomLeft() );
1161 ADD_VERTICE( rRect.BottomLeft() );
1162 ADD_VERTICE( rRect.TopRight() );
1163 ADD_VERTICE( rRect.BottomRight() );
1165 #undef ADD_VERTICE
1167 ApplyProgramMatrices();
1168 mpProgram->DrawArrays(GL_TRIANGLES, aVertices);
1169 CHECK_GL_ERROR();
1172 void OpenGLSalGraphicsImpl::DrawTextureRect( OpenGLTexture& rTexture, const SalTwoRect& rPosAry, bool bInverted )
1174 OpenGLZone aZone;
1176 SAL_INFO("vcl.opengl", "draw texture rect");
1178 GLfloat aTexCoord[8];
1179 rTexture.GetCoord( aTexCoord, rPosAry, bInverted );
1180 mpProgram->SetTextureCoord( aTexCoord );
1181 DrawRect( rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight );
1184 void OpenGLSalGraphicsImpl::DrawTexture( OpenGLTexture& rTexture, const SalTwoRect& pPosAry, bool bInverted )
1186 OpenGLZone aZone;
1188 SAL_INFO("vcl.opengl", "draw texture");
1190 if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
1191 return;
1192 mpProgram->SetShaderType(TextureShaderType::Normal);
1193 mpProgram->SetIdentityTransform("transform");
1194 mpProgram->SetTexture("texture", rTexture);
1195 DrawTextureRect( rTexture, pPosAry, bInverted );
1196 mpProgram->Clean();
1199 namespace {
1201 bool scaleTexture(const rtl::Reference< OpenGLContext > &xContext,
1202 OpenGLTexture& rOutTexture, const double& ixscale, const double& iyscale, OpenGLTexture& rTexture)
1204 int nWidth = rTexture.GetWidth();
1205 int nHeight = rTexture.GetHeight();
1206 int nNewWidth = nWidth / ixscale;
1207 int nNewHeight = nHeight / iyscale;
1209 OpenGLProgram* pProgram = xContext->UseProgram("textureVertexShader", "areaScaleFragmentShader");
1210 if (pProgram == nullptr)
1211 return false;
1213 OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
1214 OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer(aScratchTex);
1216 pProgram->SetUniform1f("xscale", ixscale);
1217 pProgram->SetUniform1f("yscale", iyscale);
1218 pProgram->SetUniform1i("swidth", nWidth);
1219 pProgram->SetUniform1i("sheight", nHeight);
1220 // For converting between <0,nWidth-1> and <0.0,1.0> coordinate systems.
1221 pProgram->SetUniform1f("xsrcconvert", 1.0 / (nWidth - 1));
1222 pProgram->SetUniform1f("ysrcconvert", 1.0 / (nHeight - 1));
1223 pProgram->SetUniform1f("xdestconvert", 1.0 * (nNewWidth - 1));
1224 pProgram->SetUniform1f("ydestconvert", 1.0 * (nNewHeight - 1));
1226 pProgram->SetTexture("sampler", rTexture);
1227 pProgram->DrawTexture(rTexture);
1228 pProgram->Clean();
1230 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
1232 CHECK_GL_ERROR();
1234 rOutTexture = aScratchTex;
1235 return true;
1240 void OpenGLSalGraphicsImpl::DrawTransformedTexture(
1241 OpenGLTexture& rTexture,
1242 OpenGLTexture& rMask,
1243 const basegfx::B2DPoint& rNull,
1244 const basegfx::B2DPoint& rX,
1245 const basegfx::B2DPoint& rY )
1247 OpenGLZone aZone;
1249 std::vector<GLfloat> aVertices = {
1250 0, GLfloat(rTexture.GetHeight()),
1251 0, 0,
1252 GLfloat(rTexture.GetWidth()), 0,
1253 GLfloat(rTexture.GetWidth()), GLfloat(rTexture.GetHeight())
1256 GLfloat aTexCoord[8];
1258 const long nDestWidth = basegfx::fround(basegfx::B2DVector(rX - rNull).getLength());
1259 const long nDestHeight = basegfx::fround(basegfx::B2DVector(rY - rNull).getLength());
1261 // Invisibly small images shouldn't divide by zero.
1262 if( nDestHeight == 0 || nDestWidth == 0 )
1263 return;
1265 // inverted scale ratios
1266 double ixscale = rTexture.GetWidth() / double(nDestWidth);
1267 double iyscale = rTexture.GetHeight() / double(nDestHeight);
1269 // If downscaling at a higher scale ratio, use the area scaling algorithm rather
1270 // than plain OpenGL's scaling (texture mapping), for better results.
1271 // See OpenGLSalBitmap::ImplScaleArea().
1272 bool areaScaling = false;
1273 bool fastAreaScaling = false;
1274 OUString textureFragmentShader;
1275 if( ixscale >= 2 && iyscale >= 2 ) // scale ratio less than 50%
1277 areaScaling = true;
1278 fastAreaScaling = ( ixscale == int( ixscale ) && iyscale == int( iyscale ));
1279 // The generic case has arrays only up to 16 ratio downscaling and is performed in 2 passes,
1280 // when the ratio is in the 16-100 range, which is hopefully enough in practice, but protect
1281 // against buffer overflows in case such an extreme case happens (and in such case the precision
1282 // of the generic algorithm probably doesn't matter anyway).
1283 if( ixscale > 100 || iyscale > 100 )
1284 fastAreaScaling = true;
1285 if( fastAreaScaling )
1286 textureFragmentShader = "areaScaleFastFragmentShader";
1287 else
1288 textureFragmentShader = "areaScaleFragmentShader";
1291 OpenGLTexture aInTexture = rTexture;
1292 OpenGLTexture aInMask = rMask;
1294 // When using the area scaling algorithm we need to reduce the texture size in 2 passes
1295 // in order to not use a big array inside the fragment shader.
1296 if (areaScaling && !fastAreaScaling)
1298 // Perform a first texture downscaling by an inverted scale ratio equal to
1299 // the square root of the whole inverted scale ratio.
1300 if (ixscale > 16 || iyscale > 16)
1302 // The scissor area is set to the current window size in PreDraw,
1303 // so if we do not disable the scissor test, the texture produced
1304 // by the first downscaling is clipped to the current window size.
1305 mpContext->state()->scissor().disable();
1306 mpContext->state()->stencil().disable();
1308 // the square root of the whole inverted scale ratio
1309 double ixscalesqrt = std::floor(std::sqrt(ixscale));
1310 double iyscalesqrt = std::floor(std::sqrt(iyscale));
1311 ixscale /= ixscalesqrt; // second pass inverted x-scale factor
1312 iyscale /= iyscalesqrt; // second pass inverted y-scale factor
1314 scaleTexture(mpContext, aInTexture, ixscalesqrt, iyscalesqrt, rTexture);
1316 if (rMask) // we need to downscale the mask too
1318 scaleTexture(mpContext, aInMask, ixscalesqrt, iyscalesqrt, rMask);
1321 // We need to re-acquire the off-screen texture.
1322 CheckOffscreenTexture();
1323 CHECK_GL_ERROR();
1325 // Re-enable scissor and stencil tests if needed.
1326 if (mbUseScissor)
1327 mpContext->state()->scissor().enable();
1329 if (mbUseStencil)
1330 mpContext->state()->stencil().enable();
1334 if( aInMask )
1336 if( !UseProgram( "transformedTextureVertexShader",
1337 textureFragmentShader.isEmpty() ? "maskedTextureFragmentShader" : textureFragmentShader,
1338 "#define MASKED" ) )
1339 return;
1340 mpProgram->SetTexture( "mask", aInMask );
1341 GLfloat aMaskCoord[8];
1342 aInMask.GetWholeCoord(aMaskCoord);
1343 mpProgram->SetMaskCoord(aMaskCoord);
1344 aInMask.SetFilter( GL_LINEAR );
1345 mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1347 else
1349 if( !UseProgram( "transformedTextureVertexShader",
1350 textureFragmentShader.isEmpty() ? "textureFragmentShader" : textureFragmentShader ) )
1351 return;
1354 if(areaScaling)
1356 int nWidth = aInTexture.GetWidth();
1357 int nHeight = aInTexture.GetHeight();
1359 // From OpenGLSalBitmap::ImplScaleArea().
1360 if (fastAreaScaling && nWidth && nHeight)
1362 mpProgram->SetUniform1i( "xscale", ixscale );
1363 mpProgram->SetUniform1i( "yscale", iyscale );
1364 mpProgram->SetUniform1f( "xstep", 1.0 / nWidth );
1365 mpProgram->SetUniform1f( "ystep", 1.0 / nHeight );
1366 mpProgram->SetUniform1f( "ratio", 1.0 / ( ixscale * iyscale ));
1368 else if (nHeight > 1 && nWidth > 1)
1370 mpProgram->SetUniform1f( "xscale", ixscale );
1371 mpProgram->SetUniform1f( "yscale", iyscale );
1372 mpProgram->SetUniform1i( "swidth", nWidth );
1373 mpProgram->SetUniform1i( "sheight", nHeight );
1374 // For converting between <0,nWidth-1> and <0.0,1.0> coordinate systems.
1375 mpProgram->SetUniform1f( "xsrcconvert", 1.0 / ( nWidth - 1 ));
1376 mpProgram->SetUniform1f( "ysrcconvert", 1.0 / ( nHeight - 1 ));
1377 mpProgram->SetUniform1f( "xdestconvert", 1.0 * (( nWidth / ixscale ) - 1 ));
1378 mpProgram->SetUniform1f( "ydestconvert", 1.0 * (( nHeight / iyscale ) - 1 ));
1382 ApplyProgramMatrices();
1383 mpProgram->SetUniform2f( "viewport", GetWidth(), GetHeight() );
1384 // Here, in order to get the correct transformation we need to pass the original texture,
1385 // since it has been used for initializing the rectangle vertices.
1386 mpProgram->SetTransform( "transform", rTexture, rNull, rX, rY );
1387 aInTexture.GetWholeCoord(aTexCoord);
1388 mpProgram->SetTexture("sampler", aInTexture);
1389 aInTexture.SetFilter(GL_LINEAR);
1390 mpProgram->SetTextureCoord( aTexCoord );
1391 mpProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
1393 CHECK_GL_ERROR();
1394 mpProgram->Clean();
1397 void OpenGLSalGraphicsImpl::DrawAlphaTexture( OpenGLTexture& rTexture, const SalTwoRect& rPosAry, bool bInverted, bool bPremultiplied )
1399 OpenGLZone aZone;
1401 if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
1402 return;
1403 mpProgram->SetShaderType(TextureShaderType::Normal);
1404 mpProgram->SetIdentityTransform("transform");
1405 mpProgram->SetTexture("texture", rTexture);
1406 mpProgram->SetBlendMode( bPremultiplied ? GL_ONE : GL_SRC_ALPHA,
1407 GL_ONE_MINUS_SRC_ALPHA );
1408 DrawTextureRect( rTexture, rPosAry, bInverted );
1409 mpProgram->Clean();
1412 void OpenGLSalGraphicsImpl::DrawTextureDiff( OpenGLTexture& rTexture, OpenGLTexture& rMask, const SalTwoRect& rPosAry, bool bInverted )
1414 OpenGLZone aZone;
1416 if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
1417 return;
1418 mpProgram->SetShaderType(TextureShaderType::Diff);
1419 mpProgram->SetIdentityTransform("transform");
1420 mpProgram->SetTexture( "texture", rTexture );
1421 mpProgram->SetTexture( "mask", rMask );
1422 mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1424 GLfloat aMaskCoord[8];
1425 rMask.GetCoord(aMaskCoord, rPosAry, bInverted);
1426 mpProgram->SetMaskCoord(aMaskCoord);
1428 DrawTextureRect( rTexture, rPosAry, bInverted );
1429 mpProgram->Clean();
1432 void OpenGLSalGraphicsImpl::DrawTextureWithMask( OpenGLTexture& rTexture, OpenGLTexture& rMask, const SalTwoRect& rPosAry )
1434 OpenGLZone aZone;
1436 if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
1437 return;
1438 mpProgram->SetShaderType(TextureShaderType::Masked);
1439 mpProgram->SetIdentityTransform("transform");
1440 mpProgram->SetTexture( "texture", rTexture );
1441 mpProgram->SetTexture( "mask", rMask );
1442 mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1444 GLfloat aTexCoord[8];
1445 rTexture.GetCoord(aTexCoord, rPosAry);
1446 mpProgram->SetTextureCoord(aTexCoord);
1448 GLfloat aMaskCoord[8];
1449 rMask.GetCoord(aMaskCoord, rPosAry);
1450 mpProgram->SetMaskCoord(aMaskCoord);
1452 DrawRect(rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight);
1453 mpProgram->Clean();
1456 void OpenGLSalGraphicsImpl::DrawBlendedTexture( OpenGLTexture& rTexture, OpenGLTexture& rMask, OpenGLTexture& rAlpha, const SalTwoRect& rPosAry )
1458 OpenGLZone aZone;
1460 if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
1461 return;
1462 mpProgram->SetShaderType(TextureShaderType::Blend);
1463 mpProgram->SetTexture( "texture", rTexture );
1464 mpProgram->SetTexture( "mask", rMask );
1465 mpProgram->SetTexture( "alpha", rAlpha );
1467 GLfloat aAlphaCoord[8];
1468 rAlpha.GetCoord(aAlphaCoord, rPosAry);
1469 mpProgram->SetAlphaCoord(aAlphaCoord);
1471 GLfloat aMaskCoord[8];
1472 rMask.GetCoord(aMaskCoord, rPosAry);
1473 mpProgram->SetMaskCoord(aMaskCoord);
1475 mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1476 DrawTextureRect( rTexture, rPosAry );
1477 mpProgram->Clean();
1480 void OpenGLSalGraphicsImpl::DrawMask( OpenGLTexture& rMask, SalColor nMaskColor, const SalTwoRect& pPosAry )
1482 OpenGLZone aZone;
1484 if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
1485 return;
1486 mpProgram->SetShaderType(TextureShaderType::MaskedColor);
1487 mpProgram->SetIdentityTransform("transform");
1488 mpProgram->SetColor( "color", nMaskColor, 0 );
1489 mpProgram->SetTexture("texture", rMask);
1490 mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1491 DrawTextureRect( rMask, pPosAry );
1492 mpProgram->Clean();
1495 void OpenGLSalGraphicsImpl::DeferredTextDraw(OpenGLTexture& rTexture, SalColor aMaskColor, const SalTwoRect& rPosAry)
1497 mpAccumulatedTextures->insert(rTexture, aMaskColor, rPosAry);
1500 void OpenGLSalGraphicsImpl::FlushDeferredDrawing()
1502 if (mpAccumulatedTextures->empty())
1503 return;
1505 InitializePreDrawState();
1507 VCL_GL_INFO("FlushDeferredDrawing");
1509 OpenGLZone aZone;
1511 #if 0 // Draw a background rect under text for debugging - same color shows text from the same texture
1512 static sal_uInt8 r = 0xBE;
1513 static sal_uInt8 g = 0xF0;
1514 static sal_uInt8 b = 0xFF;
1515 static std::unordered_map<GLuint, Color> aColorForTextureMap;
1518 for (auto& rPair : mpAccumulatedTextures->getAccumulatedTexturesMap())
1520 OpenGLTexture& rTexture = rPair.second->maTexture;
1521 Color aUseColor;
1522 if (aColorForTextureMap.find(rTexture.Id()) == aColorForTextureMap.end())
1524 Color aColor(r, g, b);
1525 sal_uInt16 h,s,br;
1526 aColor.RGBtoHSB(h, s, br);
1527 aColor = Color::HSBtoRGB((h + 40) % 360, s, br);
1528 r = aColor.GetRed();
1529 g = aColor.GetGreen();
1530 b = aColor.GetBlue();
1531 aColorForTextureMap[rTexture.Id()] = aColor;
1533 aUseColor = aColorForTextureMap[rTexture.Id()];
1535 if (!UseSolid(MAKE_SALCOLOR(aUseColor.GetRed(), aUseColor.GetGreen(), aUseColor.GetBlue())))
1536 return;
1537 for (auto rColorTwoRectPair: rPair.second->maColorTextureDrawParametersMap)
1539 TextureDrawParameters& rParameters = rColorTwoRectPair.second;
1540 ApplyProgramMatrices();
1541 mpProgram->SetTextureCoord(rParameters.maTextureCoords.data());
1542 mpProgram->DrawArrays(GL_TRIANGLES, rParameters.maVertices);
1545 #endif
1547 if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
1548 return;
1549 mpProgram->SetShaderType(TextureShaderType::MaskedColor);
1550 mpProgram->SetIdentityTransform("transform");
1551 mpProgram->SetBlendMode(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1553 for (auto& rPair : mpAccumulatedTextures->getAccumulatedTexturesMap())
1555 OpenGLTexture& rTexture = rPair.second->maTexture;
1556 mpProgram->SetTexture("texture", rTexture);
1557 for (auto& rColorTwoRectPair: rPair.second->maColorTextureDrawParametersMap)
1559 mpProgram->SetColor("color", rColorTwoRectPair.first, 0);
1560 TextureDrawParameters& rParameters = rColorTwoRectPair.second;
1561 ApplyProgramMatrices();
1562 mpProgram->SetTextureCoord(rParameters.maTextureCoords.data());
1563 mpProgram->DrawArrays(GL_TRIANGLES, rParameters.maVertices);
1566 mpProgram->Clean();
1567 mpAccumulatedTextures->clear();
1569 PostDraw();
1572 void OpenGLSalGraphicsImpl::DrawLinearGradient( const Gradient& rGradient, const Rectangle& rRect )
1574 OpenGLZone aZone;
1576 if( !UseProgram( "textureVertexShader", "linearGradientFragmentShader" ) )
1577 return;
1578 Color aStartCol = rGradient.GetStartColor();
1579 Color aEndCol = rGradient.GetEndColor();
1580 long nFactor = rGradient.GetStartIntensity();
1581 mpProgram->SetColorWithIntensity( "start_color", aStartCol, nFactor );
1582 nFactor = rGradient.GetEndIntensity();
1583 mpProgram->SetColorWithIntensity( "end_color", aEndCol, nFactor );
1585 Rectangle aBoundRect;
1586 Point aCenter;
1587 rGradient.GetBoundRect( rRect, aBoundRect, aCenter );
1588 tools::Polygon aPoly( aBoundRect );
1589 aPoly.Rotate( aCenter, rGradient.GetAngle() % 3600 );
1591 GLfloat aTexCoord[8] = { 0, 1, 1, 1, 1, 0, 0, 0 };
1592 GLfloat fMin = 1.0 - 100.0 / (100.0 - rGradient.GetBorder());
1593 aTexCoord[5] = aTexCoord[7] = fMin;
1594 mpProgram->SetTextureCoord( aTexCoord );
1595 DrawConvexPolygon( aPoly, true );
1598 void OpenGLSalGraphicsImpl::DrawAxialGradient( const Gradient& rGradient, const Rectangle& rRect )
1600 OpenGLZone aZone;
1602 if( !UseProgram( "textureVertexShader", "linearGradientFragmentShader" ) )
1603 return;
1604 Color aStartCol = rGradient.GetStartColor();
1605 Color aEndCol = rGradient.GetEndColor();
1606 long nFactor = rGradient.GetStartIntensity();
1607 mpProgram->SetColorWithIntensity( "start_color", aStartCol, nFactor );
1608 nFactor = rGradient.GetEndIntensity();
1609 mpProgram->SetColorWithIntensity( "end_color", aEndCol, nFactor );
1612 * Draw two rectangles with linear gradient.
1614 * 1 *---* 2
1615 * | /|
1616 * | / | Points 0 and 3 have start color
1617 * 0 |/__| 3 Points 1, 2, 4 and 5 have end color
1618 * |\ |
1619 * | \ |
1620 * | \|
1621 * 5 *---* 4
1625 Rectangle aRect;
1626 Point aCenter;
1627 rGradient.GetBoundRect( rRect, aRect, aCenter );
1629 // determine points 0 and 3
1630 Point aPt0( aRect.Left(), (aRect.Top() + aRect.Bottom() + 1) / 2 );
1631 Point aPt3( aRect.Right(), (aRect.Top() + aRect.Bottom() + 1) / 2 );
1633 tools::Polygon aPoly( 7 );
1634 aPoly.SetPoint( aPt0, 0 );
1635 aPoly.SetPoint( aRect.TopLeft(), 1 );
1636 aPoly.SetPoint( aRect.TopRight(), 2 );
1637 aPoly.SetPoint( aPt3, 3 );
1638 aPoly.SetPoint( aRect.BottomRight(), 4 );
1639 aPoly.SetPoint( aRect.BottomLeft(), 5 );
1640 aPoly.SetPoint( aPt0, 6 );
1641 aPoly.Rotate( aCenter, rGradient.GetAngle() % 3600 );
1643 GLfloat aTexCoord[12] = { 0, 1, 1, 0, 2, 0, 3, 1, 4, 0, 5, 0 };
1644 GLfloat fMin = 1.0 - 100.0 / (100.0 - rGradient.GetBorder());
1645 aTexCoord[3] = aTexCoord[5] = aTexCoord[9] = aTexCoord[11] = fMin;
1646 mpProgram->SetTextureCoord( aTexCoord );
1647 DrawConvexPolygon( aPoly, true );
1650 void OpenGLSalGraphicsImpl::DrawRadialGradient( const Gradient& rGradient, const Rectangle& rRect )
1652 OpenGLZone aZone;
1654 if( !UseProgram( "textureVertexShader", "radialGradientFragmentShader" ) )
1655 return;
1656 Color aStartCol = rGradient.GetStartColor();
1657 Color aEndCol = rGradient.GetEndColor();
1658 long nFactor = rGradient.GetStartIntensity();
1659 mpProgram->SetColorWithIntensity( "start_color", aStartCol, nFactor );
1660 nFactor = rGradient.GetEndIntensity();
1661 mpProgram->SetColorWithIntensity( "end_color", aEndCol, nFactor );
1663 Rectangle aRect;
1664 Point aCenter;
1665 rGradient.GetBoundRect( rRect, aRect, aCenter );
1667 // adjust coordinates so that radius has distance equals to 1.0
1668 double fRadius = aRect.GetWidth() / 2.0f;
1669 GLfloat fWidth = rRect.GetWidth() / fRadius;
1670 GLfloat fHeight = rRect.GetHeight() / fRadius;
1671 GLfloat aTexCoord[8] = { 0, 0, 0, fHeight, fWidth, fHeight, fWidth, 0 };
1672 mpProgram->SetTextureCoord( aTexCoord );
1673 mpProgram->SetUniform2f( "center", (aCenter.X() - rRect.Left()) / fRadius,
1674 (aCenter.Y() - rRect.Top()) / fRadius );
1675 DrawRect( rRect );
1679 // draw --> LineColor and FillColor and RasterOp and ClipRegion
1680 void OpenGLSalGraphicsImpl::drawPixel( long nX, long nY )
1682 VCL_GL_INFO( "::drawPixel" );
1683 if( mnLineColor != SALCOLOR_NONE )
1685 PreDraw( XOROption::IMPLEMENT_XOR );
1686 if( UseSolid( mnLineColor ) )
1687 DrawPoint( nX, nY );
1688 PostDraw();
1692 void OpenGLSalGraphicsImpl::drawPixel( long nX, long nY, SalColor nSalColor )
1694 VCL_GL_INFO( "::drawPixel" );
1695 if( nSalColor != SALCOLOR_NONE )
1697 PreDraw( XOROption::IMPLEMENT_XOR );
1698 if( UseSolid( nSalColor ) )
1699 DrawPoint( nX, nY );
1700 PostDraw();
1704 void OpenGLSalGraphicsImpl::drawLine( long nX1, long nY1, long nX2, long nY2 )
1706 VCL_GL_INFO( "::drawLine" );
1707 if( mnLineColor != SALCOLOR_NONE )
1709 PreDraw( XOROption::IMPLEMENT_XOR );
1710 if (UseLine(mnLineColor, 0.0, 1.0f, mrParent.getAntiAliasB2DDraw()))
1711 DrawLineSegment(nX1, nY1, nX2, nY2);
1712 PostDraw();
1716 void OpenGLSalGraphicsImpl::drawRect( long nX, long nY, long nWidth, long nHeight )
1718 VCL_GL_INFO( "::drawRect" );
1719 PreDraw( XOROption::IMPLEMENT_XOR );
1721 if( UseSolid( mnFillColor ) )
1722 DrawRect( nX, nY, nWidth, nHeight );
1724 if( UseSolid( mnLineColor ) )
1726 GLfloat fX1(nX);
1727 GLfloat fY1(nY);
1728 GLfloat fX2(nX + nWidth - 1);
1729 GLfloat fY2(nY + nHeight - 1);
1731 std::vector<GLfloat> pPoints {
1732 fX1, fY1,
1733 fX2, fY1,
1734 fX2, fY2,
1735 fX1, fY2
1738 ApplyProgramMatrices(0.5f);
1739 mpProgram->DrawArrays(GL_LINE_LOOP, pPoints);
1740 CHECK_GL_ERROR();
1743 PostDraw();
1746 void OpenGLSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const SalPoint* pPtAry )
1748 basegfx::B2DPolygon aPoly;
1749 aPoly.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
1750 for (sal_uInt32 i = 1; i < nPoints; ++i)
1751 aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
1752 aPoly.setClosed(false);
1754 drawPolyLine(aPoly, 0.0, basegfx::B2DVector(1.0, 1.0), basegfx::B2DLineJoin::Miter,
1755 css::drawing::LineCap_BUTT, 15.0 * F_PI180 /*default*/);
1758 void OpenGLSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
1760 basegfx::B2DPolygon aPoly;
1761 aPoly.append(basegfx::B2DPoint(pPtAry->mnX, pPtAry->mnY), nPoints);
1762 for (sal_uInt32 i = 1; i < nPoints; ++i)
1763 aPoly.setB2DPoint(i, basegfx::B2DPoint(pPtAry[i].mnX, pPtAry[i].mnY));
1765 drawPolyPolygon(basegfx::B2DPolyPolygon(aPoly), 0.0);
1768 void OpenGLSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPointCounts, PCONSTSALPOINT* pPtAry )
1770 basegfx::B2DPolyPolygon aPolyPoly;
1771 for(sal_uInt32 nPolygon = 0; nPolygon < nPoly; ++nPolygon)
1773 sal_uInt32 nPoints = pPointCounts[nPolygon];
1774 if (nPoints)
1776 PCONSTSALPOINT pPoints = pPtAry[nPolygon];
1777 basegfx::B2DPolygon aPoly;
1778 aPoly.append( basegfx::B2DPoint(pPoints->mnX, pPoints->mnY), nPoints);
1779 for (sal_uInt32 i = 1; i < nPoints; ++i)
1780 aPoly.setB2DPoint(i, basegfx::B2DPoint( pPoints[i].mnX, pPoints[i].mnY));
1782 aPolyPoly.append(aPoly);
1786 drawPolyPolygon(aPolyPoly, 0.0);
1789 bool OpenGLSalGraphicsImpl::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency )
1791 VCL_GL_INFO( "::drawPolyPolygon trans " << fTransparency );
1792 if( rPolyPolygon.count() <= 0 )
1793 return true;
1795 PreDraw( XOROption::IMPLEMENT_XOR );
1797 if( UseSolid( mnFillColor, fTransparency ) )
1798 DrawPolyPolygon( rPolyPolygon );
1800 if( mnLineColor != mnFillColor && UseSolid( mnLineColor, fTransparency ))
1802 basegfx::B2DTrapezoidVector aB2DTrapVector;
1803 basegfx::tools::createLineTrapezoidFromB2DPolyPolygon( aB2DTrapVector, rPolyPolygon );
1804 for(basegfx::B2DTrapezoid & i : aB2DTrapVector)
1805 DrawTrapezoid( i );
1808 PostDraw();
1810 return true;
1813 bool OpenGLSalGraphicsImpl::drawPolyLine(
1814 const basegfx::B2DPolygon& rPolygon,
1815 double fTransparency,
1816 const basegfx::B2DVector& rLineWidth,
1817 basegfx::B2DLineJoin eLineJoin,
1818 css::drawing::LineCap eLineCap,
1819 double fMiterMinimumAngle)
1821 VCL_GL_INFO( "::drawPolyLine trans " << fTransparency );
1822 if( mnLineColor == SALCOLOR_NONE )
1823 return true;
1825 const bool bIsHairline = (rLineWidth.getX() == rLineWidth.getY()) && (rLineWidth.getX() <= 1.2);
1826 const float fLineWidth = bIsHairline ? 1.0f : rLineWidth.getX();
1828 PreDraw(XOROption::IMPLEMENT_XOR);
1830 if (UseLine(mnLineColor, 0.0f, fLineWidth, mrParent.getAntiAliasB2DDraw()))
1832 basegfx::B2DPolygon aPolygon(rPolygon);
1834 if (aPolygon.areControlPointsUsed())
1835 aPolygon = aPolygon.getDefaultAdaptiveSubdivision();
1837 DrawPolyLine(aPolygon, fLineWidth, eLineJoin, eLineCap, fMiterMinimumAngle);
1839 PostDraw();
1841 return true;
1844 bool OpenGLSalGraphicsImpl::drawPolyLineBezier(
1845 sal_uInt32 /*nPoints*/,
1846 const SalPoint* /*pPtAry*/,
1847 const sal_uInt8* /*pFlgAry*/ )
1849 return false;
1852 bool OpenGLSalGraphicsImpl::drawPolygonBezier(
1853 sal_uInt32 /*nPoints*/,
1854 const SalPoint* /*pPtAry*/,
1855 const sal_uInt8* /*pFlgAry*/ )
1857 return false;
1860 bool OpenGLSalGraphicsImpl::drawPolyPolygonBezier(
1861 sal_uInt32 /*nPoly*/,
1862 const sal_uInt32* /*pPoints*/,
1863 const SalPoint* const* /*pPtAry*/,
1864 const sal_uInt8* const* /*pFlgAry*/ )
1866 return false;
1869 // CopyArea --> No RasterOp, but ClipRegion
1870 void OpenGLSalGraphicsImpl::copyArea(
1871 long nDestX, long nDestY,
1872 long nSrcX, long nSrcY,
1873 long nSrcWidth, long nSrcHeight, bool /*bWindowInvalidate*/ )
1875 VCL_GL_INFO( "::copyArea " << nSrcX << "," << nSrcY << " >> " << nDestX << "," << nDestY << " (" << nSrcWidth << "," << nSrcHeight << ")" );
1876 OpenGLTexture aTexture;
1877 SalTwoRect aPosAry(0, 0, nSrcWidth, nSrcHeight, nDestX, nDestY, nSrcWidth, nSrcHeight);
1879 PreDraw();
1880 // TODO offscreen case
1881 aTexture = OpenGLTexture( nSrcX, GetHeight() - nSrcY - nSrcHeight,
1882 nSrcWidth, nSrcHeight );
1883 DrawTexture( aTexture, aPosAry );
1884 PostDraw();
1887 // CopyBits and DrawBitmap --> RasterOp and ClipRegion
1888 // CopyBits() --> pSrcGraphics == NULL, then CopyBits on same Graphics
1889 void OpenGLSalGraphicsImpl::DoCopyBits( const SalTwoRect& rPosAry, OpenGLSalGraphicsImpl& rImpl )
1891 VCL_GL_INFO( "::copyBits" );
1893 rImpl.FlushDeferredDrawing();
1895 if( !rImpl.maOffscreenTex )
1897 VCL_GL_INFO( "::copyBits - skipping copy of un-initialized framebuffer contents of size "
1898 << rImpl.GetWidth() << "x" << rImpl.GetHeight() );
1899 return;
1902 if( &rImpl == this &&
1903 (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
1904 (rPosAry.mnSrcHeight == rPosAry.mnDestHeight))
1906 // short circuit if there is nothing to do
1907 if( (rPosAry.mnSrcX == rPosAry.mnDestX) &&
1908 (rPosAry.mnSrcY == rPosAry.mnDestY))
1909 return;
1910 // use copyArea() if source and destination context are identical
1911 copyArea( rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnSrcX, rPosAry.mnSrcY,
1912 rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, false/*bWindowInvalidate*/ );
1913 return;
1916 PreDraw();
1917 DrawTexture( rImpl.maOffscreenTex, rPosAry );
1918 PostDraw();
1921 void OpenGLSalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap )
1923 // check that carefully only in the debug mode
1924 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBitmap));
1926 OpenGLZone aZone;
1928 const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSalBitmap);
1929 OpenGLTexture& rTexture = rBitmap.GetTexture();
1931 VCL_GL_INFO( "::drawBitmap" );
1932 PreDraw();
1933 DrawTexture( rTexture, rPosAry );
1934 PostDraw();
1937 void OpenGLSalGraphicsImpl::drawBitmap(
1938 const SalTwoRect& rPosAry,
1939 const SalBitmap& rSalBitmap,
1940 const SalBitmap& rMaskBitmap )
1942 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBitmap));
1943 assert(dynamic_cast<const OpenGLSalBitmap*>(&rMaskBitmap));
1945 OpenGLZone aZone;
1947 const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSalBitmap);
1948 const OpenGLSalBitmap& rMask = static_cast<const OpenGLSalBitmap&>(rMaskBitmap);
1949 OpenGLTexture& rTexture( rBitmap.GetTexture() );
1950 OpenGLTexture& rMaskTex( rMask.GetTexture() );
1952 VCL_GL_INFO( "::drawBitmap with MASK" );
1953 PreDraw();
1954 DrawTextureWithMask( rTexture, rMaskTex, rPosAry );
1955 PostDraw();
1958 void OpenGLSalGraphicsImpl::drawMask(
1959 const SalTwoRect& rPosAry,
1960 const SalBitmap& rSalBitmap,
1961 SalColor nMaskColor )
1963 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBitmap));
1965 OpenGLZone aZone;
1967 const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSalBitmap);
1968 OpenGLTexture& rTexture( rBitmap.GetTexture() );
1970 VCL_GL_INFO( "::drawMask" );
1971 PreDraw();
1972 DrawMask( rTexture, nMaskColor, rPosAry );
1973 PostDraw();
1976 SalBitmap* OpenGLSalGraphicsImpl::getBitmap( long nX, long nY, long nWidth, long nHeight )
1978 OpenGLZone aZone;
1980 OpenGLSalBitmap* pBitmap = new OpenGLSalBitmap;
1981 VCL_GL_INFO( "::getBitmap " << nX << "," << nY <<
1982 " " << nWidth << "x" << nHeight );
1983 //TODO really needed?
1984 PreDraw();
1985 if( !pBitmap->Create( maOffscreenTex, nX, nY, nWidth, nHeight ) )
1987 delete pBitmap;
1988 pBitmap = nullptr;
1990 PostDraw();
1991 return pBitmap;
1994 SalColor OpenGLSalGraphicsImpl::getPixel( long nX, long nY )
1996 FlushDeferredDrawing();
1998 char pixel[3] = { 0, 0, 0 };
2000 PreDraw( XOROption::IMPLEMENT_XOR );
2001 nY = GetHeight() - nY - 1;
2002 glReadPixels( nX, nY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixel);
2003 CHECK_GL_ERROR();
2004 PostDraw();
2006 return MAKE_SALCOLOR( pixel[0], pixel[1], pixel[2] );
2009 // invert --> ClipRegion (only Windows or VirDevs)
2010 void OpenGLSalGraphicsImpl::invert(
2011 long nX, long nY,
2012 long nWidth, long nHeight,
2013 SalInvert nFlags)
2015 PreDraw();
2017 if( UseInvert( nFlags ) )
2019 if( nFlags & SalInvert::TrackFrame )
2020 { // FIXME: could be more efficient.
2021 DrawRect( nX, nY, nWidth, 1 );
2022 DrawRect( nX, nY + nHeight, nWidth, 1 );
2023 DrawRect( nX, nY, 1, nHeight );
2024 DrawRect( nX + nWidth, nY, 1, nHeight );
2026 else
2027 DrawRect( nX, nY, nWidth, nHeight );
2030 PostDraw();
2033 void OpenGLSalGraphicsImpl::invert( sal_uInt32 nPoints, const SalPoint* pPtAry, SalInvert nFlags )
2035 PreDraw();
2037 if( UseInvert( nFlags ) )
2038 DrawPolygon( nPoints, pPtAry );
2040 PostDraw();
2043 bool OpenGLSalGraphicsImpl::drawEPS(
2044 long /*nX*/, long /*nY*/,
2045 long /*nWidth*/, long /*nHeight*/,
2046 void* /*pPtr*/,
2047 sal_uLong /*nSize*/ )
2049 return false;
2052 bool OpenGLSalGraphicsImpl::blendBitmap(
2053 const SalTwoRect& rPosAry,
2054 const SalBitmap& rSalBitmap )
2056 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBitmap));
2058 OpenGLZone aZone;
2060 const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSalBitmap);
2061 OpenGLTexture& rTexture( rBitmap.GetTexture() );
2063 VCL_GL_INFO( "::blendBitmap" );
2064 PreDraw();
2066 if (!UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader"))
2067 return true;
2069 mpProgram->SetShaderType(TextureShaderType::Normal);
2070 mpProgram->SetIdentityTransform("transform");
2071 mpProgram->SetTexture("texture", rTexture);
2072 mpProgram->SetBlendMode(GL_ZERO, GL_SRC_COLOR);
2073 DrawTextureRect(rTexture, rPosAry);
2074 mpProgram->Clean();
2076 PostDraw();
2077 return true;
2080 bool OpenGLSalGraphicsImpl::blendAlphaBitmap(
2081 const SalTwoRect& rPosAry,
2082 const SalBitmap& rSalSrcBitmap,
2083 const SalBitmap& rSalMaskBitmap,
2084 const SalBitmap& rSalAlphaBitmap )
2086 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalSrcBitmap));
2087 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalMaskBitmap));
2088 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalAlphaBitmap));
2090 OpenGLZone aZone;
2092 const OpenGLSalBitmap& rSrcBitmap = static_cast<const OpenGLSalBitmap&>(rSalSrcBitmap);
2093 const OpenGLSalBitmap& rMaskBitmap = static_cast<const OpenGLSalBitmap&>(rSalMaskBitmap);
2094 const OpenGLSalBitmap& rAlphaBitmap = static_cast<const OpenGLSalBitmap&>(rSalAlphaBitmap);
2095 OpenGLTexture& rTexture( rSrcBitmap.GetTexture() );
2096 OpenGLTexture& rMask( rMaskBitmap.GetTexture() );
2097 OpenGLTexture& rAlpha( rAlphaBitmap.GetTexture() );
2099 VCL_GL_INFO( "::blendAlphaBitmap" );
2100 PreDraw();
2101 DrawBlendedTexture( rTexture, rMask, rAlpha, rPosAry );
2102 PostDraw();
2103 return true;
2106 /** Render bitmap with alpha channel
2108 @param rSourceBitmap
2109 Source bitmap to blit
2111 @param rAlphaBitmap
2112 Alpha channel to use for blitting
2114 @return true, if the operation succeeded, and false
2115 otherwise. In this case, clients should try to emulate alpha
2116 compositing themselves
2118 bool OpenGLSalGraphicsImpl::drawAlphaBitmap(
2119 const SalTwoRect& rPosAry,
2120 const SalBitmap& rSalBitmap,
2121 const SalBitmap& rAlphaBitmap )
2123 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBitmap));
2124 assert(dynamic_cast<const OpenGLSalBitmap*>(&rAlphaBitmap));
2126 OpenGLZone aZone;
2128 const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSalBitmap);
2129 const OpenGLSalBitmap& rAlpha = static_cast<const OpenGLSalBitmap&>(rAlphaBitmap);
2130 OpenGLTexture& rTexture( rBitmap.GetTexture() );
2131 OpenGLTexture& rAlphaTex( rAlpha.GetTexture() );
2133 VCL_GL_INFO( "::drawAlphaBitmap" );
2134 PreDraw();
2135 DrawTextureWithMask( rTexture, rAlphaTex, rPosAry );
2136 PostDraw();
2137 return true;
2140 /** draw transformed bitmap (maybe with alpha) where Null, X, Y define the coordinate system */
2141 bool OpenGLSalGraphicsImpl::drawTransformedBitmap(
2142 const basegfx::B2DPoint& rNull,
2143 const basegfx::B2DPoint& rX,
2144 const basegfx::B2DPoint& rY,
2145 const SalBitmap& rSrcBitmap,
2146 const SalBitmap* pAlphaBitmap)
2148 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSrcBitmap));
2149 assert(!pAlphaBitmap || dynamic_cast<const OpenGLSalBitmap*>(pAlphaBitmap));
2151 OpenGLZone aZone;
2153 const OpenGLSalBitmap& rBitmap = static_cast<const OpenGLSalBitmap&>(rSrcBitmap);
2154 const OpenGLSalBitmap* pMaskBitmap = static_cast<const OpenGLSalBitmap*>(pAlphaBitmap);
2155 OpenGLTexture& rTexture( rBitmap.GetTexture() );
2156 OpenGLTexture aMask; // no texture
2158 if( pMaskBitmap != nullptr )
2159 aMask = pMaskBitmap->GetTexture();
2161 VCL_GL_INFO( "::drawTransformedBitmap" );
2162 PreDraw();
2163 DrawTransformedTexture( rTexture, aMask, rNull, rX, rY );
2164 PostDraw();
2166 return true;
2169 /** Render solid rectangle with given transparency
2171 @param nTransparency
2172 Transparency value (0-255) to use. 0 blits and opaque, 255 a
2173 fully transparent rectangle
2175 bool OpenGLSalGraphicsImpl::drawAlphaRect(
2176 long nX, long nY,
2177 long nWidth, long nHeight,
2178 sal_uInt8 nTransparency )
2180 VCL_GL_INFO( "::drawAlphaRect" );
2181 if( mnFillColor != SALCOLOR_NONE && nTransparency < 100 )
2183 PreDraw();
2184 UseSolid( mnFillColor, nTransparency );
2185 DrawRect( nX, nY, nWidth, nHeight );
2186 PostDraw();
2189 return true;
2192 bool OpenGLSalGraphicsImpl::drawGradient(const tools::PolyPolygon& rPolyPoly,
2193 const Gradient& rGradient)
2195 Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
2197 VCL_GL_INFO( "::drawGradient" );
2199 if( aBoundRect.IsEmpty() )
2200 return true;
2202 if( rGradient.GetStyle() != GradientStyle_LINEAR &&
2203 rGradient.GetStyle() != GradientStyle_AXIAL &&
2204 rGradient.GetStyle() != GradientStyle_RADIAL )
2205 return false;
2207 aBoundRect.Left()--;
2208 aBoundRect.Top()--;
2209 aBoundRect.Right()++;
2210 aBoundRect.Bottom()++;
2212 PreDraw( XOROption::IMPLEMENT_XOR );
2214 #define FIXME_BROKEN_STENCIL_FOR_GRADIENTS 0
2215 #if FIXME_BROKEN_STENCIL_FOR_GRADIENTS
2216 ImplSetClipBit( vcl::Region( rPolyPoly ), 0x02 );
2217 if( mbUseStencil )
2219 glEnable( GL_STENCIL_TEST );
2220 CHECK_GL_ERROR();
2221 glStencilFunc( GL_EQUAL, 3, 0xFF );
2222 CHECK_GL_ERROR();
2224 else
2226 glEnable( GL_STENCIL_TEST );
2227 CHECK_GL_ERROR();
2228 glStencilFunc( GL_EQUAL, 2, 0xFF );
2229 CHECK_GL_ERROR();
2231 #endif
2233 // if border >= 100%, draw solid rectangle with start color
2234 if( rGradient.GetBorder() >= 100.0 )
2236 Color aCol = rGradient.GetStartColor();
2237 long nF = rGradient.GetStartIntensity();
2238 if( UseSolid( MAKE_SALCOLOR( aCol.GetRed() * nF / 100,
2239 aCol.GetGreen() * nF / 100,
2240 aCol.GetBlue() * nF / 100 ) ) )
2241 DrawRect( aBoundRect );
2243 else if( rGradient.GetStyle() == GradientStyle_LINEAR )
2245 DrawLinearGradient( rGradient, aBoundRect );
2247 else if( rGradient.GetStyle() == GradientStyle_AXIAL )
2249 DrawAxialGradient( rGradient, aBoundRect );
2251 else if( rGradient.GetStyle() == GradientStyle_RADIAL )
2253 DrawRadialGradient( rGradient, aBoundRect );
2256 #if FIXME_BROKEN_STENCIL_FOR_GRADIENTS
2257 if( !mbUseStencil )
2259 glDisable( GL_STENCIL_TEST );
2260 CHECK_GL_ERROR();
2262 #endif
2263 PostDraw();
2265 return true;
2268 void OpenGLSalGraphicsImpl::flush()
2270 FlushDeferredDrawing();
2272 if( IsOffscreen() )
2273 return;
2275 if( !Application::IsInExecute() )
2277 // otherwise nothing would trigger idle rendering
2278 doFlush();
2280 else if( !mpFlush->IsActive() )
2281 mpFlush->Start();
2284 void OpenGLSalGraphicsImpl::doFlush()
2286 FlushDeferredDrawing();
2288 if (OpenGLContext::hasCurrent())
2290 mpContext->state()->scissor().disable();
2291 mpContext->state()->stencil().disable();
2294 if( IsOffscreen() )
2295 return;
2297 if( !maOffscreenTex )
2299 VCL_GL_INFO( "doFlush - odd no texture !" );
2300 return;
2303 if( mnDrawCountAtFlush == mnDrawCount )
2305 VCL_GL_INFO( "eliding redundant doFlush, no drawing since last!" );
2306 return;
2309 mnDrawCountAtFlush = mnDrawCount;
2311 OpenGLZone aZone;
2313 VCL_GL_INFO( "doFlush" );
2315 if( !mpWindowContext.is() )
2317 // ensure everything is released from the old context.
2318 OpenGLContext::clearCurrent();
2319 mpWindowContext = CreateWinContext();
2320 VCL_GL_INFO( "late creation of window context" );
2323 assert( mpWindowContext.is() );
2325 // Interesting ! -> this destroys a context [ somehow ] ...
2326 mpWindowContext->makeCurrent();
2327 CHECK_GL_ERROR();
2329 VCL_GL_INFO( "doFlush - acquire default framebuffer" );
2331 mpWindowContext->state()->sync();
2333 mpWindowContext->AcquireDefaultFramebuffer();
2334 CHECK_GL_ERROR();
2336 mpWindowContext->state()->viewport(Rectangle(Point(0, 0), Size(GetWidth(), GetHeight())));
2337 mpWindowContext->state()->scissor().disable();
2338 mpWindowContext->state()->stencil().disable();
2340 #if OSL_DEBUG_LEVEL > 0 // random background glClear
2341 glClearColor((float)rand()/RAND_MAX, (float)rand()/RAND_MAX,
2342 (float)rand()/RAND_MAX, 1.0);
2343 #else
2344 glClearColor(1.0, 1.0, 1.0, 1.0);
2345 #endif
2346 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
2347 CHECK_GL_ERROR();
2349 VCL_GL_INFO( "Texture height " << maOffscreenTex.GetHeight() << " vs. window height " << GetHeight() );
2351 OpenGLProgram *pProgram =
2352 mpWindowContext->UseProgram("combinedTextureVertexShader", "combinedTextureFragmentShader", "// flush shader\n" ); // flush helps profiling
2353 if( !pProgram )
2354 VCL_GL_INFO( "Can't compile simple copying shader !" );
2355 else
2357 pProgram->SetShaderType(TextureShaderType::Normal);
2358 pProgram->SetIdentityTransform("transform");
2359 pProgram->SetTexture("texture", maOffscreenTex);
2361 SalTwoRect aPosAry( 0, 0, maOffscreenTex.GetWidth(), maOffscreenTex.GetHeight(),
2362 0, 0, maOffscreenTex.GetWidth(), maOffscreenTex.GetHeight() );
2364 GLfloat aTexCoord[8];
2365 maOffscreenTex.GetCoord( aTexCoord, aPosAry );
2366 pProgram->SetTextureCoord( aTexCoord );
2368 GLfloat fWidth( maOffscreenTex.GetWidth() );
2369 GLfloat fHeight( maOffscreenTex.GetHeight() );
2370 std::vector<GLfloat> aVertices {
2371 0, fHeight,
2372 0, 0,
2373 fWidth, 0,
2374 fWidth, fHeight
2377 pProgram->ApplyMatrix(GetWidth(), GetHeight(), 0.0);
2378 pProgram->DrawArrays(GL_TRIANGLE_FAN, aVertices);
2380 pProgram->Clean();
2382 maOffscreenTex.Unbind();
2384 static bool bNoSwap = getenv("SAL_GL_NO_SWAP");
2385 if (!bNoSwap)
2386 mpWindowContext->swapBuffers();
2389 VCL_GL_INFO( "doFlush - end." );
2392 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */