summaryrefslogtreecommitdiffstats
path: root/vcl/source/gdi/gradient.cxx
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/source/gdi/gradient.cxx681
1 files changed, 681 insertions, 0 deletions
diff --git a/vcl/source/gdi/gradient.cxx b/vcl/source/gdi/gradient.cxx
new file mode 100644
index 000000000..b4e7db904
--- /dev/null
+++ b/vcl/source/gdi/gradient.cxx
@@ -0,0 +1,681 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <tools/gen.hxx>
+
+#include <vcl/gradient.hxx>
+#include <vcl/metaact.hxx>
+#include <cmath>
+
+class Gradient::Impl
+{
+public:
+ GradientStyle meStyle;
+ Color maStartColor;
+ Color maEndColor;
+ Degree10 mnAngle;
+ sal_uInt16 mnBorder;
+ sal_uInt16 mnOfsX;
+ sal_uInt16 mnOfsY;
+ sal_uInt16 mnIntensityStart;
+ sal_uInt16 mnIntensityEnd;
+ sal_uInt16 mnStepCount;
+
+ Impl()
+ : meStyle (GradientStyle::Linear)
+ , maStartColor(COL_BLACK)
+ , maEndColor(COL_WHITE)
+ , mnAngle(0)
+ , mnBorder(0)
+ , mnOfsX(50)
+ , mnOfsY(50)
+ , mnIntensityStart(100)
+ , mnIntensityEnd(100)
+ , mnStepCount(0)
+ {
+ }
+
+ Impl(const Impl& rImplGradient)
+ : meStyle (rImplGradient.meStyle)
+ , maStartColor(rImplGradient.maStartColor)
+ , maEndColor(rImplGradient.maEndColor)
+ , mnAngle(rImplGradient.mnAngle)
+ , mnBorder(rImplGradient.mnBorder)
+ , mnOfsX(rImplGradient.mnOfsX)
+ , mnOfsY(rImplGradient.mnOfsY)
+ , mnIntensityStart(rImplGradient.mnIntensityStart)
+ , mnIntensityEnd(rImplGradient.mnIntensityEnd)
+ , mnStepCount(rImplGradient.mnStepCount)
+ {
+ }
+
+ bool operator==(const Impl& rImpl_Gradient) const
+ {
+ return (meStyle == rImpl_Gradient.meStyle)
+ && (mnAngle == rImpl_Gradient.mnAngle)
+ && (mnBorder == rImpl_Gradient.mnBorder)
+ && (mnOfsX == rImpl_Gradient.mnOfsX)
+ && (mnOfsY == rImpl_Gradient.mnOfsY)
+ && (mnStepCount == rImpl_Gradient.mnStepCount)
+ && (mnIntensityStart == rImpl_Gradient.mnIntensityStart)
+ && (mnIntensityEnd == rImpl_Gradient.mnIntensityEnd)
+ && (maStartColor == rImpl_Gradient.maStartColor)
+ && (maEndColor == rImpl_Gradient.maEndColor);
+ }
+};
+
+Gradient::Gradient() = default;
+
+Gradient::Gradient( const Gradient& ) = default;
+
+Gradient::Gradient( Gradient&& ) = default;
+
+Gradient::Gradient( GradientStyle eStyle,
+ const Color& rStartColor, const Color& rEndColor )
+{
+ mpImplGradient->meStyle = eStyle;
+ mpImplGradient->maStartColor = rStartColor;
+ mpImplGradient->maEndColor = rEndColor;
+}
+
+Gradient::~Gradient() = default;
+
+
+GradientStyle Gradient::GetStyle() const
+{
+ return mpImplGradient->meStyle;
+}
+
+void Gradient::SetStyle( GradientStyle eStyle )
+{
+ mpImplGradient->meStyle = eStyle;
+}
+
+const Color& Gradient::GetStartColor() const
+{
+ return mpImplGradient->maStartColor;
+}
+
+void Gradient::SetStartColor( const Color& rColor )
+{
+ mpImplGradient->maStartColor = rColor;
+}
+
+const Color& Gradient::GetEndColor() const
+{
+ return mpImplGradient->maEndColor;
+}
+
+void Gradient::SetEndColor( const Color& rColor )
+{
+ mpImplGradient->maEndColor = rColor;
+}
+
+Degree10 Gradient::GetAngle() const
+{
+ return mpImplGradient->mnAngle;
+}
+
+void Gradient::SetAngle( Degree10 nAngle )
+{
+ mpImplGradient->mnAngle = nAngle;
+}
+
+sal_uInt16 Gradient::GetBorder() const
+{
+ return mpImplGradient->mnBorder;
+}
+
+void Gradient::SetBorder( sal_uInt16 nBorder )
+{
+ mpImplGradient->mnBorder = nBorder;
+}
+
+sal_uInt16 Gradient::GetOfsX() const
+{
+ return mpImplGradient->mnOfsX;
+}
+
+void Gradient::SetOfsX( sal_uInt16 nOfsX )
+{
+ mpImplGradient->mnOfsX = nOfsX;
+}
+
+sal_uInt16 Gradient::GetOfsY() const
+{
+ return mpImplGradient->mnOfsY;
+}
+
+void Gradient::SetOfsY( sal_uInt16 nOfsY )
+{
+ mpImplGradient->mnOfsY = nOfsY;
+}
+
+sal_uInt16 Gradient::GetStartIntensity() const
+{
+ return mpImplGradient->mnIntensityStart;
+}
+
+void Gradient::SetStartIntensity( sal_uInt16 nIntens )
+{
+ mpImplGradient->mnIntensityStart = nIntens;
+}
+
+sal_uInt16 Gradient::GetEndIntensity() const
+{
+ return mpImplGradient->mnIntensityEnd;
+}
+
+void Gradient::SetEndIntensity( sal_uInt16 nIntens )
+{
+ mpImplGradient->mnIntensityEnd = nIntens;
+}
+
+sal_uInt16 Gradient::GetSteps() const
+{
+ return mpImplGradient->mnStepCount;
+}
+
+void Gradient::SetSteps( sal_uInt16 nSteps )
+{
+ mpImplGradient->mnStepCount = nSteps;
+}
+
+void Gradient::GetBoundRect( const tools::Rectangle& rRect, tools::Rectangle& rBoundRect, Point& rCenter ) const
+{
+ tools::Rectangle aRect( rRect );
+ Degree10 nAngle = GetAngle() % 3600_deg10;
+
+ if( GetStyle() == GradientStyle::Linear || GetStyle() == GradientStyle::Axial )
+ {
+ const double fAngle = toRadians(nAngle);
+ const double fWidth = aRect.GetWidth();
+ const double fHeight = aRect.GetHeight();
+ double fDX = fWidth * fabs( cos( fAngle ) ) +
+ fHeight * fabs( sin( fAngle ) );
+ double fDY = fHeight * fabs( cos( fAngle ) ) +
+ fWidth * fabs( sin( fAngle ) );
+ fDX = (fDX - fWidth) * 0.5 + 0.5;
+ fDY = (fDY - fHeight) * 0.5 + 0.5;
+ aRect.AdjustLeft( -static_cast<tools::Long>(fDX) );
+ aRect.AdjustRight(static_cast<tools::Long>(fDX) );
+ aRect.AdjustTop( -static_cast<tools::Long>(fDY) );
+ aRect.AdjustBottom(static_cast<tools::Long>(fDY) );
+
+ rBoundRect = aRect;
+ rCenter = rRect.Center();
+ }
+ else
+ {
+ if( GetStyle() == GradientStyle::Square || GetStyle() == GradientStyle::Rect )
+ {
+ const double fAngle = toRadians(nAngle);
+ const double fWidth = aRect.GetWidth();
+ const double fHeight = aRect.GetHeight();
+ double fDX = fWidth * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle ) );
+ double fDY = fHeight * fabs( cos( fAngle ) ) + fWidth * fabs( sin( fAngle ) );
+
+ fDX = ( fDX - fWidth ) * 0.5 + 0.5;
+ fDY = ( fDY - fHeight ) * 0.5 + 0.5;
+
+ aRect.AdjustLeft( -static_cast<tools::Long>(fDX) );
+ aRect.AdjustRight(static_cast<tools::Long>(fDX) );
+ aRect.AdjustTop( -static_cast<tools::Long>(fDY) );
+ aRect.AdjustBottom(static_cast<tools::Long>(fDY) );
+ }
+
+ Size aSize( aRect.GetSize() );
+
+ if( GetStyle() == GradientStyle::Radial )
+ {
+ // Calculation of radii for circle
+ aSize.setWidth( static_cast<tools::Long>(0.5 + sqrt(static_cast<double>(aSize.Width())*static_cast<double>(aSize.Width()) + static_cast<double>(aSize.Height())*static_cast<double>(aSize.Height()))) );
+ aSize.setHeight( aSize.Width() );
+ }
+ else if( GetStyle() == GradientStyle::Elliptical )
+ {
+ // Calculation of radii for ellipse
+ aSize.setWidth( static_cast<tools::Long>( 0.5 + static_cast<double>(aSize.Width()) * M_SQRT2 ) );
+ aSize.setHeight( static_cast<tools::Long>( 0.5 + static_cast<double>(aSize.Height()) * M_SQRT2) );
+ }
+
+ // Calculate new centers
+ tools::Long nZWidth = aRect.GetWidth() * static_cast<tools::Long>(GetOfsX()) / 100;
+ tools::Long nZHeight = aRect.GetHeight() * static_cast<tools::Long>(GetOfsY()) / 100;
+ tools::Long nBorderX = static_cast<tools::Long>(GetBorder()) * aSize.Width() / 100;
+ tools::Long nBorderY = static_cast<tools::Long>(GetBorder()) * aSize.Height() / 100;
+ rCenter = Point( aRect.Left() + nZWidth, aRect.Top() + nZHeight );
+
+ // Respect borders
+ aSize.AdjustWidth( -nBorderX );
+ aSize.AdjustHeight( -nBorderY );
+
+ // Recalculate output rectangle
+ aRect.SetLeft( rCenter.X() - ( aSize.Width() >> 1 ) );
+ aRect.SetTop( rCenter.Y() - ( aSize.Height() >> 1 ) );
+
+ aRect.SetSize( aSize );
+ rBoundRect = aRect;
+ }
+}
+
+void Gradient::MakeGrayscale()
+{
+ Color aStartCol(GetStartColor());
+ Color aEndCol(GetEndColor());
+ sal_uInt8 cStartLum = aStartCol.GetLuminance();
+ sal_uInt8 cEndLum = aEndCol.GetLuminance();
+
+ aStartCol = Color(cStartLum, cStartLum, cStartLum);
+ aEndCol = Color(cEndLum, cEndLum, cEndLum);
+
+ SetStartColor(aStartCol);
+ SetEndColor(aEndCol);
+}
+
+Gradient& Gradient::operator=( const Gradient& ) = default;
+
+Gradient& Gradient::operator=( Gradient&& ) = default;
+
+bool Gradient::operator==( const Gradient& rGradient ) const
+{
+ return mpImplGradient == rGradient.mpImplGradient;
+}
+
+const sal_uInt32 GRADIENT_DEFAULT_STEPCOUNT = 0;
+
+void Gradient::AddGradientActions(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile)
+{
+ tools::Rectangle aRect(rRect);
+ aRect.Justify();
+
+ // do nothing if the rectangle is empty
+ if (aRect.IsEmpty())
+ return;
+
+ rMetaFile.AddAction(new MetaPushAction(vcl::PushFlags::ALL));
+ rMetaFile.AddAction(new MetaISectRectClipRegionAction( aRect));
+ rMetaFile.AddAction(new MetaLineColorAction(Color(), false));
+
+ // because we draw with no border line, we have to expand gradient
+ // rect to avoid missing lines on the right and bottom edge
+ aRect.AdjustLeft( -1 );
+ aRect.AdjustTop( -1 );
+ aRect.AdjustRight( 1 );
+ aRect.AdjustBottom( 1 );
+
+ // calculate step count if necessary
+ if (!GetSteps())
+ SetSteps(GRADIENT_DEFAULT_STEPCOUNT);
+
+ if (GetStyle() == GradientStyle::Linear || GetStyle() == GradientStyle::Axial)
+ DrawLinearGradientToMetafile(aRect, rMetaFile);
+ else
+ DrawComplexGradientToMetafile(aRect, rMetaFile);
+
+ rMetaFile.AddAction(new MetaPopAction());
+}
+
+tools::Long Gradient::GetMetafileSteps(tools::Rectangle const& rRect) const
+{
+ // calculate step count
+ tools::Long nStepCount = GetSteps();
+
+ if (nStepCount)
+ return nStepCount;
+
+ if (GetStyle() == GradientStyle::Linear || GetStyle() == GradientStyle::Axial)
+ return rRect.GetHeight();
+ else
+ return std::min(rRect.GetWidth(), rRect.GetHeight());
+}
+
+
+static sal_uInt8 GetGradientColorValue(tools::Long nValue)
+{
+ if ( nValue < 0 )
+ return 0;
+ else if ( nValue > 0xFF )
+ return 0xFF;
+ else
+ return static_cast<sal_uInt8>(nValue);
+}
+
+void Gradient::DrawLinearGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const
+{
+ // get BoundRect of rotated rectangle
+ tools::Rectangle aRect;
+ Point aCenter;
+ Degree10 nAngle = GetAngle() % 3600_deg10;
+
+ GetBoundRect(rRect, aRect, aCenter);
+
+ bool bLinear = (GetStyle() == GradientStyle::Linear);
+ double fBorder = GetBorder() * aRect.GetHeight() / 100.0;
+ if ( !bLinear )
+ {
+ fBorder /= 2.0;
+ }
+ tools::Rectangle aMirrorRect = aRect; // used in style axial
+ aMirrorRect.SetTop( ( aRect.Top() + aRect.Bottom() ) / 2 );
+ if ( !bLinear )
+ {
+ aRect.SetBottom( aMirrorRect.Top() );
+ }
+
+ // colour-intensities of start- and finish; change if needed
+ tools::Long nFactor;
+ Color aStartCol = GetStartColor();
+ Color aEndCol = GetEndColor();
+ tools::Long nStartRed = aStartCol.GetRed();
+ tools::Long nStartGreen = aStartCol.GetGreen();
+ tools::Long nStartBlue = aStartCol.GetBlue();
+ tools::Long nEndRed = aEndCol.GetRed();
+ tools::Long nEndGreen = aEndCol.GetGreen();
+ tools::Long nEndBlue = aEndCol.GetBlue();
+ nFactor = GetStartIntensity();
+ nStartRed = (nStartRed * nFactor) / 100;
+ nStartGreen = (nStartGreen * nFactor) / 100;
+ nStartBlue = (nStartBlue * nFactor) / 100;
+ nFactor = GetEndIntensity();
+ nEndRed = (nEndRed * nFactor) / 100;
+ nEndGreen = (nEndGreen * nFactor) / 100;
+ nEndBlue = (nEndBlue * nFactor) / 100;
+
+ // gradient style axial has exchanged start and end colors
+ if ( !bLinear)
+ {
+ tools::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
+ tools::Rectangle aBorderRect = aRect;
+ tools::Polygon aPoly( 4 );
+ if (fBorder > 0.0)
+ {
+ nRed = static_cast<sal_uInt8>(nStartRed);
+ nGreen = static_cast<sal_uInt8>(nStartGreen);
+ nBlue = static_cast<sal_uInt8>(nStartBlue);
+
+ rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+
+ aBorderRect.SetBottom( static_cast<tools::Long>( aBorderRect.Top() + fBorder ) );
+ aRect.SetTop( aBorderRect.Bottom() );
+ aPoly[0] = aBorderRect.TopLeft();
+ aPoly[1] = aBorderRect.TopRight();
+ aPoly[2] = aBorderRect.BottomRight();
+ aPoly[3] = aBorderRect.BottomLeft();
+ aPoly.Rotate( aCenter, nAngle );
+
+ rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
+
+ if ( !bLinear)
+ {
+ aBorderRect = aMirrorRect;
+ aBorderRect.SetTop( static_cast<tools::Long>( aBorderRect.Bottom() - fBorder ) );
+ aMirrorRect.SetBottom( aBorderRect.Top() );
+ aPoly[0] = aBorderRect.TopLeft();
+ aPoly[1] = aBorderRect.TopRight();
+ aPoly[2] = aBorderRect.BottomRight();
+ aPoly[3] = aBorderRect.BottomLeft();
+ aPoly.Rotate( aCenter, nAngle );
+
+ rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
+ }
+ }
+
+ tools::Long nStepCount = GetMetafileSteps(aRect);
+
+ // minimal three steps and maximal as max color steps
+ tools::Long nAbsRedSteps = std::abs( nEndRed - nStartRed );
+ tools::Long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
+ tools::Long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue );
+ tools::Long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
+ nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
+ tools::Long nSteps = std::min( nStepCount, nMaxColorSteps );
+ if ( nSteps < 3)
+ {
+ nSteps = 3;
+ }
+
+ double fScanInc = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps);
+ double fGradientLine = static_cast<double>(aRect.Top());
+ double fMirrorGradientLine = static_cast<double>(aMirrorRect.Bottom());
+
+ const double fStepsMinus1 = static_cast<double>(nSteps) - 1.0;
+ if ( !bLinear)
+ {
+ nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
+ }
+ for ( tools::Long i = 0; i < nSteps; i++ )
+ {
+ // linear interpolation of color
+ double fAlpha = static_cast<double>(i) / fStepsMinus1;
+ double fTempColor = static_cast<double>(nStartRed) * (1.0-fAlpha) + static_cast<double>(nEndRed) * fAlpha;
+ nRed = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
+ fTempColor = static_cast<double>(nStartGreen) * (1.0-fAlpha) + static_cast<double>(nEndGreen) * fAlpha;
+ nGreen = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
+ fTempColor = static_cast<double>(nStartBlue) * (1.0-fAlpha) + static_cast<double>(nEndBlue) * fAlpha;
+ nBlue = GetGradientColorValue(static_cast<tools::Long>(fTempColor));
+
+ rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+
+ // Polygon for this color step
+ aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(i) * fScanInc ) );
+ aRect.SetBottom( static_cast<tools::Long>( fGradientLine + ( static_cast<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 );
+
+ rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
+
+ if ( !bLinear )
+ {
+ aMirrorRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(i) * fScanInc ) );
+ aMirrorRect.SetTop( static_cast<tools::Long>( fMirrorGradientLine - (static_cast<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 );
+
+ rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
+ }
+ }
+ if ( bLinear)
+ return;
+
+ // draw middle polygon with end color
+ nRed = GetGradientColorValue(nEndRed);
+ nGreen = GetGradientColorValue(nEndGreen);
+ nBlue = GetGradientColorValue(nEndBlue);
+
+ rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+
+ aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(nSteps) * fScanInc ) );
+ aRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(nSteps) * fScanInc ) );
+ aPoly[0] = aRect.TopLeft();
+ aPoly[1] = aRect.TopRight();
+ aPoly[2] = aRect.BottomRight();
+ aPoly[3] = aRect.BottomLeft();
+ aPoly.Rotate( aCenter, nAngle );
+
+ rMetaFile.AddAction( new MetaPolygonAction( aPoly ) );
+
+}
+
+void Gradient::DrawComplexGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const
+{
+ // Determine if we output via Polygon or PolyPolygon
+ // For all rasteroperations other than 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.
+
+ tools::Rectangle aRect;
+ Point aCenter;
+ GetBoundRect(rRect, aRect, aCenter);
+
+ std::optional<tools::PolyPolygon> xPolyPoly;
+ xPolyPoly = tools::PolyPolygon( 2 );
+
+ // last parameter - true if complex gradient, false if linear
+ tools::Long nStepCount = GetMetafileSteps(rRect);
+
+ // at least three steps and at most the number of colour differences
+ tools::Long nSteps = std::max(nStepCount, tools::Long(2));
+
+ Color aStartCol(GetStartColor());
+ Color aEndCol(GetEndColor());
+
+ tools::Long nStartRed = (static_cast<tools::Long>(aStartCol.GetRed()) * GetStartIntensity()) / 100;
+ tools::Long nStartGreen = (static_cast<tools::Long>(aStartCol.GetGreen()) * GetStartIntensity()) / 100;
+ tools::Long nStartBlue = (static_cast<tools::Long>(aStartCol.GetBlue()) * GetStartIntensity()) / 100;
+
+ tools::Long nEndRed = (static_cast<tools::Long>(aEndCol.GetRed()) * GetEndIntensity()) / 100;
+ tools::Long nEndGreen = (static_cast<tools::Long>(aEndCol.GetGreen()) * GetEndIntensity()) / 100;
+ tools::Long nEndBlue = (static_cast<tools::Long>(aEndCol.GetBlue()) * GetEndIntensity()) / 100;
+
+ tools::Long nRedSteps = nEndRed - nStartRed;
+ tools::Long nGreenSteps = nEndGreen - nStartGreen;
+ tools::Long nBlueSteps = nEndBlue - nStartBlue;
+
+ tools::Long nCalcSteps = std::abs(nRedSteps);
+ tools::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
+ tools::Polygon aPoly;
+ double fScanLeft = aRect.Left();
+ double fScanTop = aRect.Top();
+ double fScanRight = aRect.Right();
+ double fScanBottom = aRect.Bottom();
+ double fScanIncX = static_cast<double>(aRect.GetWidth()) / static_cast<double>(nSteps) * 0.5;
+ double fScanIncY = static_cast<double>(aRect.GetHeight()) / static_cast<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 (GetStyle() != GradientStyle::Square)
+ {
+ fScanIncY = std::min( fScanIncY, fScanIncX );
+ fScanIncX = fScanIncY;
+ }
+ sal_uInt8 nRed = static_cast<sal_uInt8>(nStartRed), nGreen = static_cast<sal_uInt8>(nStartGreen), nBlue = static_cast<sal_uInt8>(nStartBlue);
+ bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
+
+ rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+
+ aPoly = rRect;
+ xPolyPoly->Insert( aPoly );
+ xPolyPoly->Insert( aPoly );
+
+ // loop to output Polygon/PolyPolygon sequentially
+ for( tools::Long i = 1; i < nSteps; i++ )
+ {
+ // calculate new Polygon
+ fScanLeft += fScanIncX;
+ aRect.SetLeft( static_cast<tools::Long>( fScanLeft ) );
+ fScanTop += fScanIncY;
+ aRect.SetTop( static_cast<tools::Long>( fScanTop ) );
+ fScanRight -= fScanIncX;
+ aRect.SetRight( static_cast<tools::Long>( fScanRight ) );
+ fScanBottom -= fScanIncY;
+ aRect.SetBottom( static_cast<tools::Long>( fScanBottom ) );
+
+ if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
+ break;
+
+ if (GetStyle() == GradientStyle::Radial || GetStyle() == GradientStyle::Elliptical)
+ aPoly = tools::Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
+ else
+ aPoly = tools::Polygon( aRect );
+
+ aPoly.Rotate(aCenter, GetAngle() % 3600_deg10);
+
+ // adapt colour accordingly
+ const tools::Long nStepIndex = ( xPolyPoly ? 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
+
+ xPolyPoly->Replace( xPolyPoly->GetObject( 1 ), 0 );
+ xPolyPoly->Replace( aPoly, 1 );
+
+ rMetaFile.AddAction( new MetaPolyPolygonAction( *xPolyPoly ) );
+
+ // #107349# Set fill color _after_ geometry painting:
+ // xPolyPoly'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.
+ rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+ }
+
+ const tools::Polygon& rPoly = xPolyPoly->GetObject( 1 );
+
+ if( rPoly.GetBoundRect().IsEmpty() )
+ return;
+
+ // #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 );
+ }
+
+ rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
+ rMetaFile.AddAction( new MetaPolygonAction( rPoly ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */