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