use vcl lin. gradient drawing in drawinglayer + cairo impl.

This adds a divert for drawing of linear gradients drawing, which
can be implemented natively with a much higher quality and speed.

This also adds a implementation of drawing linear gradients with
cairo.

Change-Id: I8c39915c3579e6eb88cdce8ae4ac9694ffdb4957
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103374
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
index 716c230..4314c65 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx
@@ -53,6 +53,7 @@
#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
#include <drawinglayer/primitive2d/controlprimitive2d.hxx>
#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
@@ -66,6 +67,13 @@
#include <com/sun/star/awt/XWindow2.hpp>
#include <com/sun/star/awt/XControl.hpp>

#include <vcl/window.hxx>
#include <vcl/gradient.hxx>
#include <svtools/borderhelper.hxx>
#include <editeng/borderline.hxx>

#include <com/sun/star/table/BorderLineStyle.hpp>

using namespace com::sun::star;

namespace drawinglayer::processor2d
@@ -198,6 +206,30 @@ bool VclPixelProcessor2D::tryDrawPolygonStrokePrimitive2DDirect(
        /* false bBypassAACheck, default*/);
}

namespace
{
GradientStyle convertGradientStyle(drawinglayer::attribute::GradientStyle eGradientStyle)
{
    switch (eGradientStyle)
    {
        case drawinglayer::attribute::GradientStyle::Axial:
            return GradientStyle::Axial;
        case drawinglayer::attribute::GradientStyle::Radial:
            return GradientStyle::Radial;
        case drawinglayer::attribute::GradientStyle::Elliptical:
            return GradientStyle::Elliptical;
        case drawinglayer::attribute::GradientStyle::Square:
            return GradientStyle::Square;
        case drawinglayer::attribute::GradientStyle::Rect:
            return GradientStyle::Rect;
        case drawinglayer::attribute::GradientStyle::Linear:
        default:
            return GradientStyle::Linear;
    }
}

} // end anonymous namespace

void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
{
    switch (rCandidate.getPrimitive2DID())
@@ -400,6 +432,12 @@ void VclPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitiv
                static_cast<const drawinglayer::primitive2d::ShadowPrimitive2D&>(rCandidate));
            break;
        }
        case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D:
        {
            processFillGradientPrimitive2D(
                static_cast<const drawinglayer::primitive2d::FillGradientPrimitive2D&>(rCandidate));
            break;
        }
        default:
        {
            SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(
@@ -1141,6 +1179,39 @@ void VclPixelProcessor2D::processShadowPrimitive2D(const primitive2d::ShadowPrim
        SAL_WARN("drawinglayer", "Temporary buffered virtual device is not visible");
}

void VclPixelProcessor2D::processFillGradientPrimitive2D(
    const primitive2d::FillGradientPrimitive2D& rPrimitive)
{
    const attribute::FillGradientAttribute& rFillGradient = rPrimitive.getFillGradient();

    if (rFillGradient.getSteps() > 0
        || rFillGradient.getStyle() != drawinglayer::attribute::GradientStyle::Linear)
    {
        process(rPrimitive);
        return;
    }

    GradientStyle eGradientStyle = convertGradientStyle(rFillGradient.getStyle());

    basegfx::B2DRange aRange(rPrimitive.getOutputRange());
    aRange.transform(maCurrentTransformation);

    const tools::Rectangle aRectangle(
        sal_Int32(std::floor(aRange.getMinX())), sal_Int32(std::floor(aRange.getMinY())),
        sal_Int32(std::ceil(aRange.getMaxX())), sal_Int32(std::ceil(aRange.getMaxY())));

    Gradient aGradient(eGradientStyle, Color(rFillGradient.getStartColor()),
                       Color(rFillGradient.getEndColor()));

    aGradient.SetAngle(rFillGradient.getAngle() / F_PI1800);
    aGradient.SetBorder(rFillGradient.getBorder() * 100);
    aGradient.SetOfsX(rFillGradient.getOffsetX() * 100.0);
    aGradient.SetOfsY(rFillGradient.getOffsetY() * 100.0);
    aGradient.SetSteps(rFillGradient.getSteps());

    mpOutputDevice->DrawGradient(aRectangle, aGradient);
}

} // end of namespace

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
index c3bd191..480fdca 100644
--- a/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
+++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.hxx
@@ -42,6 +42,7 @@ class BorderLinePrimitive2D;
class GlowPrimitive2D;
class ShadowPrimitive2D;
class SoftEdgePrimitive2D;
class FillGradientPrimitive2D;
}

