bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / outdev / polygon.cxx
blob9b0cb3819cc7505519aee35aeeda485aba4f56c3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #include <cassert>
22 #include <sal/types.h>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <memory>
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>
32 #include <salgdi.hxx>
34 #define OUTDEV_POLYPOLY_STACKBUF 32
36 void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
38 assert(!is_double_buffered_window());
40 if( mpMetaFile )
41 mpMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) );
43 sal_uInt16 nPoly = rPolyPoly.Count();
45 if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || !nPoly || ImplIsRecordLayout() )
46 return;
48 // we need a graphics
49 if ( !mpGraphics && !AcquireGraphics() )
50 return;
52 if ( mbInitClipRegion )
53 InitClipRegion();
55 if ( mbOutputClipped )
56 return;
58 if ( mbInitLineColor )
59 InitLineColor();
61 if ( mbInitFillColor )
62 InitFillColor();
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());
72 bool bSuccess(true);
74 // ensure closed - may be asserted, will prevent buffering
75 if(!aB2DPolyPolygon.isClosed())
77 aB2DPolyPolygon.setClosed(true);
80 if(IsFillColor())
82 bSuccess = mpGraphics->DrawPolyPolygon(
83 aTransform,
84 aB2DPolyPolygon,
85 0.0,
86 this);
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(
97 aTransform,
98 rPolygon,
99 0.0,
100 aB2DLineWidth,
101 basegfx::B2DLineJoin::NONE,
102 css::drawing::LineCap_BUTT,
103 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
104 bPixelSnapHairline,
105 this);
106 if (!bSuccess)
107 break;
111 if(bSuccess)
113 if( mpAlphaVDev )
114 mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
115 return;
119 if ( nPoly == 1 )
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 );
130 mpMetaFile = pOldMF;
133 else
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 ) );
140 if( mpAlphaVDev )
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());
160 if( mpMetaFile )
161 mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
163 sal_uInt16 nPoints = rPoly.GetSize();
165 if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || (nPoints < 2) || ImplIsRecordLayout() )
166 return;
168 // we need a graphics
169 if ( !mpGraphics && !AcquireGraphics() )
170 return;
172 if ( mbInitClipRegion )
173 InitClipRegion();
175 if ( mbOutputClipped )
176 return;
178 if ( mbInitLineColor )
179 InitLineColor();
181 if ( mbInitFillColor )
182 InitFillColor();
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());
192 bool bSuccess(true);
194 // ensure closed - maybe assert, hinders buffering
195 if(!aB2DPolygon.isClosed())
197 aB2DPolygon.setClosed(true);
200 if(IsFillColor())
202 bSuccess = mpGraphics->DrawPolyPolygon(
203 aTransform,
204 basegfx::B2DPolyPolygon(aB2DPolygon),
205 0.0,
206 this);
209 if(bSuccess && IsLineColor())
211 const basegfx::B2DVector aB2DLineWidth( 1.0, 1.0 );
212 const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline);
214 bSuccess = mpGraphics->DrawPolyLine(
215 aTransform,
216 aB2DPolygon,
217 0.0,
218 aB2DLineWidth,
219 basegfx::B2DLineJoin::NONE,
220 css::drawing::LineCap_BUTT,
221 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
222 bPixelSnapHairline,
223 this);
226 if(bSuccess)
228 if( mpAlphaVDev )
229 mpAlphaVDev->DrawPolygon( rPoly );
230 return;
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 );
248 else
250 mpGraphics->DrawPolygon( nPoints, pPtAry, this );
252 if( mpAlphaVDev )
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());
264 if( mpMetaFile )
265 mpMetaFile->AddAction( new MetaPolyPolygonAction( tools::PolyPolygon( rB2DPolyPoly ) ) );
267 // call helper
268 ImplDrawPolyPolygonWithB2DPolyPolygon(rB2DPolyPoly);
271 void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPolygon& rB2DPolyPoly)
273 // Do not paint empty PolyPolygons
274 if(!rB2DPolyPoly.count() || !IsDeviceOutputNecessary())
275 return;
277 // we need a graphics
278 if( !mpGraphics && !AcquireGraphics() )
279 return;
281 if( mbInitClipRegion )
282 InitClipRegion();
284 if( mbOutputClipped )
285 return;
287 if( mbInitLineColor )
288 InitLineColor();
290 if( mbInitFillColor )
291 InitFillColor();
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);
300 bool bSuccess(true);
302 // ensure closed - maybe assert, hinders buffering
303 if(!aB2DPolyPolygon.isClosed())
305 aB2DPolyPolygon.setClosed(true);
308 if(IsFillColor())
310 bSuccess = mpGraphics->DrawPolyPolygon(
311 aTransform,
312 aB2DPolyPolygon,
313 0.0,
314 this);
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(
325 aTransform,
326 rPolygon,
327 0.0,
328 aB2DLineWidth,
329 basegfx::B2DLineJoin::NONE,
330 css::drawing::LineCap_BUTT,
331 basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default
332 bPixelSnapHairline,
333 this);
334 if (!bSuccess)
335 break;
339 if(bSuccess)
341 return;
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
355 if(!nPoly)
356 return;
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;
364 sal_uInt16 i = 0;
365 sal_uInt16 j = 0;
366 sal_uInt16 last = 0;
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];
374 else
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();
385 if ( nSize )
387 pPointAry[j] = nSize;
388 pPointAryAry[j] = reinterpret_cast<PCONSTSALPOINT>(rPoly.GetConstPointAry());
389 pFlagAryAry[j] = rPoly.GetConstFlagAry();
390 last = i;
392 if( pFlagAryAry[j] )
393 bHaveBezier = true;
395 ++j;
397 ++i;
399 while ( i < nPoly );
401 if ( j == 1 )
403 // #100127# Forward beziers to sal, if any
404 if( bHaveBezier )
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 );
412 else
414 mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this );
417 else
419 // #100127# Forward beziers to sal, if any
420 if( bHaveBezier )
422 if( !mpGraphics->DrawPolyPolygonBezier( j, pPointAry, pPointAryAry, pFlagAryAry, this ) )
424 tools::PolyPolygon aPolyPoly = tools::PolyPolygon::SubdivideBezier( rPolyPoly );
425 ImplDrawPolyPolygon( aPolyPoly.Count(), aPolyPoly );
428 else
430 mpGraphics->DrawPolyPolygon( j, pPointAry, pPointAryAry, this );
434 if ( pPointAry != aStackAry1 )
436 delete[] pPointAry;
437 delete[] pPointAryAry;
438 delete[] pFlagAryAry;
442 void OutputDevice::ImplDrawPolygon( const tools::Polygon& rPoly, const tools::PolyPolygon* pClipPolyPoly )
444 if( pClipPolyPoly )
446 ImplDrawPolyPolygon( rPoly, pClipPolyPoly );
448 else
450 sal_uInt16 nPoints = rPoly.GetSize();
452 if ( nPoints < 2 )
453 return;
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;
464 if( pClipPolyPoly )
466 pPolyPoly = new tools::PolyPolygon;
467 rPolyPoly.GetIntersection( *pClipPolyPoly, *pPolyPoly );
469 else
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();
478 if( nSize >= 2 )
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]);
489 sal_uInt16 i = 0;
492 const tools::Polygon& rPoly = pPolyPoly->GetObject( i );
493 sal_uInt16 nSize = rPoly.GetSize();
494 if ( nSize )
496 pPointAry[i] = nSize;
497 pPointAryAry[i] = reinterpret_cast<PCONSTSALPOINT>(rPoly.GetConstPointAry());
498 i++;
500 else
501 nCount--;
503 while( i < nCount );
505 if( nCount == 1 )
506 mpGraphics->DrawPolygon( pPointAry[0], pPointAryAry[0], this );
507 else
508 mpGraphics->DrawPolyPolygon( nCount, pPointAry.get(), pPointAryAry.get(), this );
511 if( pClipPolyPoly )
512 delete pPolyPoly;
515 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */