diff options
Diffstat (limited to '')
-rw-r--r-- | vcl/source/outdev/polygon.cxx | 518 |
1 files changed, 518 insertions, 0 deletions
diff --git a/vcl/source/outdev/polygon.cxx b/vcl/source/outdev/polygon.cxx new file mode 100644 index 000000000..e031fb059 --- /dev/null +++ b/vcl/source/outdev/polygon.cxx @@ -0,0 +1,518 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <cassert> + +#include <sal/types.h> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <memory> +#include <tools/poly.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/metaact.hxx> +#include <vcl/outdev.hxx> +#include <vcl/virdev.hxx> + +#include <salgdi.hxx> + +#define OUTDEV_POLYPOLY_STACKBUF 32 + +void OutputDevice::DrawPolyPolygon( const tools::PolyPolygon& rPolyPoly ) +{ + assert(!is_double_buffered_window()); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) ); + + sal_uInt16 nPoly = rPolyPoly.Count(); + + if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || !nPoly || ImplIsRecordLayout() ) + return; + + // we need a graphics + if ( !mpGraphics && !AcquireGraphics() ) + return; + + if ( mbInitClipRegion ) + InitClipRegion(); + + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + InitLineColor(); + + if ( mbInitFillColor ) + InitFillColor(); + + // use b2dpolygon drawing if possible + if((mnAntialiasing & AntialiasingFlags::EnableB2dDraw) && + mpGraphics->supportsOperation(OutDevSupportType::B2DDraw) && + RasterOp::OverPaint == GetRasterOp() && + (IsLineColor() || IsFillColor())) + { + const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation()); + basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPoly.getB2DPolyPolygon()); + bool bSuccess(true); + + // ensure closed - may be asserted, will prevent buffering + if(!aB2DPolyPolygon.isClosed()) + { + aB2DPolyPolygon.setClosed(true); + } + + if(IsFillColor()) + { + bSuccess = mpGraphics->DrawPolyPolygon( + aTransform, + aB2DPolyPolygon, + 0.0, + this); + } + + if(bSuccess && IsLineColor()) + { + const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline); + + for(auto const& rPolygon : aB2DPolyPolygon) + { + bSuccess = mpGraphics->DrawPolyLine( + aTransform, + rPolygon, + 0.0, + 0.0, // tdf#124848 hairline + nullptr, // MM01 + basegfx::B2DLineJoin::NONE, + css::drawing::LineCap_BUTT, + basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default + bPixelSnapHairline, + this); + if (!bSuccess) + break; + } + } + + if(bSuccess) + { + if( mpAlphaVDev ) + mpAlphaVDev->DrawPolyPolygon( rPolyPoly ); + return; + } + } + + if ( nPoly == 1 ) + { + // #100127# Map to DrawPolygon + const tools::Polygon& aPoly = rPolyPoly.GetObject( 0 ); + if( aPoly.GetSize() >= 2 ) + { + GDIMetaFile* pOldMF = mpMetaFile; + mpMetaFile = nullptr; + + DrawPolygon( aPoly ); + + mpMetaFile = pOldMF; + } + } + else + { + // #100127# moved real tools::PolyPolygon draw to separate method, + // have to call recursively, avoiding duplicate + // ImplLogicToDevicePixel calls + ImplDrawPolyPolygon( nPoly, ImplLogicToDevicePixel( rPolyPoly ) ); + } + if( mpAlphaVDev ) + mpAlphaVDev->DrawPolyPolygon( rPolyPoly ); +} + +void OutputDevice::DrawPolygon( const basegfx::B2DPolygon& rB2DPolygon) +{ + assert(!is_double_buffered_window()); + + // AW: Do NOT paint empty polygons + if(rB2DPolygon.count()) + { + basegfx::B2DPolyPolygon aPP( rB2DPolygon ); + DrawPolyPolygon( aPP ); + } +} + +void OutputDevice::DrawPolygon( const tools::Polygon& rPoly ) +{ + assert(!is_double_buffered_window()); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) ); + + sal_uInt16 nPoints = rPoly.GetSize(); + + if ( !IsDeviceOutputNecessary() || (!mbLineColor && !mbFillColor) || (nPoints < 2) || ImplIsRecordLayout() ) + return; + + // we need a graphics + if ( !mpGraphics && !AcquireGraphics() ) + return; + + if ( mbInitClipRegion ) + InitClipRegion(); + + if ( mbOutputClipped ) + return; + + if ( mbInitLineColor ) + InitLineColor(); + + if ( mbInitFillColor ) + InitFillColor(); + + // use b2dpolygon drawing if possible + if((mnAntialiasing & AntialiasingFlags::EnableB2dDraw) && + mpGraphics->supportsOperation(OutDevSupportType::B2DDraw) && + RasterOp::OverPaint == GetRasterOp() && + (IsLineColor() || IsFillColor())) + { + const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation()); + basegfx::B2DPolygon aB2DPolygon(rPoly.getB2DPolygon()); + bool bSuccess(true); + + // ensure closed - maybe assert, hinders buffering + if(!aB2DPolygon.isClosed()) + { + aB2DPolygon.setClosed(true); + } + + if(IsFillColor()) + { + bSuccess = mpGraphics->DrawPolyPolygon( + aTransform, + basegfx::B2DPolyPolygon(aB2DPolygon), + 0.0, + this); + } + + if(bSuccess && IsLineColor()) + { + const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline); + + bSuccess = mpGraphics->DrawPolyLine( + aTransform, + aB2DPolygon, + 0.0, + 0.0, // tdf#124848 hairline + nullptr, // MM01 + basegfx::B2DLineJoin::NONE, + css::drawing::LineCap_BUTT, + basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default + bPixelSnapHairline, + this); + } + + if(bSuccess) + { + if( mpAlphaVDev ) + mpAlphaVDev->DrawPolygon( rPoly ); + return; + } + } + + tools::Polygon aPoly = ImplLogicToDevicePixel( rPoly ); + const SalPoint* pPtAry = reinterpret_cast<const SalPoint*>(aPoly.GetConstPointAry()); + + // #100127# Forward beziers to sal, if any + if( aPoly.HasFlags() ) + { + const PolyFlags* pFlgAry = aPoly.GetConstFlagAry(); + if( !mpGraphics->DrawPolygonBezier( nPoints, pPtAry, pFlgAry, this ) ) + { + aPoly = tools::Polygon::SubdivideBezier(aPoly); + pPtAry = reinterpret_cast<const SalPoint*>(aPoly.GetConstPointAry()); + mpGraphics->DrawPolygon( aPoly.GetSize(), pPtAry, this ); + } + } + else + { + mpGraphics->DrawPolygon( nPoints, pPtAry, this ); + } + if( mpAlphaVDev ) + mpAlphaVDev->DrawPolygon( rPoly ); +} + +// Caution: This method is nearly the same as +// OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly, double fTransparency), +// so when changes are made here do not forget to make changes there, too + +void OutputDevice::DrawPolyPolygon( const basegfx::B2DPolyPolygon& rB2DPolyPoly ) +{ + assert(!is_double_buffered_window()); + + if( mpMetaFile ) + mpMetaFile->AddAction( new MetaPolyPolygonAction( tools::PolyPolygon( rB2DPolyPoly ) ) ); + + // call helper + ImplDrawPolyPolygonWithB2DPolyPolygon(rB2DPolyPoly); +} + +void OutputDevice::ImplDrawPolyPolygonWithB2DPolyPolygon(const basegfx::B2DPolyPolygon& rB2DPolyPoly) +{ + // Do not paint empty PolyPolygons + if(!rB2DPolyPoly.count() || !IsDeviceOutputNecessary()) + return; + + // we need a graphics + if( !mpGraphics && !AcquireGraphics() ) + return; + + if( mbInitClipRegion ) + InitClipRegion(); + + if( mbOutputClipped ) + return; + + if( mbInitLineColor ) + InitLineColor(); + + if( mbInitFillColor ) + InitFillColor(); + + bool bSuccess(false); + + if((mnAntialiasing & AntialiasingFlags::EnableB2dDraw) && + mpGraphics->supportsOperation(OutDevSupportType::B2DDraw) && + RasterOp::OverPaint == GetRasterOp() && + (IsLineColor() || IsFillColor())) + { + const basegfx::B2DHomMatrix aTransform(ImplGetDeviceTransformation()); + basegfx::B2DPolyPolygon aB2DPolyPolygon(rB2DPolyPoly); + bSuccess = true; + + // ensure closed - maybe assert, hinders buffering + if(!aB2DPolyPolygon.isClosed()) + { + aB2DPolyPolygon.setClosed(true); + } + + if(IsFillColor()) + { + bSuccess = mpGraphics->DrawPolyPolygon( + aTransform, + aB2DPolyPolygon, + 0.0, + this); + } + + if(bSuccess && IsLineColor()) + { + const bool bPixelSnapHairline(mnAntialiasing & AntialiasingFlags::PixelSnapHairline); + + for(auto const& rPolygon : aB2DPolyPolygon) + { + bSuccess = mpGraphics->DrawPolyLine( + aTransform, + rPolygon, + 0.0, + 0.0, // tdf#124848 hairline + nullptr, // MM01 + basegfx::B2DLineJoin::NONE, + css::drawing::LineCap_BUTT, + basegfx::deg2rad(15.0), // not used with B2DLineJoin::NONE, but the correct default + bPixelSnapHairline, + this); + if (!bSuccess) + break; + } + } + } + + if (!bSuccess) + { + // fallback to old polygon drawing if needed + const tools::PolyPolygon aToolsPolyPolygon(rB2DPolyPoly); + const tools::PolyPolygon aPixelPolyPolygon = ImplLogicToDevicePixel(aToolsPolyPolygon); + ImplDrawPolyPolygon(aPixelPolyPolygon.Count(), aPixelPolyPolygon); + } + + if (mpAlphaVDev) + mpAlphaVDev->ImplDrawPolyPolygonWithB2DPolyPolygon(rB2DPolyPoly); +} + +// #100127# Extracted from OutputDevice::DrawPolyPolygon() +void OutputDevice::ImplDrawPolyPolygon( sal_uInt16 nPoly, const tools::PolyPolygon& rPolyPoly ) +{ + // AW: This crashes on empty PolyPolygons, avoid that + if(!nPoly) + return; + + sal_uInt32 aStackAry1[OUTDEV_POLYPOLY_STACKBUF]; + PCONSTSALPOINT aStackAry2[OUTDEV_POLYPOLY_STACKBUF]; + PolyFlags* aStackAry3[OUTDEV_POLYPOLY_STACKBUF]; + sal_uInt32* pPointAry; + PCONSTSALPOINT* pPointAryAry; + const PolyFlags** pFlagAryAry; + sal_uInt16 i = 0; + sal_uInt16 j = 0; + sal_uInt16 last = 0; + bool bHaveBezier = false; + if ( nPoly > OUTDEV_POLYPOLY_STACKBUF ) + { + pPointAry = new sal_uInt32[nPoly]; + pPointAryAry = new PCONSTSALPOINT[nPoly]; + pFlagAryAry = new const PolyFlags*[nPoly]; + } + else + { + pPointAry = aStackAry1; + pPointAryAry = aStackAry2; + pFlagAryAry = const_cast<const PolyFlags**>(aStackAry3); + } + + do + { + const tools::Polygon& rPoly = rPolyPoly.GetObject( i ); + sal_uInt16 nSize = rPoly.GetSize(); + if ( nSize ) + { + pPointAry[j] = nSize; + pPointAryAry[j] = reinterpret_cast<PCONSTSALPOINT>(rPoly.GetConstPointAry()); + pFlagAryAry[j] = rPoly.GetConstFlagAry(); + last = i; + + if( pFlagAryAry[j] ) + bHaveBezier = true; + + ++j; + } + ++i; + } + while ( i < nPoly ); + + if ( j == 1 ) + { + // #100127# Forward beziers to sal, if any + if( bHaveBezier ) + { + if( !mpGraphics->DrawPolygonBezier( *pPointAry, *pPointAryAry, *pFlagAryAry, this ) ) + { + tools::Polygon aPoly = tools::Polygon::SubdivideBezier( rPolyPoly.GetObject( last ) ); + mpGraphics->DrawPolygon( aPoly.GetSize(), reinterpret_cast<const SalPoint*>(aPoly.GetConstPointAry()), this ); + } + } + else + { + mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this ); + } + } + else + { + // #100127# Forward beziers to sal, if any + if( bHaveBezier ) + { + if( !mpGraphics->DrawPolyPolygonBezier( j, pPointAry, pPointAryAry, pFlagAryAry, this ) ) + { + tools::PolyPolygon aPolyPoly = tools::PolyPolygon::SubdivideBezier( rPolyPoly ); + ImplDrawPolyPolygon( aPolyPoly.Count(), aPolyPoly ); + } + } + else + { + mpGraphics->DrawPolyPolygon( j, pPointAry, pPointAryAry, this ); + } + } + + if ( pPointAry != aStackAry1 ) + { + delete[] pPointAry; + delete[] pPointAryAry; + delete[] pFlagAryAry; + } +} + +void OutputDevice::ImplDrawPolygon( const tools::Polygon& rPoly, const tools::PolyPolygon* pClipPolyPoly ) +{ + if( pClipPolyPoly ) + { + ImplDrawPolyPolygon( rPoly, pClipPolyPoly ); + } + else + { + sal_uInt16 nPoints = rPoly.GetSize(); + + if ( nPoints < 2 ) + return; + + const SalPoint* pPtAry = reinterpret_cast<const SalPoint*>(rPoly.GetConstPointAry()); + mpGraphics->DrawPolygon( nPoints, pPtAry, this ); + } +} + +void OutputDevice::ImplDrawPolyPolygon( const tools::PolyPolygon& rPolyPoly, const tools::PolyPolygon* pClipPolyPoly ) +{ + tools::PolyPolygon* pPolyPoly; + + if( pClipPolyPoly ) + { + pPolyPoly = new tools::PolyPolygon; + rPolyPoly.GetIntersection( *pClipPolyPoly, *pPolyPoly ); + } + else + { + pPolyPoly = const_cast<tools::PolyPolygon*>(&rPolyPoly); + } + if( pPolyPoly->Count() == 1 ) + { + const tools::Polygon& rPoly = pPolyPoly->GetObject( 0 ); + sal_uInt16 nSize = rPoly.GetSize(); + + if( nSize >= 2 ) + { + const SalPoint* pPtAry = reinterpret_cast<const SalPoint*>(rPoly.GetConstPointAry()); + mpGraphics->DrawPolygon( nSize, pPtAry, this ); + } + } + else if( pPolyPoly->Count() ) + { + sal_uInt16 nCount = pPolyPoly->Count(); + std::unique_ptr<sal_uInt32[]> pPointAry(new sal_uInt32[nCount]); + std::unique_ptr<PCONSTSALPOINT[]> pPointAryAry(new PCONSTSALPOINT[nCount]); + sal_uInt16 i = 0; + do + { + const tools::Polygon& rPoly = pPolyPoly->GetObject( i ); + sal_uInt16 nSize = rPoly.GetSize(); + if ( nSize ) + { + pPointAry[i] = nSize; + pPointAryAry[i] = reinterpret_cast<PCONSTSALPOINT>(rPoly.GetConstPointAry()); + i++; + } + else + nCount--; + } + while( i < nCount ); + + if( nCount == 1 ) + mpGraphics->DrawPolygon( pPointAry[0], pPointAryAry[0], this ); + else + mpGraphics->DrawPolyPolygon( nCount, pPointAry.get(), pPointAryAry.get(), this ); + } + + if( pClipPolyPoly ) + delete pPolyPoly; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |