diff options
-rw-r--r-- | canvas/source/cairo/cairo_canvashelper.cxx | 21 | ||||
-rw-r--r-- | vcl/headless/svpgdi.cxx | 18 | ||||
-rw-r--r-- | vcl/win/gdi/gdiimpl.cxx | 32 |
3 files changed, 66 insertions, 5 deletions
diff --git a/canvas/source/cairo/cairo_canvashelper.cxx b/canvas/source/cairo/cairo_canvashelper.cxx index 91bc052b509b..bd86c8977152 100644 --- a/canvas/source/cairo/cairo_canvashelper.cxx +++ b/canvas/source/cairo/cairo_canvashelper.cxx @@ -276,6 +276,8 @@ namespace cairocanvas useStates( viewState, renderState, true ); cairo_move_to( mpCairo.get(), aBezierSegment.Px + 0.5, aBezierSegment.Py + 0.5 ); + // tdf#99165 correction of control poinits not needed here, only hairlines drawn + // (see cairo_set_line_width above) cairo_curve_to( mpCairo.get(), aBezierSegment.C1x + 0.5, aBezierSegment.C1y + 0.5, aBezierSegment.C2x + 0.5, aBezierSegment.C2y + 0.5, @@ -949,7 +951,7 @@ namespace cairocanvas bool bOpToDo = false; cairo_matrix_t aOrigMatrix, aIdentityMatrix; - double nX, nY, nBX, nBY, nAX, nAY; + double nX, nY, nBX, nBY, nAX, nAY, nLastX, nLastY; cairo_get_matrix( pCairo, &aOrigMatrix ); cairo_matrix_init_identity( &aIdentityMatrix ); @@ -1022,6 +1024,20 @@ namespace cairocanvas nBY += 0.5; } + // tdf#99165 if the control points are 'empty', create the mathematical + // correct replacement ones to avoid problems with the graphical sub-system + if(basegfx::fTools::equal(nAX, nLastX) && basegfx::fTools::equal(nAY, nLastY)) + { + nAX = nLastX + ((nBX - nLastX) * 0.3); + nAY = nLastY + ((nBY - nLastY) * 0.3); + } + + if(basegfx::fTools::equal(nBX, nX) && basegfx::fTools::equal(nBY, nY)) + { + nBX = nX + ((nAX - nX) * 0.3); + nBY = nY + ((nAY - nY) * 0.3); + } + cairo_curve_to( pCairo, nAX, nAY, nBX, nBY, nX, nY ); } else @@ -1031,6 +1047,9 @@ namespace cairocanvas } bOpToDo = true; } + + nLastX = nX; + nLastY = nY; } if( aPolygon.isClosed() ) diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx index e87811357015..37e0e17bbbe2 100644 --- a/vcl/headless/svpgdi.cxx +++ b/vcl/headless/svpgdi.cxx @@ -587,6 +587,8 @@ static void AddPolygonToPath(cairo_t* cr, const basegfx::B2DPolygon& rPolygon, b } const bool bHasCurves = rPolygon.areControlPointsUsed(); + basegfx::B2DPoint aLast; + for( int nPointIdx = 0, nPrevIdx = 0;; nPrevIdx = nPointIdx++ ) { int nClosedIdx = nPointIdx; @@ -621,6 +623,7 @@ static void AddPolygonToPath(cairo_t* cr, const basegfx::B2DPolygon& rPolygon, b { // first point => just move there cairo_move_to(cr, aPoint.getX(), aPoint.getY()); + aLast = aPoint; continue; } @@ -644,9 +647,24 @@ static void AddPolygonToPath(cairo_t* cr, const basegfx::B2DPolygon& rPolygon, b aCP1 += aHalfPointOfs; aCP2 += aHalfPointOfs; } + + // tdf#99165 if the control points are 'empty', create the mathematical + // correct replacement ones to avoid problems with the graphical sub-system + if(aCP1.equal(aLast)) + { + aCP1 = aLast + ((aCP2 - aLast) * 0.3); + } + + if(aCP2.equal(aPoint)) + { + aCP2 = aPoint + ((aCP1 - aPoint) * 0.3); + } + cairo_curve_to(cr, aCP1.getX(), aCP1.getY(), aCP2.getX(), aCP2.getY(), aPoint.getX(), aPoint.getY()); } + + aLast = aPoint; } if( bClosePath ) diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx index 2366fbe21f61..b3a39f162847 100644 --- a/vcl/win/gdi/gdiimpl.cxx +++ b/vcl/win/gdi/gdiimpl.cxx @@ -1888,11 +1888,31 @@ void impAddB2DPolygonToGDIPlusGraphicsPathReal( { const sal_uInt32 nNextIndex((a + 1) % nCount); const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex)); + const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a)); + const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex)); - if(bControls && (rPolygon.isNextControlPointUsed(a) || rPolygon.isPrevControlPointUsed(nNextIndex))) + if(b1stControlPointUsed || b2ndControlPointUsed) { - const basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a)); - const basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex)); + basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a)); + basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex)); + + // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines + // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has + // no 1st or 2nd control point, despite that these are mathematicaly correct definitions + // (basegfx can handle that). To solve, create replacement vectors to thre resp. next + // control point with 1/3rd of length (the default control vector for these cases). + // Only one of this can happen here, else the is(Next|Prev)ControlPointUsed wopuld have + // both been false. + // Caution: This error (and it's correction) might be necessary for other graphical + // sub-systems in a similar way + if(!b1stControlPointUsed) + { + aCa = aCurr + ((aCb - aCurr) * 0.3); + } + else if(!b2ndControlPointUsed) + { + aCb = aNext + ((aCa - aNext) * 0.3); + } rGraphicsPath.AddBezier( static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()), @@ -2018,7 +2038,11 @@ bool WinSalGraphicsImpl::drawPolyLine( const Gdiplus::REAL aMiterLimit(15.0); aPen.SetMiterLimit(aMiterLimit); - aPen.SetLineJoin(Gdiplus::LineJoinMiter); + // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional + // graphics, somewhere clipped in some distance from the edge point, dependent + // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use + // that instead + aPen.SetLineJoin(Gdiplus::LineJoinMiterClipped); break; } case basegfx::B2DLineJoin::Round : |