summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2016-07-01 14:00:00 +0200
committerArmin Le Grand <Armin.Le.Grand@cib.de>2016-07-07 16:34:25 +0200
commit80d0a759e66201ac38d82f01787a68322a8b560c (patch)
tree5100f070e22e6eb3309c639f8b7347594baffe40
parent518332e9268eb6dde47d0f4faf8b7d007569af93 (diff)
tdf#50613 speedup fat line drawing on linux using cairo
Drawing fat lines is slow on linux due to X11 having no direct support for it. This leads to creating the PolyPolygon geometry for each fat line, then tesselate and draw as trapezoids. This is not buffered in any way and is done at each paint. As a side effect, fat lines composed of multiple anti-aliased lines also show errors since AA-ed edges do not add up graphically. Since we have cairo now available it makes sense to use it for fat line drawing, it is markedly faster despite being a software renderer. No such gains for PolyPolygons though. Change-Id: If4001556e2dd4c15ecf2587cad6ce1e864558f2d
-rw-r--r--basegfx/source/curve/b2dcubicbezier.cxx61
-rw-r--r--include/basegfx/curve/b2dcubicbezier.hxx7
-rw-r--r--vcl/inc/unx/salgdi.h9
-rw-r--r--vcl/unx/generic/gdi/salgdi.cxx271
4 files changed, 348 insertions, 0 deletions
diff --git a/basegfx/source/curve/b2dcubicbezier.cxx b/basegfx/source/curve/b2dcubicbezier.cxx
index bbafb7bbfa0d..13c2d77c92b0 100644
--- a/basegfx/source/curve/b2dcubicbezier.cxx
+++ b/basegfx/source/curve/b2dcubicbezier.cxx
@@ -20,6 +20,7 @@
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <basegfx/vector/b2dvector.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <osl/diagnose.h>
@@ -1021,6 +1022,66 @@ namespace basegfx
}
}
+ void B2DCubicBezier::transform(const basegfx::B2DHomMatrix& rMatrix)
+ {
+ if(!rMatrix.isIdentity())
+ {
+ if(maControlPointA == maStartPoint)
+ {
+ maControlPointA = maStartPoint = rMatrix * maStartPoint;
+ }
+ else
+ {
+ maStartPoint *= rMatrix;
+ maControlPointA *= rMatrix;
+ }
+
+ if(maControlPointB == maEndPoint)
+ {
+ maControlPointB = maEndPoint = rMatrix * maEndPoint;
+ }
+ else
+ {
+ maEndPoint *= rMatrix;
+ maControlPointB *= rMatrix;
+ }
+ }
+ }
+
+ void B2DCubicBezier::fround()
+ {
+ if(maControlPointA == maStartPoint)
+ {
+ maControlPointA = maStartPoint = basegfx::B2DPoint(
+ basegfx::fround(maStartPoint.getX()),
+ basegfx::fround(maStartPoint.getY()));
+ }
+ else
+ {
+ maStartPoint = basegfx::B2DPoint(
+ basegfx::fround(maStartPoint.getX()),
+ basegfx::fround(maStartPoint.getY()));
+ maControlPointA = basegfx::B2DPoint(
+ basegfx::fround(maControlPointA.getX()),
+ basegfx::fround(maControlPointA.getY()));
+ }
+
+ if(maControlPointB == maEndPoint)
+ {
+ maControlPointB = maEndPoint = basegfx::B2DPoint(
+ basegfx::fround(maEndPoint.getX()),
+ basegfx::fround(maEndPoint.getY()));
+ }
+ else
+ {
+ maEndPoint = basegfx::B2DPoint(
+ basegfx::fround(maEndPoint.getX()),
+ basegfx::fround(maEndPoint.getY()));
+ maControlPointB = basegfx::B2DPoint(
+ basegfx::fround(maControlPointB.getX()),
+ basegfx::fround(maControlPointB.getY()));
+ }
+ }
} // end of namespace basegfx
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/basegfx/curve/b2dcubicbezier.hxx b/include/basegfx/curve/b2dcubicbezier.hxx
index bde53183f362..f8314f0694e4 100644
--- a/include/basegfx/curve/b2dcubicbezier.hxx
+++ b/include/basegfx/curve/b2dcubicbezier.hxx
@@ -33,6 +33,7 @@ namespace basegfx
{
class BASEGFX_DLLPUBLIC B2DCubicBezier
{
+ private:
B2DPoint maStartPoint;
B2DPoint maEndPoint;
B2DPoint maControlPointA;
@@ -187,6 +188,12 @@ namespace basegfx
sense to use reserve(4) at the vector as preparation.
*/
void getAllExtremumPositions(::std::vector< double >& rResults) const;
+
+ /// apply transformation given in matrix form
+ void transform(const basegfx::B2DHomMatrix& rMatrix);
+
+ /// fround content
+ void fround();
};
} // end of namespace basegfx
diff --git a/vcl/inc/unx/salgdi.h b/vcl/inc/unx/salgdi.h
index 84f1e8ff14eb..a21fe787d55a 100644
--- a/vcl/inc/unx/salgdi.h
+++ b/vcl/inc/unx/salgdi.h
@@ -271,6 +271,10 @@ public:
virtual OpenGLContext *BeginPaint() SAL_OVERRIDE;
+#if ENABLE_CAIRO_CANVAS
+ void clipRegion(cairo_t* cr);
+#endif // ENABLE_CAIRO_CANVAS
+
bool TryRenderCachedNativeControl(ControlCacheKey& aControlCacheKey,
int nX, int nY);
@@ -338,6 +342,11 @@ protected:
Region pPaintRegion_;
Region mpClipRegion;
+#if ENABLE_CAIRO_CANVAS
+ vcl::Region maClipRegion;
+ SalColor mnPenColor;
+ SalColor mnFillColor;
+#endif // ENABLE_CAIRO_CANVAS
GC pFontGC_; // Font attributes
Pixel nTextPixel_;
diff --git a/vcl/unx/generic/gdi/salgdi.cxx b/vcl/unx/generic/gdi/salgdi.cxx
index e2a25b5de4b6..5cb18608473a 100644
--- a/vcl/unx/generic/gdi/salgdi.cxx
+++ b/vcl/unx/generic/gdi/salgdi.cxx
@@ -39,6 +39,7 @@
#include "basegfx/matrix/b2dhommatrixtools.hxx"
#include "basegfx/polygon/b2dpolypolygoncutter.hxx"
#include "basegfx/polygon/b2dtrapezoid.hxx"
+#include <basegfx/curve/b2dcubicbezier.hxx>
#include <vcl/jobdata.hxx>
#include <vcl/virdev.hxx>
@@ -78,6 +79,11 @@ X11SalGraphics::X11SalGraphics():
m_aXRenderPicture(0),
pPaintRegion_(NULL),
mpClipRegion(NULL),
+#if ENABLE_CAIRO_CANVAS
+ maClipRegion(),
+ mnPenColor(SALCOLOR_NONE),
+ mnFillColor(SALCOLOR_NONE),
+#endif // ENABLE_CAIRO_CANVAS
pFontGC_(NULL),
nTextPixel_(0),
hBrush_(None),
@@ -328,26 +334,43 @@ void X11SalGraphics::ResetClipRegion()
bool X11SalGraphics::setClipRegion( const vcl::Region& i_rClip )
{
+ maClipRegion = i_rClip;
return mxImpl->setClipRegion( i_rClip );
}
void X11SalGraphics::SetLineColor()
{
+#if ENABLE_CAIRO_CANVAS
+ mnPenColor = SALCOLOR_NONE;
+#endif // ENABLE_CAIRO_CANVAS
+
mxImpl->SetLineColor();
}
void X11SalGraphics::SetLineColor( SalColor nSalColor )
{
+#if ENABLE_CAIRO_CANVAS
+ mnPenColor = nSalColor;
+#endif // ENABLE_CAIRO_CANVAS
+
mxImpl->SetLineColor( nSalColor );
}
void X11SalGraphics::SetFillColor()
{
+#if ENABLE_CAIRO_CANVAS
+ mnFillColor = SALCOLOR_NONE;
+#endif // ENABLE_CAIRO_CANVAS
+
mxImpl->SetFillColor();
}
void X11SalGraphics::SetFillColor( SalColor nSalColor )
{
+#if ENABLE_CAIRO_CANVAS
+ mnFillColor = nSalColor;
+#endif // ENABLE_CAIRO_CANVAS
+
mxImpl->SetFillColor( nSalColor );
}
@@ -529,9 +552,134 @@ css::uno::Any X11SalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rS
// draw a poly-polygon
bool X11SalGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon& rOrigPolyPoly, double fTransparency )
{
+ if(fTransparency >= 1.0)
+ {
+ return true;
+ }
+
+ const sal_uInt32 nPolyCount(rOrigPolyPoly.count());
+
+ if(nPolyCount <= 0)
+ {
+ return true;
+ }
+
+ if(SALCOLOR_NONE == mnFillColor && SALCOLOR_NONE == mnPenColor)
+ {
+ return true;
+ }
+
+#if ENABLE_CAIRO_CANVAS
+ static bool bUseCairoForPolygons = false;
+
+ if(bUseCairoForPolygons && SupportsCairo())
+ {
+ // snap to raster if requested
+ basegfx::B2DPolyPolygon aPolyPoly(rOrigPolyPoly);
+ const bool bSnapPoints(!getAntiAliasB2DDraw());
+
+ if(bSnapPoints)
+ {
+ aPolyPoly = basegfx::tools::snapPointsOfHorizontalOrVerticalEdges(aPolyPoly);
+ }
+
+ cairo_t* cr = getCairoContext();
+ clipRegion(cr);
+
+ for(sal_uInt32 a(0); a < nPolyCount; ++a)
+ {
+ const basegfx::B2DPolygon aPolygon(aPolyPoly.getB2DPolygon(a));
+ const sal_uInt32 nPointCount(aPolygon.count());
+
+ if(nPointCount)
+ {
+ const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount : nPointCount - 1);
+
+ if(nEdgeCount)
+ {
+ basegfx::B2DCubicBezier aEdge;
+
+ for(sal_uInt32 b = 0; b < nEdgeCount; ++b)
+ {
+ aPolygon.getBezierSegment(b, aEdge);
+
+ if(!b)
+ {
+ const basegfx::B2DPoint aStart(aEdge.getStartPoint());
+ cairo_move_to(cr, aStart.getX(), aStart.getY());
+ }
+
+ const basegfx::B2DPoint aEnd(aEdge.getEndPoint());
+
+ if(aEdge.isBezier())
+ {
+ const basegfx::B2DPoint aCP1(aEdge.getControlPointA());
+ const basegfx::B2DPoint aCP2(aEdge.getControlPointB());
+ cairo_curve_to(cr,
+ aCP1.getX(), aCP1.getY(),
+ aCP2.getX(), aCP2.getY(),
+ aEnd.getX(), aEnd.getY());
+ }
+ else
+ {
+ cairo_line_to(cr, aEnd.getX(), aEnd.getY());
+ }
+ }
+
+ cairo_close_path(cr);
+ }
+ }
+ }
+
+ if(SALCOLOR_NONE != mnFillColor)
+ {
+ cairo_set_source_rgba(cr,
+ SALCOLOR_RED(mnFillColor)/255.0,
+ SALCOLOR_GREEN(mnFillColor)/255.0,
+ SALCOLOR_BLUE(mnFillColor)/255.0,
+ 1.0 - fTransparency);
+ cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+ cairo_fill_preserve(cr);
+ }
+
+ if(SALCOLOR_NONE != mnPenColor)
+ {
+ cairo_set_source_rgba(cr,
+ SALCOLOR_RED(mnPenColor)/255.0,
+ SALCOLOR_GREEN(mnPenColor)/255.0,
+ SALCOLOR_BLUE(mnPenColor)/255.0,
+ 1.0 - fTransparency);
+ cairo_stroke_preserve(cr);
+ }
+
+ releaseCairoContext(cr);
+ return true;
+ }
+#endif // ENABLE_CAIRO_CANVAS
+
return mxImpl->drawPolyPolygon( rOrigPolyPoly, fTransparency );
}
+#if ENABLE_CAIRO_CANVAS
+void X11SalGraphics::clipRegion(cairo_t* cr)
+{
+ if(!maClipRegion.IsEmpty())
+ {
+ RectangleVector aRectangles;
+ maClipRegion.GetRegionRectangles(aRectangles);
+
+ if (!aRectangles.empty())
+ {
+ for (RectangleVector::const_iterator aRectIter(aRectangles.begin()); aRectIter != aRectangles.end(); ++aRectIter)
+ {
+ cairo_rectangle(cr, aRectIter->Left(), aRectIter->Top(), aRectIter->GetWidth(), aRectIter->GetHeight());
+ }
+ cairo_clip(cr);
+ }
+ }
+}
+#endif // ENABLE_CAIRO_CANVAS
+
bool X11SalGraphics::drawPolyLine(
const ::basegfx::B2DPolygon& rPolygon,
double fTransparency,
@@ -539,6 +687,129 @@ bool X11SalGraphics::drawPolyLine(
basegfx::B2DLineJoin eLineJoin,
com::sun::star::drawing::LineCap eLineCap)
{
+ const int nPointCount(rPolygon.count());
+
+ if(nPointCount <= 0)
+ {
+ return true;
+ }
+
+ if(fTransparency >= 1.0)
+ {
+ return true;
+ }
+
+#if ENABLE_CAIRO_CANVAS
+ static bool bUseCairoForFatLines = true;
+
+ if(bUseCairoForFatLines && SupportsCairo())
+ {
+ cairo_t* cr = getCairoContext();
+ clipRegion(cr);
+ cairo_line_join_t eCairoLineJoin(CAIRO_LINE_JOIN_MITER);
+ bool bNoJoin(false);
+
+ switch(eLineJoin)
+ {
+ case basegfx::B2DLineJoin::Bevel:
+ eCairoLineJoin = CAIRO_LINE_JOIN_BEVEL;
+ break;
+ case basegfx::B2DLineJoin::Round:
+ eCairoLineJoin = CAIRO_LINE_JOIN_ROUND;
+ break;
+ case basegfx::B2DLineJoin::NONE:
+ bNoJoin = true;
+ SAL_FALLTHROUGH;
+ case basegfx::B2DLineJoin::Miter:
+ eCairoLineJoin = CAIRO_LINE_JOIN_MITER;
+ break;
+ }
+
+ // setup cap attribute
+ cairo_line_cap_t eCairoLineCap(CAIRO_LINE_CAP_BUTT);
+ switch(eLineCap)
+ {
+ default: // css::drawing::LineCap_BUTT:
+ {
+ eCairoLineCap = CAIRO_LINE_CAP_BUTT;
+ break;
+ }
+ case css::drawing::LineCap_ROUND:
+ {
+ eCairoLineCap = CAIRO_LINE_CAP_ROUND;
+ break;
+ }
+ case css::drawing::LineCap_SQUARE:
+ {
+ eCairoLineCap = CAIRO_LINE_CAP_SQUARE;
+ break;
+ }
+ }
+
+ cairo_set_source_rgba(cr,
+ SALCOLOR_RED(mnPenColor)/255.0,
+ SALCOLOR_GREEN(mnPenColor)/255.0,
+ SALCOLOR_BLUE(mnPenColor)/255.0,
+ 1.0 - fTransparency);
+ cairo_set_line_join(cr, eCairoLineJoin);
+ cairo_set_line_cap(cr, eCairoLineCap);
+ cairo_set_line_width(cr, (fabs(rLineWidth.getX()) + fabs(rLineWidth.getY())) * 0.5);
+
+ if(CAIRO_LINE_JOIN_MITER == eCairoLineJoin)
+ {
+ cairo_set_miter_limit(cr, 15.0);
+ }
+
+ const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nPointCount : nPointCount - 1);
+
+ if(nEdgeCount)
+ {
+ const bool bSnapPoints(!getAntiAliasB2DDraw());
+ static basegfx::B2DHomMatrix aHalfPointTransform(basegfx::tools::createTranslateB2DHomMatrix(0.5, 0.5));
+ basegfx::B2DCubicBezier aEdge;
+
+ for(sal_uInt32 i = 0; i < nEdgeCount; ++i)
+ {
+ rPolygon.getBezierSegment(i, aEdge);
+
+ aEdge.transform(aHalfPointTransform);
+
+ if(bSnapPoints)
+ {
+ aEdge.fround();
+ }
+
+ if(!i || bNoJoin)
+ {
+ const basegfx::B2DPoint aStart(aEdge.getStartPoint());
+ cairo_move_to(cr, aStart.getX(), aStart.getY());
+ }
+
+ const basegfx::B2DPoint aEnd(aEdge.getEndPoint());
+
+ if(aEdge.isBezier())
+ {
+ const basegfx::B2DPoint aCP1(aEdge.getControlPointA());
+ const basegfx::B2DPoint aCP2(aEdge.getControlPointB());
+ cairo_curve_to(cr,
+ aCP1.getX(), aCP1.getY(),
+ aCP2.getX(), aCP2.getY(),
+ aEnd.getX(), aEnd.getY());
+ }
+ else
+ {
+ cairo_line_to(cr, aEnd.getX(), aEnd.getY());
+ }
+ }
+
+ cairo_stroke(cr);
+ }
+
+ releaseCairoContext(cr);
+ return true;
+ }
+#endif // ENABLE_CAIRO_CANVAS
+
return mxImpl->drawPolyLine( rPolygon, fTransparency, rLineWidth,
eLineJoin, eLineCap );
}