Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / vcl / opengl / RenderList.cxx
blob1d78b9d499b64576c4f9e2624641a7c49f1aeaff
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 */
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>
20 namespace
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);
56 else if (nPoints > 2)
58 int i = 0;
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);
71 if (!bClosed)
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
79 p0 = p1;
80 previousLineVector = nextLineVector;
82 else
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
97 continue;
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);
105 else
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);
116 p0 = p1;
117 previousLineVector = nextLineVector;
120 if (!bClosed)
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))
131 return;
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)
201 return;
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)
214 return;
215 if (fTransparency == 1.0f)
216 return;
218 GLfloat fX1(nX);
219 GLfloat fY1(nY);
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)
241 return;
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)
263 return;
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)
280 return;
281 if (nLineColor == SALCOLOR_NONE && nFillColor == SALCOLOR_NONE)
282 return;
283 if (fTransparency == 1.0)
284 return;
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)
316 return;
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();
331 if (nPoints <= 1)
332 continue;
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)
354 if (!rTexture)
355 return;
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)
381 return;
382 if (nLineColor == SALCOLOR_NONE)
383 return;
384 if (fTransparency == 1.0)
385 return;
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: */