From d18ad8a7fb3257001a5045e11f3f770a48a7fa69 Mon Sep 17 00:00:00 2001 From: Tomaž Vajngerl Date: Wed, 24 Feb 2016 00:44:58 +0100 Subject: opengl: shader based polyline rendering - fixes tdf#97137 for OGL Adds native opengl polyline rendering to draw polylines, line joins and line caps as triangle strips. The vertex shader allows for the dynamic line width by calculating the correct vertex posiitons, and the fragment shader is used for anti-aliasing. Change-Id: If7982c828cae1fae59c57194c8ac77e5ad7f1d26 --- basegfx/source/polygon/b2dlinegeometry.cxx | 29 +++ include/basegfx/polygon/b2dlinegeometry.hxx | 6 + vcl/Package_opengl.mk | 2 + vcl/inc/opengl/program.hxx | 4 +- vcl/inc/openglgdiimpl.hxx | 4 + vcl/opengl/gdiimpl.cxx | 364 ++++++++++++++++++++++++---- vcl/opengl/lineFragmentShader.glsl | 36 +++ vcl/opengl/lineVertexShader.glsl | 37 +++ vcl/opengl/program.cxx | 10 +- 9 files changed, 442 insertions(+), 50 deletions(-) create mode 100644 vcl/opengl/lineFragmentShader.glsl create mode 100644 vcl/opengl/lineVertexShader.glsl diff --git a/basegfx/source/polygon/b2dlinegeometry.cxx b/basegfx/source/polygon/b2dlinegeometry.cxx index 5527dd0d0a0f..33dd5707a4fb 100644 --- a/basegfx/source/polygon/b2dlinegeometry.cxx +++ b/basegfx/source/polygon/b2dlinegeometry.cxx @@ -779,6 +779,35 @@ namespace basegfx namespace tools { + B2DPolygon polygonSubdivide(const B2DPolygon& rCandidate, double fMaxAllowedAngle, double fMaxPartOfEdge) + { + if(fMaxAllowedAngle > F_PI2) + { + fMaxAllowedAngle = F_PI2; + } + else if(fMaxAllowedAngle < 0.01 * F_PI2) + { + fMaxAllowedAngle = 0.01 * F_PI2; + } + + if(fMaxPartOfEdge > 1.0) + { + fMaxPartOfEdge = 1.0; + } + else if(fMaxPartOfEdge < 0.01) + { + fMaxPartOfEdge = 0.01; + } + + B2DPolygon aCandidate(rCandidate); + const double fMaxCos(cos(fMaxAllowedAngle)); + + aCandidate.removeDoublePoints(); + aCandidate = subdivideToSimple(aCandidate, fMaxCos * fMaxCos, fMaxPartOfEdge * fMaxPartOfEdge); + + return aCandidate; + } + B2DPolyPolygon createAreaGeometry( const B2DPolygon& rCandidate, double fHalfLineWidth, diff --git a/include/basegfx/polygon/b2dlinegeometry.hxx b/include/basegfx/polygon/b2dlinegeometry.hxx index 77cafdc6c8ef..d336456d0cd3 100644 --- a/include/basegfx/polygon/b2dlinegeometry.hxx +++ b/include/basegfx/polygon/b2dlinegeometry.hxx @@ -137,6 +137,12 @@ namespace basegfx double fMaxAllowedAngle = (12.5 * F_PI180), double fMaxPartOfEdge = 0.4, double fMiterMinimumAngle = (15.0 * F_PI180)); + + BASEGFX_DLLPUBLIC B2DPolygon polygonSubdivide( + const B2DPolygon& rCandidate, + double fMaxAllowedAngle = (12.5 * F_PI180), + double fMaxPartOfEdge = 0.4); + } // end of namespace tools } // end of namespace basegfx diff --git a/vcl/Package_opengl.mk b/vcl/Package_opengl.mk index b8851df57625..a0f6e9a27128 100644 --- a/vcl/Package_opengl.mk +++ b/vcl/Package_opengl.mk @@ -21,6 +21,8 @@ $(eval $(call gb_Package_add_files,vcl_opengl_shader,$(LIBO_ETC_FOLDER)/opengl,\ invert50FragmentShader.glsl \ convolutionFragmentShader.glsl \ linearGradientFragmentShader.glsl \ + lineFragmentShader.glsl \ + lineVertexShader.glsl \ maskFragmentShader.glsl \ maskedTextureVertexShader.glsl \ maskedTextureFragmentShader.glsl \ diff --git a/vcl/inc/opengl/program.hxx b/vcl/inc/opengl/program.hxx index 5de3c1bdbe5f..780cba72380f 100644 --- a/vcl/inc/opengl/program.hxx +++ b/vcl/inc/opengl/program.hxx @@ -37,6 +37,7 @@ private: GLuint mnTexCoordAttrib; GLuint mnAlphaCoordAttrib; GLuint mnMaskCoordAttrib; + GLuint mnNormalAttrib; TextureList maTextures; bool mbBlending; @@ -59,6 +60,7 @@ public: void SetTextureCoord( const GLvoid* pData ); void SetAlphaCoord( const GLvoid* pData ); void SetMaskCoord(const GLvoid* pData); + void SetExtrusionVectors(const GLvoid* pData); void SetUniform1f( const OString& rName, GLfloat v1 ); void SetUniform2f( const OString& rName, GLfloat v1, GLfloat v2 ); @@ -80,7 +82,7 @@ public: bool DrawTexture( const OpenGLTexture& rTexture ); protected: - void SetVertexAttrib( GLuint& rAttrib, const OString& rName, const GLvoid* pData ); + void SetVertexAttrib( GLuint& rAttrib, const OString& rName, const GLvoid* pData, GLint nSize = 2 ); GLuint GetUniformLocation( const OString& rName ); }; diff --git a/vcl/inc/openglgdiimpl.hxx b/vcl/inc/openglgdiimpl.hxx index 3b629005c712..f3247f9f8d22 100644 --- a/vcl/inc/openglgdiimpl.hxx +++ b/vcl/inc/openglgdiimpl.hxx @@ -113,6 +113,7 @@ public: bool UseSolid( SalColor nColor ); bool UseSolidAA( SalColor nColor, double fTransparency ); bool UseSolidAA( SalColor nColor ); + bool UseLine(SalColor nColor, double fTransparency, GLfloat fLineWidth, bool bUseAA); bool UseInvert50(); bool UseInvert(SalInvert nFlags); @@ -127,6 +128,9 @@ public: void DrawRect( long nX, long nY, long nWidth, long nHeight ); void DrawRect( const Rectangle& rRect ); void DrawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ); + void DrawLineSegment(float x1, float y1, float x2, float y2); + void DrawLineCap(float x1, float y1, float x2, float y2, css::drawing::LineCap eLineCap, float fLineWidth); + void DrawPolyLine( const basegfx::B2DPolygon& rPolygon, float fLineWidth, basegfx::B2DLineJoin eLineJoin, css::drawing::LineCap eLineCap); void DrawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon, bool blockAA = false ); void DrawRegionBand( const RegionBand& rRegion ); void DrawTextureRect( OpenGLTexture& rTexture, const SalTwoRect& rPosAry, bool bInverted = false ); diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx index 6828efb9b49d..2efac9741908 100644 --- a/vcl/opengl/gdiimpl.cxx +++ b/vcl/opengl/gdiimpl.cxx @@ -37,6 +37,9 @@ #include +#include +#include + #include class OpenGLFlushIdle : public Idle @@ -624,6 +627,311 @@ void OpenGLSalGraphicsImpl::DrawLine( double nX1, double nY1, double nX2, double CHECK_GL_ERROR(); } +namespace +{ + +inline void addVertex(std::vector& rVertices, std::vector& rExtrusionVectors, glm::vec2 point, glm::vec2 extrusionVector, float length) +{ + rVertices.push_back(point.x); + rVertices.push_back(point.y); + + rExtrusionVectors.push_back(extrusionVector.x); + rExtrusionVectors.push_back(extrusionVector.y); + rExtrusionVectors.push_back(length); +} + +inline void addVertexPair(std::vector& rVertices, std::vector& rExtrusionVectors, glm::vec2 point, glm::vec2 extrusionVector, float length) +{ + addVertex(rVertices, rExtrusionVectors, point, -extrusionVector, -length); + addVertex(rVertices, rExtrusionVectors, point, extrusionVector, length); +} + +inline glm::vec2 normalize(const glm::vec2& vector) +{ + if (glm::length(vector) > 0.0) + return glm::normalize(vector); + return vector; +} + +} // end anonymous namespace + +void OpenGLSalGraphicsImpl::DrawLineCap(float x1, float y1, float x2, float y2, css::drawing::LineCap eLineCap, float fLineWidth) +{ + if (eLineCap != css::drawing::LineCap_ROUND && eLineCap != css::drawing::LineCap_SQUARE) + return; + + OpenGLZone aZone; + + const int nRoundCapIteration = 12; + + std::vector aVertices; + std::vector aExtrusionVectors; + + glm::vec2 p1(x1, y1); + glm::vec2 p2(x2, y2); + glm::vec2 lineVector = normalize(p2 - p1); + glm::vec2 normal = glm::vec2(-lineVector.y, lineVector.x); + + if (eLineCap == css::drawing::LineCap_ROUND) + { + for (int nFactor = 0; nFactor <= nRoundCapIteration; nFactor++) + { + float angle = float(nFactor) * (M_PI / float(nRoundCapIteration)); + glm::vec2 roundNormal(normal.x * glm::cos(angle) - normal.y * glm::sin(angle), + normal.x * glm::sin(angle) + normal.y * glm::cos(angle)); + + addVertexPair(aVertices, aExtrusionVectors, p1, roundNormal, 1.0f); + } + } + else if (eLineCap == css::drawing::LineCap_SQUARE) + { + glm::vec2 extrudedPoint = p1 + -lineVector * (fLineWidth / 2.0f); + + addVertexPair(aVertices, aExtrusionVectors, extrudedPoint, normal, 1.0f); + addVertexPair(aVertices, aExtrusionVectors, p1, normal, 1.0f); + } + + ApplyProgramMatrices(0.0f); + mpProgram->SetExtrusionVectors(aExtrusionVectors.data()); + mpProgram->SetVertices(aVertices.data()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, aVertices.size() / 2); + + CHECK_GL_ERROR(); +} + +void OpenGLSalGraphicsImpl::DrawLineSegment(float x1, float y1, float x2, float y2) +{ + glm::vec2 p1(x1, y1); + glm::vec2 p2(x2, y2); + + if (p1.x == p2.x && p1.y == p2.y) + return; + + std::vector aPoints; + std::vector aExtrusionVectors; + + OpenGLZone aZone; + + glm::vec2 lineVector = normalize(p2 - p1); + glm::vec2 normal = glm::vec2(-lineVector.y, lineVector.x); + + addVertexPair(aPoints, aExtrusionVectors, p1, normal, 1.0f); + addVertexPair(aPoints, aExtrusionVectors, p2, normal, 1.0f); + + ApplyProgramMatrices(0.0f); + mpProgram->SetExtrusionVectors(aExtrusionVectors.data()); + mpProgram->SetVertices(aPoints.data()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, aPoints.size() / 2); + + CHECK_GL_ERROR(); +} + +/** Draw a simple (non bezier) polyline + * + * OpenGL polyline drawing algorithm inspired by: + * - http://mattdesl.svbtle.com/drawing-lines-is-hard + * - https://www.mapbox.com/blog/drawing-antialiased-lines/ + * - https://cesiumjs.org/2013/04/22/Robust-Polyline-Rendering-with-WebGL/ + * - http://artgrammer.blogspot.si/2011/05/drawing-nearly-perfect-2d-line-segments.html + * - http://artgrammer.blogspot.si/2011/07/drawing-polylines-by-tessellation.html + * + */ +void OpenGLSalGraphicsImpl::DrawPolyLine(const basegfx::B2DPolygon& rPolygon, float fLineWidth, basegfx::B2DLineJoin eLineJoin, css::drawing::LineCap eLineCap) +{ + sal_uInt32 nPoints = rPolygon.count(); + bool bClosed = rPolygon.isClosed(); + + if (!bClosed && nPoints >= 2) + { + // draw begin cap + { + glm::vec2 p1(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY()); + glm::vec2 p2(rPolygon.getB2DPoint(1).getX(), rPolygon.getB2DPoint(1).getY()); + DrawLineCap(p1.x, p1.y, p2.x, p2.y, eLineCap, fLineWidth); + } + + // draw end cap + { + glm::vec2 p1(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY()); + glm::vec2 p2(rPolygon.getB2DPoint(nPoints - 2).getX(), rPolygon.getB2DPoint(nPoints - 2).getY()); + DrawLineCap(p1.x, p1.y, p2.x, p2.y, eLineCap, fLineWidth); + } + } + + if (nPoints == 2 || eLineJoin == basegfx::B2DLineJoin::NONE) + { + // If line joint is NONE or a simple line with 2 points, draw the polyline + // each line segment separatly. + for (int i = 0; i < int(nPoints) - 1; ++i) + { + glm::vec2 p1(rPolygon.getB2DPoint(i+0).getX(), rPolygon.getB2DPoint(i+0).getY()); + glm::vec2 p2(rPolygon.getB2DPoint(i+1).getX(), rPolygon.getB2DPoint(i+1).getY()); + DrawLineSegment(p1.x, p1.y, p2.x, p2.y); + } + if (bClosed) + { + glm::vec2 p1(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY()); + glm::vec2 p2(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY()); + DrawLineSegment(p1.x, p1.y, p2.x, p2.y); + } + } + else if (nPoints > 2) + { + OpenGLZone aZone; + + int i = 0; + int lastPoint = int(nPoints); + + std::vector aVertices; + std::vector aExtrusionVectors; + + // First guess on the size, but we could know relatively exactly + // how much vertices we need. + aVertices.reserve(nPoints * 4); + aExtrusionVectors.reserve(nPoints * 6); + + // Handle first point + + glm::vec2 nextLineVector; + glm::vec2 previousLineVector; + glm::vec2 normal; // perpendicular to the line vector + + glm::vec2 p0(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY()); + glm::vec2 p1(rPolygon.getB2DPoint(0).getX(), rPolygon.getB2DPoint(0).getY()); + glm::vec2 p2(rPolygon.getB2DPoint(1).getX(), rPolygon.getB2DPoint(1).getY()); + + nextLineVector = normalize(p2 - p1); + + if (!bClosed) + { + normal = glm::vec2(-nextLineVector.y, nextLineVector.x); // make perpendicular + addVertexPair(aVertices, aExtrusionVectors, p1, normal, 1.0f); + + i++; // first point done already + lastPoint--; // last point will be calculated separatly from the loop + + p0 = p1; + previousLineVector = nextLineVector; + } + else + { + lastPoint++; // we need to connect last point to first point so one more line segment to calculate + + previousLineVector = normalize(p1 - p0); + } + + for (; i < lastPoint; ++i) + { + int index1 = (i + 0) % nPoints; // loop indices - important when polyline is closed + int index2 = (i + 1) % nPoints; + + p1 = glm::vec2(rPolygon.getB2DPoint(index1).getX(), rPolygon.getB2DPoint(index1).getY()); + p2 = glm::vec2(rPolygon.getB2DPoint(index2).getX(), rPolygon.getB2DPoint(index2).getY()); + + if (p1 == p2) // skip equal points, normals could div-by-0 + continue; + + nextLineVector = normalize(p2 - p1); + + if (eLineJoin == basegfx::B2DLineJoin::Miter) + { + // With miter join we calculate the extrusion vector by adding normals of + // previous and next line segment. The vector shows the way but we also + // need the length (otherwise the line will be deformed). Length factor is + // calculated as dot product of extrusion vector and one of the normals. + // The value we get is the inverse length (used in the shader): + // length = line_width / dot(extrusionVector, normal) + + normal = glm::vec2(-previousLineVector.y, previousLineVector.x); + + glm::vec2 tangent = normalize(nextLineVector + previousLineVector); + glm::vec2 extrusionVector(-tangent.y, tangent.x); + GLfloat length = glm::dot(extrusionVector, normal); + + addVertexPair(aVertices, aExtrusionVectors, p1, extrusionVector, length); + } + else if (eLineJoin == basegfx::B2DLineJoin::Bevel) + { + // For bevel join we just add 2 additional vertices and use previous + // line segment normal and next line segment normal as extrusion vector. + // All the magic is done by the fact that we draw triangle strips, so we + // cover the joins correctly. + + glm::vec2 previousNormal = glm::vec2(-previousLineVector.y, previousLineVector.x); + glm::vec2 nextNormal = glm::vec2(-nextLineVector.y, nextLineVector.x); + + addVertexPair(aVertices, aExtrusionVectors, p1, previousNormal, 1.0f); + addVertexPair(aVertices, aExtrusionVectors, p1, nextNormal, 1.0f); + } + else if (eLineJoin == basegfx::B2DLineJoin::Round) + { + // For round join we do a similar thing as in bevel, we add more intermediate + // vertices and add normals to get extrusion vectors in the between the + // both normals. + + // 3 additional extrusion vectors + normals are enough to make most + // line joins look round. Ideally the number of vectors could be + // calculated. + + glm::vec2 previousNormal = glm::vec2(-previousLineVector.y, previousLineVector.x); + glm::vec2 nextNormal = glm::vec2(-nextLineVector.y, nextLineVector.x); + + glm::vec2 middle = normalize(previousNormal + nextNormal); + glm::vec2 middleLeft = normalize(previousNormal + middle); + glm::vec2 middleRight = normalize(middle + nextNormal); + + addVertexPair(aVertices, aExtrusionVectors, p1, previousNormal, 1.0f); + addVertexPair(aVertices, aExtrusionVectors, p1, middleLeft, 1.0f); + addVertexPair(aVertices, aExtrusionVectors, p1, middle, 1.0f); + addVertexPair(aVertices, aExtrusionVectors, p1, middleRight, 1.0f); + addVertexPair(aVertices, aExtrusionVectors, p1, nextNormal, 1.0f); + } + p0 = p1; + previousLineVector = nextLineVector; + } + + if (!bClosed) + { + // Create vertices for the last point. There is no line join so just + // use the last line segment normal as the extrusion vector. + + p1 = glm::vec2(rPolygon.getB2DPoint(nPoints - 1).getX(), rPolygon.getB2DPoint(nPoints - 1).getY()); + + normal = glm::vec2(-previousLineVector.y, previousLineVector.x); + + addVertexPair(aVertices, aExtrusionVectors, p1, normal, 1.0f); + } + + ApplyProgramMatrices(0.0f); + mpProgram->SetExtrusionVectors(aExtrusionVectors.data()); + mpProgram->SetVertices(aVertices.data()); + glDrawArrays(GL_TRIANGLE_STRIP, 0, aVertices.size() / 2); + + CHECK_GL_ERROR(); + } +} + +bool OpenGLSalGraphicsImpl::UseLine(SalColor nColor, double fTransparency, GLfloat fLineWidth, bool bUseAA) +{ + if( nColor == SALCOLOR_NONE ) + return false; + if( !UseProgram( "lineVertexShader", "lineFragmentShader" ) ) + return false; + mpProgram->SetColorf("color", nColor, fTransparency); + mpProgram->SetUniform1f("line_width", fLineWidth); + // The width of the feather - area we make lineary transparent in VS. + // Good AA value is 0.5, 0.0 means the no AA will be done. + 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 ); +#ifdef DBG_UTIL + mProgramIsSolidColor = true; +#endif + mProgramSolidColor = nColor; + mProgramSolidTransparency = fTransparency; + return true; +} + void OpenGLSalGraphicsImpl::DrawLineAA( double nX1, double nY1, double nX2, double nY2 ) { OpenGLZone aZone; @@ -1539,58 +1847,20 @@ bool OpenGLSalGraphicsImpl::drawPolyLine( return true; const bool bIsHairline = (rLineWidth.getX() == rLineWidth.getY()) && (rLineWidth.getX() <= 1.2); + const float fLineWidth = bIsHairline ? 1.0f : rLineWidth.getX(); - // #i101491# - if( !bIsHairline && (rPolygon.count() > 1000) ) - { - // the used basegfx::tools::createAreaGeometry is simply too - // expensive with very big polygons; fallback to caller (who - // should use ImplLineConverter normally) - // AW: ImplLineConverter had to be removed since it does not even - // know LineJoins, so the fallback will now prepare the line geometry - // the same way. - return false; - } - - // shortcut for hairline drawing to improve performance - if (bIsHairline) - { - // Let's just leave it to OutputDevice to do the bezier subdivision, - // drawPolyLine(sal_uInt32 nPoints, const SalPoint* pPtAry) will be - // called with the result. - return false; - } - - // #i11575#desc5#b adjust B2D tesselation result to raster positions - basegfx::B2DPolygon aPolygon = rPolygon; - const double fHalfWidth = 0.5 * rLineWidth.getX(); + PreDraw(XOROption::IMPLEMENT_XOR); - // get the area polygon for the line polygon - if( (rLineWidth.getX() != rLineWidth.getY()) - && !basegfx::fTools::equalZero( rLineWidth.getY() ) ) + if (UseLine(mnLineColor, 0.0f, fLineWidth, true)) { - // prepare for createAreaGeometry() with anisotropic linewidth - aPolygon.transform( basegfx::tools::createScaleB2DHomMatrix(1.0, rLineWidth.getX() / rLineWidth.getY())); - } - - // create the area-polygon for the line - const basegfx::B2DPolyPolygon aAreaPolyPoly( basegfx::tools::createAreaGeometry(aPolygon, fHalfWidth, eLineJoin, eLineCap) ); + basegfx::B2DPolygon aPolygon(rPolygon); - if( (rLineWidth.getX() != rLineWidth.getY()) - && !basegfx::fTools::equalZero( rLineWidth.getX() ) ) - { - // postprocess createAreaGeometry() for anisotropic linewidth - aPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(1.0, rLineWidth.getY() / rLineWidth.getX())); - } + if (aPolygon.areControlPointsUsed()) + aPolygon = basegfx::tools::polygonSubdivide(aPolygon, 5 * F_PI180); + else + aPolygon.removeDoublePoints(); - PreDraw( XOROption::IMPLEMENT_XOR ); - if( UseSolid( mnLineColor, fTransparency ) ) - { - for( sal_uInt32 i = 0; i < aAreaPolyPoly.count(); i++ ) - { - const basegfx::B2DPolyPolygon aOnePoly( aAreaPolyPoly.getB2DPolygon( i ) ); - DrawPolyPolygon( aOnePoly ); - } + DrawPolyLine(aPolygon, fLineWidth, eLineJoin, eLineCap); } PostDraw(); diff --git a/vcl/opengl/lineFragmentShader.glsl b/vcl/opengl/lineFragmentShader.glsl new file mode 100644 index 000000000000..a8c73d6b80cc --- /dev/null +++ b/vcl/opengl/lineFragmentShader.glsl @@ -0,0 +1,36 @@ +/* -*- 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/. + */ + +varying float fade_factor; // 0->1 fade factor used for AA +uniform vec4 color; + +uniform float line_width; +uniform float feather; + +void main() +{ + float start = (line_width / 2.0) - feather; // where we start to apply alpha + float end = (line_width / 2.0) + feather; // where we end to apply alpha + + // Calculate the multiplier so we can transform the 0->1 fade factor + // to take feather and line width into account. + float multiplied = 1.0 / (1.0 - (start / end)); + + float dist = (1.0 - abs(fade_factor)) * multiplied; + + float alpha = clamp(dist, 0.0, 1.0); + + // modify the alpha chanel only + vec4 result_color = color; + result_color.a = result_color.a * alpha; + + gl_FragColor = result_color; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/opengl/lineVertexShader.glsl b/vcl/opengl/lineVertexShader.glsl new file mode 100644 index 000000000000..0adcb4908201 --- /dev/null +++ b/vcl/opengl/lineVertexShader.glsl @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +attribute vec2 position; +attribute vec4 extrusion_vectors; + +varying float fade_factor; // fade factor for anti-aliasing + +uniform float line_width; +uniform float feather; // width where we fade the line + +uniform mat4 mvp; + +void main() +{ + vec2 extrusion_vector = extrusion_vectors.xy; + // miter factor to additionaly lenghten the distance of vertex (needed for miter) + // if 1.0 - miter_factor has no effect + float miter_factor = 1.0f / abs(extrusion_vectors.z); + // fade factor is always -1.0 or 1.0 -> we transport that info together with length + fade_factor = sign(extrusion_vectors.z); + + float rendered_thickness = (line_width + feather * 2.0) * miter_factor; + + // lengthen the vertex in directon of the extrusion vector by line width. + vec4 position = vec4(position + (extrusion_vector * (rendered_thickness / 2.0) ), 0.0, 1.0); + + gl_Position = mvp * position; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/opengl/program.cxx b/vcl/opengl/program.cxx index 588700b537d9..340b10f6bb8f 100644 --- a/vcl/opengl/program.cxx +++ b/vcl/opengl/program.cxx @@ -22,6 +22,7 @@ OpenGLProgram::OpenGLProgram() : mnTexCoordAttrib( SAL_MAX_UINT32 ), mnAlphaCoordAttrib( SAL_MAX_UINT32 ), mnMaskCoordAttrib( SAL_MAX_UINT32 ), + mnNormalAttrib( SAL_MAX_UINT32 ), mbBlending( false ), mfLastWidth(0.0), mfLastHeight(0.0), @@ -100,7 +101,7 @@ bool OpenGLProgram::Clean() return true; } -void OpenGLProgram::SetVertexAttrib( GLuint& rAttrib, const OString& rName, const GLvoid* pData ) +void OpenGLProgram::SetVertexAttrib( GLuint& rAttrib, const OString& rName, const GLvoid* pData, GLint nSize ) { if( rAttrib == SAL_MAX_UINT32 ) { @@ -113,7 +114,7 @@ void OpenGLProgram::SetVertexAttrib( GLuint& rAttrib, const OString& rName, cons CHECK_GL_ERROR(); mnEnabledAttribs |= ( 1 << rAttrib ); } - glVertexAttribPointer( rAttrib, 2, GL_FLOAT, GL_FALSE, 0, pData ); + glVertexAttribPointer( rAttrib, nSize, GL_FLOAT, GL_FALSE, 0, pData ); CHECK_GL_ERROR(); } @@ -137,6 +138,11 @@ void OpenGLProgram::SetMaskCoord(const GLvoid* pData) SetVertexAttrib(mnMaskCoordAttrib, "mask_coord_in", pData); } +void OpenGLProgram::SetExtrusionVectors(const GLvoid* pData) +{ + SetVertexAttrib(mnNormalAttrib, "extrusion_vectors", pData, 3); +} + GLuint OpenGLProgram::GetUniformLocation( const OString& rName ) { auto it = maUniformLocations.find( rName ); -- cgit v1.2.3