1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
11 #include <opengl/RenderList.hxx>
12 #include <opengl/VertexUtils.hxx>
13 #include <opengl/LineRenderUtils.hxx>
15 #include <basegfx/polygon/b2dpolygontools.hxx>
16 #include <basegfx/polygon/b2dpolygontriangulator.hxx>
17 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
18 #include <basegfx/polygon/b2dtrapezoid.hxx>
23 /** Append vertices for the polyline
25 * OpenGL polyline drawing algorithm inspired by:
26 * - http://mattdesl.svbtle.com/drawing-lines-is-hard
27 * - https://www.mapbox.com/blog/drawing-antialiased-lines/
28 * - https://cesiumjs.org/2013/04/22/Robust-Polyline-Rendering-with-WebGL/
29 * - http://artgrammer.blogspot.si/2011/05/drawing-nearly-perfect-2d-line-segments.html
30 * - http://artgrammer.blogspot.si/2011/07/drawing-polylines-by-tessellation.html
33 void appendPolyLine(vcl::LineBuilder
& rBuilder
, const basegfx::B2DPolygon
& rPolygon
,
34 basegfx::B2DLineJoin eLineJoin
, css::drawing::LineCap eLineCap
,
35 double fMiterMinimumAngle
)
37 sal_uInt32 nPoints
= rPolygon
.count();
38 bool bClosed
= rPolygon
.isClosed();
40 if (nPoints
== 2 || eLineJoin
== basegfx::B2DLineJoin::NONE
)
42 // If line joint is NONE or a simple line with 2 points, draw the polyline
43 // each line segment separately.
45 for (sal_uInt32 i
= 0; i
< (bClosed
? nPoints
: nPoints
- 1); ++i
)
47 sal_uInt32 index1
= (i
+ 0) % nPoints
; // loop indices - important when polyline is closed
48 sal_uInt32 index2
= (i
+ 1) % nPoints
;
50 glm::vec2
aPoint1(rPolygon
.getB2DPoint(index1
).getX(), rPolygon
.getB2DPoint(index1
).getY());
51 glm::vec2
aPoint2(rPolygon
.getB2DPoint(index2
).getX(), rPolygon
.getB2DPoint(index2
).getY());
53 rBuilder
.appendLine(aPoint1
, aPoint2
);
59 int lastPoint
= int(nPoints
);
61 glm::vec2
p0(rPolygon
.getB2DPoint(nPoints
- 1).getX(), rPolygon
.getB2DPoint(nPoints
- 1).getY());
62 glm::vec2
p1(rPolygon
.getB2DPoint(0).getX(), rPolygon
.getB2DPoint(0).getY());
63 glm::vec2
p2(rPolygon
.getB2DPoint(1).getX(), rPolygon
.getB2DPoint(1).getY());
65 glm::vec2 nextLineVector
;
66 glm::vec2 previousLineVector
;
67 glm::vec2 normal
; // perpendicular to the line vector
69 nextLineVector
= vcl::vertex::normalize(p2
- p1
);
73 normal
= glm::vec2(-nextLineVector
.y
, nextLineVector
.x
); // make perpendicular
74 rBuilder
.appendAndConnectLinePoint(p1
, normal
, 1.0f
);
76 i
++; // first point done already
77 lastPoint
--; // last point will be calculated separately from the loop
80 previousLineVector
= nextLineVector
;
84 lastPoint
++; // we need to connect last point to first point so one more line segment to calculate
85 previousLineVector
= vcl::vertex::normalize(p1
- p0
);
88 for (; i
< lastPoint
; ++i
)
90 int index1
= (i
+ 0) % nPoints
; // loop indices - important when polyline is closed
91 int index2
= (i
+ 1) % nPoints
;
93 p1
= glm::vec2(rPolygon
.getB2DPoint(index1
).getX(), rPolygon
.getB2DPoint(index1
).getY());
94 p2
= glm::vec2(rPolygon
.getB2DPoint(index2
).getX(), rPolygon
.getB2DPoint(index2
).getY());
96 if (p1
== p2
) // skip equal points, normals could div-by-0
99 nextLineVector
= vcl::vertex::normalize(p2
- p1
);
101 if (eLineJoin
== basegfx::B2DLineJoin::Miter
)
103 if (vcl::vertex::lineVectorAngle(previousLineVector
, nextLineVector
) < fMiterMinimumAngle
)
104 rBuilder
.appendBevelJoint(p1
, previousLineVector
, nextLineVector
);
106 rBuilder
.appendMiterJoint(p1
, previousLineVector
, nextLineVector
);
108 else if (eLineJoin
== basegfx::B2DLineJoin::Bevel
)
110 rBuilder
.appendBevelJoint(p1
, previousLineVector
, nextLineVector
);
112 else if (eLineJoin
== basegfx::B2DLineJoin::Round
)
114 rBuilder
.appendRoundJoint(p1
, previousLineVector
, nextLineVector
);
117 previousLineVector
= nextLineVector
;
122 // Create vertices for the last point. There is no line join so just
123 // use the last line segment normal as the extrusion vector.
124 p1
= glm::vec2(rPolygon
.getB2DPoint(nPoints
- 1).getX(), rPolygon
.getB2DPoint(nPoints
- 1).getY());
125 normal
= glm::vec2(-previousLineVector
.y
, previousLineVector
.x
);
126 rBuilder
.appendAndConnectLinePoint(p1
, normal
, 1.0f
);
130 if (bClosed
|| nPoints
< 2 || (eLineCap
!= css::drawing::LineCap_ROUND
&& eLineCap
!= css::drawing::LineCap_SQUARE
))
133 glm::vec2
aBeginCapPoint1(rPolygon
.getB2DPoint(0).getX(), rPolygon
.getB2DPoint(0).getY());
134 glm::vec2
aBeginCapPoint2(rPolygon
.getB2DPoint(1).getX(), rPolygon
.getB2DPoint(1).getY());
136 glm::vec2
aEndCapPoint1(rPolygon
.getB2DPoint(nPoints
- 1).getX(), rPolygon
.getB2DPoint(nPoints
- 1).getY());
137 glm::vec2
aEndCapPoint2(rPolygon
.getB2DPoint(nPoints
- 2).getX(), rPolygon
.getB2DPoint(nPoints
- 2).getY());
139 if (eLineCap
== css::drawing::LineCap_ROUND
)
141 rBuilder
.appendRoundLineCapVertices(aBeginCapPoint1
, aBeginCapPoint2
);
142 rBuilder
.appendRoundLineCapVertices(aEndCapPoint1
, aEndCapPoint2
);
144 else if (eLineCap
== css::drawing::LineCap_SQUARE
)
146 rBuilder
.appendSquareLineCapVertices(aBeginCapPoint1
, aBeginCapPoint2
);
147 rBuilder
.appendSquareLineCapVertices(aEndCapPoint1
, aEndCapPoint2
);
151 void appendTrapezoid(std::vector
<Vertex
>& rVertices
, std::vector
<GLuint
>& rIndices
,
152 GLfloat x1
, GLfloat y1
, GLfloat x2
, GLfloat y2
,
153 GLfloat x3
, GLfloat y3
, GLfloat x4
, GLfloat y4
,
154 Color nColor
, GLfloat fTransparency
)
156 GLubyte nR
, nG
, nB
, nA
;
157 vcl::vertex::createColor(nColor
, fTransparency
, nR
, nG
, nB
, nA
);
159 GLuint zero
= rVertices
.size();
161 rVertices
.insert(rVertices
.end(), {
162 {glm::vec2
{x1
, y1
}, glm::vec4
{nR
, nG
, nB
, nA
}, glm::vec4
{0.0f
, 0.0f
, 0.0f
, 0.0f
}},
163 {glm::vec2
{x2
, y2
}, glm::vec4
{nR
, nG
, nB
, nA
}, glm::vec4
{0.0f
, 0.0f
, 0.0f
, 0.0f
}},
164 {glm::vec2
{x3
, y3
}, glm::vec4
{nR
, nG
, nB
, nA
}, glm::vec4
{0.0f
, 0.0f
, 0.0f
, 0.0f
}},
165 {glm::vec2
{x4
, y4
}, glm::vec4
{nR
, nG
, nB
, nA
}, glm::vec4
{0.0f
, 0.0f
, 0.0f
, 0.0f
}},
168 rIndices
.insert(rIndices
.end(), {
169 zero
+ 0, zero
+ 1, zero
+ 2,
170 zero
+ 2, zero
+ 1, zero
+ 3
174 void appendRectangle(std::vector
<Vertex
>& rVertices
, std::vector
<GLuint
>& rIndices
,
175 GLfloat x1
, GLfloat y1
, GLfloat x2
, GLfloat y2
,
176 Color nColor
, GLfloat fTransparency
)
178 GLubyte nR
, nG
, nB
, nA
;
179 vcl::vertex::createColor(nColor
, fTransparency
, nR
, nG
, nB
, nA
);
181 GLuint zero
= rVertices
.size();
183 rVertices
.insert(rVertices
.end(), {
184 {glm::vec2
{x1
, y1
}, glm::vec4
{nR
, nG
, nB
, nA
}, glm::vec4
{0.0f
, 0.0f
, 0.0f
, 0.0f
}},
185 {glm::vec2
{x2
, y1
}, glm::vec4
{nR
, nG
, nB
, nA
}, glm::vec4
{0.0f
, 0.0f
, 0.0f
, 0.0f
}},
186 {glm::vec2
{x1
, y2
}, glm::vec4
{nR
, nG
, nB
, nA
}, glm::vec4
{0.0f
, 0.0f
, 0.0f
, 0.0f
}},
187 {glm::vec2
{x2
, y2
}, glm::vec4
{nR
, nG
, nB
, nA
}, glm::vec4
{0.0f
, 0.0f
, 0.0f
, 0.0f
}},
190 rIndices
.insert(rIndices
.end(), {
191 zero
+ 0, zero
+ 1, zero
+ 2,
192 zero
+ 2, zero
+ 1, zero
+ 3
196 } // end anonymous namespace
198 void RenderList::addDrawPixel(tools::Long nX
, tools::Long nY
, Color nColor
)
200 if (nColor
== SALCOLOR_NONE
)
203 checkOverlapping(basegfx::B2DRange(nX
, nY
, nX
, nY
));
205 RenderParameters
& rRenderParameter
= maRenderEntries
.back().maTriangleParameters
;
206 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
207 nX
- 0.5f
, nY
- 0.5f
, nX
+ 0.5f
, nY
+ 0.5f
, nColor
, 0.0f
);
210 void RenderList::addDrawRectangle(tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
, double fTransparency
,
211 Color nLineColor
, Color nFillColor
)
213 if (nLineColor
== SALCOLOR_NONE
&& nFillColor
== SALCOLOR_NONE
)
215 if (fTransparency
== 1.0f
)
220 GLfloat
fX2(nX
+ nWidth
- 1);
221 GLfloat
fY2(nY
+ nHeight
- 1);
223 checkOverlapping(basegfx::B2DRange(fX1
, fY1
, fX2
, fY2
));
225 RenderParameters
& rRenderParameter
= maRenderEntries
.back().maTriangleParameters
;
227 // Draw rectangle stroke with line color
228 if (nLineColor
!= SALCOLOR_NONE
)
230 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
231 fX1
- 0.5f
, fY1
- 0.5f
, fX1
+ 0.5f
, fY2
+ 0.5f
, nLineColor
, fTransparency
);
232 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
233 fX1
- 0.5f
, fY1
- 0.5f
, fX2
+ 0.5f
, fY1
+ 0.5f
, nLineColor
, fTransparency
);
234 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
235 fX2
- 0.5f
, fY1
- 0.5f
, fX2
+ 0.5f
, fY2
+ 0.5f
, nLineColor
, fTransparency
);
236 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
237 fX1
- 0.5f
, fY2
- 0.5f
, fX2
+ 0.5f
, fY2
+ 0.5f
, nLineColor
, fTransparency
);
240 if (nFillColor
== SALCOLOR_NONE
)
243 // coverity[copy_paste_error : FALSE] - this is correct nLineColor not nFillColor
244 if (nLineColor
== SALCOLOR_NONE
)
246 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
247 fX1
- 0.5f
, fY1
- 0.5f
, fX1
+ 0.5f
, fY2
+ 0.5f
, nFillColor
, fTransparency
);
248 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
249 fX1
- 0.5f
, fY1
- 0.5f
, fX2
+ 0.5f
, fY1
+ 0.5f
, nFillColor
, fTransparency
);
250 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
251 fX2
- 0.5f
, fY1
- 0.5f
, fX2
+ 0.5f
, fY2
+ 0.5f
, nFillColor
, fTransparency
);
252 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
253 fX1
- 0.5f
, fY2
- 0.5f
, fX2
+ 0.5f
, fY2
+ 0.5f
, nFillColor
, fTransparency
);
255 // Draw rectangle fill with fill color
256 appendRectangle(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
,
257 fX1
+ 0.5f
, fY1
+ 0.5f
, fX2
- 0.5f
, fY2
- 0.5f
, nFillColor
, fTransparency
);
260 void RenderList::addDrawLine(tools::Long nX1
, tools::Long nY1
, tools::Long nX2
, tools::Long nY2
, Color nLineColor
, bool bUseAA
)
262 if (nLineColor
== SALCOLOR_NONE
)
265 checkOverlapping(basegfx::B2DRange(nX1
, nY1
, nX2
, nY2
));
267 RenderParameters
& rRenderParameter
= maRenderEntries
.back().maLineParameters
;
269 glm::vec2
aPoint1(nX1
, nY1
);
270 glm::vec2
aPoint2(nX2
, nY2
);
272 vcl::LineBuilder
aBuilder(rRenderParameter
.maVertices
, rRenderParameter
.maIndices
, nLineColor
, 0.0f
, 1.0f
, bUseAA
);
273 aBuilder
.appendLine(aPoint1
, aPoint2
);
276 void RenderList::addDrawPolyPolygon(const basegfx::B2DPolyPolygon
& rPolyPolygon
, double fTransparency
,
277 Color nLineColor
, Color nFillColor
, bool bUseAA
)
279 if (rPolyPolygon
.count() <= 0)
281 if (nLineColor
== SALCOLOR_NONE
&& nFillColor
== SALCOLOR_NONE
)
283 if (fTransparency
== 1.0)
286 checkOverlapping(rPolyPolygon
.getB2DRange());
288 if (nFillColor
!= SALCOLOR_NONE
)
290 basegfx::B2DTrapezoidVector aTrapezoidVector
;
291 basegfx::utils::trapezoidSubdivide(aTrapezoidVector
, rPolyPolygon
);
293 if (!aTrapezoidVector
.empty())
295 RenderParameters
& rTriangleRenderParameter
= maRenderEntries
.back().maTriangleParameters
;
297 for (const basegfx::B2DTrapezoid
& rTrapezoid
: aTrapezoidVector
)
299 GLfloat topX1
= rTrapezoid
.getTopXLeft();
300 GLfloat topX2
= rTrapezoid
.getTopXRight();
301 GLfloat topY
= rTrapezoid
.getTopY();
303 GLfloat bottomX1
= rTrapezoid
.getBottomXLeft();
304 GLfloat bottomX2
= rTrapezoid
.getBottomXRight();
305 GLfloat bottomY
= rTrapezoid
.getBottomY();
307 appendTrapezoid(rTriangleRenderParameter
.maVertices
, rTriangleRenderParameter
.maIndices
,
308 topX1
, topY
, topX2
, topY
,
309 bottomX1
, bottomY
, bottomX2
, bottomY
,
310 nFillColor
, fTransparency
);
315 if (nLineColor
== SALCOLOR_NONE
&& !bUseAA
)
318 RenderParameters
& rLineRenderParameter
= maRenderEntries
.back().maLineParameters
;
319 Color nColor
= (nLineColor
== SALCOLOR_NONE
) ? nFillColor
: nLineColor
;
321 vcl::LineBuilder
aBuilder(rLineRenderParameter
.maVertices
, rLineRenderParameter
.maIndices
,
322 nColor
, fTransparency
, 1.0f
, bUseAA
);
324 for (const basegfx::B2DPolygon
& rPolygon
: rPolyPolygon
)
326 basegfx::B2DPolygon
aPolygon(rPolygon
);
327 if (rPolygon
.areControlPointsUsed())
328 aPolygon
= rPolygon
.getDefaultAdaptiveSubdivision();
330 sal_uInt32 nPoints
= aPolygon
.count();
334 GLfloat x1
, y1
, x2
, y2
;
335 sal_uInt32 index1
, index2
;
337 for (sal_uInt32 i
= 0; i
<= nPoints
; ++i
)
339 index1
= i
% nPoints
;
340 index2
= (i
+ 1) % nPoints
;
342 x1
= aPolygon
.getB2DPoint(index1
).getX();
343 y1
= aPolygon
.getB2DPoint(index1
).getY();
344 x2
= aPolygon
.getB2DPoint(index2
).getX();
345 y2
= aPolygon
.getB2DPoint(index2
).getY();
347 aBuilder
.appendLine(glm::vec2(x1
, y1
), glm::vec2(x2
, y2
));
352 void RenderList::addDrawTextureWithMaskColor(OpenGLTexture
const & rTexture
, Color nColor
, const SalTwoRect
& r2Rect
)
357 GLfloat fX1
= r2Rect
.mnDestX
;
358 GLfloat fY1
= r2Rect
.mnDestY
;
359 GLfloat fX2
= fX1
+ r2Rect
.mnDestWidth
;
360 GLfloat fY2
= fY1
+ r2Rect
.mnDestHeight
;
362 checkOverlapping(basegfx::B2DRange(fX1
, fY1
, fX2
, fY2
));
364 GLuint nTextureId
= rTexture
.Id();
366 RenderTextureParameters
& rTextureParameter
= maRenderEntries
.back().maTextureParametersMap
[nTextureId
];
367 rTextureParameter
.maTexture
= rTexture
;
369 rTexture
.FillCoords
<GL_TRIANGLES
>(rTextureParameter
.maTextureCoords
, r2Rect
);
371 vcl::vertex::addRectangle
<GL_TRIANGLES
>(rTextureParameter
.maVertices
, fX1
, fY1
, fX2
, fY2
);
372 vcl::vertex::addQuadColors
<GL_TRIANGLES
>(rTextureParameter
.maColors
, nColor
, 0.0f
);
375 void RenderList::addDrawPolyLine(const basegfx::B2DPolygon
& rPolygon
, double fTransparency
,
376 double fLineWidth
, basegfx::B2DLineJoin eLineJoin
,
377 css::drawing::LineCap eLineCap
, double fMiterMinimumAngle
,
378 Color nLineColor
, bool bUseAA
)
380 if (rPolygon
.count() <= 1)
382 if (nLineColor
== SALCOLOR_NONE
)
384 if (fTransparency
== 1.0)
387 const bool bIsHairline
= fLineWidth
<= 1.2;
388 fLineWidth
= bIsHairline
? 1.0f
: fLineWidth
;
390 basegfx::B2DPolygon
aPolygon(rPolygon
);
391 if (rPolygon
.areControlPointsUsed())
392 aPolygon
= rPolygon
.getDefaultAdaptiveSubdivision();
394 checkOverlapping(aPolygon
.getB2DRange());
396 RenderParameters
& rParameter
= maRenderEntries
.back().maLineParameters
;
398 vcl::LineBuilder
aBuilder(rParameter
.maVertices
, rParameter
.maIndices
,
399 nLineColor
, fTransparency
, fLineWidth
, bUseAA
);
401 appendPolyLine(aBuilder
, aPolygon
, eLineJoin
, eLineCap
, fMiterMinimumAngle
);
404 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */