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