diff options
author | Armin Le Grand (allotropia) <armin.le.grand.extern@allotropia.de> | 2023-04-13 12:13:19 +0200 |
---|---|---|
committer | Armin Le Grand <Armin.Le.Grand@me.com> | 2023-04-14 11:56:46 +0200 |
commit | 70684ab604c5fb32a16614c708f986671dd2da3c (patch) | |
tree | 50b68b1256983c0fffa7d5efdd275d09b8bcc8d8 | |
parent | 8b8a2844addbd262befb1a2d193dfb590dfa20be (diff) |
MCGR: Adaptions to oox export
This is a 1st version and might need more fine-tuning,
so it is still 'hidden' behind the MCGR_TEST env var
being set (as the import is, too).
Still, when used, it can now import a MCGR with
transparence and export it again. I will now do extended
testing/experimenting, fine-tuning where needed and
prepare final change/drive forward.
The current state in master should not be changed as
long as the mentioned env var is not set, but this
will need to be checked, too, due to changes to the
export api in oox.
Corrected an error in GetAlphaFromTransparenceGradient
method(s), css::rendering::RGBColor is [0.0 .. 1.0]
Corercted an error in WriteSolidFill where transparence
gradient gets checked. This should really check if
TrGr is used, but only checks for 'empty/unused'
color instead (assuming COL_BLACK is 'unused').
All usages of GetAlphaFromTransparenceGradient should
be checked and adapted.
Change-Id: If59d7a06b9207e2efe9e71f3f8ddeca4476408f3
Change-Id: If97f8abdd0e1597aa1fd865b7e884e06a22b71f5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150391
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
-rw-r--r-- | basegfx/source/tools/gradienttools.cxx | 260 | ||||
-rw-r--r-- | include/basegfx/utils/gradienttools.hxx | 49 | ||||
-rw-r--r-- | include/oox/export/drawingml.hxx | 27 | ||||
-rw-r--r-- | oox/source/drawingml/fillproperties.cxx | 31 | ||||
-rw-r--r-- | oox/source/export/chartexport.cxx | 75 | ||||
-rw-r--r-- | oox/source/export/drawingml.cxx | 361 |
6 files changed, 684 insertions, 119 deletions
diff --git a/basegfx/source/tools/gradienttools.cxx b/basegfx/source/tools/gradienttools.cxx index ca315f33f973..46d8fc7ca216 100644 --- a/basegfx/source/tools/gradienttools.cxx +++ b/basegfx/source/tools/gradienttools.cxx @@ -264,6 +264,237 @@ namespace basegfx namespace utils { + /* Tooling method to extract data from given awt::Gradient2 + to ColorStops, doing some corrections, partitally based + on given SingleColor. + This will do quite some preparations for the gradient + as follows: + - It will check for single color (resetting rSingleColor when + this is the case) and return with empty ColorStops + - It will blend ColorStops to Intensity if StartIntensity/ + EndIntensity != 100 is set in awt::Gradient2, so applying + that value(s) to the gadient directly + - It will adapt to Border if Border != 0 is set at the + given awt::Gradient2, so applying that value to the gadient + directly + */ + void prepareColorStops( + const com::sun::star::awt::Gradient2& rGradient, + ColorStops& rColorStops, + BColor& rSingleColor) + { + fillColorStopsFromGradient2(rColorStops, rGradient); + + if (isSingleColor(rColorStops, rSingleColor)) + { + // when single color, preserve value in rSingleColor + // and clear the ColorStops, done. + rColorStops.clear(); + return; + } + + if (rGradient.StartIntensity != 100 || rGradient.EndIntensity != 100) + { + // apply 'old' blend stuff, blend against black + blendColorStopsToIntensity( + rColorStops, + rGradient.StartIntensity * 0.01, + rGradient.EndIntensity * 0.01, + basegfx::BColor()); // COL_BLACK + + // can lead to single color (e.g. both zero, so all black), + // so check again + if (isSingleColor(rColorStops, rSingleColor)) + { + rColorStops.clear(); + return; + } + } + + if (rGradient.Border != 0) + { + // apply Border if set + // NOTE: no new start node is added. If this is needed, + // do that in the caller + const double fFactor(rGradient.Border * 0.01); + ColorStops aNewStops; + + for (const auto& candidate : rColorStops) + { + aNewStops.emplace_back(candidate.getStopOffset() * fFactor, candidate.getStopColor()); + } + + rColorStops = aNewStops; + } + } + + /* Tooling method to synchronize the given ColorStops. + The intention is that a color GradientStops and an + alpha/transparence GradientStops gets synchronized + for export. + Fo the corrections the single values for color and + alpha may be used, e.g. when ColorStops is given + and not empty, but AlphaStops is empty, it will get + sycronized so that it will have the same number and + offsets in AlphaStops as in ColorStops, but with + the given SingleAlpha as value. + At return it guarantees that both have the same + number of entries with the same StopOffsets, so + that synchonized pair of ColorStops can e.g. be used + to export a Gradient with defined/adapted alpha + being 'coupled' indirectly using the + 'FillTransparenceGradient' method (at import time). + */ + void synchronizeColorStops( + ColorStops& rColorStops, + ColorStops& rAlphaStops, + const BColor& rSingleColor, + const BColor& rSingleAlpha) + { + if (rColorStops.empty()) + { + if (rAlphaStops.empty()) + { + // no AlphaStops and no ColorStops + // create two-stop fallbacks for both + rColorStops = ColorStops { + ColorStop(0.0, rSingleColor), + ColorStop(1.0, rSingleColor) }; + rAlphaStops = ColorStops { + ColorStop(0.0, rSingleAlpha), + ColorStop(1.0, rSingleAlpha) }; + } + else + { + // AlphaStops but no ColorStops + // create fallback synched with existing AlphaStops + for (const auto& cand : rAlphaStops) + { + rColorStops.emplace_back(cand.getStopOffset(), rSingleColor); + } + } + + // preparations complete, we are done + return; + } + else if (rAlphaStops.empty()) + { + // ColorStops but no AlphaStops + // create fallback AlphaStops synched with existing ColorStops using SingleAlpha + for (const auto& cand : rColorStops) + { + rAlphaStops.emplace_back(cand.getStopOffset(), rSingleAlpha); + } + + // preparations complete, we are done + return; + } + + // here we have ColorStops and AlphaStops not empty. Check if we need to + // synchronize both or if they are already usable/in a synched state so + // that they have same count and same StopOffsets + bool bNeedToSyncronize(rColorStops.size() != rAlphaStops.size()); + + if (!bNeedToSyncronize) + { + // check for same StopOffsets + ColorStops::const_iterator aCurrColor(rColorStops.begin()); + ColorStops::const_iterator aCurrAlpha(rAlphaStops.begin()); + + while (!bNeedToSyncronize && + aCurrColor != rColorStops.end() && + aCurrAlpha != rAlphaStops.end()) + { + if (fTools::equal(aCurrColor->getStopOffset(), aCurrAlpha->getStopOffset())) + { + aCurrColor++; + aCurrAlpha++; + } + else + { + bNeedToSyncronize = true; + } + } + } + + if (bNeedToSyncronize) + { + // synchronize sizes & StopOffsets + ColorStops::const_iterator aCurrColor(rColorStops.begin()); + ColorStops::const_iterator aCurrAlpha(rAlphaStops.begin()); + ColorStops aNewColor; + ColorStops aNewAlpha; + ColorStopRange aColorStopRange; + ColorStopRange aAlphaStopRange; + bool bRealChange(false); + + do { + const bool bColor(aCurrColor != rColorStops.end()); + const bool bAlpha(aCurrAlpha != rAlphaStops.end()); + + if (bColor && bAlpha) + { + const double fColorOff(aCurrColor->getStopOffset()); + const double fAlphaOff(aCurrAlpha->getStopOffset()); + + if (fTools::less(fColorOff, fAlphaOff)) + { + // copy color, create alpha + aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor()); + aNewAlpha.emplace_back(fColorOff, utils::modifyBColor(rAlphaStops, fColorOff, 0, aAlphaStopRange)); + bRealChange = true; + aCurrColor++; + } + else if (fTools::more(fColorOff, fAlphaOff)) + { + // copy alpha, create color + aNewColor.emplace_back(fAlphaOff, utils::modifyBColor(rColorStops, fAlphaOff, 0, aColorStopRange)); + aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor()); + bRealChange = true; + aCurrAlpha++; + } + else + { + // equal: copy both, advance + aNewColor.emplace_back(fColorOff, aCurrColor->getStopColor()); + aNewAlpha.emplace_back(fAlphaOff, aCurrAlpha->getStopColor()); + aCurrColor++; + aCurrAlpha++; + } + } + else if (bColor) + { + const double fColorOff(aCurrColor->getStopOffset()); + aNewAlpha.emplace_back(fColorOff, utils::modifyBColor(rAlphaStops, fColorOff, 0, aAlphaStopRange)); + bRealChange = true; + aCurrColor++; + } + else if (bAlpha) + { + const double fAlphaOff(aCurrAlpha->getStopOffset()); + aNewColor.emplace_back(fAlphaOff, utils::modifyBColor(rColorStops, fAlphaOff, 0, aColorStopRange)); + bRealChange = true; + aCurrAlpha++; + } + else + { + // no more input, break do..while loop + break; + } + } + while(true); + + if (bRealChange) + { + // copy on 'real' change, that means data was added. + // This should always be the cease and should have been + // detected as such above, see bNeedToSyncronize + rColorStops = aNewColor; + rAlphaStops = aNewColor; + } + } + } + /* Tooling method to linearly blend the Colors contained in a given ColorStop vector against a given Color using the given intensity values. @@ -345,18 +576,13 @@ namespace basegfx candidate = ColorStop(1.0 - candidate.getStopOffset(), candidate.getStopColor()); } - /* Tooling method to convert UNO API data to ColorStops + /* Tooling method to convert UNO API data to ColorStops. This will try to extract ColorStop data from the given - Any, so if it's of type awt::Gradient2 that data will be - extracted, converted and copied into the given ColorStops. + awt::Gradient2. */ - void fillColorStopsFromAny(ColorStops& rColorStops, const css::uno::Any& rVal) + void fillColorStopsFromGradient2(ColorStops& rColorStops, const com::sun::star::awt::Gradient2& rGradient) { - css::awt::Gradient2 aGradient2; - if (!(rVal >>= aGradient2)) - return; - - const sal_Int32 nLen(aGradient2.ColorStops.getLength()); + const sal_Int32 nLen(rGradient.ColorStops.getLength()); if (0 == nLen) return; @@ -364,7 +590,7 @@ namespace basegfx // we have ColorStops rColorStops.clear(); rColorStops.reserve(nLen); - const css::awt::ColorStop* pSourceColorStop(aGradient2.ColorStops.getConstArray()); + const css::awt::ColorStop* pSourceColorStop(rGradient.ColorStops.getConstArray()); for (sal_Int32 a(0); a < nLen; a++, pSourceColorStop++) { @@ -374,6 +600,20 @@ namespace basegfx } } + /* Tooling method to convert UNO API data to ColorStops. + This will try to extract ColorStop data from the given + Any, so if it's of type awt::Gradient2 that data will be + extracted, converted and copied into the given ColorStops. + */ + void fillColorStopsFromAny(ColorStops& rColorStops, const css::uno::Any& rVal) + { + css::awt::Gradient2 aGradient2; + if (!(rVal >>= aGradient2)) + return; + + fillColorStopsFromGradient2(rColorStops, aGradient2); + } + /* Tooling method to fill a awt::ColorStopSequence with the data from the given ColorStops. This is used in UNO API implementations. diff --git a/include/basegfx/utils/gradienttools.hxx b/include/basegfx/utils/gradienttools.hxx index f438fdf3bd4a..4e78ad2ff960 100644 --- a/include/basegfx/utils/gradienttools.hxx +++ b/include/basegfx/utils/gradienttools.hxx @@ -30,6 +30,7 @@ #include <com/sun/star/awt/ColorStopSequence.hdl> namespace com { namespace sun { namespace star { namespace uno { class Any; } } } } +namespace com { namespace sun { namespace star { namespace awt { struct Gradient2; } } } } namespace basegfx { class B2DRange; } namespace basegfx @@ -213,6 +214,48 @@ namespace basegfx namespace utils { + /* Tooling method to extract data from given awt::Gradient2 + to ColorStops, doing some corrections, partitally based + on given SingleColor. + This will do quite some preparations for the gradient + as follows: + - It will check for single color (resetting rSingleColor when + this is the case) and return with empty ColorStops + - It will blend ColorStops to Intensity if StartIntensity/ + EndIntensity != 100 is set in awt::Gradient2, so applying + that value(s) to the gadient directly + - It will adapt to Border if Border != 0 is set at the + given awt::Gradient2, so applying that value to the gadient + directly + */ + BASEGFX_DLLPUBLIC void prepareColorStops( + const com::sun::star::awt::Gradient2& rGradient, + ColorStops& rColorStops, + BColor& rSingleColor); + + /* Tooling method to synchronize the given ColorStops. + The intention is that a color GradientStops and an + alpha/transparence GradientStops gets synchronized + for export. + Fo the corrections the single values for color and + alpha may be used, e.g. when ColorStops is given + and not empty, but AlphaStops is empty, it will get + sycronized so that it will have the same number and + offsets in AlphaStops as in ColorStops, but with + the given SingleAlpha as value. + At return it guarantees that both have the same + number of entries with the same StopOffsets, so + that synchonized pair of ColorStops can e.g. be used + to export a Gradient with defined/adapted alpha + being 'coupled' indirectly using the + 'FillTransparenceGradient' method (at import time). + */ + BASEGFX_DLLPUBLIC void synchronizeColorStops( + ColorStops& rColorStops, + ColorStops& rAlphaStops, + const BColor& rSingleColor, + const BColor& rSingleAlpha); + /* Tooling method to linearly blend the Colors contained in a given ColorStop vector against a given Color using the given intensity values. @@ -244,6 +287,12 @@ namespace basegfx /* Tooling method to convert UNO API data to ColorStops. This will try to extract ColorStop data from the given + awt::Gradient2. + */ + BASEGFX_DLLPUBLIC void fillColorStopsFromGradient2(ColorStops& rColorStops, const com::sun::star::awt::Gradient2& rGradient); + + /* Tooling method to convert UNO API data to ColorStops. + This will try to extract ColorStop data from the given Any, so if it's of type awt::Gradient2 that data will be extracted, converted and copied into the given ColorStops. */ diff --git a/include/oox/export/drawingml.hxx b/include/oox/export/drawingml.hxx index 9179625bd6b7..bfab16f12aff 100644 --- a/include/oox/export/drawingml.hxx +++ b/include/oox/export/drawingml.hxx @@ -58,7 +58,7 @@ enum class SvxTimeFormat; namespace com::sun::star { namespace awt { struct FontDescriptor; - struct Gradient; + struct Gradient2; } namespace beans { struct PropertyValue; @@ -107,6 +107,11 @@ namespace core { namespace drawingml { +/// Tooling method to fill awt::Gradient2 from data contained in the given Any +bool fillGradient2FromAny( + css::awt::Gradient2& rGradient, + const css::uno::Any& rVal); + class OOX_DLLPUBLIC URLTransformer { public: @@ -198,7 +203,7 @@ protected: const char* GetComponentDir() const; const char* GetRelationCompPrefix() const; - static bool EqualGradients( css::awt::Gradient aGradient1, css::awt::Gradient aGradient2 ); + static bool EqualGradients( const css::awt::Gradient2& rGradient1, const css::awt::Gradient2& rGradient2 ); bool IsFontworkShape(const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet); void WriteGlowEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet); @@ -244,10 +249,20 @@ public: void WriteSolidFill( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ); void WriteGradientFill( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ); - void WriteGradientFill( css::awt::Gradient rGradient, css::awt::Gradient rTransparenceGradient, - const css::uno::Reference<css::beans::XPropertySet>& rXPropSet = css::uno::Reference<css::beans::XPropertySet>()); - - void WriteGrabBagGradientFill( const css::uno::Sequence< css::beans::PropertyValue >& aGradientStops, css::awt::Gradient rGradient); + /* New API for WriteGradientFill: + If a awt::Gradient2 is given, it will be used. Else, the 'Fix' entry will be used for + Color or Transparency. That way, less Pseudo(Color|Transparency)Gradients have to be + created at caller side. + NOTE: Giving no Gradient at all (both nullptr) is an error. + */ + void WriteGradientFill( + const css::awt::Gradient2* pColorGradient, sal_Int32 nFixColor, + const css::awt::Gradient2* pTransparenceGradient, sal_Int32 nFixTransparence); + void WriteGradientFill2( + const css::awt::Gradient2* pColorGradient, sal_Int32 nFixColor, + const css::awt::Gradient2* pTransparenceGradient, sal_Int32 nFixTransparence); + + void WriteGrabBagGradientFill( const css::uno::Sequence< css::beans::PropertyValue >& aGradientStops, const css::awt::Gradient2& rGradient); void WriteBlipOrNormalFill(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet, const OUString& rURLPropName, const css::awt::Size& rSize = {}); diff --git a/oox/source/drawingml/fillproperties.cxx b/oox/source/drawingml/fillproperties.cxx index cc69e04f6359..a9f105e32521 100644 --- a/oox/source/drawingml/fillproperties.cxx +++ b/oox/source/drawingml/fillproperties.cxx @@ -572,6 +572,28 @@ void FillProperties::pushToPropMap(ShapePropertyMap& rPropMap, const GraphicHelp // set ColorStops using UNO API basegfx::utils::fillColorStopSequenceFromColorStops(aGradient.ColorStops, aColorStops); + // for compatibility, still set StartColor/EndColor + // NOTE: All code after adapting to multi color gradients works + // using the ColorSteps, so in principle Start/EndColor might + // be either + // (a) ignored consequently everywhere or + // (b) be set/added consequently everywhere + // since this is - in principle - redundant data. + // Be aware thet e.g. cases like DrawingML::EqualGradients + // and others would have to be identified and adapted (!) + // Since awt::Gradient2 is UNO API data there might + // be cases where just awt::Gradient is transfered, so (b) + // is far better backwards compatible and thus more safe, so + // all changes will make use of additionally using/setting + // these additionally, but will only make use of the given + // ColorSteps if these are not empty, assuming that these + // already contain Start/EndColor. + // In principle that redundancy and that it is conflict-free + // could even be checked and asserted, but consequently using + // (b) methodically should be safe. + aGradient.StartColor = static_cast<sal_Int32>(::Color(aColorStops.front().getStopColor())); + aGradient.EndColor = static_cast<sal_Int32>(::Color(aColorStops.back().getStopColor())); + // push gradient or named gradient to property map if (rPropMap.setProperty(ShapeProperty::FillGradient, aGradient)) { @@ -590,7 +612,7 @@ void FillProperties::pushToPropMap(ShapePropertyMap& rPropMap, const GraphicHelp { sal_Int32 nEndTrans = 0; sal_Int32 nStartTrans = 0; - awt::Gradient aGradient; + awt::Gradient2 aGradient; aGradient.Angle = 900; aGradient.StartIntensity = 100; aGradient.EndIntensity = 100; @@ -886,6 +908,13 @@ void FillProperties::pushToPropMap(ShapePropertyMap& rPropMap, const GraphicHelp aGradient.Border = rtl::math::round(100*nBorder); } + // for temporary backward compatibility, complete data by creating ColorStops for awt::Gradient2 + basegfx::utils::fillColorStopSequenceFromColorStops( + aGradient.ColorStops, + basegfx::ColorStops { + basegfx::ColorStop(0.0, ::Color(ColorTransparency, aGradient.StartColor).getBColor()), + basegfx::ColorStop(1.0, ::Color(ColorTransparency, aGradient.EndColor).getBColor()) }); + // push gradient or named gradient to property map if( rPropMap.setProperty( ShapeProperty::FillGradient, aGradient ) ) eFillStyle = FillStyle_GRADIENT; diff --git a/oox/source/export/chartexport.cxx b/oox/source/export/chartexport.cxx index e4031e6b2b0a..37868d512ce1 100644 --- a/oox/source/export/chartexport.cxx +++ b/oox/source/export/chartexport.cxx @@ -25,11 +25,12 @@ #include <oox/token/relationship.hxx> #include <oox/export/utils.hxx> #include <drawingml/chart/typegroupconverter.hxx> +#include <basegfx/utils/gradienttools.hxx> #include <cstdio> #include <limits> -#include <com/sun/star/awt/Gradient.hpp> +#include <com/sun/star/awt/Gradient2.hpp> #include <com/sun/star/chart/XChartDocument.hpp> #include <com/sun/star/chart/ChartLegendPosition.hpp> #include <com/sun/star/chart/XTwoAxisXSupplier.hpp> @@ -480,10 +481,23 @@ static sal_Int32 lcl_generateRandomValue() return comphelper::rng::uniform_int_distribution(0, 100000000-1); } -static sal_Int32 lcl_getAlphaFromTransparenceGradient(const awt::Gradient& rGradient, bool bStart) +static sal_Int32 lcl_getAlphaFromTransparenceGradient(const awt::Gradient2& rGradient, bool bStart) { // Our alpha is a gray color value. - sal_uInt8 nRed = ::Color(ColorTransparency, bStart ? rGradient.StartColor : rGradient.EndColor).GetRed(); + sal_uInt8 nRed(0); + + if (rGradient.ColorStops.getLength() > 0) + { + if (bStart) + nRed = static_cast<sal_uInt8>(rGradient.ColorStops[0].StopColor.Red * 255.0); + else + nRed = static_cast<sal_uInt8>(rGradient.ColorStops[rGradient.ColorStops.getLength() - 1].StopColor.Red * 255.0); + } + else + { + nRed = ::Color(ColorTransparency, bStart ? rGradient.StartColor : rGradient.EndColor).GetRed(); + } + // drawingML alpha is a percentage on a 0..100000 scale. return (255 - nRed) * oox::drawingml::MAX_PERCENT / 255; } @@ -1910,7 +1924,7 @@ void ChartExport::exportSolidFill(const Reference< XPropertySet >& xPropSet) } // OOXML has no separate transparence gradient but uses transparency in the gradient stops. // So we merge transparency and color and use gradient fill in such case. - awt::Gradient aTransparenceGradient; + awt::Gradient2 aTransparenceGradient; bool bNeedGradientFill(false); OUString sFillTransparenceGradientName; if (GetProperty(xPropSet, "FillTransparenceGradientName") @@ -1920,28 +1934,27 @@ void ChartExport::exportSolidFill(const Reference< XPropertySet >& xPropSet) uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY ); uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY); uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName); - rTransparenceValue >>= aTransparenceGradient; - if (aTransparenceGradient.StartColor != aTransparenceGradient.EndColor) - bNeedGradientFill = true; - else if (aTransparenceGradient.StartColor != 0) + + if (fillGradient2FromAny(aTransparenceGradient, rTransparenceValue)) + { + basegfx::ColorStops aColorStops; + basegfx::utils::fillColorStopsFromAny(aColorStops, rTransparenceValue); + basegfx::BColor aSingleColor; + bNeedGradientFill = !basegfx::utils::isSingleColor(aColorStops, aSingleColor); + } + + if (!bNeedGradientFill && 0 != aTransparenceGradient.StartColor) + { nAlpha = lcl_getAlphaFromTransparenceGradient(aTransparenceGradient, true); + } } // write XML if (bNeedGradientFill) { - awt::Gradient aPseudoColorGradient; - aPseudoColorGradient.XOffset = aTransparenceGradient.XOffset; - aPseudoColorGradient.YOffset = aTransparenceGradient.YOffset; - aPseudoColorGradient.StartIntensity = 100; - aPseudoColorGradient.EndIntensity = 100; - aPseudoColorGradient.Angle = aTransparenceGradient.Angle; - aPseudoColorGradient.Border = aTransparenceGradient.Border; - aPseudoColorGradient.Style = aTransparenceGradient.Style; - aPseudoColorGradient.StartColor = nFillColor; - aPseudoColorGradient.EndColor = nFillColor; - aPseudoColorGradient.StepCount = aTransparenceGradient.StepCount; + // no longer create copy/PseudoColorGradient, use new API of + // WriteGradientFill to express fix fill color mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0"); - WriteGradientFill(aPseudoColorGradient, aTransparenceGradient); + WriteGradientFill(nullptr, nFillColor, &aTransparenceGradient, 0); mpFS->endElementNS(XML_a, XML_gradFill); } else @@ -2009,23 +2022,33 @@ void ChartExport::exportGradientFill( const Reference< XPropertySet >& xPropSet { uno::Reference< container::XNameAccess > xGradient( xFact->createInstance("com.sun.star.drawing.GradientTable"), uno::UNO_QUERY ); uno::Any rGradientValue = xGradient->getByName( sFillGradientName ); - awt::Gradient aGradient; - if( rGradientValue >>= aGradient ) + awt::Gradient2 aGradient; + + if (fillGradient2FromAny(aGradient, rGradientValue)) { - awt::Gradient aTransparenceGradient; + awt::Gradient2 aTransparenceGradient; mpFS->startElementNS(XML_a, XML_gradFill); OUString sFillTransparenceGradientName; if( (xPropSet->getPropertyValue("FillTransparenceGradientName") >>= sFillTransparenceGradientName) && !sFillTransparenceGradientName.isEmpty()) { uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY); uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName); - rTransparenceValue >>= aTransparenceGradient; - WriteGradientFill(aGradient, aTransparenceGradient); + fillGradient2FromAny(aTransparenceGradient, rTransparenceValue); + WriteGradientFill(&aGradient, 0, &aTransparenceGradient, 0); + } + else if (GetProperty(xPropSet, "FillTransparence") ) + { + // no longer create PseudoTransparencyGradient, use new API of + // WriteGradientFill to express fix transparency + sal_Int32 nTransparency = 0; + mAny >>= nTransparency; + WriteGradientFill(&aGradient, 0, nullptr, nTransparency); } else { - WriteGradientFill(aGradient, aTransparenceGradient, xPropSet); + WriteGradientFill(&aGradient, 0, &aTransparenceGradient, 0); } + mpFS->endElementNS(XML_a, XML_gradFill); } } diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index 5560a41bf86a..f2de44be6767 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -42,6 +42,7 @@ #include <i18nlangtag/languagetag.hxx> #include <basegfx/matrix/b2dhommatrixtools.hxx> #include <basegfx/range/b2drange.hxx> +#include <basegfx/utils/gradienttools.hxx> #include <numeric> #include <string_view> @@ -53,6 +54,7 @@ #include <com/sun/star/awt/FontWeight.hpp> #include <com/sun/star/awt/FontUnderline.hpp> #include <com/sun/star/awt/Gradient.hpp> +#include <com/sun/star/awt/Gradient2.hpp> #include <com/sun/star/beans/XPropertySet.hpp> #include <com/sun/star/beans/XPropertyState.hpp> #include <com/sun/star/beans/XPropertySetInfo.hpp> @@ -151,10 +153,23 @@ using ::sax_fastparser::FastSerializerHelper; namespace { /// Extracts start or end alpha information from a transparency gradient. -sal_Int32 GetAlphaFromTransparenceGradient(const awt::Gradient& rGradient, bool bStart) +sal_Int32 GetAlphaFromTransparenceGradient(const awt::Gradient2& rGradient, bool bStart) { // Our alpha is a gray color value. - sal_uInt8 nRed = ::Color(ColorTransparency, bStart ? rGradient.StartColor : rGradient.EndColor).GetRed(); + sal_uInt8 nRed(0); + + if (rGradient.ColorStops.getLength() > 0) + { + if (bStart) + nRed = static_cast<sal_uInt8>(rGradient.ColorStops[0].StopColor.Red * 255.0); + else + nRed = static_cast<sal_uInt8>(rGradient.ColorStops[rGradient.ColorStops.getLength() - 1].StopColor.Red * 255.0); + } + else + { + nRed = ::Color(ColorTransparency, bStart ? rGradient.StartColor : rGradient.EndColor).GetRed(); + } + // drawingML alpha is a percentage on a 0..100000 scale. return (255 - nRed) * oox::drawingml::MAX_PERCENT / 255; } @@ -177,6 +192,48 @@ const char* g_aPredefinedClrNames[] = { namespace oox::drawingml { +/// Tooling method to fill awt::Gradient2 from data contained in the given Any +bool fillGradient2FromAny(css::awt::Gradient2& rGradient, const css::uno::Any& rVal) +{ + bool bRetval(false); + + if (rVal.has< css::awt::Gradient2 >()) + { + // we can use awt::Gradient2 directly + bRetval = (rVal >>= rGradient); + } + else if (rVal.has< css::awt::Gradient >()) + { + // 1st get awt::Gradient + css::awt::Gradient aTmp; + + if (rVal >>= aTmp) + { + // copy all awt::Gradient data to awt::Gradient2 + rGradient.Style = aTmp.Style; + rGradient.StartColor = aTmp.StartColor; + rGradient.EndColor = aTmp.EndColor; + rGradient.Angle = aTmp.Angle; + rGradient.Border = aTmp.Border; + rGradient.XOffset = aTmp.XOffset; + rGradient.YOffset = aTmp.YOffset; + rGradient.StartIntensity = aTmp.StartIntensity; + rGradient.EndIntensity = aTmp.EndIntensity; + rGradient.StepCount = aTmp.StepCount; + + // complete data by creating ColorStops for awt::Gradient2 + basegfx::utils::fillColorStopSequenceFromColorStops( + rGradient.ColorStops, + basegfx::ColorStops { + basegfx::ColorStop(0.0, ::Color(ColorTransparency, aTmp.StartColor).getBColor()), + basegfx::ColorStop(1.0, ::Color(ColorTransparency, aTmp.EndColor).getBColor()) }); + bRetval = true; + } + } + + return bRetval; +} + URLTransformer::~URLTransformer() { } @@ -213,7 +270,7 @@ static css::uno::Any getLineDash( const css::uno::Reference<css::frame::XModel>& namespace { -void WriteGradientPath(const awt::Gradient& rGradient, const FSHelperPtr& pFS, const bool bCircle) +void WriteGradientPath(const awt::Gradient2& rGradient, const FSHelperPtr& pFS, const bool bCircle) { pFS->startElementNS(XML_a, XML_path, XML_path, bCircle ? "circle" : "rect"); @@ -495,33 +552,32 @@ void DrawingML::WriteSolidFill( const Reference< XPropertySet >& rXPropSet ) // OOXML has no separate transparence gradient but uses transparency in the gradient stops. // So we merge transparency and color and use gradient fill in such case. - awt::Gradient aTransparenceGradient; + awt::Gradient2 aTransparenceGradient; bool bNeedGradientFill(false); + if (GetProperty(rXPropSet, "FillTransparenceGradient")) { - mAny >>= aTransparenceGradient; - if (aTransparenceGradient.StartColor != aTransparenceGradient.EndColor) - bNeedGradientFill = true; - else if (aTransparenceGradient.StartColor != 0) + if (fillGradient2FromAny(aTransparenceGradient, mAny)) + { + basegfx::ColorStops aColorStops; + basegfx::utils::fillColorStopsFromAny(aColorStops, mAny); + basegfx::BColor aSingleColor; + bNeedGradientFill = !basegfx::utils::isSingleColor(aColorStops, aSingleColor); + } + + if (!bNeedGradientFill && 0 != aTransparenceGradient.StartColor) + { nAlpha = GetAlphaFromTransparenceGradient(aTransparenceGradient, true); + } } // write XML if (bNeedGradientFill) { - awt::Gradient aPseudoColorGradient; - aPseudoColorGradient.XOffset = aTransparenceGradient.XOffset; - aPseudoColorGradient.YOffset = aTransparenceGradient.YOffset; - aPseudoColorGradient.StartIntensity = 100; - aPseudoColorGradient.EndIntensity = 100; - aPseudoColorGradient.Angle = aTransparenceGradient.Angle; - aPseudoColorGradient.Border = aTransparenceGradient.Border; - aPseudoColorGradient.Style = aTransparenceGradient.Style; - aPseudoColorGradient.StartColor = nFillColor; - aPseudoColorGradient.EndColor = nFillColor; - aPseudoColorGradient.StepCount = aTransparenceGradient.StepCount; + // no longer create copy/PseudoColorGradient, use new API of + // WriteGradientFill to express fix fill color mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0"); - WriteGradientFill(aPseudoColorGradient, aTransparenceGradient); + WriteGradientFill(nullptr, nFillColor, &aTransparenceGradient, 0); mpFS->endElementNS( XML_a, XML_gradFill ); } else if ( nFillColor != nOriginalColor ) @@ -615,30 +671,57 @@ void DrawingML::WriteGradientStop(sal_uInt16 nStop, ::Color nColor, sal_Int32 nA | ( ( ( ( ( nColor & 0xff0000 ) >> 8 ) * nIntensity ) / 100 ) << 8 )); } -bool DrawingML::EqualGradients( awt::Gradient aGradient1, awt::Gradient aGradient2 ) +bool DrawingML::EqualGradients( const awt::Gradient2& rGradient1, const awt::Gradient2& rGradient2 ) { - return aGradient1.Style == aGradient2.Style && - aGradient1.StartColor == aGradient2.StartColor && - aGradient1.EndColor == aGradient2.EndColor && - aGradient1.Angle == aGradient2.Angle && - aGradient1.Border == aGradient2.Border && - aGradient1.XOffset == aGradient2.XOffset && - aGradient1.YOffset == aGradient2.YOffset && - aGradient1.StartIntensity == aGradient2.StartIntensity && - aGradient1.EndIntensity == aGradient2.EndIntensity && - aGradient1.StepCount == aGradient2.StepCount; + if (rGradient1.Style == rGradient2.Style && + rGradient1.StartColor == rGradient2.StartColor && + rGradient1.EndColor == rGradient2.EndColor && + rGradient1.Angle == rGradient2.Angle && + rGradient1.Border == rGradient2.Border && + rGradient1.XOffset == rGradient2.XOffset && + rGradient1.YOffset == rGradient2.YOffset && + rGradient1.StartIntensity == rGradient2.StartIntensity && + rGradient1.EndIntensity == rGradient2.EndIntensity && + rGradient1.StepCount == rGradient2.StepCount && + rGradient1.ColorStops.getLength() == rGradient2.ColorStops.getLength()) + { + const sal_Int32 nLen(rGradient1.ColorStops.getLength()); + + if (0 == nLen) + return true; + + const awt::ColorStop* pColorStop1(rGradient1.ColorStops.getConstArray()); + const awt::ColorStop* pColorStop2(rGradient2.ColorStops.getConstArray()); + + for (sal_Int32 a(0); a < nLen; a++, pColorStop1++, pColorStop2++) + { + if (pColorStop1->StopOffset != pColorStop2->StopOffset || + pColorStop1->StopColor.Red != pColorStop2->StopColor.Red || + pColorStop1->StopColor.Green != pColorStop2->StopColor.Green || + pColorStop1->StopColor.Blue != pColorStop2->StopColor.Blue) + { + return false; + } + } + + return true; + } + + return false; } void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet ) { - awt::Gradient aGradient; + awt::Gradient2 aGradient; + if (!GetProperty(rXPropSet, "FillGradient")) return; - aGradient = *o3tl::doAccess<awt::Gradient>(mAny); + // use fillGradient2FromAny to evtl. take care of Gradient/Gradient2 + fillGradient2FromAny(aGradient, mAny); // get InteropGrabBag and search the relevant attributes - awt::Gradient aOriginalGradient; + awt::Gradient2 aOriginalGradient; Sequence< PropertyValue > aGradientStops; if ( GetProperty( rXPropSet, "InteropGrabBag" ) ) { @@ -648,7 +731,8 @@ void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet ) if( rProp.Name == "GradFillDefinition" ) rProp.Value >>= aGradientStops; else if( rProp.Name == "OriginalGradFill" ) - rProp.Value >>= aOriginalGradient; + // use fillGradient2FromAny to evtl. take care of Gradient/Gradient2 + fillGradient2FromAny(aOriginalGradient, rProp.Value); } // check if an ooxml gradient had been imported and if the user has modified it @@ -666,33 +750,36 @@ void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet ) } else { - awt::Gradient aTransparenceGradient; mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0"); + + awt::Gradient2 aTransparenceGradient; + awt::Gradient2* pTransparenceGradient(nullptr); + sal_Int32 nTransparency(0); OUString sFillTransparenceGradientName; + if (GetProperty(rXPropSet, "FillTransparenceGradientName") && (mAny >>= sFillTransparenceGradientName) && !sFillTransparenceGradientName.isEmpty()) { if (GetProperty(rXPropSet, "FillTransparenceGradient")) - aTransparenceGradient = *o3tl::doAccess<awt::Gradient>(mAny); + aTransparenceGradient = *o3tl::doAccess<awt::Gradient2>(mAny); + + pTransparenceGradient = &aTransparenceGradient; } else if (GetProperty(rXPropSet, "FillTransparence")) { - // currently only StartColor and EndColor are evaluated in WriteGradientFill() - sal_Int32 nTransparency = 0; + // no longer create PseudoTransparencyGradient, use new API of + // WriteGradientFill to express fix transparency mAny >>= nTransparency; - // convert percent to gray color - nTransparency = nTransparency * 255/100; - const sal_Int32 aGrayColor = static_cast<sal_Int32>( nTransparency | nTransparency << 8 | nTransparency << 16 ); - aTransparenceGradient.StartColor = aGrayColor; - aTransparenceGradient.EndColor = aGrayColor; } - WriteGradientFill(aGradient, aTransparenceGradient); + + WriteGradientFill(&aGradient, 0, pTransparenceGradient, nTransparency); + mpFS->endElementNS(XML_a, XML_gradFill); } } -void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGradientStops, awt::Gradient rGradient ) +void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGradientStops, const awt::Gradient2& rGradient ) { // write back the original gradient mpFS->startElementNS(XML_a, XML_gsLst); @@ -751,64 +838,186 @@ void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGrad } } -void DrawingML::WriteGradientFill(awt::Gradient rGradient, awt::Gradient rTransparenceGradient, - const uno::Reference<beans::XPropertySet>& rXPropSet) +void DrawingML::WriteGradientFill2( + const awt::Gradient2* pColorGradient, sal_Int32 nFixColor, + const awt::Gradient2* pTransparenceGradient, sal_Int32 nFixTransparence) { - sal_Int32 nStartAlpha; - sal_Int32 nEndAlpha; - if( rXPropSet.is() && GetProperty(rXPropSet, "FillTransparence") ) + basegfx::ColorStops aColorStops; + basegfx::ColorStops aAlphaStops; + basegfx::BColor aSingleColor(::Color(ColorTransparency, nFixColor).getBColor()); + basegfx::BColor aSingleAlpha(::Color(ColorTransparency, nFixTransparence).getBColor().luminance()); + awt::Gradient2 aGradient; + + if (nullptr != pColorGradient) { - sal_Int32 nTransparency = 0; - mAny >>= nTransparency; - nStartAlpha = nEndAlpha = (MAX_PERCENT - (PER_PERCENT * nTransparency)); + // remember basic Gradient definition to use + aGradient = *pColorGradient; + + // extract and corrrect/process ColorStops + basegfx::utils::prepareColorStops(*pColorGradient, aColorStops, aSingleColor); + } + + if (nullptr != pTransparenceGradient) + { + // remember basic Gradient definition to use + if (nullptr == pColorGradient) + { + aGradient = *pTransparenceGradient; + } + + // extract and corrrect/process AlphaStops + basegfx::utils::prepareColorStops(*pTransparenceGradient, aAlphaStops, aSingleAlpha); + } + + // synchronize ColorStops and AlphaStops as peparation to export + // so also gradients 'coupled' indirectly using the 'FillTransparenceGradient' + // method (at import time) will be exported again + basegfx::utils::synchronizeColorStops(aColorStops, aAlphaStops, aSingleColor, aSingleAlpha); + + if (aColorStops.size() == aAlphaStops.size()) + { + // export GradientStops (with alpha) + mpFS->startElementNS(XML_a, XML_gsLst); + + basegfx::ColorStops::const_iterator aCurrColor(aColorStops.begin()); + basegfx::ColorStops::const_iterator aCurrAlpha(aAlphaStops.begin()); + + while (aCurrColor != aColorStops.end() && aCurrAlpha != aAlphaStops.end()) + { + WriteGradientStop( + static_cast<sal_uInt16>(aCurrColor->getStopOffset() * 100.0), + ::Color(aCurrColor->getStopColor()), + sal_Int32(::Color(aCurrAlpha->getStopColor()))); + aCurrColor++; + aCurrAlpha++; + } + + mpFS->endElementNS( XML_a, XML_gsLst ); } else { - nStartAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, true); - nEndAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, false); + // this is an error - synchronizeColorStops above *has* to create that + // state, see desciption there (!) + assert(false && "oox::WriteGradientFill: non-synchronized gradients (!)"); } - switch( rGradient.Style ) + + if (awt::GradientStyle_LINEAR == aGradient.Style || + awt::GradientStyle_AXIAL == aGradient.Style) + { + // cases where gradient rotation has to be exported + mpFS->singleElementNS( + XML_a, XML_lin, XML_ang, + OString::number(((3600 - aGradient.Angle + 900) * 6000) % 21600000)); + } + + if (awt::GradientStyle_RADIAL == aGradient.Style || + awt::GradientStyle_ELLIPTICAL == aGradient.Style || + awt::GradientStyle_RECT == aGradient.Style || + awt::GradientStyle_SQUARE == aGradient.Style) + { + // cases where gradient path has to be exported + const bool bCircle(aGradient.Style == awt::GradientStyle_RADIAL || + aGradient.Style == awt::GradientStyle_ELLIPTICAL); + + WriteGradientPath(aGradient, mpFS, bCircle); + } +} + +void DrawingML::WriteGradientFill( + const awt::Gradient2* pColorGradient, sal_Int32 nFixColor, + const awt::Gradient2* pTransparenceGradient, sal_Int32 nFixTransparence) +{ + static bool bMCGR(nullptr != std::getenv("MCGR_TEST")); + + if (bMCGR) + { + WriteGradientFill2(pColorGradient, nFixColor, pTransparenceGradient, nFixTransparence); + return; + } + + // evtl need a temporary pseudo-color gradient + awt::Gradient2 aColorGradient; + + if (nullptr == pColorGradient && nullptr == pTransparenceGradient) + { + // this is an error: *one* gradient has to be given + return; + } + + if (nullptr == pColorGradient) + { + // create complete tempoay copy to keep orig export working + aColorGradient = *pTransparenceGradient; + + // change parameters specific for PseudoColorGradient + aColorGradient.StartIntensity = 100; + aColorGradient.EndIntensity = 100; + aColorGradient.StartColor = nFixColor; + aColorGradient.EndColor = nFixColor; + + basegfx::utils::fillColorStopSequenceFromColorStops( + aColorGradient.ColorStops, + basegfx::ColorStops { + basegfx::ColorStop(0.0, ::Color(ColorTransparency, nFixColor).getBColor()) }); + + pColorGradient = &aColorGradient; + } + + sal_Int32 nStartAlpha(MAX_PERCENT); + sal_Int32 nEndAlpha(MAX_PERCENT); + + if (nullptr == pTransparenceGradient) + { + nStartAlpha = nEndAlpha = (MAX_PERCENT - (PER_PERCENT * nFixTransparence)); + } + else + { + nStartAlpha = GetAlphaFromTransparenceGradient(*pTransparenceGradient, true); + nEndAlpha = GetAlphaFromTransparenceGradient(*pTransparenceGradient, false); + } + + switch( pColorGradient->Style ) { default: case awt::GradientStyle_LINEAR: { mpFS->startElementNS(XML_a, XML_gsLst); - WriteGradientStop(rGradient.Border, ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity), + WriteGradientStop(pColorGradient->Border, ColorWithIntensity(pColorGradient->StartColor, pColorGradient->StartIntensity), nStartAlpha); - WriteGradientStop(100, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), + WriteGradientStop(100, ColorWithIntensity(pColorGradient->EndColor, pColorGradient->EndIntensity), nEndAlpha); mpFS->endElementNS( XML_a, XML_gsLst ); mpFS->singleElementNS( XML_a, XML_lin, XML_ang, - OString::number(((3600 - rGradient.Angle + 900) * 6000) % 21600000)); + OString::number(((3600 - pColorGradient->Angle + 900) * 6000) % 21600000)); break; } case awt::GradientStyle_AXIAL: { mpFS->startElementNS(XML_a, XML_gsLst); - WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), + WriteGradientStop(0, ColorWithIntensity(pColorGradient->EndColor, pColorGradient->EndIntensity), nEndAlpha); - if (rGradient.Border > 0 && rGradient.Border < 100) + if (pColorGradient->Border > 0 && pColorGradient->Border < 100) { - WriteGradientStop(rGradient.Border/2, - ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), + WriteGradientStop(pColorGradient->Border/2, + ColorWithIntensity(pColorGradient->EndColor, pColorGradient->EndIntensity), nEndAlpha); } - WriteGradientStop(50, ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity), + WriteGradientStop(50, ColorWithIntensity(pColorGradient->StartColor, pColorGradient->StartIntensity), nStartAlpha); - if (rGradient.Border > 0 && rGradient.Border < 100) + if (pColorGradient->Border > 0 && pColorGradient->Border < 100) { - WriteGradientStop(100 - rGradient.Border/2, - ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), + WriteGradientStop(100 - pColorGradient->Border/2, + ColorWithIntensity(pColorGradient->EndColor, pColorGradient->EndIntensity), nEndAlpha); } - WriteGradientStop(100, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), + WriteGradientStop(100, ColorWithIntensity(pColorGradient->EndColor, pColorGradient->EndIntensity), nEndAlpha); mpFS->endElementNS(XML_a, XML_gsLst); mpFS->singleElementNS( XML_a, XML_lin, XML_ang, - OString::number(((3600 - rGradient.Angle + 900) * 6000) % 21600000)); + OString::number(((3600 - pColorGradient->Angle + 900) * 6000) % 21600000)); break; } @@ -818,22 +1027,22 @@ void DrawingML::WriteGradientFill(awt::Gradient rGradient, awt::Gradient rTransp case awt::GradientStyle_SQUARE: { mpFS->startElementNS(XML_a, XML_gsLst); - WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity), + WriteGradientStop(0, ColorWithIntensity(pColorGradient->EndColor, pColorGradient->EndIntensity), nEndAlpha); - if (rGradient.Border > 0 && rGradient.Border < 100) + if (pColorGradient->Border > 0 && pColorGradient->Border < 100) { // Map border to an additional gradient stop, which has the // same color as the final stop. - WriteGradientStop(100 - rGradient.Border, - ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity), + WriteGradientStop(100 - pColorGradient->Border, + ColorWithIntensity(pColorGradient->StartColor, pColorGradient->StartIntensity), nStartAlpha); } WriteGradientStop(100, - ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity), + ColorWithIntensity(pColorGradient->StartColor, pColorGradient->StartIntensity), nStartAlpha); mpFS->endElementNS(XML_a, XML_gsLst); - WriteGradientPath(rGradient, mpFS, rGradient.Style == awt::GradientStyle_RADIAL || rGradient.Style == awt::GradientStyle_ELLIPTICAL); + WriteGradientPath(*pColorGradient, mpFS, pColorGradient->Style == awt::GradientStyle_RADIAL || pColorGradient->Style == awt::GradientStyle_ELLIPTICAL); break; } } |