LibreOffice Module vcl (master) 1
line.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
24#include <tools/debug.hxx>
26
27#include <vcl/lineinfo.hxx>
28#include <vcl/metaact.hxx>
29#include <vcl/virdev.hxx>
30
31#include <drawmode.hxx>
32#include <salgdi.hxx>
33
34#include <cassert>
35#include <numeric>
36
38{
39
40 if ( mpMetaFile )
42
43 if ( mbLineColor )
44 {
45 mbInitLineColor = true;
46 mbLineColor = false;
48 }
49
50 if( mpAlphaVDev )
52}
53
54void OutputDevice::SetLineColor( const Color& rColor )
55{
56
57 Color aColor = vcl::drawmode::GetLineColor(rColor, GetDrawMode(), GetSettings().GetStyleSettings());
58
59 if( mpMetaFile )
60 mpMetaFile->AddAction( new MetaLineColorAction( aColor, true ) );
61
62 if( aColor.IsTransparent() )
63 {
64 if ( mbLineColor )
65 {
66 mbInitLineColor = true;
67 mbLineColor = false;
69 }
70 }
71 else
72 {
73 if( maLineColor != aColor )
74 {
75 mbInitLineColor = true;
76 mbLineColor = true;
77 maLineColor = aColor;
78 }
79 }
80
81 if( mpAlphaVDev )
83}
84
86{
88
89 if( mbLineColor )
90 {
93 else if( RasterOp::N1 == meRasterOp )
95 else if( RasterOp::Invert == meRasterOp )
97 else
99 }
100 else
101 {
103 }
104
105 mbInitLineColor = false;
106}
107
108void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt,
109 const LineInfo& rLineInfo )
110{
111 assert(!is_double_buffered_window());
112
113 if ( rLineInfo.IsDefault() )
114 {
115 DrawLine( rStartPt, rEndPt );
116 return;
117 }
118
119 if ( mpMetaFile )
120 mpMetaFile->AddAction( new MetaLineAction( rStartPt, rEndPt, rLineInfo ) );
121
122 if ( !IsDeviceOutputNecessary() || !mbLineColor || ( LineStyle::NONE == rLineInfo.GetStyle() ) || ImplIsRecordLayout() )
123 return;
124
125 if( !mpGraphics && !AcquireGraphics() )
126 return;
127 assert(mpGraphics);
128
129 if ( mbInitClipRegion )
131
132 if ( mbOutputClipped )
133 return;
134
135 const Point aStartPt( ImplLogicToDevicePixel( rStartPt ) );
136 const Point aEndPt( ImplLogicToDevicePixel( rEndPt ) );
137 const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) );
138 const bool bDashUsed(LineStyle::Dash == aInfo.GetStyle());
139 const bool bLineWidthUsed(aInfo.GetWidth() > 1);
140
141 if ( mbInitLineColor )
143
144 if(bDashUsed || bLineWidthUsed)
145 {
146 basegfx::B2DPolygon aLinePolygon;
147 aLinePolygon.append(basegfx::B2DPoint(aStartPt.X(), aStartPt.Y()));
148 aLinePolygon.append(basegfx::B2DPoint(aEndPt.X(), aEndPt.Y()));
149
150 drawLine( basegfx::B2DPolyPolygon(aLinePolygon), aInfo );
151 }
152 else
153 {
154 mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), *this );
155 }
156
157 if( mpAlphaVDev )
158 mpAlphaVDev->DrawLine( rStartPt, rEndPt, rLineInfo );
159}
160
161void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt )
162{
163 assert(!is_double_buffered_window());
164
165 if ( mpMetaFile )
166 mpMetaFile->AddAction( new MetaLineAction( rStartPt, rEndPt ) );
167
169 return;
170
171 if ( !mpGraphics && !AcquireGraphics() )
172 return;
173 assert(mpGraphics);
174
175 if ( mbInitClipRegion )
177
178 if ( mbOutputClipped )
179 return;
180
181 if ( mbInitLineColor )
183
184 bool bDrawn = false;
185
186 // #i101598# support AA and snap for lines, too
189 && IsLineColor())
190 {
191 // at least transform with double precision to device coordinates; this will
192 // avoid pixel snap of single, appended lines
194 basegfx::B2DPolygon aB2DPolyLine;
195
196 aB2DPolyLine.append(basegfx::B2DPoint(rStartPt.X(), rStartPt.Y()));
197 aB2DPolyLine.append(basegfx::B2DPoint(rEndPt.X(), rEndPt.Y()));
198 aB2DPolyLine.transform( aTransform );
199
200 const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
201
202 bDrawn = mpGraphics->DrawPolyLine(
204 aB2DPolyLine,
205 0.0,
206 0.0, // tdf#124848 hairline
207 nullptr, // MM01
209 css::drawing::LineCap_BUTT,
210 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
211 bPixelSnapHairline,
212 *this);
213 }
214 if(!bDrawn)
215 {
216 const Point aStartPt(ImplLogicToDevicePixel(rStartPt));
217 const Point aEndPt(ImplLogicToDevicePixel(rEndPt));
218 mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), *this );
219 }
220
221 if( mpAlphaVDev )
222 mpAlphaVDev->DrawLine( rStartPt, rEndPt );
223}
224
225void OutputDevice::drawLine( basegfx::B2DPolyPolygon aLinePolyPolygon, const LineInfo& rInfo )
226{
227 static const bool bFuzzing = utl::ConfigManager::IsFuzzing();
230 && IsLineColor());
231 basegfx::B2DPolyPolygon aFillPolyPolygon;
232 const bool bDashUsed(LineStyle::Dash == rInfo.GetStyle());
233 const bool bLineWidthUsed(rInfo.GetWidth() > 1);
234
235 if (!bFuzzing && bDashUsed && aLinePolyPolygon.count())
236 {
237 ::std::vector< double > fDotDashArray = rInfo.GetDotDashArray();
238 const double fAccumulated(::std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0));
239
240 if(fAccumulated > 0.0)
241 {
243
244 for(auto const& rPolygon : std::as_const(aLinePolyPolygon))
245 {
246 basegfx::B2DPolyPolygon aLineTarget;
248 rPolygon,
249 fDotDashArray,
250 &aLineTarget);
251 aResult.append(aLineTarget);
252 }
253
254 aLinePolyPolygon = aResult;
255 }
256 }
257
258 if(bLineWidthUsed && aLinePolyPolygon.count())
259 {
260 const double fHalfLineWidth((rInfo.GetWidth() * 0.5) + 0.5);
261
262 if(aLinePolyPolygon.areControlPointsUsed())
263 {
264 // #i110768# When area geometry has to be created, do not
265 // use the fallback bezier decomposition inside createAreaGeometry,
266 // but one that is at least as good as ImplSubdivideBezier was.
267 // There, Polygon::AdaptiveSubdivide was used with default parameter
268 // 1.0 as quality index.
269 static int nRecurseLimit = utl::ConfigManager::IsFuzzing() ? 10 : 30;
270 aLinePolyPolygon = basegfx::utils::adaptiveSubdivideByDistance(aLinePolyPolygon, 1.0, nRecurseLimit);
271 }
272
273 for(auto const& rPolygon : std::as_const(aLinePolyPolygon))
274 {
276 rPolygon,
277 fHalfLineWidth,
278 rInfo.GetLineJoin(),
279 rInfo.GetLineCap()));
280 }
281
282 aLinePolyPolygon.clear();
283 }
284
285 GDIMetaFile* pOldMetaFile = mpMetaFile;
286 mpMetaFile = nullptr;
287
288 if(aLinePolyPolygon.count())
289 {
290 for(auto const& rB2DPolygon : std::as_const(aLinePolyPolygon))
291 {
292 const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
293 bool bDone(false);
294
295 if(bTryB2d)
296 {
297 bDone = mpGraphics->DrawPolyLine(
299 rB2DPolygon,
300 0.0,
301 0.0, // tdf#124848 hairline
302 nullptr, // MM01
304 css::drawing::LineCap_BUTT,
305 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
306 bPixelSnapHairline,
307 *this);
308 }
309
310 if(!bDone)
311 {
312 tools::Polygon aPolygon(rB2DPolygon);
314 aPolygon.GetSize(),
315 aPolygon.GetPointAry(),
316 *this);
317 }
318 }
319 }
320
321 if(aFillPolyPolygon.count())
322 {
323 const Color aOldLineColor( maLineColor );
324 const Color aOldFillColor( maFillColor );
325
326 SetLineColor();
328 SetFillColor( aOldLineColor );
330
331 bool bDone(false);
332
333 if (bFuzzing)
334 {
335 const basegfx::B2DRange aRange(basegfx::utils::getRange(aFillPolyPolygon));
336 if (aRange.getMaxX() - aRange.getMinX() > 0x10000000
337 || aRange.getMaxY() - aRange.getMinY() > 0x10000000)
338 {
339 SAL_WARN("vcl.gdi", "drawLine, skipping suspicious range of: "
340 << aRange << " for fuzzing performance");
341 bDone = true;
342 }
343 }
344
345 if (bTryB2d && !bDone)
346 {
349 aFillPolyPolygon,
350 0.0,
351 *this);
352 }
353
354 if(!bDone)
355 {
356 for(auto const& rB2DPolygon : std::as_const(aFillPolyPolygon))
357 {
358 tools::Polygon aPolygon(rB2DPolygon);
359
360 // need to subdivide, mpGraphics->DrawPolygon ignores curves
361 aPolygon.AdaptiveSubdivide(aPolygon);
362 mpGraphics->DrawPolygon(aPolygon.GetSize(), aPolygon.GetConstPointAry(), *this);
363 }
364 }
365
366 SetFillColor( aOldFillColor );
367 SetLineColor( aOldLineColor );
368 }
369
370 mpMetaFile = pOldMetaFile;
371}
372
373/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsTransparent() const
void AddAction(const rtl::Reference< MetaAction > &pAction)
Definition: gdimtf.cxx:585
virtual void InitClipRegion()
SAL_DLLPRIVATE bool is_double_buffered_window() const
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
void DrawLine(const Point &rStartPt, const Point &rEndPt)
Definition: line.cxx:161
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
bool IsLineColor() const
Definition: outdev.hxx:511
bool mbInitClipRegion
Definition: outdev.hxx:252
RasterOp meRasterOp
Definition: outdev.hxx:232
void SetFillColor()
Definition: fill.cxx:29
AntialiasingFlags mnAntialiasing
Definition: outdev.hxx:237
bool IsDeviceOutputNecessary() const
Definition: outdev.hxx:481
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
DrawModeFlags GetDrawMode() const
Definition: outdev.hxx:487
Color maFillColor
Definition: outdev.hxx:227
const AllSettings & GetSettings() const
Definition: outdev.hxx:288
bool mbLineColor
Definition: outdev.hxx:246
constexpr tools::Long Y() const
constexpr tools::Long X() const
virtual void SetROPLineColor(SalROPColor nROPColor)=0
virtual void SetLineColor()=0
void DrawLine(tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2, const OutputDevice &rOutDev)
virtual bool supportsOperation(OutDevSupportType) const =0
void DrawPolyLine(sal_uInt32 nPoints, Point const *pPtAry, const OutputDevice &rOutDev)
void DrawPolyPolygon(sal_uInt32 nPoly, const sal_uInt32 *pPoints, const Point **pPtAry, const OutputDevice &rOutDev)
void DrawPolygon(sal_uInt32 nPoints, const Point *pPtAry, const OutputDevice &rOutDev)
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
bool areControlPointsUsed() const
sal_uInt32 count() const
void transform(const basegfx::B2DHomMatrix &rMatrix)
void append(const basegfx::B2DPoint &rPoint, sal_uInt32 nCount)
TYPE getMaxX() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
const Point * GetConstPointAry() const
sal_uInt16 GetSize() const
Point * GetPointAry()
void AdaptiveSubdivide(tools::Polygon &rResult, const double d=1.0) const
static bool IsFuzzing()
constexpr ::Color COL_ALPHA_OPAQUE(0xff, 0xff, 0xff)
constexpr ::Color COL_TRANSPARENT(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
#define DBG_TESTSOLARMUTEX()
#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))
B2DPolygon adaptiveSubdivideByDistance(const B2DPolygon &rCandidate, double fDistanceBound, int nRecurseLimit)
void applyLineDashing(const B2DPolygon &rCandidate, const std::vector< double > &rDotDashArray, B2DPolyPolygon *pLineTarget, B2DPolyPolygon *pGapTarget, double fDotDashLength)
B2DRange getRange(const B2DPolygon &rCandidate)
constexpr double deg2rad(double v)
Color GetLineColor(Color const &rColor, DrawModeFlags nDrawMode, StyleSettings const &rStyleSettings)
Definition: drawmode.cxx:30