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