namespace drawinglayer::processor2d
@@ -99,6 +100,7 @@ class VclPixelProcessor2D final : public VclProcessor2D
    void processGlowPrimitive2D(const primitive2d::GlowPrimitive2D& rCandidate);
    void processSoftEdgePrimitive2D(const primitive2d::SoftEdgePrimitive2D& rCandidate);
    void processShadowPrimitive2D(const primitive2d::ShadowPrimitive2D& rCandidate);
    void processFillGradientPrimitive2D(const primitive2d::FillGradientPrimitive2D& rPrimitive);

public:
    /// constructor/destructor
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index 458ac9c..e769474 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -1910,6 +1910,55 @@ bool SvpSalGraphics::drawPolyPolygon(
    return true;
}

bool SvpSalGraphics::drawGradient(const tools::PolyPolygon& rPolyPolygon, const Gradient& rGradient)
{
    cairo_t* cr = getCairoContext(true);
    clipRegion(cr);

    basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPolygon.getB2DPolyPolygon());

    for (auto const & rPolygon : aB2DPolyPolygon)
    {
        basegfx::B2DHomMatrix rObjectToDevice;
        AddPolygonToPath(cr, rPolygon, rObjectToDevice, !getAntiAliasB2DDraw(), false);
    }

    Gradient aGradient(rGradient);

    tools::Rectangle aInputRect(rPolyPolygon.GetBoundRect());
    tools::Rectangle aBoundRect;
    Point aCenter;

    aGradient.SetAngle(aGradient.GetAngle() + 2700);
    aGradient.GetBoundRect(aInputRect, aBoundRect, aCenter);

    tools::Polygon aPoly(aBoundRect);
    aPoly.Rotate(aCenter, aGradient.GetAngle() % 3600);

    cairo_pattern_t* pattern;
    pattern = cairo_pattern_create_linear(aPoly[0].X(), aPoly[0].Y(), aPoly[1].X(), aPoly[1].Y());

    cairo_pattern_add_color_stop_rgba(pattern, aGradient.GetBorder() / 100.0,
                                                    aGradient.GetStartColor().GetRed()   / 255.0,
                                                    aGradient.GetStartColor().GetGreen() / 255.0,
                                                    aGradient.GetStartColor().GetBlue()  / 255.0,
                                                    1.0);

    cairo_pattern_add_color_stop_rgba(pattern, 1.0,
                                                    aGradient.GetEndColor().GetRed()   / 255.0,
                                                    aGradient.GetEndColor().GetGreen() / 255.0,
                                                    aGradient.GetEndColor().GetBlue()  / 255.0,
                                                    1.0);
    cairo_set_source(cr, pattern);

    basegfx::B2DRange extents = getClippedFillDamage(cr);
    cairo_fill_preserve(cr);

    releaseCairoContext(cr, true, extents);

    return true;
}

bool SvpSalGraphics::implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolygon, SalGradient const & rGradient)
{
    cairo_t* cr = getCairoContext(true);
diff --git a/vcl/inc/headless/svpgdi.hxx b/vcl/inc/headless/svpgdi.hxx
index 3cffdcd..8ffe104 100644
--- a/vcl/inc/headless/svpgdi.hxx
+++ b/vcl/inc/headless/svpgdi.hxx
@@ -232,7 +232,8 @@ public:
                                                   const sal_uInt32* pPoints,
                                                   const SalPoint* const* pPtAry,
                                                   const PolyFlags* const* pFlgAry ) override;
    virtual bool            drawGradient( const tools::PolyPolygon&, const Gradient& ) override { return false; };

    virtual bool drawGradient(tools::PolyPolygon const & rPolyPolygon, Gradient const & rGradient) override;

    virtual bool implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolygon, SalGradient const & rGradient) override;