| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * This file is part of the Collabora Office 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: */ |