LibreOffice Module vcl (master) 1
polyline.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <cassert>
21
22#include <sal/types.h>
23
26#include <vcl/gdimtf.hxx>
27#include <vcl/metaact.hxx>
28#include <vcl/outdev.hxx>
29#include <vcl/virdev.hxx>
30
31#include <salgdi.hxx>
32
34{
36
37 if( mpMetaFile )
39
40 sal_uInt16 nPoints = rPoly.GetSize();
41
42 if ( !IsDeviceOutputNecessary() || !mbLineColor || (nPoints < 2) || ImplIsRecordLayout() )
43 return;
44
45 // we need a graphics
46 if ( !mpGraphics && !AcquireGraphics() )
47 return;
48 assert(mpGraphics);
49
50 if ( mbInitClipRegion )
52
53 if ( mbOutputClipped )
54 return;
55
56 if ( mbInitLineColor )
58
59 // use b2dpolygon drawing if possible
62 rPoly.getB2DPolygon()))
63 {
64 return;
65 }
66
67 const basegfx::B2DPolygon aB2DPolyLine(rPoly.getB2DPolygon());
69 const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
70
71 bool bDrawn = mpGraphics->DrawPolyLine(
72 aTransform,
73 aB2DPolyLine,
74 0.0,
75 0.0, // tdf#124848 hairline
76 nullptr, // MM01
78 css::drawing::LineCap_BUTT,
79 basegfx::deg2rad(15.0) /*default fMiterMinimumAngle, not used*/,
80 bPixelSnapHairline,
81 *this);
82
83 if(!bDrawn)
84 {
86 Point* pPtAry = aPoly.GetPointAry();
87
88 // #100127# Forward beziers to sal, if any
89 if( aPoly.HasFlags() )
90 {
91 const PolyFlags* pFlgAry = aPoly.GetConstFlagAry();
92 if( !mpGraphics->DrawPolyLineBezier( nPoints, pPtAry, pFlgAry, *this ) )
93 {
95 pPtAry = aPoly.GetPointAry();
96 mpGraphics->DrawPolyLine( aPoly.GetSize(), pPtAry, *this );
97 }
98 }
99 else
100 {
101 mpGraphics->DrawPolyLine( nPoints, pPtAry, *this );
102 }
103 }
104
105 if( mpAlphaVDev )
106 mpAlphaVDev->DrawPolyLine( rPoly );
107}
108
109void OutputDevice::DrawPolyLine( const tools::Polygon& rPoly, const LineInfo& rLineInfo )
110{
111 assert(!is_double_buffered_window());
112
113 if ( rLineInfo.IsDefault() )
114 {
115 DrawPolyLine( rPoly );
116 return;
117 }
118
120 {
121 auto eLineStyle = rLineInfo.GetStyle();
122 switch (eLineStyle)
123 {
124 case LineStyle::NONE:
125 case LineStyle::Dash:
126 // use drawPolyLine for these
127 break;
128 case LineStyle::Solid:
129 // #i101491# Try direct Fallback to B2D-Version of DrawPolyLine
131 rPoly.getB2DPolygon(),
132 rLineInfo.GetWidth(),
133 rLineInfo.GetLineJoin(),
134 rLineInfo.GetLineCap(),
135 basegfx::deg2rad(15.0) /* default fMiterMinimumAngle, value not available in LineInfo */);
136 return;
137 default:
138 SAL_WARN("vcl.gdi", "Unknown LineStyle: " << static_cast<int>(eLineStyle));
139 return;
140 }
141 }
142
143 if ( mpMetaFile )
144 mpMetaFile->AddAction( new MetaPolyLineAction( rPoly, rLineInfo ) );
145
146 drawPolyLine(rPoly, rLineInfo);
147}
148
150 double fLineWidth,
151 basegfx::B2DLineJoin eLineJoin,
152 css::drawing::LineCap eLineCap,
153 double fMiterMinimumAngle)
154{
155 assert(!is_double_buffered_window());
156
157 if( mpMetaFile )
158 {
159 LineInfo aLineInfo;
160 if( fLineWidth != 0.0 )
161 aLineInfo.SetWidth( fLineWidth );
162
163 aLineInfo.SetLineJoin(eLineJoin);
164 aLineInfo.SetLineCap(eLineCap);
165
166 tools::Polygon aToolsPolygon( rB2DPolygon );
167 mpMetaFile->AddAction( new MetaPolyLineAction( std::move(aToolsPolygon), std::move(aLineInfo) ) );
168 }
169
170 // Do not paint empty PolyPolygons
171 if(!rB2DPolygon.count() || !IsDeviceOutputNecessary())
172 return;
173
174 // we need a graphics
175 if( !mpGraphics && !AcquireGraphics() )
176 return;
177 assert(mpGraphics);
178
179 if( mbInitClipRegion )
181
182 if( mbOutputClipped )
183 return;
184
185 if( mbInitLineColor )
187
188 // use b2dpolygon drawing if possible
191 rB2DPolygon,
192 fLineWidth,
193 0.0,
194 nullptr, // MM01
195 eLineJoin,
196 eLineCap,
197 fMiterMinimumAngle))
198 {
199 return;
200 }
201
202 // #i101491#
203 // no output yet; fallback to geometry decomposition and use filled polygon paint
204 // when line is fat and not too complex. ImplDrawPolyPolygonWithB2DPolyPolygon
205 // will do internal needed AA checks etc.
206 if(fLineWidth >= 2.5 &&
207 rB2DPolygon.count() &&
208 rB2DPolygon.count() <= 1000)
209 {
210 const double fHalfLineWidth((fLineWidth * 0.5) + 0.5);
211 const basegfx::B2DPolyPolygon aAreaPolyPolygon(
213 fHalfLineWidth,
214 eLineJoin,
215 eLineCap,
216 fMiterMinimumAngle));
217 const Color aOldLineColor(maLineColor);
218 const Color aOldFillColor(maFillColor);
219
220 SetLineColor();
222 SetFillColor(aOldLineColor);
224
225 // draw using a loop; else the topology will paint a PolyPolygon
226 for(auto const& rPolygon : aAreaPolyPolygon)
227 {
229 basegfx::B2DPolyPolygon(rPolygon));
230 }
231
232 SetLineColor(aOldLineColor);
234 SetFillColor(aOldFillColor);
236
237 // when AA it is necessary to also paint the filled polygon's outline
238 // to avoid optical gaps
239 for(auto const& rPolygon : aAreaPolyPolygon)
240 {
243 rPolygon);
244 }
245 }
246 else
247 {
248 // fallback to old polygon drawing if needed
249 const tools::Polygon aToolsPolygon( rB2DPolygon );
250 LineInfo aLineInfo;
251 if( fLineWidth != 0.0 )
252 aLineInfo.SetWidth( fLineWidth );
253
254 drawPolyLine( aToolsPolygon, aLineInfo );
255 }
256}
257
258void OutputDevice::drawPolyLine(const tools::Polygon& rPoly, const LineInfo& rLineInfo)
259{
260 sal_uInt16 nPoints(rPoly.GetSize());
261
262 if ( !IsDeviceOutputNecessary() || !mbLineColor || ( nPoints < 2 ) || ( LineStyle::NONE == rLineInfo.GetStyle() ) || ImplIsRecordLayout() )
263 return;
264
265 // we need a graphics
266 if ( !mpGraphics && !AcquireGraphics() )
267 return;
268 assert(mpGraphics);
269
270 if ( mbInitClipRegion )
272
273 if ( mbOutputClipped )
274 return;
275
276 if ( mbInitLineColor )
278
279 const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) );
280 const bool bDashUsed(LineStyle::Dash == aInfo.GetStyle());
281 const bool bLineWidthUsed(aInfo.GetWidth() > 1);
282
283 if (bDashUsed || bLineWidthUsed)
284 {
286 drawLine(basegfx::B2DPolyPolygon(aPoly), aInfo);
287 }
288 else
289 {
291
292 // #100127# the subdivision HAS to be done here since only a pointer
293 // to an array of points is given to the DrawPolyLine method, there is
294 // NO way to find out there that it's a curve.
295 if( aPoly.HasFlags() )
296 {
297 aPoly = tools::Polygon::SubdivideBezier( aPoly );
298 nPoints = aPoly.GetSize();
299 }
300
301 mpGraphics->DrawPolyLine(nPoints, aPoly.GetPointAry(), *this);
302 }
303
304 if( mpAlphaVDev )
305 mpAlphaVDev->DrawPolyLine( rPoly, rLineInfo );
306}
307
309 const basegfx::B2DHomMatrix& rObjectTransform,
310 const basegfx::B2DPolygon& rB2DPolygon,
311 double fLineWidth,
312 double fTransparency,
313 const std::vector< double >* pStroke, // MM01
314 basegfx::B2DLineJoin eLineJoin,
315 css::drawing::LineCap eLineCap,
316 double fMiterMinimumAngle)
317{
318 if(DrawPolyLineDirectInternal(rObjectTransform, rB2DPolygon, fLineWidth, fTransparency,
319 pStroke, eLineJoin, eLineCap, fMiterMinimumAngle))
320 {
321 // Worked, add metafile action (if recorded). This is done only here,
322 // because this function is public, other OutDev functions already add metafile
323 // actions, so they call the internal function directly.
324 if( mpMetaFile )
325 {
326 LineInfo aLineInfo;
327 if( fLineWidth != 0.0 )
328 aLineInfo.SetWidth( fLineWidth );
329 // Transport known information, might be needed
330 aLineInfo.SetLineJoin(eLineJoin);
331 aLineInfo.SetLineCap(eLineCap);
332 // MiterMinimumAngle does not exist yet in LineInfo
333 tools::Polygon aToolsPolygon( rB2DPolygon );
334 mpMetaFile->AddAction( new MetaPolyLineAction( std::move(aToolsPolygon), std::move(aLineInfo) ) );
335 }
336 return true;
337 }
338 return false;
339}
340
342 const basegfx::B2DHomMatrix& rObjectTransform,
343 const basegfx::B2DPolygon& rB2DPolygon,
344 double fLineWidth,
345 double fTransparency,
346 const std::vector< double >* pStroke, // MM01
347 basegfx::B2DLineJoin eLineJoin,
348 css::drawing::LineCap eLineCap,
349 double fMiterMinimumAngle)
350{
351 assert(!is_double_buffered_window());
352
353 // AW: Do NOT paint empty PolyPolygons
354 if(!rB2DPolygon.count())
355 return true;
356
357 // we need a graphics
358 if( !mpGraphics && !AcquireGraphics() )
359 return false;
360 assert(mpGraphics);
361
362 if( mbInitClipRegion )
364
365 if( mbOutputClipped )
366 return true;
367
368 if( mbInitLineColor )
370
373 IsLineColor());
374
375 if(bTryB2d)
376 {
377 // combine rObjectTransform with WorldToDevice
378 const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation() * rObjectTransform);
379 const bool bPixelSnapHairline((mnAntialiasing & AntialiasingFlags::PixelSnapHairline) && rB2DPolygon.count() < 1000);
380
381 const double fAdjustedTransparency = mpAlphaVDev ? 0 : fTransparency;
382 // draw the polyline
383 bool bDrawSuccess = mpGraphics->DrawPolyLine(
384 aTransform,
385 rB2DPolygon,
386 fAdjustedTransparency,
387 fLineWidth, // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline)
388 pStroke, // MM01
389 eLineJoin,
390 eLineCap,
391 fMiterMinimumAngle,
392 bPixelSnapHairline,
393 *this);
394
395 if( bDrawSuccess )
396 {
397 if (mpAlphaVDev)
398 mpAlphaVDev->DrawPolyLineDirect(rObjectTransform, rB2DPolygon, fLineWidth,
399 fTransparency, pStroke, eLineJoin, eLineCap,
400 fMiterMinimumAngle);
401
402 return true;
403 }
404 }
405 return false;
406}
407
408/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:581
virtual void InitClipRegion()
SAL_DLLPRIVATE bool is_double_buffered_window() const
void DrawPolyLine(const tools::Polygon &rPoly)
Render the given polygon as a line stroke.
Definition: polyline.cxx:33
SAL_DLLPRIVATE void InitLineColor()
Definition: line.cxx:85
bool mbOutputClipped
Definition: outdev.hxx:246
RasterOp GetRasterOp() const
Definition: outdev.hxx:501
SAL_DLLPRIVATE tools::Rectangle ImplLogicToDevicePixel(const tools::Rectangle &rLogicRect) const
Convert a logical rectangle to a rectangle in physical device pixel units.
Definition: map.cxx:386
virtual bool AcquireGraphics() const =0
Acquire a graphics device that the output device uses to draw on.
SAL_DLLPRIVATE bool ImplIsRecordLayout() const
Definition: outdev.cxx:724
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:185
SAL_DLLPRIVATE void InitFillColor()
Definition: fill.cxx:76
SAL_DLLPRIVATE void drawLine(basegfx::B2DPolyPolygon aLinePolyPolygon, const LineInfo &rInfo)
Helper for line geometry paint with support for graphic expansion (pattern and fat_to_area)
Definition: line.cxx:225
void SetLineColor()
Definition: line.cxx:37
bool mbInitLineColor
Definition: outdev.hxx:249
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:182
SAL_DLLPRIVATE void drawPolyLine(const tools::Polygon &rPoly, const LineInfo &rLineInfo)
Definition: polyline.cxx:258
bool IsLineColor() const
Definition: outdev.hxx:516
SAL_DLLPRIVATE void ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPolygon &rB2DPolyPoly)
Definition: polygon.cxx:271
bool mbInitClipRegion
Definition: outdev.hxx:253
bool DrawPolyLineDirect(const basegfx::B2DHomMatrix &rObjectTransform, const basegfx::B2DPolygon &rB2DPolygon, double fLineWidth=0.0, double fTransparency=0.0, const std::vector< double > *=nullptr, basegfx::B2DLineJoin eLineJoin=basegfx::B2DLineJoin::NONE, css::drawing::LineCap eLineCap=css::drawing::LineCap_BUTT, double fMiterMinimumAngle=basegfx::deg2rad(15.0))
Definition: polyline.cxx:308
void SetFillColor()
Definition: fill.cxx:29
AntialiasingFlags mnAntialiasing
Definition: outdev.hxx:237
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:482
bool DrawPolyLineDirectInternal(const basegfx::B2DHomMatrix &rObjectTransform, const basegfx::B2DPolygon &rB2DPolygon, double fLineWidth=0.0, double fTransparency=0.0, const std::vector< double > *=nullptr, basegfx::B2DLineJoin eLineJoin=basegfx::B2DLineJoin::NONE, css::drawing::LineCap eLineCap=css::drawing::LineCap_BUTT, double fMiterMinimumAngle=basegfx::deg2rad(15.0))
Definition: polyline.cxx:341
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:196
SAL_DLLPRIVATE basegfx::B2DHomMatrix ImplGetDeviceTransformation() const
Get device transformation.
Definition: map.cxx:924
Color maLineColor
Definition: outdev.hxx:226
Color maFillColor
Definition: outdev.hxx:227
bool mbLineColor
Definition: outdev.hxx:247
virtual bool supportsOperation(OutDevSupportType) const =0
void DrawPolyLine(sal_uInt32 nPoints, Point const *pPtAry, const OutputDevice &rOutDev)
bool DrawPolyLineBezier(sal_uInt32 nPoints, const Point *pPtAry, const PolyFlags *pFlgAry, const OutputDevice &rOutDev)
sal_uInt32 count() const
::basegfx::B2DPolygon getB2DPolygon() const
bool HasFlags() const
sal_uInt16 GetSize() const
static Polygon SubdivideBezier(const Polygon &rPoly)
const PolyFlags * GetConstFlagAry() const
Point * GetPointAry()
#define SAL_WARN(area, stream)
B2DPolyPolygon createAreaGeometry(const B2DPolygon &rCandidate, double fHalfLineWidth, B2DLineJoin eJoin, css::drawing::LineCap eCap, double fMaxAllowedAngle=basegfx::deg2rad(12.5), double fMaxPartOfEdge=0.4, double fMiterMinimumAngle=basegfx::deg2rad(15.0), basegfx::triangulator::B2DTriangleVector *pTriangles=nullptr)
constexpr double deg2rad(double v)
PolyFlags