1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
22 #include <sal/types.h>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
26 #include <tools/poly.hxx>
27 #include <vcl/gdimtf.hxx>
28 #include <vcl/metaact.hxx>
29 #include <vcl/outdev.hxx>
30 #include <vcl/virdev.hxx>
34 #define OUTDEV_POLYPOLY_STACKBUF 32
36 void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon
& rPolyPoly
)
38 assert(!is_double_buffered_window());
41 mpMetaFile
->AddAction( new MetaPolyPolygonAction( rPolyPoly
) );
43 sal_uInt16 nPoly
= rPolyPoly
.Count();
45 if ( !IsDeviceOutputNecessary() || (!mbLineColor
&& !mbFillColor
) || !nPoly
|| ImplIsRecordLayout() )
49 if ( !mpGraphics
&& !AcquireGraphics() )
52 if ( mbInitClipRegion
)
55 if ( mbOutputClipped
)
58 if ( mbInitLineColor
)
61 if ( mbInitFillColor
)
64 // use b2dpolygon drawing if possible
65 if((mnAntialiasing
& AntialiasingFlags::EnableB2dDraw
) &&
66 mpGraphics
->supportsOperation(OutDevSupportType::B2DDraw
) &&
67 RasterOp::OverPaint
== GetRasterOp() &&
68 (IsLineColor() || IsFillColor()))
70 const basegfx::B2DHomMatrix
aTransform(ImplGetDeviceTransformation());
71 basegfx::B2DPolyPolygon
aB2DPolyPolygon(rPolyPoly
.getB2DPolyPolygon());
74 // ensure closed - may be asserted, will prevent buffering
75 if(!aB2DPolyPolygon
.isClosed())
77 aB2DPolyPolygon
.setClosed(true);
82 bSuccess
= mpGraphics
->DrawPolyPolygon(
89 if(bSuccess
&& IsLineColor())
91 const basegfx::B2DVector
aB2DLineWidth( 1.0, 1.0 );
92 const bool bPixelSnapHairline(mnAntialiasing
& AntialiasingFlags::PixelSnapHairline
);
94 for(auto const& rPolygon
: aB2DPolyPolygon
)
96 bSuccess
= mpGraphics
->DrawPolyLine(
101 basegfx::B2DLineJoin::NONE
,
102 css::drawing::LineCap_BUTT
,
103 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
114 mpAlphaVDev
->DrawPolyPolygon( rPolyPoly
);
121 // #100127# Map to DrawPolygon
122 const tools::Polygon
& aPoly
= rPolyPoly
.GetObject( 0 );
123 if( aPoly
.GetSize() >= 2 )
125 GDIMetaFile
* pOldMF
= mpMetaFile
;
126 mpMetaFile
= nullptr;
128 DrawPolygon( aPoly
);
135 // #100127# moved real tools::PolyPolygon draw to separate method,
136 // have to call recursively, avoiding duplicate
137 // ImplLogicToDevicePixel calls
138 ImplDrawPolyPolygon( nPoly
, ImplLogicToDevicePixel( rPolyPoly
) );
141 mpAlphaVDev
->DrawPolyPolygon( rPolyPoly
);
144 void OutputDevice::DrawPolygon( const basegfx::B2DPolygon
& rB2DPolygon
)
146 assert(!is_double_buffered_window());
148 // AW: Do NOT paint empty polygons
149 if(rB2DPolygon
.count())
151 basegfx::B2DPolyPolygon
aPP( rB2DPolygon
);
152 DrawPolyPolygon( aPP
);
156 void OutputDevice::DrawPolygon( const tools::Polygon
& rPoly
)
158 assert(!is_double_buffered_window());
161 mpMetaFile
->AddAction( new MetaPolygonAction( rPoly
) );
163 sal_uInt16 nPoints
= rPoly
.GetSize();
165 if ( !IsDeviceOutputNecessary() || (!mbLineColor
&& !mbFillColor
) || (nPoints
< 2) || ImplIsRecordLayout() )
168 // we need a graphics
169 if ( !mpGraphics
&& !AcquireGraphics() )
172 if ( mbInitClipRegion
)
175 if ( mbOutputClipped
)
178 if ( mbInitLineColor
)
181 if ( mbInitFillColor
)
184 // use b2dpolygon drawing if possible
185 if((mnAntialiasing
& AntialiasingFlags::EnableB2dDraw
) &&
186 mpGraphics
->supportsOperation(OutDevSupportType::B2DDraw
) &&
187 RasterOp::OverPaint
== GetRasterOp() &&
188 (IsLineColor() || IsFillColor()))
190 const basegfx::B2DHomMatrix
aTransform(ImplGetDeviceTransformation());
191 basegfx::B2DPolygon
aB2DPolygon(rPoly
.getB2DPolygon());
194 // ensure closed - maybe assert, hinders buffering
195 if(!aB2DPolygon
.isClosed())
197 aB2DPolygon
.setClosed(true);
202 bSuccess
= mpGraphics
->DrawPolyPolygon(
204 basegfx::B2DPolyPolygon(aB2DPolygon
),
209 if(bSuccess
&& IsLineColor())
211 const basegfx::B2DVector
aB2DLineWidth( 1.0, 1.0 );
212 const bool bPixelSnapHairline(mnAntialiasing
& AntialiasingFlags::PixelSnapHairline
);
214 bSuccess
= mpGraphics
->DrawPolyLine(
219 basegfx::B2DLineJoin::NONE
,
220 css::drawing::LineCap_BUTT
,
221 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
229 mpAlphaVDev
->DrawPolygon( rPoly
);
234 tools::Polygon aPoly
= ImplLogicToDevicePixel( rPoly
);
235 const SalPoint
* pPtAry
= reinterpret_cast<const SalPoint
*>(aPoly
.GetConstPointAry());
237 // #100127# Forward beziers to sal, if any
238 if( aPoly
.HasFlags() )
240 const PolyFlags
* pFlgAry
= aPoly
.GetConstFlagAry();
241 if( !mpGraphics
->DrawPolygonBezier( nPoints
, pPtAry
, pFlgAry
, this ) )
243 aPoly
= tools::Polygon::SubdivideBezier(aPoly
);
244 pPtAry
= reinterpret_cast<const SalPoint
*>(aPoly
.GetConstPointAry());
245 mpGraphics
->DrawPolygon( aPoly
.GetSize(), pPtAry
, this );
250 mpGraphics
->DrawPolygon( nPoints
, pPtAry
, this );
253 mpAlphaVDev
->DrawPolygon( rPoly
);
256 // Caution: This method is nearly the same as
257 // OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, double fTransparency),
258 // so when changes are made here do not forget to make changes there, too
260 void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon
& rB2DPolyPoly
)
262 assert(!is_double_buffered_window());
265 mpMetaFile
->AddAction( new MetaPolyPolygonAction( tools::PolyPolygon( rB2DPolyPoly
) ) );
268 ImplDrawPolyPolygonWithB2DPolyPolygon(rB2DPolyPoly
);
271 void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPolygon
& rB2DPolyPoly
)
273 // Do not paint empty PolyPolygons
274 if(!rB2DPolyPoly
.count() || !IsDeviceOutputNecessary())
277 // we need a graphics
278 if( !mpGraphics
&& !AcquireGraphics() )
281 if( mbInitClipRegion
)
284 if( mbOutputClipped
)
287 if( mbInitLineColor
)
290 if( mbInitFillColor
)
293 if((mnAntialiasing
& AntialiasingFlags::EnableB2dDraw
) &&
294 mpGraphics
->supportsOperation(OutDevSupportType::B2DDraw
) &&
295 RasterOp::OverPaint
== GetRasterOp() &&
296 (IsLineColor() || IsFillColor()))
298 const basegfx::B2DHomMatrix
aTransform(ImplGetDeviceTransformation());
299 basegfx::B2DPolyPolygon
aB2DPolyPolygon(rB2DPolyPoly
);
302 // ensure closed - maybe assert, hinders buffering
303 if(!aB2DPolyPolygon
.isClosed())
305 aB2DPolyPolygon
.setClosed(true);
310 bSuccess
= mpGraphics
->DrawPolyPolygon(
317 if(bSuccess
&& IsLineColor())
319 const basegfx::B2DVector
aB2DLineWidth( 1.0, 1.0 );
320 const bool bPixelSnapHairline(mnAntialiasing
& AntialiasingFlags::PixelSnapHairline
);
322 for(auto const& rPolygon
: aB2DPolyPolygon
)
324 bSuccess
= mpGraphics
->DrawPolyLine(
329 basegfx::B2DLineJoin::NONE
,
330 css::drawing::LineCap_BUTT
,
331 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
345 // fallback to old polygon drawing if needed
346 const tools::PolyPolygon
aToolsPolyPolygon( rB2DPolyPoly
);
347 const tools::PolyPolygon aPixelPolyPolygon
= ImplLogicToDevicePixel( aToolsPolyPolygon
);
348 ImplDrawPolyPolygon( aPixelPolyPolygon
.Count(), aPixelPolyPolygon
);
351 // #100127# Extracted from OutputDevice::DrawPolyPolygon()
352 void OutputDevice::ImplDrawPolyPolygon( sal_uInt16 nPoly
, const tools::PolyPolygon
& rPolyPoly
)
354 // AW: This crashes on empty PolyPolygons, avoid that
358 sal_uInt32 aStackAry1
[OUTDEV_POLYPOLY_STACKBUF
];
359 PCONSTSALPOINT aStackAry2
[OUTDEV_POLYPOLY_STACKBUF
];
360 PolyFlags
* aStackAry3
[OUTDEV_POLYPOLY_STACKBUF
];
361 sal_uInt32
* pPointAry
;
362 PCONSTSALPOINT
* pPointAryAry
;
363 const PolyFlags
** pFlagAryAry
;
367 bool bHaveBezier
= false;
368 if ( nPoly
> OUTDEV_POLYPOLY_STACKBUF
)
370 pPointAry
= new sal_uInt32
[nPoly
];
371 pPointAryAry
= new PCONSTSALPOINT
[nPoly
];
372 pFlagAryAry
= new const PolyFlags
*[nPoly
];
376 pPointAry
= aStackAry1
;
377 pPointAryAry
= aStackAry2
;
378 pFlagAryAry
= const_cast<const PolyFlags
**>(aStackAry3
);
383 const tools::Polygon
& rPoly
= rPolyPoly
.GetObject( i
);
384 sal_uInt16 nSize
= rPoly
.GetSize();
387 pPointAry
[j
] = nSize
;
388 pPointAryAry
[j
] = reinterpret_cast<PCONSTSALPOINT
>(rPoly
.GetConstPointAry());
389 pFlagAryAry
[j
] = rPoly
.GetConstFlagAry();
403 // #100127# Forward beziers to sal, if any
406 if( !mpGraphics
->DrawPolygonBezier( *pPointAry
, *pPointAryAry
, *pFlagAryAry
, this ) )
408 tools::Polygon aPoly
= tools::Polygon::SubdivideBezier( rPolyPoly
.GetObject( last
) );
409 mpGraphics
->DrawPolygon( aPoly
.GetSize(), reinterpret_cast<const SalPoint
*>(aPoly
.GetConstPointAry()), this );
414 mpGraphics
->DrawPolygon( *pPointAry
, *pPointAryAry
, this );
419 // #100127# Forward beziers to sal, if any
422 if( !mpGraphics
->DrawPolyPolygonBezier( j
, pPointAry
, pPointAryAry
, pFlagAryAry
, this ) )
424 tools::PolyPolygon aPolyPoly
= tools::PolyPolygon::SubdivideBezier( rPolyPoly
);
425 ImplDrawPolyPolygon( aPolyPoly
.Count(), aPolyPoly
);
430 mpGraphics
->DrawPolyPolygon( j
, pPointAry
, pPointAryAry
, this );
434 if ( pPointAry
!= aStackAry1
)
437 delete[] pPointAryAry
;
438 delete[] pFlagAryAry
;
442 void OutputDevice::ImplDrawPolygon( const tools::Polygon
& rPoly
, const tools::PolyPolygon
* pClipPolyPoly
)
446 ImplDrawPolyPolygon( rPoly
, pClipPolyPoly
);
450 sal_uInt16 nPoints
= rPoly
.GetSize();
455 const SalPoint
* pPtAry
= reinterpret_cast<const SalPoint
*>(rPoly
.GetConstPointAry());
456 mpGraphics
->DrawPolygon( nPoints
, pPtAry
, this );
460 void OutputDevice::ImplDrawPolyPolygon( const tools::PolyPolygon
& rPolyPoly
, const tools::PolyPolygon
* pClipPolyPoly
)
462 tools::PolyPolygon
* pPolyPoly
;
466 pPolyPoly
= new tools::PolyPolygon
;
467 rPolyPoly
.GetIntersection( *pClipPolyPoly
, *pPolyPoly
);
471 pPolyPoly
= const_cast<tools::PolyPolygon
*>(&rPolyPoly
);
473 if( pPolyPoly
->Count() == 1 )
475 const tools::Polygon
& rPoly
= pPolyPoly
->GetObject( 0 );
476 sal_uInt16 nSize
= rPoly
.GetSize();
480 const SalPoint
* pPtAry
= reinterpret_cast<const SalPoint
*>(rPoly
.GetConstPointAry());
481 mpGraphics
->DrawPolygon( nSize
, pPtAry
, this );
484 else if( pPolyPoly
->Count() )
486 sal_uInt16 nCount
= pPolyPoly
->Count();
487 std::unique_ptr
<sal_uInt32
[]> pPointAry(new sal_uInt32
[nCount
]);
488 std::unique_ptr
<PCONSTSALPOINT
[]> pPointAryAry(new PCONSTSALPOINT
[nCount
]);
492 const tools::Polygon
& rPoly
= pPolyPoly
->GetObject( i
);
493 sal_uInt16 nSize
= rPoly
.GetSize();
496 pPointAry
[i
] = nSize
;
497 pPointAryAry
[i
] = reinterpret_cast<PCONSTSALPOINT
>(rPoly
.GetConstPointAry());
506 mpGraphics
->DrawPolygon( pPointAry
[0], pPointAryAry
[0], this );
508 mpGraphics
->DrawPolyPolygon( nCount
, pPointAry
.get(), pPointAryAry
.get(), this );
515 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */