[Libreoffice-commits] core.git: include/vcl vcl/inc vcl/source

Chris Sherlock chris.sherlock79 at gmail.com
Mon Nov 3 14:54:42 PST 2014


 include/vcl/outdev.hxx           |    8 
 vcl/inc/generic/genpspgraphics.h |    2 
 vcl/inc/headless/svpgdi.hxx      |    1 
 vcl/inc/quartz/salgdi.h          |    1 
 vcl/inc/salgdi.hxx               |   11 
 vcl/inc/unx/salgdi.h             |    2 
 vcl/inc/win/salgdi.h             |    1 
 vcl/source/gdi/salgdilayout.cxx  |    5 
 vcl/source/outdev/gradient.cxx   |  521 ++++++++++++++++++++++++++++++++-------
 9 files changed, 461 insertions(+), 91 deletions(-)

New commits:
commit ad6d94009cf8ea526eb70bf1a07e5c6a21320f83
Author: Chris Sherlock <chris.sherlock79 at gmail.com>
Date:   Sat Oct 25 18:51:51 2014 +1100

    vcl: Allow SalGraphics to draw gradients natively
    
    The aim of this patch is to allow for native gradient rendering in
    SalGraphics (i.e. let OpenGL do this natively). It is a two step
    process:
    
    1. I need to allow gradient draw into SalGraphics, however the current
    completely intertwined with the metafile code in OutputDevice. I am
    seperating the gradient metafile code from the gradient drawing code.
    
    2. After splitting the metafile stuff from the actual gradient drawing,
    I am now able to call on SalGraphics::DrawGradient(). This just
    calls on SalGraphics::drawGradient() which returns false if there is
    no way of drawing native gradients, and true if there is. If false,
    then we use OutputDevice's DrawGradient() functionality.
    
    Change-Id: Ibaaabe13b76a8e7a037d9f751b5f662653a50566
    Reviewed-on: https://gerrit.libreoffice.org/12119
    Reviewed-by: Chris Sherlock <chris.sherlock79 at gmail.com>
    Tested-by: Chris Sherlock <chris.sherlock79 at gmail.com>

diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index 30da2ea..7603351 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -833,8 +833,12 @@ protected:
 
 private:
 
-    SAL_DLLPRIVATE void         DrawLinearGradient( const Rectangle& rRect, const Gradient& rGradient, bool bMtf, const tools::PolyPolygon* pClipPolyPoly );
-    SAL_DLLPRIVATE void         DrawComplexGradient( const Rectangle& rRect, const Gradient& rGradient, bool bMtf, const tools::PolyPolygon* pClipPolyPoly );
+    SAL_DLLPRIVATE void         DrawLinearGradient( const Rectangle& rRect, const Gradient& rGradient, const tools::PolyPolygon* pClipPolyPoly );
+    SAL_DLLPRIVATE void         DrawComplexGradient( const Rectangle& rRect, const Gradient& rGradient, const tools::PolyPolygon* pClipPolyPoly );
+
+    SAL_DLLPRIVATE void         DrawGradientToMetafile( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient );
+    SAL_DLLPRIVATE void         DrawLinearGradientToMetafile( const Rectangle& rRect, const Gradient& rGradient );
+    SAL_DLLPRIVATE void         DrawComplexGradientToMetafile( const Rectangle& rRect, const Gradient& rGradient );
 
     SAL_DLLPRIVATE long         GetGradientSteps( const Gradient& rGradient, const Rectangle& rRect, bool bMtf, bool bComplex=false );
 
diff --git a/vcl/inc/generic/genpspgraphics.h b/vcl/inc/generic/genpspgraphics.h
index 2a562c3..30c8fec 100644
--- a/vcl/inc/generic/genpspgraphics.h
+++ b/vcl/inc/generic/genpspgraphics.h
@@ -148,6 +148,8 @@ public:
                                                    const sal_uInt32* pPoints,
                                                    const SalPoint* const* pPtAry,
                                                    const sal_uInt8* const* pFlgAry ) SAL_OVERRIDE;
+    virtual bool            drawGradient( const tools::PolyPolygon&, const Gradient& ) SAL_OVERRIDE { return false; };
+
     virtual void            copyArea( long nDestX,
                                       long nDestY,
                                       long nSrcX,
diff --git a/vcl/inc/headless/svpgdi.hxx b/vcl/inc/headless/svpgdi.hxx
index a872d4a..cbbac7a 100644
--- a/vcl/inc/headless/svpgdi.hxx
+++ b/vcl/inc/headless/svpgdi.hxx
@@ -211,6 +211,7 @@ public:
                                                    const sal_uInt32* pPoints,
                                                    const SalPoint* const* pPtAry,
                                                    const sal_uInt8* const* pFlgAry ) SAL_OVERRIDE;
+    virtual bool            drawGradient( const tools::PolyPolygon&, const Gradient& ) SAL_OVERRIDE { return false; };
 
     virtual void            copyArea( long nDestX,
                                       long nDestY,
diff --git a/vcl/inc/quartz/salgdi.h b/vcl/inc/quartz/salgdi.h
index 23d3bb3..eb21e09 100644
--- a/vcl/inc/quartz/salgdi.h
+++ b/vcl/inc/quartz/salgdi.h
@@ -237,6 +237,7 @@ public:
                                 const ::basegfx::B2DVector& rLineWidths,
                                 basegfx::B2DLineJoin,
                                 com::sun::star::drawing::LineCap eLineCap) SAL_OVERRIDE;
+    virtual bool            drawGradient( const tools::PolyPolygon&, const Gradient& ) SAL_OVERRIDE { return false; };
 
     // CopyArea --> No RasterOp, but ClipRegion
     virtual void            copyArea( long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth,
diff --git a/vcl/inc/salgdi.hxx b/vcl/inc/salgdi.hxx
index 7e06ee6..2266d87 100644
--- a/vcl/inc/salgdi.hxx
+++ b/vcl/inc/salgdi.hxx
@@ -288,6 +288,12 @@ public:
                                     const sal_uInt8* const* pFlgAry,
                                     const OutputDevice *pOutDev );
 
+    bool                        DrawGradient(
+                                    const tools::PolyPolygon& rPolyPoly,
+                                    const Gradient& rGradient,
+                                    OutputDevice* );
+
+
     // CopyArea --> No RasterOp, but ClipRegion
     void                        CopyArea(
                                     long nDestX, long nDestY,
@@ -451,6 +457,11 @@ protected:
                                     const SalPoint* const* pPtAry,
                                     const sal_uInt8* const* pFlgAry ) = 0;
 
+
+    virtual bool                drawGradient(
+                                    const tools::PolyPolygon& rPolyPoly,
+                                    const Gradient& rGradient ) = 0;
+
     // CopyArea --> No RasterOp, but ClipRegion
     virtual void                copyArea(
                                     long nDestX, long nDestY,
diff --git a/vcl/inc/unx/salgdi.h b/vcl/inc/unx/salgdi.h
index c4eea0f..f564112 100644
--- a/vcl/inc/unx/salgdi.h
+++ b/vcl/inc/unx/salgdi.h
@@ -277,6 +277,8 @@ public:
         basegfx::B2DLineJoin,
         com::sun::star::drawing::LineCap) SAL_OVERRIDE;
     virtual bool            drawFilledTrapezoids( const ::basegfx::B2DTrapezoid*, int nTrapCount, double fTransparency );
+    virtual bool            drawGradient( const tools::PolyPolygon&, const Gradient& ) SAL_OVERRIDE { return false; };
+
 
 #if 1 // TODO: remove these obselete methods
     virtual bool        drawPolyLineBezier( sal_uInt32 nPoints,
diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index 66c5618..ca423fb 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -219,6 +219,7 @@ protected:
     virtual bool    drawPolyLineBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry );
     virtual bool    drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry );
     virtual bool    drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints, const SalPoint* const* pPtAry, const BYTE* const* pFlgAry );
