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 
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 
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  return;
83  }
84 
85  tools::Polygon aPoly = ImplLogicToDevicePixel( rPoly );
86  SalPoint* pPtAry = reinterpret_cast<SalPoint*>(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 = reinterpret_cast<SalPoint*>(aPoly.GetPointAry());
96  mpGraphics->DrawPolyLine( aPoly.GetSize(), pPtAry, this );
97  }
98  }
99  else
100  {
101  mpGraphics->DrawPolyLine( nPoints, pPtAry, this );
102  }
103 
104  if( mpAlphaVDev )
105  mpAlphaVDev->DrawPolyLine( rPoly );
106 }
107 
108 void OutputDevice::DrawPolyLine( const tools::Polygon& rPoly, const LineInfo& rLineInfo )
109 {
110  assert(!is_double_buffered_window());
111 
112  if ( rLineInfo.IsDefault() )
113  {
114  DrawPolyLine( rPoly );
115  return;
116  }
117 
118  // #i101491#
119  // Try direct Fallback to B2D-Version of DrawPolyLine
121  LineStyle::Solid == rLineInfo.GetStyle())
122  {
123  DrawPolyLine(
124  rPoly.getB2DPolygon(),
125  static_cast< double >(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( static_cast<long>(fLineWidth+0.5) );
151 
152  const tools::Polygon aToolsPolygon( rB2DPolygon );
153  mpMetaFile->AddAction( new MetaPolyLineAction( aToolsPolygon, aLineInfo ) );
154  }
155 
156  // Do not paint empty PolyPolygons
157  if(!rB2DPolygon.count() || !IsDeviceOutputNecessary())
158  return;
159 
160  // we need a graphics
161  if( !mpGraphics && !AcquireGraphics() )
162  return;
163 
164  if( mbInitClipRegion )
165  InitClipRegion();
166 
167  if( mbOutputClipped )
168  return;
169 
170  if( mbInitLineColor )
171  InitLineColor();
172 
173  // use b2dpolygon drawing if possible
176  rB2DPolygon,
177  fLineWidth,
178  0.0,
179  nullptr, // MM01
180  eLineJoin,
181  eLineCap,
182  fMiterMinimumAngle))
183  {
184  return;
185  }
186 
187  // #i101491#
188  // no output yet; fallback to geometry decomposition and use filled polygon paint
189  // when line is fat and not too complex. ImplDrawPolyPolygonWithB2DPolyPolygon
190  // will do internal needed AA checks etc.
191  if(fLineWidth >= 2.5 &&
192  rB2DPolygon.count() &&
193  rB2DPolygon.count() <= 1000)
194  {
195  const double fHalfLineWidth((fLineWidth * 0.5) + 0.5);
196  const basegfx::B2DPolyPolygon aAreaPolyPolygon(
198  fHalfLineWidth,
199  eLineJoin,
200  eLineCap,
201  fMiterMinimumAngle));
202  const Color aOldLineColor(maLineColor);
203  const Color aOldFillColor(maFillColor);
204 
205  SetLineColor();
206  InitLineColor();
207  SetFillColor(aOldLineColor);
208  InitFillColor();
209 
210  // draw using a loop; else the topology will paint a PolyPolygon
211  for(auto const& rPolygon : aAreaPolyPolygon)
212  {
214  basegfx::B2DPolyPolygon(rPolygon));
215  }
216 
217  SetLineColor(aOldLineColor);
218  InitLineColor();
219  SetFillColor(aOldFillColor);
220  InitFillColor();
221 
222  const bool bTryAA((mnAntialiasing & AntialiasingFlags::EnableB2dDraw) &&
225  IsLineColor());
226 
227  // when AA it is necessary to also paint the filled polygon's outline
228  // to avoid optical gaps
229  for(auto const& rPolygon : aAreaPolyPolygon)
230  {
233  rPolygon,
234  0.0,
235  0.0,
236  nullptr, // MM01
238  css::drawing::LineCap_BUTT,
239  basegfx::deg2rad(15.0) /*default, not used*/,
240  bTryAA);
241  }
242  }
243  else
244  {
245  // fallback to old polygon drawing if needed
246  const tools::Polygon aToolsPolygon( rB2DPolygon );
247  LineInfo aLineInfo;
248  if( fLineWidth != 0.0 )
249  aLineInfo.SetWidth( static_cast<long>(fLineWidth+0.5) );
250 
251  drawPolyLine( aToolsPolygon, aLineInfo );
252  }
253 }
254 
255 void OutputDevice::drawPolyLine(const tools::Polygon& rPoly, const LineInfo& rLineInfo)
256 {
257  sal_uInt16 nPoints(rPoly.GetSize());
258 
259  if ( !IsDeviceOutputNecessary() || !mbLineColor || ( nPoints < 2 ) || ( LineStyle::NONE == rLineInfo.GetStyle() ) || ImplIsRecordLayout() )
260  return;
261 
262  tools::Polygon aPoly = ImplLogicToDevicePixel( rPoly );
263 
264  // we need a graphics
265  if ( !mpGraphics && !AcquireGraphics() )
266  return;
267 
268  if ( mbInitClipRegion )
269  InitClipRegion();
270 
271  if ( mbOutputClipped )
272  return;
273 
274  if ( mbInitLineColor )
275  InitLineColor();
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  {
283  drawLine ( basegfx::B2DPolyPolygon(aPoly.getB2DPolygon()), aInfo );
284  }
285  else
286  {
287  // #100127# the subdivision HAS to be done here since only a pointer
288  // to an array of points is given to the DrawPolyLine method, there is
289  // NO way to find out there that it's a curve.
290  if( aPoly.HasFlags() )
291  {
292  aPoly = tools::Polygon::SubdivideBezier( aPoly );
293  nPoints = aPoly.GetSize();
294  }
295 
296  mpGraphics->DrawPolyLine(nPoints, reinterpret_cast<SalPoint*>(aPoly.GetPointAry()), this);
297  }
298 
299  if( mpAlphaVDev )
300  mpAlphaVDev->DrawPolyLine( rPoly, rLineInfo );
301 }
302 
304  const basegfx::B2DHomMatrix& rObjectTransform,
305  const basegfx::B2DPolygon& rB2DPolygon,
306  double fLineWidth,
307  double fTransparency,
308  const std::vector< double >* pStroke, // MM01
309  basegfx::B2DLineJoin eLineJoin,
310  css::drawing::LineCap eLineCap,
311  double fMiterMinimumAngle,
312  bool bBypassAACheck)
313 {
314  assert(!is_double_buffered_window());
315 
316  // AW: Do NOT paint empty PolyPolygons
317  if(!rB2DPolygon.count())
318  return true;
319 
320  // we need a graphics
321  if( !mpGraphics && !AcquireGraphics() )
322  return false;
323 
324  if( mbInitClipRegion )
325  InitClipRegion();
326 
327  if( mbOutputClipped )
328  return true;
329 
330  if( mbInitLineColor )
331  InitLineColor();
332 
333  const bool bTryAA( bBypassAACheck ||
337  IsLineColor()));
338 
339  if(bTryAA)
340  {
341  // combine rObjectTransform with WorldToDevice
342  const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation() * rObjectTransform);
343  const bool bPixelSnapHairline((mnAntialiasing & AntialiasingFlags::PixelSnapHairline) && rB2DPolygon.count() < 1000);
344 
345  // draw the polyline
346  bool bDrawSuccess = mpGraphics->DrawPolyLine(
347  aTransform,
348  rB2DPolygon,
349  fTransparency,
350  fLineWidth, // tdf#124848 use LineWidth direct, do not try to solve for zero-case (aka hairline)
351  pStroke, // MM01
352  eLineJoin,
353  eLineCap,
354  fMiterMinimumAngle,
355  bPixelSnapHairline,
356  this);
357 
358  if( bDrawSuccess )
359  {
360  // worked, add metafile action (if recorded) and return true
361  if( mpMetaFile )
362  {
363  LineInfo aLineInfo;
364  if( fLineWidth != 0.0 )
365  aLineInfo.SetWidth( static_cast<long>(fLineWidth+0.5) );
366  // Transport known information, might be needed
367  aLineInfo.SetLineJoin(eLineJoin);
368  aLineInfo.SetLineCap(eLineCap);
369  // MiterMinimumAngle does not exist yet in LineInfo
370  const tools::Polygon aToolsPolygon( rB2DPolygon );
371  mpMetaFile->AddAction( new MetaPolyLineAction( aToolsPolygon, aLineInfo ) );
372  }
373  return true;
374  }
375  }
376  return false;
377 }
378 
379 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SAL_DLLPRIVATE bool ImplIsRecordLayout() const
Definition: outdev.cxx:650
virtual bool supportsOperation(OutDevSupportType) const =0
Color maLineColor
Definition: outdev.hxx:362
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), bool bBypassAACheck=false)
Definition: polyline.cxx:303
bool mbOutputClipped
Definition: outdev.hxx:381
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()
AntialiasingFlags mnAntialiasing
Definition: outdev.hxx:373
Color maFillColor
Definition: outdev.hxx:363
void DrawPolyLine(sal_uInt32 nPoints, SalPoint const *pPtAry, const OutputDevice *pOutDev)
SalGraphics * mpGraphics
Graphics context to draw on.
Definition: outdev.hxx:316
SAL_DLLPRIVATE basegfx::B2DHomMatrix ImplGetDeviceTransformation() const
Get device transformation.
Definition: map.cxx:925
void SetLineColor()
const PolyFlags * GetConstFlagAry() const
SAL_DLLPRIVATE void InitFillColor()
Point * GetPointAry()
bool IsLineColor() const
Definition: outdev.hxx:625
virtual bool AcquireGraphics() const =0
Acquire a graphics device that the output device uses to draw on.
bool mbInitLineColor
Definition: outdev.hxx:384
bool HasFlags() const
void SetFillColor()
bool mbLineColor
Definition: outdev.hxx:382
sal_uInt16 GetSize() const
constexpr double deg2rad(double v)
::basegfx::B2DPolygon getB2DPolygon() const
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:152
SAL_DLLPRIVATE void ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPolygon &rB2DPolyPoly)
Definition: polygon.cxx:271
VclPtr< VirtualDevice > mpAlphaVDev
Definition: outdev.hxx:331
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:543
bool mbInitClipRegion
Definition: outdev.hxx:388
virtual void InitClipRegion()
RasterOp GetRasterOp() const
Definition: outdev.hxx:610
SAL_DLLPRIVATE void drawPolyLine(const tools::Polygon &rPoly, const LineInfo &rLineInfo)
Definition: polyline.cxx:255
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:504
bool DrawPolyLineBezier(sal_uInt32 nPoints, const SalPoint *pPtAry, const PolyFlags *pFlgAry, const OutputDevice *pOutDev)
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:595
GDIMetaFile * mpMetaFile
Definition: outdev.hxx:319
PolyFlags
sal_uInt32 count() const
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo