diff options
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/unx/generic/gdi/gdiimpl.cxx | 274 | ||||
-rw-r--r-- | vcl/unx/generic/gdi/gdiimpl.hxx | 6 | ||||
-rw-r--r-- | vcl/unx/generic/gdi/salgdi.cxx | 10 | ||||
-rw-r--r-- | vcl/unx/generic/gdi/xrender_peer.hxx | 11 |
4 files changed, 225 insertions, 76 deletions
diff --git a/vcl/unx/generic/gdi/gdiimpl.cxx b/vcl/unx/generic/gdi/gdiimpl.cxx index a5fcf86cd3a7..8b0742b4ec68 100644 --- a/vcl/unx/generic/gdi/gdiimpl.cxx +++ b/vcl/unx/generic/gdi/gdiimpl.cxx @@ -50,6 +50,7 @@ #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <basegfx/polygon/b2dpolypolygoncutter.hxx> #include <basegfx/polygon/b2dtrapezoid.hxx> +#include <basegfx/utils/systemdependentdata.hxx> #include <ControlCacheKey.hxx> #undef SALGDI2_TESTTRANS @@ -1569,6 +1570,110 @@ bool X11SalGraphicsImpl::drawFilledTrapezoids( const basegfx::B2DTrapezoid* pB2D return true; } +bool X11SalGraphicsImpl::drawFilledTriangles( + const basegfx::B2DHomMatrix& rObjectToDevice, + const basegfx::triangulator::B2DTriangleVector& rTriangles, + double fTransparency, + bool bPixelOffset) +{ + if(rTriangles.empty()) + return true; + + Picture aDstPic = GetXRenderPicture(); + // check xrender support for this drawable + if( !aDstPic ) + { + return false; + } + + // prepare transformation for ObjectToDevice coordinate system + basegfx::B2DHomMatrix aObjectToDevice(rObjectToDevice); + + if(bPixelOffset) + { + aObjectToDevice = basegfx::utils::createTranslateB2DHomMatrix(0.5, 0.5) * aObjectToDevice; + } + + // convert the Triangles into XRender-Triangles + std::vector<XTriangle> aTriVector(rTriangles.size()); + sal_uInt32 nIndex(0); + + for(const auto& rCandidate : rTriangles) + { + const basegfx::B2DPoint aP1(aObjectToDevice * rCandidate.getA()); + const basegfx::B2DPoint aP2(aObjectToDevice * rCandidate.getB()); + const basegfx::B2DPoint aP3(aObjectToDevice * rCandidate.getC()); + XTriangle& rTri(aTriVector[nIndex++]); + + rTri.p1.x = XDoubleToFixed(aP1.getX()); + rTri.p1.y = XDoubleToFixed(aP1.getY()); + + rTri.p2.x = XDoubleToFixed(aP2.getX()); + rTri.p2.y = XDoubleToFixed(aP2.getY()); + + rTri.p3.x = XDoubleToFixed(aP3.getX()); + rTri.p3.y = XDoubleToFixed(aP3.getY()); + } + + // get xrender Picture for polygon foreground + // TODO: cache it like the target picture which uses GetXRenderPicture() + XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); + SalDisplay::RenderEntry& rEntry = mrParent.GetDisplay()->GetRenderEntries( mrParent.m_nXScreen )[ 32 ]; + if( !rEntry.m_aPicture ) + { + Display* pXDisplay = mrParent.GetXDisplay(); + + rEntry.m_aPixmap = limitXCreatePixmap( pXDisplay, mrParent.hDrawable_, 1, 1, 32 ); + XRenderPictureAttributes aAttr; + aAttr.repeat = int(true); + + XRenderPictFormat* pXRPF = rRenderPeer.FindStandardFormat( PictStandardARGB32 ); + rEntry.m_aPicture = rRenderPeer.CreatePicture( rEntry.m_aPixmap, pXRPF, CPRepeat, &aAttr ); + } + + // set polygon foreground color and opacity + XRenderColor aRenderColor = GetXRenderColor( mnBrushColor , fTransparency ); + rRenderPeer.FillRectangle( PictOpSrc, rEntry.m_aPicture, &aRenderColor, 0, 0, 1, 1 ); + + // set clipping + // TODO: move into GetXRenderPicture? + if( mrParent.mpClipRegion && !XEmptyRegion( mrParent.mpClipRegion ) ) + rRenderPeer.SetPictureClipRegion( aDstPic, mrParent.mpClipRegion ); + + // render the trapezoids + const XRenderPictFormat* pMaskFormat = rRenderPeer.GetStandardFormatA8(); + rRenderPeer.CompositeTriangles( PictOpOver, + rEntry.m_aPicture, aDstPic, pMaskFormat, 0, 0, &aTriVector[0], aTriVector.size() ); + + return true; +} + +class SystemDependentData_Triangulation : public basegfx::SystemDependentData +{ +private: + basegfx::triangulator::B2DTriangleVector maTriangles; + basegfx::B2DVector maLineWidth; + +public: + SystemDependentData_Triangulation( + basegfx::SystemDependentDataManager& rSystemDependentDataManager, + const basegfx::triangulator::B2DTriangleVector& rTriangles, + const basegfx::B2DVector& rLineWidth); + + const basegfx::triangulator::B2DTriangleVector& getTriangles() const { return maTriangles; } + const basegfx::B2DVector& getLineWidth() const { return maLineWidth; } +}; + +SystemDependentData_Triangulation::SystemDependentData_Triangulation( + basegfx::SystemDependentDataManager& rSystemDependentDataManager, + const basegfx::triangulator::B2DTriangleVector& rTriangles, + const basegfx::B2DVector& rLineWidth) +: basegfx::SystemDependentData(rSystemDependentDataManager), + maTriangles(rTriangles), + maLineWidth(rLineWidth) +{ +} + bool X11SalGraphicsImpl::drawPolyLine( const basegfx::B2DHomMatrix& rObjectToDevice, const basegfx::B2DPolygon& rPolygon, @@ -1579,96 +1684,121 @@ bool X11SalGraphicsImpl::drawPolyLine( double fMiterMinimumAngle, bool bPixelSnapHairline) { - // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline - const basegfx::B2DVector aLineWidth(rObjectToDevice * rLineWidth); - const bool bIsHairline((aLineWidth.getX() == aLineWidth.getY()) && (aLineWidth.getX() <= 1.2)); - - // #i101491# - if( !bIsHairline && (rPolygon.count() > 1000) ) + // short circuit if there is nothing to do + if(0 == rPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) { - // the used basegfx::utils::createAreaGeometry is simply too - // expensive with very big polygons; fallback to caller (who - // should use ImplLineConverter normally) - // AW: ImplLineConverter had to be removed since it does not even - // know LineJoins, so the fallback will now prepare the line geometry - // the same way. - return false; + return true; } - // Transform to DeviceCoordinates, get DeviceLineWidth, execute PixelSnapHairline - basegfx::B2DPolygon aPolyLine(rPolygon); - aPolyLine.transform(rObjectToDevice); - if(bPixelSnapHairline) { aPolyLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyLine); } + // need to check/handle LineWidth when ObjectToDevice transformation is used + basegfx::B2DVector aLineWidth(rLineWidth); + const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity()); + const basegfx::B2DVector aDeviceLineWidths(bObjectToDeviceIsIdentity ? rLineWidth : rObjectToDevice * rLineWidth); + const bool bCorrectLineWidth(!bObjectToDeviceIsIdentity && aDeviceLineWidths.getX() < 1.0 && aLineWidth.getX() >= 1.0); + basegfx::B2DHomMatrix aObjectToDeviceInv; + basegfx::B2DPolygon aPolygon(rPolygon); - // temporarily adjust brush color to pen color - // since the line is drawn as an area-polygon - const Color aKeepBrushColor = mnBrushColor; - mnBrushColor = mnPenColor; + if(bCorrectLineWidth) + { + if(aObjectToDeviceInv.isIdentity()) + { + aObjectToDeviceInv = rObjectToDevice; + aObjectToDeviceInv.invert(); + } - // #i11575#desc5#b adjust B2D tessellation result to raster positions - // basegfx::B2DPolygon aPolygon = rPolygon; - const double fHalfWidth = 0.5 * aLineWidth.getX(); + // calculate-back logical LineWidth for a hairline + aLineWidth = aObjectToDeviceInv * basegfx::B2DVector(1.0, 1.0); + } - // #i122456# This is probably thought to happen to align hairlines to pixel positions, so - // it should be a 0.5 translation, not more. It will definitely go wrong with fat lines - aPolyLine.transform( basegfx::utils::createTranslateB2DHomMatrix(0.5, 0.5) ); + // try to access buffered data + std::shared_ptr<SystemDependentData_Triangulation> pSystemDependentData_Triangulation( + rPolygon.getSystemDependentData<SystemDependentData_Triangulation>()); - // shortcut for hairline drawing to improve performance - bool bDrawnOk = true; - if( bIsHairline ) + if(pSystemDependentData_Triangulation) { - // hairlines can benefit from a simplified tessellation - // e.g. for hairlines the linejoin style can be ignored - basegfx::B2DTrapezoidVector aB2DTrapVector; - basegfx::utils::createLineTrapezoidFromB2DPolygon( aB2DTrapVector, aPolyLine, aLineWidth.getX() ); - - // draw tessellation result - const int nTrapCount = aB2DTrapVector.size(); - if( nTrapCount > 0 ) - bDrawnOk = drawFilledTrapezoids( &aB2DTrapVector[0], nTrapCount, fTransparency ); - - // restore the original brush GC - mnBrushColor = aKeepBrushColor; - return bDrawnOk; + // check data validity + if(pSystemDependentData_Triangulation->getLineWidth() != aLineWidth) + { + // sometimes small inconsistencies, use a percentage tolerance + const double fFactorX(basegfx::fTools::equalZero(aLineWidth.getX()) + ? 0.0 + : fabs(1.0 - (pSystemDependentData_Triangulation->getLineWidth().getX() / aLineWidth.getX()))); + const double fFactorY(basegfx::fTools::equalZero(aLineWidth.getY()) + ? 0.0 + : fabs(1.0 - (pSystemDependentData_Triangulation->getLineWidth().getY() / aLineWidth.getY()))); + + // compare with 5.0% tolerance + if(basegfx::fTools::more(fFactorX, 0.05) || basegfx::fTools::more(fFactorY, 0.05)) + { + // data invalid, forget + pSystemDependentData_Triangulation.reset(); + } + } } - // get the area polygon for the line polygon - if( (aLineWidth.getX() != aLineWidth.getY()) && !basegfx::fTools::equalZero( aLineWidth.getY() ) ) + if(!pSystemDependentData_Triangulation) { - // prepare for createAreaGeometry() with anisotropic linewidth - aPolyLine.transform( basegfx::utils::createScaleB2DHomMatrix(1.0, aLineWidth.getX() / aLineWidth.getY())); - } + // try to create data + if(bPixelSnapHairline) + { + if(!bObjectToDeviceIsIdentity) + { + aPolygon.transform(rObjectToDevice); + } - // create the area-polygon for the line - const basegfx::B2DPolyPolygon aAreaPolyPoly( - basegfx::utils::createAreaGeometry( - aPolyLine, - fHalfWidth, - eLineJoin, - eLineCap, - fMiterMinimumAngle)); + aPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolygon); - if( (aLineWidth.getX() != aLineWidth.getY()) && !basegfx::fTools::equalZero( aLineWidth.getX() ) ) - { - // postprocess createAreaGeometry() for anisotropic linewidth - aPolyLine.transform(basegfx::utils::createScaleB2DHomMatrix(1.0, aLineWidth.getY() / aLineWidth.getX())); + if(!bObjectToDeviceIsIdentity) + { + if(aObjectToDeviceInv.isIdentity()) + { + aObjectToDeviceInv = rObjectToDevice; + aObjectToDeviceInv.invert(); + } + + aPolygon.transform(aObjectToDeviceInv); + } + } + + basegfx::triangulator::B2DTriangleVector aTriangles; + const basegfx::B2DPolyPolygon aAreaPolyPoly( + basegfx::utils::createAreaGeometry( + aPolygon, + 0.5 * aLineWidth.getX(), + eLineJoin, + eLineCap, + basegfx::deg2rad(12.5), + 0.4, + fMiterMinimumAngle, + &aTriangles)); + + if(!aTriangles.empty()) + { + // add to buffering mechanism + pSystemDependentData_Triangulation = rPolygon.addOrReplaceSystemDependentData<SystemDependentData_Triangulation>( + ImplGetSystemDependentDataManager(), + aTriangles, + aLineWidth); + } } - // draw each area polypolygon component individually - // to emulate the polypolygon winding rule "non-zero" - const int nPolyCount = aAreaPolyPoly.count(); - for( int nPolyIdx = 0; nPolyIdx < nPolyCount; ++nPolyIdx ) + if(!pSystemDependentData_Triangulation) { - const basegfx::B2DPolyPolygon aOnePoly( aAreaPolyPoly.getB2DPolygon( nPolyIdx ) ); + return false; + } - bDrawnOk = drawPolyPolygon( - basegfx::B2DHomMatrix(), - aOnePoly, - fTransparency); + // temporarily adjust brush color to pen color + // since the line is drawn as an area-polygon + const Color aKeepBrushColor = mnBrushColor; + mnBrushColor = mnPenColor; - if( !bDrawnOk ) - break; - } + // create the area-polygon for the line + const bool bDrawnOk( + drawFilledTriangles( + rObjectToDevice, + pSystemDependentData_Triangulation->getTriangles(), + fTransparency, + true)); // restore the original brush GC mnBrushColor = aKeepBrushColor; diff --git a/vcl/unx/generic/gdi/gdiimpl.hxx b/vcl/unx/generic/gdi/gdiimpl.hxx index ac80ec6f0c40..db13696c4ffc 100644 --- a/vcl/unx/generic/gdi/gdiimpl.hxx +++ b/vcl/unx/generic/gdi/gdiimpl.hxx @@ -30,6 +30,7 @@ #include <salgdiimpl.hxx> #include <basegfx/polygon/b2dtrapezoid.hxx> +#include <basegfx/polygon/b2dpolygontriangulator.hxx> #include <ControlCacheKey.hxx> /* From <X11/Intrinsic.h> */ @@ -93,6 +94,11 @@ private: XID GetXRenderPicture(); bool drawFilledTrapezoids( const basegfx::B2DTrapezoid*, int nTrapCount, double fTransparency ); + bool drawFilledTriangles( + const basegfx::B2DHomMatrix& rObjectToDevice, + const basegfx::triangulator::B2DTriangleVector& rTriangles, + double fTransparency, + bool bPixelOffset); long GetGraphicsHeight() const; diff --git a/vcl/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx index 632d08d1a35b..fd82c3929c88 100644 --- a/vcl/unx/generic/gdi/salgdi.cxx +++ b/vcl/unx/generic/gdi/salgdi.cxx @@ -604,9 +604,10 @@ bool X11SalGraphics::drawPolyPolygon( return true; } - static bool bUseCairoForPolygons = false; + // enable by setting to something + static const char* pUseCairoForPolygons(getenv("SAL_ENABLE_USE_CAIRO_FOR_POLYGONS")); - if (!m_bOpenGL && bUseCairoForPolygons && SupportsCairo()) + if (!m_bOpenGL && nullptr != pUseCairoForPolygons && SupportsCairo()) { // snap to raster if requested const bool bSnapPoints(!getAntiAliasB2DDraw()); @@ -724,9 +725,10 @@ bool X11SalGraphics::drawPolyLine( } #if ENABLE_CAIRO_CANVAS - static bool bUseCairoForFatLines = true; + // disable by setting to something + static const char* pUseCairoForFatLines(getenv("SAL_DISABLE_USE_CAIRO_FOR_FATLINES")); - if (!m_bOpenGL && bUseCairoForFatLines && SupportsCairo()) + if (!m_bOpenGL && nullptr == pUseCairoForFatLines && SupportsCairo()) { cairo_t* cr = getCairoContext(); clipRegion(cr); diff --git a/vcl/unx/generic/gdi/xrender_peer.hxx b/vcl/unx/generic/gdi/xrender_peer.hxx index 492924889595..afe793b248da 100644 --- a/vcl/unx/generic/gdi/xrender_peer.hxx +++ b/vcl/unx/generic/gdi/xrender_peer.hxx @@ -62,6 +62,9 @@ public: void CompositeTrapezoids( int nOp, Picture aSrc, Picture aDst, const XRenderPictFormat*, int nXSrc, int nYSrc, const XTrapezoid*, int nCount ) const; + void CompositeTriangles( int nOp, Picture aSrc, Picture aDst, + const XRenderPictFormat*, int nXSrc, int nYSrc, + const XTriangle*, int nCount ) const; }; inline XRenderPictFormat* XRenderPeer::GetStandardFormatA8() const @@ -127,6 +130,14 @@ inline void XRenderPeer::CompositeTrapezoids( int nOp, nXSrc, nYSrc, pXT, nCount ); } +inline void XRenderPeer::CompositeTriangles( int nOp, + Picture aSrc, Picture aDst, const XRenderPictFormat* pXRPF, + int nXSrc, int nYSrc, const XTriangle* pXT, int nCount ) const +{ + XRenderCompositeTriangles( mpDisplay, nOp, aSrc, aDst, pXRPF, + nXSrc, nYSrc, pXT, nCount ); +} + inline XRenderColor GetXRenderColor( Color rColor, double fTransparency ) { XRenderColor aRetVal; |