summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2016-04-12 17:23:34 +0200
committerArmin Le Grand <Armin.Le.Grand@cib.de>2016-04-13 10:28:06 +0000
commit9cda847a0bec307a909b927e0928cdbb0b00fc81 (patch)
treeaf8deff6fdfa9de8e81d21c3261aed1997ad7755
parentUse UNO service constructors (diff)
downloadcore-9cda847a0bec307a909b927e0928cdbb0b00fc81.tar.gz
core-9cda847a0bec307a909b927e0928cdbb0b00fc81.zip
tdf#99165 always provide control points for beziers
Some graphic sub systems cannot handle cases where control points of bezier curves are not set and produce wrong geometry for fat line drawing when MITER or similar LineCap and/or LineJoin is used. To avoid that, provide the mathematically correct fallback control points instead. Change-Id: Iabc724e51fb89e702f858db820c920f7b5b7d302 Reviewed-on: https://gerrit.libreoffice.org/24031 Tested-by: Jenkins <ci@libreoffice.org> Reviewed-by: Armin Le Grand <Armin.Le.Grand@cib.de>
-rw-r--r--canvas/source/cairo/cairo_canvashelper.cxx21
-rw-r--r--vcl/headless/svpgdi.cxx18
-rw-r--r--vcl/win/gdi/gdiimpl.cxx32
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 :