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(mpGraphics
->supportsOperation(OutDevSupportType::B2DDraw
) &&
66 RasterOp::OverPaint
== GetRasterOp() &&
67 (IsLineColor() || IsFillColor()))
69 const basegfx::B2DHomMatrix
aTransform(ImplGetDeviceTransformation());
70 basegfx::B2DPolyPolygon
aB2DPolyPolygon(rPolyPoly
.getB2DPolyPolygon());
73 // ensure closed - may be asserted, will prevent buffering
74 if(!aB2DPolyPolygon
.isClosed())
76 aB2DPolyPolygon
.setClosed(true);
81 bSuccess
= mpGraphics
->DrawPolyPolygon(
88 if(bSuccess
&& IsLineColor())
90 const bool bPixelSnapHairline(mnAntialiasing
& AntialiasingFlags::PixelSnapHairline
);
92 for(auto const& rPolygon
: aB2DPolyPolygon
)
94 bSuccess
= mpGraphics
->DrawPolyLine(
98 0.0, // tdf#124848 hairline
100 basegfx::B2DLineJoin::NONE
,
101 css::drawing::LineCap_BUTT
,
102 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
113 mpAlphaVDev
->DrawPolyPolygon( rPolyPoly
);
120 // #100127# Map to DrawPolygon
121 const tools::Polygon
& aPoly
= rPolyPoly
.GetObject( 0 );
122 if( aPoly
.GetSize() >= 2 )
124 GDIMetaFile
* pOldMF
= mpMetaFile
;
125 mpMetaFile
= nullptr;
127 DrawPolygon( aPoly
);
134 // #100127# moved real tools::PolyPolygon draw to separate method,
135 // have to call recursively, avoiding duplicate
136 // ImplLogicToDevicePixel calls
137 ImplDrawPolyPolygon( nPoly
, ImplLogicToDevicePixel( rPolyPoly
) );
140 mpAlphaVDev
->DrawPolyPolygon( rPolyPoly
);
143 void OutputDevice::DrawPolygon( const basegfx::B2DPolygon
& rB2DPolygon
)
145 assert(!is_double_buffered_window());
147 // AW: Do NOT paint empty polygons
148 if(rB2DPolygon
.count())
150 basegfx::B2DPolyPolygon
aPP( rB2DPolygon
);
151 DrawPolyPolygon( aPP
);
155 void OutputDevice::DrawPolygon( const tools::Polygon
& rPoly
)
157 assert(!is_double_buffered_window());
160 mpMetaFile
->AddAction( new MetaPolygonAction( rPoly
) );
162 sal_uInt16 nPoints
= rPoly
.GetSize();
164 if ( !IsDeviceOutputNecessary() || (!mbLineColor
&& !mbFillColor
) || (nPoints
< 2) || ImplIsRecordLayout() )
167 // we need a graphics
168 if ( !mpGraphics
&& !AcquireGraphics() )
171 if ( mbInitClipRegion
)
174 if ( mbOutputClipped
)
177 if ( mbInitLineColor
)
180 if ( mbInitFillColor
)
183 // use b2dpolygon drawing if possible
184 if(mpGraphics
->supportsOperation(OutDevSupportType::B2DDraw
) &&
185 RasterOp::OverPaint
== GetRasterOp() &&
186 (IsLineColor() || IsFillColor()))
188 const basegfx::B2DHomMatrix
aTransform(ImplGetDeviceTransformation());
189 basegfx::B2DPolygon
aB2DPolygon(rPoly
.getB2DPolygon());
192 // ensure closed - maybe assert, hinders buffering
193 if(!aB2DPolygon
.isClosed())
195 aB2DPolygon
.setClosed(true);
200 bSuccess
= mpGraphics
->DrawPolyPolygon(
202 basegfx::B2DPolyPolygon(aB2DPolygon
),
207 if(bSuccess
&& IsLineColor())
209 const bool bPixelSnapHairline(mnAntialiasing
& AntialiasingFlags::PixelSnapHairline
);
211 bSuccess
= mpGraphics
->DrawPolyLine(
215 0.0, // tdf#124848 hairline
217 basegfx::B2DLineJoin::NONE
,
218 css::drawing::LineCap_BUTT
,
219 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
227 mpAlphaVDev
->DrawPolygon( rPoly
);
232 tools::Polygon aPoly
= ImplLogicToDevicePixel( rPoly
);
233 const Point
* pPtAry
= aPoly
.GetConstPointAry();
235 // #100127# Forward beziers to sal, if any
236 if( aPoly
.HasFlags() )
238 const PolyFlags
* pFlgAry
= aPoly
.GetConstFlagAry();
239 if( !mpGraphics
->DrawPolygonBezier( nPoints
, pPtAry
, pFlgAry
, this ) )
241 aPoly
= tools::Polygon::SubdivideBezier(aPoly
);
242 pPtAry
= aPoly
.GetConstPointAry();
243 mpGraphics
->DrawPolygon( aPoly
.GetSize(), pPtAry
, this );
248 mpGraphics
->DrawPolygon( nPoints
, pPtAry
, this );
251 mpAlphaVDev
->DrawPolygon( rPoly
);
254 // Caution: This method is nearly the same as
255 // OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, double fTransparency),
256 // so when changes are made here do not forget to make changes there, too
258 void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon
& rB2DPolyPoly
)
260 assert(!is_double_buffered_window());
263 mpMetaFile
->AddAction( new MetaPolyPolygonAction( tools::PolyPolygon( rB2DPolyPoly
) ) );
266 ImplDrawPolyPolygonWithB2DPolyPolygon(rB2DPolyPoly
);
269 void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPolygon
& rB2DPolyPoly
)
271 // Do not paint empty PolyPolygons
272 if(!rB2DPolyPoly
.count() || !IsDeviceOutputNecessary())
275 // we need a graphics
276 if( !mpGraphics
&& !AcquireGraphics() )
279 if( mbInitClipRegion
)
282 if( mbOutputClipped
)
285 if( mbInitLineColor
)
288 if( mbInitFillColor
)
291 bool bSuccess(false);
293 if(mpGraphics
->supportsOperation(OutDevSupportType::B2DDraw
) &&
294 RasterOp::OverPaint
== GetRasterOp() &&
295 (IsLineColor() || IsFillColor()))
297 const basegfx::B2DHomMatrix
aTransform(ImplGetDeviceTransformation());
298 basegfx::B2DPolyPolygon
aB2DPolyPolygon(rB2DPolyPoly
);
301 // ensure closed - maybe assert, hinders buffering
302 if(!aB2DPolyPolygon
.isClosed())
304 aB2DPolyPolygon
.setClosed(true);
309 bSuccess
= mpGraphics
->DrawPolyPolygon(
316 if(bSuccess
&& IsLineColor())
318 const bool bPixelSnapHairline(mnAntialiasing
& AntialiasingFlags::PixelSnapHairline
);
320 for(auto const& rPolygon
: aB2DPolyPolygon
)
322 bSuccess
= mpGraphics
->DrawPolyLine(
326 0.0, // tdf#124848 hairline
328 basegfx::B2DLineJoin::NONE
,
329 css::drawing::LineCap_BUTT
,
330 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
341 // fallback to old polygon drawing if needed
342 const tools::PolyPolygon
aToolsPolyPolygon(rB2DPolyPoly
);
343 const tools::PolyPolygon aPixelPolyPolygon
= ImplLogicToDevicePixel(aToolsPolyPolygon
);
344 ImplDrawPolyPolygon(aPixelPolyPolygon
.Count(), aPixelPolyPolygon
);
348 mpAlphaVDev
->ImplDrawPolyPolygonWithB2DPolyPolygon(rB2DPolyPoly
);
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 const Point
* aStackAry2
[OUTDEV_POLYPOLY_STACKBUF
];
360 PolyFlags
* aStackAry3
[OUTDEV_POLYPOLY_STACKBUF
];
361 sal_uInt32
* pPointAry
;
362 const Point
** pPointAryAry
;
363 const PolyFlags
** pFlagAryAry
;
367 bool bHaveBezier
= false;
368 if ( nPoly
> OUTDEV_POLYPOLY_STACKBUF
)
370 pPointAry
= new sal_uInt32
[nPoly
];
371 pPointAryAry
= new const Point
*[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
] = 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(), 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 Point
* pPtAry
= 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 Point
* pPtAry
= 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
<const Point
*[]> pPointAryAry(new const Point
*[nCount
]);
492 const tools::Polygon
& rPoly
= pPolyPoly
->GetObject( i
);
493 sal_uInt16 nSize
= rPoly
.GetSize();
496 pPointAry
[i
] = nSize
;
497 pPointAryAry
[i
] = 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: */