diff options
Diffstat (limited to '')
-rw-r--r-- | vcl/source/gdi/gradient.cxx | 681 |
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: */ |