+    virtual bool        drawGradient( const tools::PolyPolygon&, const Gradient& ) SAL_OVERRIDE { return false; };
 
     // CopyArea --> No RasterOp, but ClipRegion
     virtual void        copyArea( long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth,
diff --git a/vcl/source/gdi/salgdilayout.cxx b/vcl/source/gdi/salgdilayout.cxx
index a331f0b..599500b 100644
--- a/vcl/source/gdi/salgdilayout.cxx
+++ b/vcl/source/gdi/salgdilayout.cxx
@@ -544,6 +544,11 @@ bool SalGraphics::DrawPolyLine( const basegfx::B2DPolygon& i_rPolygon,
     return bRet;
 }
 
+bool SalGraphics::DrawGradient( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient, OutputDevice* )
+{
+    return drawGradient( rPolyPoly, rGradient );
+}
+
 void SalGraphics::CopyArea( long nDestX, long nDestY,
                             long nSrcX, long nSrcY,
                             long nSrcWidth, long nSrcHeight,
diff --git a/vcl/source/outdev/gradient.cxx b/vcl/source/outdev/gradient.cxx
index f8b0b21..c018fea 100644
--- a/vcl/source/outdev/gradient.cxx
+++ b/vcl/source/outdev/gradient.cxx
@@ -50,6 +50,12 @@ void OutputDevice::DrawGradient( const tools::PolyPolygon& rPolyPoly,
     if ( mbOutputClipped )
         return;
 
+    if ( mpGraphics || AcquireGraphics() )
+    {
+        if ( mpGraphics->DrawGradient( rPolyPoly, rGradient, this ) )
+            return;
+    }
+
     if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
     {
         if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) )
@@ -71,27 +77,7 @@ void OutputDevice::DrawGradient( const tools::PolyPolygon& rPolyPoly,
             SetGrayscaleColors( aGradient );
         }
 
-        if( mpMetaFile )
-        {
-            const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
-
-            if ( rPolyPoly.IsRect() )
-            {
-                mpMetaFile->AddAction( new MetaGradientAction( aBoundRect, aGradient ) );
-            }
-            else
-            {
-                mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
-                mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
-
-                Push( PushFlags::CLIPREGION );
-                IntersectClipRegion(vcl::Region(rPolyPoly));
-                DrawGradient( aBoundRect, rGradient );
-                Pop();
-
-                mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
-            }
-        }
+        DrawGradientToMetafile( rPolyPoly, rGradient );
 
         if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
             return;
@@ -148,9 +134,9 @@ void OutputDevice::DrawGradient( const tools::PolyPolygon& rPolyPoly,
                     // if the clipping polypolygon is a rectangle, then it's the same size as the bounding of the
                     // polypolygon, so pass in a NULL for the clipping parameter
                     if( aGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL )
-                        DrawLinearGradient( aRect, aGradient, false, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
+                        DrawLinearGradient( aRect, aGradient, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
                     else
-                        DrawComplexGradient( aRect, aGradient, false, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
+                        DrawComplexGradient( aRect, aGradient, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
                 }
 
                 Pop();
@@ -162,6 +148,83 @@ void OutputDevice::DrawGradient( const tools::PolyPolygon& rPolyPoly,
         mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
 }
 
+void OutputDevice::DrawGradientToMetafile ( const tools::PolyPolygon& rPolyPoly,
+                                            const Gradient& rGradient )
+{
+    if ( !mpMetaFile )
+        return;
+
+    if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
+    {
+        Gradient aGradient( rGradient );
+
+        if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
+        {
+            SetGrayscaleColors( aGradient );
+        }
+
+        const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
+
+        if ( rPolyPoly.IsRect() )
+        {
+            mpMetaFile->AddAction( new MetaGradientAction( aBoundRect, aGradient ) );
+        }
+        else
+        {
+            mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
+            mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
+
+            Push( PushFlags::CLIPREGION );
+            IntersectClipRegion(vcl::Region(rPolyPoly));
+            DrawGradient( aBoundRect, rGradient );
+            Pop();
+
+            mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
+        }
+
+        if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
+            return;
+
+        // Clip and then draw the gradient
+        if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
+        {
+            // convert rectangle to pixels
+            Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
+            aRect.Justify();
+
+            // do nothing if the rectangle is empty
+            if ( !aRect.IsEmpty() )
+            {
+                if( !mbOutputClipped )
+                {
+                    tools::PolyPolygon aClipPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) );
+
+                    // calculate step count if necessary
+                    if ( !aGradient.GetSteps() )
+                        aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
+
+                    if ( rPolyPoly.IsRect() )
+                    {
+                        // because we draw with no border line, we have to expand gradient
+                        // rect to avoid missing lines on the right and bottom edge
+                        aRect.Left()--;
+                        aRect.Top()--;
+                        aRect.Right()++;
+                        aRect.Bottom()++;
+                    }
+
+                    // if the clipping polypolygon is a rectangle, then it's the same size as the bounding of the
+                    // polypolygon, so pass in a NULL for the clipping parameter
+                    if( aGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL )
+                        DrawLinearGradientToMetafile( aRect, aGradient );
+                    else
+                        DrawComplexGradientToMetafile( aRect, aGradient );
+                }
+            }
+        }
+    }
+}
+
 namespace
 {
     inline sal_uInt8 GetGradientColorValue( long nValue )
@@ -177,7 +240,7 @@ namespace
 
 void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
                                        const Gradient& rGradient,
-                                       bool bMtf, const tools::PolyPolygon* pClipPolyPoly )
+                                       const tools::PolyPolygon* pClipPolyPoly )
 {
     // get BoundRect of rotated rectangle
     Rectangle aRect;
@@ -244,10 +307,8 @@ void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
         nRed        = (sal_uInt8)nStartRed;
         nGreen      = (sal_uInt8)nStartGreen;
         nBlue       = (sal_uInt8)nStartBlue;
-        if ( bMtf )
-            mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
-        else
-            mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
+
+        mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
 
         aBorderRect.Bottom() = (long)( aBorderRect.Top() + fBorder );
         aRect.Top() = aBorderRect.Bottom();
@@ -256,10 +317,9 @@ void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
         aPoly[2] = aBorderRect.BottomRight();
         aPoly[3] = aBorderRect.BottomLeft();
         aPoly.Rotate( aCenter, nAngle );
-        if ( bMtf )
-            mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
-        else
-            ImplDrawPolygon( aPoly, pClipPolyPoly );
+
+        ImplDrawPolygon( aPoly, pClipPolyPoly );
+
         if ( !bLinear)
         {
             aBorderRect = aMirrorRect;
@@ -270,14 +330,13 @@ void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
             aPoly[2] = aBorderRect.BottomRight();
             aPoly[3] = aBorderRect.BottomLeft();
             aPoly.Rotate( aCenter, nAngle );
-            if ( bMtf )
-                mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
-            else
-                ImplDrawPolygon( aPoly, pClipPolyPoly );
+
+            ImplDrawPolygon( aPoly, pClipPolyPoly );
         }
     }
 
     // calculate step count
+    bool    bMtf = false;
     long    nStepCount  = GetGradientSteps( rGradient, aRect, bMtf );
 
     // minimal three steps and maximal as max color steps
@@ -313,10 +372,8 @@ void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
         nGreen = GetGradientColorValue((long)fTempColor);
         fTempColor = ((double)nStartBlue) * (1.0-fAlpha) + ((double)nEndBlue) * fAlpha;
         nBlue = GetGradientColorValue((long)fTempColor);
-        if ( bMtf )
-            mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
-        else
-            mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
+
+        mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
 
         // Polygon for this color step
         aRect.Top() = (long)( fGradientLine + ((double) i) * fScanInc );
@@ -326,10 +383,9 @@ void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
         aPoly[2] = aRect.BottomRight();
         aPoly[3] = aRect.BottomLeft();
         aPoly.Rotate( aCenter, nAngle );
-        if ( bMtf )
-            mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
-        else
-            ImplDrawPolygon( aPoly, pClipPolyPoly );
+
+        ImplDrawPolygon( aPoly, pClipPolyPoly );
+
         if ( !bLinear )
         {
             aMirrorRect.Bottom() = (long)( fMirrorGradientLine - ((double) i) * fScanInc );
@@ -339,10 +395,8 @@ void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
             aPoly[2] = aMirrorRect.BottomRight();
             aPoly[3] = aMirrorRect.BottomLeft();
             aPoly.Rotate( aCenter, nAngle );
-            if ( bMtf )
-                mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
-            else
-                ImplDrawPolygon( aPoly, pClipPolyPoly );
+
+            ImplDrawPolygon( aPoly, pClipPolyPoly );
         }
     }
     if ( !bLinear)
@@ -351,10 +405,8 @@ void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
         nRed = GetGradientColorValue(nEndRed);
         nGreen = GetGradientColorValue(nEndGreen);
         nBlue = GetGradientColorValue(nEndBlue);
-        if ( bMtf )
-            mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
-        else
-            mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
+
+        mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
 
         aRect.Top() = (long)( fGradientLine + ((double)nSteps) * fScanInc );
         aRect.Bottom() = (long)( fMirrorGradientLine - ((double) nSteps) * fScanInc );
@@ -363,16 +415,14 @@ void OutputDevice::DrawLinearGradient( const Rectangle& rRect,
         aPoly[2] = aRect.BottomRight();
         aPoly[3] = aRect.BottomLeft();
         aPoly.Rotate( aCenter, nAngle );
-        if ( bMtf )
-            mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
-        else
-            ImplDrawPolygon( aPoly, pClipPolyPoly );
+
+        ImplDrawPolygon( aPoly, pClipPolyPoly );
     }
 }
 
 void OutputDevice::DrawComplexGradient( const Rectangle& rRect,
-                                            const Gradient& rGradient,
-                                            bool bMtf, const tools::PolyPolygon* pClipPolyPoly )
+                                        const Gradient& rGradient,
+                                        const tools::PolyPolygon* pClipPolyPoly )
 {
     // Determine if we output via Polygon or PolyPolygon
     // For all rasteroperations other then Overpaint always use PolyPolygon,
@@ -398,11 +448,12 @@ void OutputDevice::DrawComplexGradient( const Rectangle& rRect,
 
     rGradient.GetBoundRect( rRect, aRect, aCenter );
 
-    if ( UsePolyPolygonForComplexGradient() || bMtf )
+    if ( UsePolyPolygonForComplexGradient() )
         pPolyPoly.reset(new tools::PolyPolygon( 2 ));
 
-    // last parameter - true if complex gradient, false if linear
-    long nStepCount = GetGradientSteps( rGradient, rRect, bMtf, true );
+    bool bMtf = false;
+    bool bComplex = true;
+    long nStepCount = GetGradientSteps( rGradient, rRect, bMtf, bComplex );
 
     // at least three steps and at most the number of colour differences
     long nSteps = std::max( nStepCount, 2L );
@@ -438,10 +489,7 @@ void OutputDevice::DrawComplexGradient( const Rectangle& rRect,
     sal_uInt8   nRed = (sal_uInt8) nStartRed, nGreen = (sal_uInt8) nStartGreen, nBlue = (sal_uInt8) nStartBlue;
     bool    bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
 
-    if( bMtf )
-        mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
-    else
-        mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
+    mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
 
     if( pPolyPoly )
     {
@@ -494,10 +542,7 @@ void OutputDevice::DrawComplexGradient( const Rectangle& rRect,
             pPolyPoly->Replace( pPolyPoly->GetObject( 1 ), 0 );
             pPolyPoly->Replace( aPoly, 1 );
 
-            if( bMtf )
-                mpMetaFile->AddAction( new MetaPolyPolygonAction( *pPolyPoly ) );
-            else
-                ImplDrawPolyPolygon( *pPolyPoly, pClipPolyPoly );
+            ImplDrawPolyPolygon( *pPolyPoly, pClipPolyPoly );
 
             // #107349# Set fill color _after_ geometry painting:
             // pPolyPoly's geometry is the band from last iteration's
@@ -506,18 +551,12 @@ void OutputDevice::DrawComplexGradient( const Rectangle& rRect,
             // full aPoly. Thus, here, we're painting the band before
             // the one painted in the window outdev path below. To get
             // matching colors, have to delay color setting here.
-            if( bMtf )
-                mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
-            else
-                mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
+            mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
         }
         else
         {
             // #107349# Set fill color _before_ geometry painting
-            if( bMtf )
-                mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
-            else
-                mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
+            mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
 
             ImplDrawPolygon( aPoly, pClipPolyPoly );
         }
@@ -540,18 +579,322 @@ void OutputDevice::DrawComplexGradient( const Rectangle& rRect,
                 nBlue = GetGradientColorValue( nEndBlue );
             }
 
-            if( bMtf )
-            {
-                mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
-                mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
-            }
-            else
-            {
-                mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
-                   ImplDrawPolygon( rPoly, pClipPolyPoly );
-            }
+            mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
+            ImplDrawPolygon( rPoly, pClipPolyPoly );
+        }
+    }
+}
+
+void OutputDevice::DrawLinearGradientToMetafile( const Rectangle& rRect,
+                                                 const Gradient& rGradient )
+{
+    // get BoundRect of rotated rectangle
+    Rectangle aRect;
+    Point     aCenter;
+    sal_uInt16    nAngle = rGradient.GetAngle() % 3600;
+
+    rGradient.GetBoundRect( rRect, aRect, aCenter );
+
+    bool bLinear = (rGradient.GetStyle() == GradientStyle_LINEAR);
+    double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0;
+    if ( !bLinear )
+    {
+        fBorder /= 2.0;
+    }
+    Rectangle aMirrorRect = aRect; // used in style axial
+    aMirrorRect.Top() = ( aRect.Top() + aRect.Bottom() ) / 2;
+    if ( !bLinear )
+    {
+        aRect.Bottom() = aMirrorRect.Top();
+    }
+
+    // colour-intensities of start- and finish; change if needed
+    long    nFactor;
+    Color   aStartCol   = rGradient.GetStartColor();
+    Color   aEndCol     = rGradient.GetEndColor();
+    long    nStartRed   = aStartCol.GetRed();
+    long    nStartGreen = aStartCol.GetGreen();
+    long    nStartBlue  = aStartCol.GetBlue();
+    long    nEndRed     = aEndCol.GetRed();
+    long    nEndGreen   = aEndCol.GetGreen();
+    long    nEndBlue    = aEndCol.GetBlue();
+            nFactor     = rGradient.GetStartIntensity();
+            nStartRed   = (nStartRed   * nFactor) / 100;
+            nStartGreen = (nStartGreen * nFactor) / 100;
+            nStartBlue  = (nStartBlue  * nFactor) / 100;
+            nFactor     = rGradient.GetEndIntensity();
+            nEndRed     = (nEndRed   * nFactor) / 100;
+            nEndGreen   = (nEndGreen * nFactor) / 100;
+            nEndBlue    = (nEndBlue  * nFactor) / 100;
+
+    // gradient style axial has exchanged start and end colors
+    if ( !bLinear)
+    {
+        long nTempColor = nStartRed;
+        nStartRed = nEndRed;
+        nEndRed = nTempColor;
+        nTempColor = nStartGreen;
+        nStartGreen = nEndGreen;
+        nEndGreen = nTempColor;
+        nTempColor = nStartBlue;
+        nStartBlue = nEndBlue;
+        nEndBlue = nTempColor;
+    }
+
+    sal_uInt8   nRed;
+    sal_uInt8   nGreen;
+    sal_uInt8   nBlue;
+
+    // Create border
+    Rectangle aBorderRect = aRect;
+    Polygon     aPoly( 4 );
+    if (fBorder > 0.0)
+    {
+        nRed        = (sal_uInt8)nStartRed;
+        nGreen      = (sal_uInt8)nStartGreen;
+        nBlue       = (sal_uInt8)nStartBlue;
+
+        mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+
+        aBorderRect.Bottom() = (long)( aBorderRect.Top() + fBorder );
+        aRect.Top() = aBorderRect.Bottom();
+        aPoly[0] = aBorderRect.TopLeft();
+        aPoly[1] = aBorderRect.TopRight();
+        aPoly[2] = aBorderRect.BottomRight();
+        aPoly[3] = aBorderRect.BottomLeft();
+        aPoly.Rotate( aCenter, nAngle );
+
+        mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
+
+        if ( !bLinear)
+        {
+            aBorderRect = aMirrorRect;
+            aBorderRect.Top() = (long) ( aBorderRect.Bottom() - fBorder );
+            aMirrorRect.Bottom() = aBorderRect.Top();
+            aPoly[0] = aBorderRect.TopLeft();
+            aPoly[1] = aBorderRect.TopRight();
+            aPoly[2] = aBorderRect.BottomRight();
+            aPoly[3] = aBorderRect.BottomLeft();
+            aPoly.Rotate( aCenter, nAngle );
+
+            mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
+        }
+    }
+
+    bool    bMtf = true;
+    long    nStepCount  = GetGradientSteps( rGradient, aRect, bMtf );
+
+    // minimal three steps and maximal as max color steps
+    long   nAbsRedSteps   = std::abs( nEndRed   - nStartRed );
+    long   nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
+    long   nAbsBlueSteps  = std::abs( nEndBlue  - nStartBlue );
+    long   nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
+    nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
+    long nSteps = std::min( nStepCount, nMaxColorSteps );
+    if ( nSteps < 3)
+    {
+        nSteps = 3;
+    }
+
+    double fScanInc = ((double)aRect.GetHeight()) / (double) nSteps;
+    double fGradientLine = (double)aRect.Top();
+    double fMirrorGradientLine = (double) aMirrorRect.Bottom();
+
+    double fAlpha = 0.0;
+    const double fStepsMinus1 = ((double)nSteps) - 1.0;
+    double fTempColor;
+    if ( !bLinear)
+    {
+        nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
+    }
+    for ( long i = 0; i < nSteps; i++ )
+    {
+        // linear interpolation of color
+        fAlpha = ((double)i) / fStepsMinus1;
+        fTempColor = ((double)nStartRed) * (1.0-fAlpha) + ((double)nEndRed) * fAlpha;
+        nRed = GetGradientColorValue((long)fTempColor);
+        fTempColor = ((double)nStartGreen) * (1.0-fAlpha) + ((double)nEndGreen) * fAlpha;
+        nGreen = GetGradientColorValue((long)fTempColor);
+        fTempColor = ((double)nStartBlue) * (1.0-fAlpha) + ((double)nEndBlue) * fAlpha;
+        nBlue = GetGradientColorValue((long)fTempColor);
+
+        mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+
+        // Polygon for this color step
+        aRect.Top() = (long)( fGradientLine + ((double) i) * fScanInc );
+        aRect.Bottom() = (long)( fGradientLine + ( ((double) i) + 1.0 ) * fScanInc );
+        aPoly[0] = aRect.TopLeft();
+        aPoly[1] = aRect.TopRight();
+        aPoly[2] = aRect.BottomRight();
+        aPoly[3] = aRect.BottomLeft();
+        aPoly.Rotate( aCenter, nAngle );
+
+        mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
+
+        if ( !bLinear )
+        {
+            aMirrorRect.Bottom() = (long)( fMirrorGradientLine - ((double) i) * fScanInc );
+            aMirrorRect.Top() = (long)( fMirrorGradientLine - (((double) i) + 1.0)* fScanInc );
+            aPoly[0] = aMirrorRect.TopLeft();
+            aPoly[1] = aMirrorRect.TopRight();
+            aPoly[2] = aMirrorRect.BottomRight();
+            aPoly[3] = aMirrorRect.BottomLeft();
+            aPoly.Rotate( aCenter, nAngle );
+
+            mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
         }
     }
+    if ( !bLinear)
+    {
+        // draw middle polygon with end color
+        nRed = GetGradientColorValue(nEndRed);
+        nGreen = GetGradientColorValue(nEndGreen);
+        nBlue = GetGradientColorValue(nEndBlue);
+
+        mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+
+        aRect.Top() = (long)( fGradientLine + ((double)nSteps) * fScanInc );
+        aRect.Bottom() = (long)( fMirrorGradientLine - ((double) nSteps) * fScanInc );
+        aPoly[0] = aRect.TopLeft();
+        aPoly[1] = aRect.TopRight();
+        aPoly[2] = aRect.BottomRight();
+        aPoly[3] = aRect.BottomLeft();
+        aPoly.Rotate( aCenter, nAngle );
+
+        mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
+    }
+}
+
+void OutputDevice::DrawComplexGradientToMetafile( const Rectangle& rRect,
+                                                  const Gradient& rGradient )
+{
+    // Determine if we output via Polygon or PolyPolygon
+    // For all rasteroperations other then Overpaint always use PolyPolygon,
+    // as we will get wrong results if we output multiple times on top of each other.
+    // Also for printers always use PolyPolygon, as not all printers
+    // can print polygons on top of each other.
+
+    boost::scoped_ptr<tools::PolyPolygon> pPolyPoly;
+    Rectangle       aRect;
+    Point           aCenter;
+    Color           aStartCol( rGradient.GetStartColor() );
+    Color           aEndCol( rGradient.GetEndColor() );
+    long            nStartRed = ( (long) aStartCol.GetRed() * rGradient.GetStartIntensity() ) / 100;
+    long            nStartGreen = ( (long) aStartCol.GetGreen() * rGradient.GetStartIntensity() ) / 100;
+    long            nStartBlue = ( (long) aStartCol.GetBlue() * rGradient.GetStartIntensity() ) / 100;
+    long            nEndRed = ( (long) aEndCol.GetRed() * rGradient.GetEndIntensity() ) / 100;
+    long            nEndGreen = ( (long) aEndCol.GetGreen() * rGradient.GetEndIntensity() ) / 100;
+    long            nEndBlue = ( (long) aEndCol.GetBlue() * rGradient.GetEndIntensity() ) / 100;
+    long            nRedSteps = nEndRed - nStartRed;
+    long            nGreenSteps = nEndGreen - nStartGreen;
+    long            nBlueSteps = nEndBlue   - nStartBlue;
+    sal_uInt16      nAngle = rGradient.GetAngle() % 3600;
+
+    rGradient.GetBoundRect( rRect, aRect, aCenter );
+
+    pPolyPoly.reset(new tools::PolyPolygon( 2 ));
+
+    // last parameter - true if complex gradient, false if linear
+    long nStepCount = GetGradientSteps( rGradient, rRect, true, true );
+
+    // at least three steps and at most the number of colour differences
+    long nSteps = std::max( nStepCount, 2L );
+    long nCalcSteps  = std::abs( nRedSteps );
+    long nTempSteps = std::abs( nGreenSteps );
+    if ( nTempSteps > nCalcSteps )
+        nCalcSteps = nTempSteps;
+    nTempSteps = std::abs( nBlueSteps );
+    if ( nTempSteps > nCalcSteps )
+        nCalcSteps = nTempSteps;
+    if ( nCalcSteps < nSteps )
+        nSteps = nCalcSteps;
+    if ( !nSteps )
+        nSteps = 1;
+
+    // determine output limits and stepsizes for all directions
+    Polygon aPoly;
+    double  fScanLeft = aRect.Left();
+    double  fScanTop = aRect.Top();
+    double  fScanRight = aRect.Right();
+    double  fScanBottom = aRect.Bottom();
+    double fScanIncX = (double) aRect.GetWidth() / (double) nSteps * 0.5;
+    double fScanIncY = (double) aRect.GetHeight() / (double) nSteps * 0.5;
+
+    // all gradients are rendered as nested rectangles which shrink
+    // equally in each dimension - except for 'square' gradients
+    // which shrink to a central vertex but are not per-se square.
+    if( rGradient.GetStyle() != GradientStyle_SQUARE )
+    {
+        fScanIncY = std::min( fScanIncY, fScanIncX );
+        fScanIncX = fScanIncY;
+    }
+    sal_uInt8   nRed = (sal_uInt8) nStartRed, nGreen = (sal_uInt8) nStartGreen, nBlue = (sal_uInt8) nStartBlue;
+    bool    bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
+
+    mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+
+    pPolyPoly->Insert( aPoly = rRect );
+    pPolyPoly->Insert( aPoly );
+
+    // loop to output Polygone/PolyPolygone sequentially
+    for( long i = 1; i < nSteps; i++ )
+    {
+        // calculate new Polygon
+        aRect.Left() = (long)( fScanLeft += fScanIncX );
+        aRect.Top() = (long)( fScanTop += fScanIncY );
+        aRect.Right() = (long)( fScanRight -= fScanIncX );
+        aRect.Bottom() = (long)( fScanBottom -= fScanIncY );
+
+        if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
+            break;
+
+        if( rGradient.GetStyle() == GradientStyle_RADIAL || rGradient.GetStyle() == GradientStyle_ELLIPTICAL )
+            aPoly = Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
+        else
+            aPoly = Polygon( aRect );
+
+        aPoly.Rotate( aCenter, nAngle );
+
+        // adapt colour accordingly
+        const long nStepIndex = ( ( pPolyPoly ) ? i : ( i + 1 ) );
+        nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
+        nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
+        nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
+
+        bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
+
+        pPolyPoly->Replace( pPolyPoly->GetObject( 1 ), 0 );
+        pPolyPoly->Replace( aPoly, 1 );
+
+        mpMetaFile->AddAction( new MetaPolyPolygonAction( *pPolyPoly ) );
+
+        // #107349# Set fill color _after_ geometry painting:
+        // pPolyPoly's geometry is the band from last iteration's
+        // aPoly to current iteration's aPoly. The window outdev
+        // path (see else below), on the other hand, paints the
+        // full aPoly. Thus, here, we're painting the band before
+        // the one painted in the window outdev path below. To get
+        // matching colors, have to delay color setting here.
+        mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+    }
+
+    const Polygon& rPoly = pPolyPoly->GetObject( 1 );
+
+    if( !rPoly.GetBoundRect().IsEmpty() )
+    {
+        // #107349# Paint last polygon with end color only if loop
+        // has generated output. Otherwise, the current
+        // (i.e. start) color is taken, to generate _any_ output.
+        if( bPaintLastPolygon )
+        {
+            nRed = GetGradientColorValue( nEndRed );
+            nGreen = GetGradientColorValue( nEndGreen );
+            nBlue = GetGradientColorValue( nEndBlue );
+        }
+
+        mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+        mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
+    }
 }
 
 long OutputDevice::GetGradientStepCount( long nMinRect )
@@ -671,9 +1014,9 @@ void OutputDevice::AddGradientActions( const Rectangle& rRect, const Gradient& r
             aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
 
         if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
-            DrawLinearGradient( aRect, aGradient, true, NULL );
+            DrawLinearGradientToMetafile( aRect, aGradient );
         else
-            DrawComplexGradient( aRect, aGradient, true, NULL );
+            DrawComplexGradientToMetafile( aRect, aGradient );
 
         mpMetaFile->AddAction( new MetaPopAction() );
         mpMetaFile = pOldMtf;


More information about the Libreoffice-commits mailing list