/* -*- 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:
css::awt::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 (css::awt::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( css::awt::GradientStyle eStyle,
const Color& rStartColor, const Color& rEndColor )
{
mpImplGradient->meStyle = eStyle;
mpImplGradient->maStartColor = rStartColor;
mpImplGradient->maEndColor = rEndColor;
}
Gradient::~Gradient() = default;
css::awt::GradientStyle Gradient::GetStyle() const
{
return mpImplGradient->meStyle;
}
void Gradient::SetStyle( css::awt::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() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::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() == css::awt::GradientStyle_SQUARE || GetStyle() == css::awt::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() == css::awt::GradientStyle_RADIAL )
{
// Calculation of radii for circle
aSize.setWidth( static_cast<tools::Long>(0.5 + std::hypot(aSize.Width(), aSize.Height())) );
aSize.setHeight( aSize.Width() );
}
else if( GetStyle() == css::awt::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.Normalize();
// 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() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::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() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::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() == css::awt::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)
{
std::swap( nStartRed, nEndRed );
std::swap( nStartGreen, nEndGreen );
std::swap( nStartBlue, nEndBlue );
}
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( std::move(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() != css::awt::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 = tools::Polygon(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() == css::awt::GradientStyle_RADIAL || GetStyle() == css::awt::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: */
↑ V547 Expression 'xPolyPoly' is always true.