summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.com>2016-02-24 00:44:58 +0100
committerMiklos Vajna <vmiklos@collabora.co.uk>2016-02-29 09:41:17 +0000
commit334288af53e15f4b16f520a48e84e84c6980b8c7 (patch)
treeff281ab3bc0ded1e69503719a118b59bc2cadea8
parent0f571cf2781d98a1897952aa53329ebb3716dc19 (diff)
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. (cherry picked from commit d18ad8a7fb3257001a5045e11f3f770a48a7fa69) Change-Id: If7982c828cae1fae59c57194c8ac77e5ad7f1d26 Reviewed-on: https://gerrit.libreoffice.org/22706 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: László Németh <nemeth@numbertext.org> Reviewed-by: Tor Lillqvist <tml@collabora.com> Tested-by: Tor Lillqvist <tml@collabora.com>
-rw-r--r--basegfx/source/polygon/b2dlinegeometry.cxx29
-rw-r--r--include/basegfx/polygon/b2dlinegeometry.hxx6
-rw-r--r--vcl/Package_opengl.mk2
-rw-r--r--vcl/inc/opengl/program.hxx4
-rw-r--r--vcl/inc/openglgdiimpl.hxx4
-rw-r--r--vcl/opengl/gdiimpl.cxx364
-rw-r--r--vcl/opengl/lineFragmentShader.glsl36
-rw-r--r--vcl/opengl/lineVertexShader.glsl37
-rw-r--r--vcl/opengl/program.cxx10
9 files changed, 442 insertions, 50 deletions
diff --git a/basegfx/source/polygon/b2dlinegeometry.cxx b/basegfx/source/polygon/b2dlinegeometry.cxx
index c259fef49504..a3095caa05f0 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 a3de97c62968..0c9b7b10cd8b 100644
--- a/include/basegfx/polygon/b2dlinegeometry.hxx
+++ b/include/basegfx/polygon/b2dlinegeometry.hxx
@@ -138,6 +138,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 a178d18f756e..ca8232ae2c45 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 1480e2c0da74..15cec83867a3 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -37,6 +37,9 @@
#include <vector>
+#include <glm/gtc/type_ptr.hpp>
+#include <glm/gtx/norm.hpp>
+
#include <stdlib.h>
class OpenGLFlushIdle : public Idle
@@ -625,6 +628,311 @@ void OpenGLSalGraphicsImpl::DrawLine( double nX1, double nY1, double nX2, double
CHECK_GL_ERROR();
}
+namespace
+{
+
+inline void addVertex(std::vector<GLfloat>& rVertices, std::vector<GLfloat>& 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<GLfloat>& rVertices, std::vector<GLfloat>& 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<GLfloat> aVertices;
+ std::vector<GLfloat> 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<GLfloat> aPoints;
+ std::vector<GLfloat> 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<GLfloat> aVertices;
+ std::vector<GLfloat> 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;
@@ -1540,58 +1848,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, 7.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 );