Kaydet (Commit) cb382034 authored tarafından Armin Le Grand's avatar Armin Le Grand Kaydeden (comit) Thorsten Behrens

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
üst 85503661
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <basegfx/curve/b2dcubicbezier.hxx> #include <basegfx/curve/b2dcubicbezier.hxx>
#include <basegfx/vector/b2dvector.hxx> #include <basegfx/vector/b2dvector.hxx>
#include <basegfx/polygon/b2dpolygon.hxx> #include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/numeric/ftools.hxx> #include <basegfx/numeric/ftools.hxx>
#include <osl/diagnose.h> #include <osl/diagnose.h>
...@@ -1021,6 +1022,66 @@ namespace basegfx ...@@ -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 } // end of namespace basegfx
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -33,6 +33,7 @@ namespace basegfx ...@@ -33,6 +33,7 @@ namespace basegfx
{ {
class BASEGFX_DLLPUBLIC B2DCubicBezier class BASEGFX_DLLPUBLIC B2DCubicBezier
{ {
private:
B2DPoint maStartPoint; B2DPoint maStartPoint;
B2DPoint maEndPoint; B2DPoint maEndPoint;
B2DPoint maControlPointA; B2DPoint maControlPointA;
...@@ -187,6 +188,12 @@ namespace basegfx ...@@ -187,6 +188,12 @@ namespace basegfx
sense to use reserve(4) at the vector as preparation. sense to use reserve(4) at the vector as preparation.
*/ */
void getAllExtremumPositions(::std::vector< double >& rResults) const; 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 } // end of namespace basegfx
......
...@@ -267,6 +267,10 @@ public: ...@@ -267,6 +267,10 @@ public:
virtual css::uno::Any GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rSurface, const basegfx::B2ISize& rSize) const override; virtual css::uno::Any GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rSurface, const basegfx::B2ISize& rSize) const override;
virtual SystemFontData GetSysFontData( int nFallbackLevel ) const override; virtual SystemFontData GetSysFontData( int nFallbackLevel ) const override;
#if ENABLE_CAIRO_CANVAS
void clipRegion(cairo_t* cr);
#endif // ENABLE_CAIRO_CANVAS
bool TryRenderCachedNativeControl(ControlCacheKey& aControlCacheKey, bool TryRenderCachedNativeControl(ControlCacheKey& aControlCacheKey,
int nX, int nY); int nX, int nY);
...@@ -330,6 +334,11 @@ protected: ...@@ -330,6 +334,11 @@ protected:
Region pPaintRegion_; Region pPaintRegion_;
Region mpClipRegion; Region mpClipRegion;
#if ENABLE_CAIRO_CANVAS
vcl::Region maClipRegion;
SalColor mnPenColor;
SalColor mnFillColor;
#endif // ENABLE_CAIRO_CANVAS
GC pFontGC_; // Font attributes GC pFontGC_; // Font attributes
Pixel nTextPixel_; Pixel nTextPixel_;
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "basegfx/matrix/b2dhommatrixtools.hxx" #include "basegfx/matrix/b2dhommatrixtools.hxx"
#include "basegfx/polygon/b2dpolypolygoncutter.hxx" #include "basegfx/polygon/b2dpolypolygoncutter.hxx"
#include "basegfx/polygon/b2dtrapezoid.hxx" #include "basegfx/polygon/b2dtrapezoid.hxx"
#include <basegfx/curve/b2dcubicbezier.hxx>
#include <vcl/jobdata.hxx> #include <vcl/jobdata.hxx>
#include <vcl/virdev.hxx> #include <vcl/virdev.hxx>
...@@ -79,6 +80,11 @@ X11SalGraphics::X11SalGraphics(): ...@@ -79,6 +80,11 @@ X11SalGraphics::X11SalGraphics():
m_aXRenderPicture(0), m_aXRenderPicture(0),
pPaintRegion_(nullptr), pPaintRegion_(nullptr),
mpClipRegion(nullptr), mpClipRegion(nullptr),
#if ENABLE_CAIRO_CANVAS
maClipRegion(),
mnPenColor(SALCOLOR_NONE),
mnFillColor(SALCOLOR_NONE),
#endif // ENABLE_CAIRO_CANVAS
pFontGC_(nullptr), pFontGC_(nullptr),
nTextPixel_(0), nTextPixel_(0),
hBrush_(None), hBrush_(None),
...@@ -354,26 +360,43 @@ void X11SalGraphics::ResetClipRegion() ...@@ -354,26 +360,43 @@ void X11SalGraphics::ResetClipRegion()
bool X11SalGraphics::setClipRegion( const vcl::Region& i_rClip ) bool X11SalGraphics::setClipRegion( const vcl::Region& i_rClip )
{ {
maClipRegion = i_rClip;
return mxImpl->setClipRegion( i_rClip ); return mxImpl->setClipRegion( i_rClip );
} }
void X11SalGraphics::SetLineColor() void X11SalGraphics::SetLineColor()
{ {
#if ENABLE_CAIRO_CANVAS
mnPenColor = SALCOLOR_NONE;
#endif // ENABLE_CAIRO_CANVAS
mxImpl->SetLineColor(); mxImpl->SetLineColor();
} }
void X11SalGraphics::SetLineColor( SalColor nSalColor ) void X11SalGraphics::SetLineColor( SalColor nSalColor )
{ {
#if ENABLE_CAIRO_CANVAS
mnPenColor = nSalColor;
#endif // ENABLE_CAIRO_CANVAS
mxImpl->SetLineColor( nSalColor ); mxImpl->SetLineColor( nSalColor );
} }
void X11SalGraphics::SetFillColor() void X11SalGraphics::SetFillColor()
{ {
#if ENABLE_CAIRO_CANVAS
mnFillColor = SALCOLOR_NONE;
#endif // ENABLE_CAIRO_CANVAS
mxImpl->SetFillColor(); mxImpl->SetFillColor();
} }
void X11SalGraphics::SetFillColor( SalColor nSalColor ) void X11SalGraphics::SetFillColor( SalColor nSalColor )
{ {
#if ENABLE_CAIRO_CANVAS
mnFillColor = nSalColor;
#endif // ENABLE_CAIRO_CANVAS
mxImpl->SetFillColor( nSalColor ); mxImpl->SetFillColor( nSalColor );
} }
...@@ -555,9 +578,134 @@ css::uno::Any X11SalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rS ...@@ -555,9 +578,134 @@ css::uno::Any X11SalGraphics::GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rS
// draw a poly-polygon // draw a poly-polygon
bool X11SalGraphics::drawPolyPolygon( const basegfx::B2DPolyPolygon& rOrigPolyPoly, double fTransparency ) 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 ); 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( bool X11SalGraphics::drawPolyLine(
const basegfx::B2DPolygon& rPolygon, const basegfx::B2DPolygon& rPolygon,
double fTransparency, double fTransparency,
...@@ -566,6 +714,129 @@ bool X11SalGraphics::drawPolyLine( ...@@ -566,6 +714,129 @@ bool X11SalGraphics::drawPolyLine(
css::drawing::LineCap eLineCap, css::drawing::LineCap eLineCap,
double fMiterMinimumAngle) double fMiterMinimumAngle)
{ {
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, return mxImpl->drawPolyLine( rPolygon, fTransparency, rLineWidth,
eLineJoin, eLineCap, fMiterMinimumAngle ); eLineJoin, eLineCap, fMiterMinimumAngle );
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment